Merge changes I392933f8,I5e4da0c3,I2134b15e into main am: f0ad731bb9

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2657259

Change-Id: Ia0cc734587bc6a69cc7f132b5ba846b9ab81d2db
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Mikhail Naganov
2023-07-13 20:31:31 +00:00
committed by Automerger Merge Worker
34 changed files with 1451 additions and 793 deletions

View File

@@ -73,18 +73,23 @@ cc_library {
"Configuration.cpp",
"EngineConfigXmlConverter.cpp",
"Module.cpp",
"ModulePrimary.cpp",
"SoundDose.cpp",
"Stream.cpp",
"StreamStub.cpp",
"Telephony.cpp",
"alsa/Mixer.cpp",
"alsa/ModuleAlsa.cpp",
"alsa/StreamAlsa.cpp",
"alsa/Utils.cpp",
"r_submix/ModuleRemoteSubmix.cpp",
"r_submix/RemoteSubmixUtils.cpp",
"r_submix/SubmixRoute.cpp",
"r_submix/StreamRemoteSubmix.cpp",
"stub/ModuleStub.cpp",
"stub/StreamStub.cpp",
"usb/ModuleUsb.cpp",
"usb/StreamUsb.cpp",
"usb/UsbAlsaMixerControl.cpp",
"usb/UsbAlsaUtils.cpp",
],
generated_sources: [
"audio_policy_configuration_aidl_default",

View File

@@ -144,10 +144,6 @@ static AudioRoute createRoute(const std::vector<AudioPort>& sources, const Audio
// - no profiles specified
// * "FM Tuner", IN_FM_TUNER
// - no profiles specified
// * "USB Out", OUT_DEVICE, CONNECTION_USB
// - no profiles specified
// * "USB In", IN_DEVICE, CONNECTION_USB
// - no profiles specified
//
// Mix ports:
// * "primary output", PRIMARY, 1 max open, 1 max active stream
@@ -172,8 +168,7 @@ static AudioRoute createRoute(const std::vector<AudioPort>& sources, const Audio
//
// Routes:
// "primary out", "compressed offload" -> "Speaker"
// "primary out", "compressed offload" -> "USB Out"
// "Built-in Mic", "USB In" -> "primary input"
// "Built-in Mic" -> "primary input"
// "telephony_tx" -> "Telephony Tx"
// "Telephony Rx" -> "telephony_rx"
// "FM Tuner" -> "fm_tuner"
@@ -185,14 +180,6 @@ static AudioRoute createRoute(const std::vector<AudioPort>& sources, const Audio
// * "Telephony Rx" device port: PCM 24-bit; MONO; 48000
// * "FM Tuner" device port: PCM 24-bit; STEREO; 48000
//
// Profiles for device port connected state:
// * USB Out":
// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
// * USB In":
// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
//
std::unique_ptr<Configuration> getPrimaryConfiguration() {
static const Configuration configuration = []() {
const std::vector<AudioProfile> standardPcmAudioProfiles = {
@@ -252,19 +239,6 @@ std::unique_ptr<Configuration> getPrimaryConfiguration() {
AudioChannelLayout::LAYOUT_STEREO, 48000, 0, true,
createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0)));
AudioPort usbOutDevice =
createPort(c.nextPortId++, "USB Out", 0, false,
createDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
AudioDeviceDescription::CONNECTION_USB));
c.ports.push_back(usbOutDevice);
c.connectedProfiles[usbOutDevice.id] = standardPcmAudioProfiles;
AudioPort usbInDevice = createPort(c.nextPortId++, "USB In", 0, true,
createDeviceExt(AudioDeviceType::IN_DEVICE, 0,
AudioDeviceDescription::CONNECTION_USB));
c.ports.push_back(usbInDevice);
c.connectedProfiles[usbInDevice.id] = standardPcmAudioProfiles;
// Mix ports
AudioPort primaryOutMix = createPort(c.nextPortId++, "primary output",
@@ -323,8 +297,7 @@ std::unique_ptr<Configuration> getPrimaryConfiguration() {
c.ports.push_back(fmTunerInMix);
c.routes.push_back(createRoute({primaryOutMix, compressedOffloadOutMix}, speakerOutDevice));
c.routes.push_back(createRoute({primaryOutMix, compressedOffloadOutMix}, usbOutDevice));
c.routes.push_back(createRoute({micInDevice, usbInDevice}, primaryInMix));
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({fmTunerInDevice}, fmTunerInMix));
@@ -406,22 +379,31 @@ std::unique_ptr<Configuration> getRSubmixConfiguration() {
// Usb configuration:
//
// Device ports:
// * "USB Device Out", OUT_DEVICE, CONNECTION_USB
// - no profiles specified
// * "USB Headset Out", OUT_HEADSET, CONNECTION_USB
// - no profiles specified
// * "USB Device In", IN_DEVICE, CONNECTION_USB
// - no profiles specified
// * "USB Headset In", IN_HEADSET, CONNECTION_USB
// - no profiles specified
//
// Mix ports:
// * "usb_headset output", 1 max open, 1 max active stream
// * "usb_device output", 1 max open, 1 max active stream
// - no profiles specified
// * "usb_headset input", 1 max open, 1 max active stream
// * "usb_device input", 1 max open, 1 max active stream
// - no profiles specified
//
// Routes:
// * "usb_device output" -> "USB Device Out"
// * "usb_device output" -> "USB Headset Out"
// * "USB Device In", "USB Headset In" -> "usb_device input"
//
// Profiles for device port connected state:
// * USB Headset Out":
// * "USB Device Out", "USB Headset Out":
// - profile PCM 16-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
// - profile PCM 24-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
// * USB Headset In":
// * "USB Device In", "USB Headset In":
// - profile PCM 16-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
// - profile PCM 24-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
//
@@ -440,6 +422,13 @@ std::unique_ptr<Configuration> getUsbConfiguration() {
// Device ports
AudioPort usbOutDevice =
createPort(c.nextPortId++, "USB Device Out", 0, false,
createDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
AudioDeviceDescription::CONNECTION_USB));
c.ports.push_back(usbOutDevice);
c.connectedProfiles[usbOutDevice.id] = standardPcmAudioProfiles;
AudioPort usbOutHeadset =
createPort(c.nextPortId++, "USB Headset Out", 0, false,
createDeviceExt(AudioDeviceType::OUT_HEADSET, 0,
@@ -447,6 +436,12 @@ std::unique_ptr<Configuration> getUsbConfiguration() {
c.ports.push_back(usbOutHeadset);
c.connectedProfiles[usbOutHeadset.id] = standardPcmAudioProfiles;
AudioPort usbInDevice = createPort(c.nextPortId++, "USB Device In", 0, true,
createDeviceExt(AudioDeviceType::IN_DEVICE, 0,
AudioDeviceDescription::CONNECTION_USB));
c.ports.push_back(usbInDevice);
c.connectedProfiles[usbInDevice.id] = standardPcmAudioProfiles;
AudioPort usbInHeadset =
createPort(c.nextPortId++, "USB Headset In", 0, true,
createDeviceExt(AudioDeviceType::IN_HEADSET, 0,
@@ -456,16 +451,110 @@ std::unique_ptr<Configuration> getUsbConfiguration() {
// Mix ports
AudioPort usbHeadsetOutMix =
createPort(c.nextPortId++, "usb_headset output", 0, false, createPortMixExt(1, 1));
c.ports.push_back(usbHeadsetOutMix);
AudioPort usbDeviceOutMix =
createPort(c.nextPortId++, "usb_device output", 0, false, createPortMixExt(1, 1));
c.ports.push_back(usbDeviceOutMix);
AudioPort usbHeadsetInMix =
createPort(c.nextPortId++, "usb_headset input", 0, true, createPortMixExt(1, 1));
c.ports.push_back(usbHeadsetInMix);
AudioPort usbDeviceInMix =
createPort(c.nextPortId++, "usb_device input", 0, true, createPortMixExt(1, 1));
c.ports.push_back(usbDeviceInMix);
c.routes.push_back(createRoute({usbHeadsetOutMix}, usbOutHeadset));
c.routes.push_back(createRoute({usbInHeadset}, usbHeadsetInMix));
c.routes.push_back(createRoute({usbDeviceOutMix}, usbOutDevice));
c.routes.push_back(createRoute({usbDeviceOutMix}, usbOutHeadset));
c.routes.push_back(createRoute({usbInDevice, usbInHeadset}, usbDeviceInMix));
return c;
}();
return std::make_unique<Configuration>(configuration);
}
// Stub configuration:
//
// Device ports:
// * "Test Out", OUT_AFE_PROXY
// - no profiles specified
// * "Test In", IN_AFE_PROXY
// - no profiles specified
//
// Mix ports:
// * "test output", 1 max open, 1 max active stream
// - 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
// * "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
//
// Routes:
// "test output", "compressed offload" -> "Test Out"
// "Test In" -> "test input"
//
// Initial port configs:
// * "Test Out" device port: PCM 24-bit; STEREO; 48000
// * "Test In" device port: PCM 24-bit; MONO; 48000
//
std::unique_ptr<Configuration> getStubConfiguration() {
static const Configuration configuration = []() {
Configuration c;
// Device ports
AudioPort testOutDevice = createPort(c.nextPortId++, "Test Out", 0, false,
createDeviceExt(AudioDeviceType::OUT_AFE_PROXY, 0));
c.ports.push_back(testOutDevice);
c.initialConfigs.push_back(
createPortConfig(testOutDevice.id, testOutDevice.id, PcmType::INT_24_BIT,
AudioChannelLayout::LAYOUT_STEREO, 48000, 0, false,
createDeviceExt(AudioDeviceType::OUT_AFE_PROXY, 0)));
AudioPort testInDevice = createPort(c.nextPortId++, "Test In", 0, true,
createDeviceExt(AudioDeviceType::IN_AFE_PROXY, 0));
c.ports.push_back(testInDevice);
c.initialConfigs.push_back(
createPortConfig(testInDevice.id, testInDevice.id, PcmType::INT_24_BIT,
AudioChannelLayout::LAYOUT_MONO, 48000, 0, true,
createDeviceExt(AudioDeviceType::IN_AFE_PROXY, 0)));
// Mix ports
AudioPort testOutMix =
createPort(c.nextPortId++, "test output", 0, false, createPortMixExt(1, 1));
testOutMix.profiles.push_back(
createProfile(PcmType::INT_24_BIT,
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
{8000, 11025, 16000, 32000, 44100, 48000}));
c.ports.push_back(testOutMix);
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 testInMIx =
createPort(c.nextPortId++, "test input", 0, true, createPortMixExt(2, 2));
testInMIx.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}));
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}));
c.ports.push_back(testInMIx);
c.routes.push_back(createRoute({testOutMix, compressedOffloadOutMix}, testOutDevice));
c.routes.push_back(createRoute({testInDevice}, testInMIx));
c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end());
return c;
}();

View File

@@ -25,13 +25,12 @@
#include <android/binder_ibinder_platform.h>
#include <error/expected_utils.h>
#include "core-impl/Bluetooth.h"
#include "core-impl/Module.h"
#include "core-impl/ModulePrimary.h"
#include "core-impl/ModuleRemoteSubmix.h"
#include "core-impl/ModuleStub.h"
#include "core-impl/ModuleUsb.h"
#include "core-impl/SoundDose.h"
#include "core-impl/StreamStub.h"
#include "core-impl/Telephony.h"
#include "core-impl/utils.h"
using aidl::android::hardware::audio::common::getFrameSizeInBytes;
@@ -110,13 +109,14 @@ bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& forma
// static
std::shared_ptr<Module> Module::createInstance(Type type) {
switch (type) {
case Module::Type::USB:
return ndk::SharedRefBase::make<ModuleUsb>(type);
case Type::R_SUBMIX:
return ndk::SharedRefBase::make<ModuleRemoteSubmix>(type);
case Type::DEFAULT:
default:
return ndk::SharedRefBase::make<Module>(type);
return ndk::SharedRefBase::make<ModulePrimary>();
case Type::R_SUBMIX:
return ndk::SharedRefBase::make<ModuleRemoteSubmix>();
case Type::STUB:
return ndk::SharedRefBase::make<ModuleStub>();
case Type::USB:
return ndk::SharedRefBase::make<ModuleUsb>();
}
}
@@ -128,6 +128,9 @@ std::ostream& operator<<(std::ostream& os, Module::Type t) {
case Module::Type::R_SUBMIX:
os << "r_submix";
break;
case Module::Type::STUB:
os << "stub";
break;
case Module::Type::USB:
os << "usb";
break;
@@ -292,6 +295,9 @@ internal::Configuration& Module::getConfig() {
case Type::R_SUBMIX:
mConfig = std::move(internal::getRSubmixConfiguration());
break;
case Type::STUB:
mConfig = std::move(internal::getStubConfiguration());
break;
case Type::USB:
mConfig = std::move(internal::getUsbConfiguration());
break;
@@ -395,38 +401,26 @@ ndk::ScopedAStatus Module::setModuleDebug(
}
ndk::ScopedAStatus Module::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
if (!mTelephony) {
mTelephony = ndk::SharedRefBase::make<Telephony>();
}
*_aidl_return = mTelephony.getPtr();
LOG(DEBUG) << __func__ << ": returning instance of ITelephony: " << _aidl_return->get();
*_aidl_return = nullptr;
LOG(DEBUG) << __func__ << ": returning null";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Module::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
if (!mBluetooth) {
mBluetooth = ndk::SharedRefBase::make<Bluetooth>();
}
*_aidl_return = mBluetooth.getPtr();
LOG(DEBUG) << __func__ << ": returning instance of IBluetooth: " << _aidl_return->get();
*_aidl_return = nullptr;
LOG(DEBUG) << __func__ << ": returning null";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Module::getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
if (!mBluetoothA2dp) {
mBluetoothA2dp = ndk::SharedRefBase::make<BluetoothA2dp>();
}
*_aidl_return = mBluetoothA2dp.getPtr();
LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
*_aidl_return = nullptr;
LOG(DEBUG) << __func__ << ": returning null";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Module::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
if (!mBluetoothLe) {
mBluetoothLe = ndk::SharedRefBase::make<BluetoothLe>();
}
*_aidl_return = mBluetoothLe.getPtr();
LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
*_aidl_return = nullptr;
LOG(DEBUG) << __func__ << ": returning null";
return ndk::ScopedAStatus::ok();
}
@@ -1334,22 +1328,6 @@ bool Module::isMmapSupported() {
return mIsMmapSupported.value();
}
ndk::ScopedAStatus Module::createInputStream(const SinkMetadata& sinkMetadata,
StreamContext&& context,
const std::vector<MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) {
return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
microphones);
}
ndk::ScopedAStatus Module::createOutputStream(const SourceMetadata& sourceMetadata,
StreamContext&& context,
const std::optional<AudioOffloadInfo>& offloadInfo,
std::shared_ptr<StreamOut>* result) {
return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
offloadInfo);
}
ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort __unused) {
LOG(VERBOSE) << __func__ << ": do nothing and return ok";
return ndk::ScopedAStatus::ok();

View File

@@ -0,0 +1,60 @@
/*
* 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 <vector>
#define LOG_TAG "AHAL_ModulePrimary"
#include <Utils.h>
#include <android-base/logging.h>
#include "core-impl/ModulePrimary.h"
#include "core-impl/StreamStub.h"
#include "core-impl/Telephony.h"
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioPort;
using aidl::android::media::audio::common::AudioPortConfig;
using aidl::android::media::audio::common::MicrophoneInfo;
namespace aidl::android::hardware::audio::core {
ndk::ScopedAStatus ModulePrimary::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
if (!mTelephony) {
mTelephony = ndk::SharedRefBase::make<Telephony>();
}
*_aidl_return = mTelephony.getPtr();
LOG(DEBUG) << __func__ << ": returning instance of ITelephony: " << _aidl_return->get();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ModulePrimary::createInputStream(const SinkMetadata& sinkMetadata,
StreamContext&& context,
const std::vector<MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) {
return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
microphones);
}
ndk::ScopedAStatus ModulePrimary::createOutputStream(
const SourceMetadata& sourceMetadata, StreamContext&& context,
const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
offloadInfo);
}
} // namespace aidl::android::hardware::audio::core

View File

@@ -110,10 +110,12 @@ void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
if (isConnected) {
reply->observable.frames = mFrameCount;
reply->observable.timeNs = ::android::elapsedRealtimeNano();
} else {
reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
if (auto status = mDriver->getPosition(&reply->observable); status == ::android::OK) {
return;
}
}
reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
}
void StreamWorkerCommonLogic::populateReplyWrongState(

View File

@@ -0,0 +1,154 @@
/*
* 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_AlsaMixer"
#include <android-base/logging.h>
#include <cmath>
#include <android/binder_status.h>
#include "Mixer.h"
namespace aidl::android::hardware::audio::core::alsa {
//-----------------------------------------------------------------------------
MixerControl::MixerControl(struct mixer_ctl* ctl)
: mCtl(ctl),
mNumValues(mixer_ctl_get_num_values(ctl)),
mMinValue(mixer_ctl_get_range_min(ctl)),
mMaxValue(mixer_ctl_get_range_max(ctl)) {}
unsigned int MixerControl::getNumValues() const {
return mNumValues;
}
int MixerControl::getMaxValue() const {
return mMaxValue;
}
int MixerControl::getMinValue() const {
return mMinValue;
}
int MixerControl::setArray(const void* array, size_t count) {
const std::lock_guard guard(mLock);
return mixer_ctl_set_array(mCtl, array, count);
}
//-----------------------------------------------------------------------------
// static
const std::map<Mixer::Control, std::vector<Mixer::ControlNamesAndExpectedCtlType>>
Mixer::kPossibleControls = {
{Mixer::MASTER_SWITCH, {{"Master Playback Switch", MIXER_CTL_TYPE_BOOL}}},
{Mixer::MASTER_VOLUME, {{"Master Playback Volume", MIXER_CTL_TYPE_INT}}},
{Mixer::HW_VOLUME,
{{"Headphone Playback Volume", MIXER_CTL_TYPE_INT},
{"Headset Playback Volume", MIXER_CTL_TYPE_INT},
{"PCM Playback Volume", MIXER_CTL_TYPE_INT}}}};
// static
std::map<Mixer::Control, std::shared_ptr<MixerControl>> Mixer::initializeMixerControls(
struct mixer* mixer) {
std::map<Mixer::Control, std::shared_ptr<MixerControl>> mixerControls;
std::string mixerCtlNames;
for (const auto& [control, possibleCtls] : kPossibleControls) {
for (const auto& [ctlName, expectedCtlType] : possibleCtls) {
struct mixer_ctl* ctl = mixer_get_ctl_by_name(mixer, ctlName.c_str());
if (ctl != nullptr && mixer_ctl_get_type(ctl) == expectedCtlType) {
mixerControls.emplace(control, std::make_unique<MixerControl>(ctl));
if (!mixerCtlNames.empty()) {
mixerCtlNames += ",";
}
mixerCtlNames += ctlName;
break;
}
}
}
LOG(DEBUG) << __func__ << ": available mixer control names=[" << mixerCtlNames << "]";
return mixerControls;
}
Mixer::Mixer(struct mixer* mixer)
: mMixer(mixer), mMixerControls(initializeMixerControls(mMixer)) {}
Mixer::~Mixer() {
mixer_close(mMixer);
}
namespace {
int volumeFloatToInteger(float fValue, int maxValue, int minValue) {
return minValue + std::ceil((maxValue - minValue) * fValue);
}
} // namespace
ndk::ScopedAStatus Mixer::setMasterMute(bool muted) {
auto it = mMixerControls.find(Mixer::MASTER_SWITCH);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
const int numValues = it->second->getNumValues();
std::vector<int> values(numValues, muted ? 0 : 1);
if (int err = it->second->setArray(values.data(), numValues); err != 0) {
LOG(ERROR) << __func__ << ": failed to set master mute, err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Mixer::setMasterVolume(float volume) {
auto it = mMixerControls.find(Mixer::MASTER_VOLUME);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
const int numValues = it->second->getNumValues();
std::vector<int> values(numValues, volumeFloatToInteger(volume, it->second->getMaxValue(),
it->second->getMinValue()));
if (int err = it->second->setArray(values.data(), numValues); err != 0) {
LOG(ERROR) << __func__ << ": failed to set master volume, err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Mixer::setVolumes(const std::vector<float>& volumes) {
auto it = mMixerControls.find(Mixer::HW_VOLUME);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
const int numValues = it->second->getNumValues();
if (numValues < 0) {
LOG(FATAL) << __func__ << ": negative number of values: " << numValues;
}
const int maxValue = it->second->getMaxValue();
const int minValue = it->second->getMinValue();
std::vector<int> values;
size_t i = 0;
for (; i < static_cast<size_t>(numValues) && i < values.size(); ++i) {
values.emplace_back(volumeFloatToInteger(volumes[i], maxValue, minValue));
}
if (int err = it->second->setArray(values.data(), values.size()); err != 0) {
LOG(ERROR) << __func__ << ": failed to set volume, err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::audio::core::alsa

View File

@@ -0,0 +1,82 @@
/*
* 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 <string>
#include <vector>
#include <android-base/thread_annotations.h>
#include <android/binder_auto_utils.h>
extern "C" {
#include <tinyalsa/mixer.h>
}
namespace aidl::android::hardware::audio::core::alsa {
class MixerControl {
public:
explicit MixerControl(struct mixer_ctl* ctl);
unsigned int getNumValues() const;
int getMaxValue() const;
int getMinValue() const;
int setArray(const void* array, size_t count);
private:
std::mutex mLock;
// The mixer_ctl object is owned by ALSA and will be released when the mixer is closed.
struct mixer_ctl* mCtl GUARDED_BY(mLock);
const unsigned int mNumValues;
const int mMinValue;
const int mMaxValue;
};
class Mixer {
public:
explicit Mixer(struct mixer* mixer);
~Mixer();
bool isValid() const { return mMixer != nullptr; }
ndk::ScopedAStatus setMasterMute(bool muted);
ndk::ScopedAStatus setMasterVolume(float volume);
ndk::ScopedAStatus setVolumes(const std::vector<float>& volumes);
private:
enum Control {
MASTER_SWITCH,
MASTER_VOLUME,
HW_VOLUME,
};
using ControlNamesAndExpectedCtlType = std::pair<std::string, enum mixer_ctl_type>;
static const std::map<Control, std::vector<ControlNamesAndExpectedCtlType>> kPossibleControls;
static std::map<Control, std::shared_ptr<MixerControl>> initializeMixerControls(
struct mixer* mixer);
// The mixer object is owned by ALSA and will be released when the mixer is closed.
struct mixer* mMixer;
// `mMixerControls` will only be initialized in constructor. After that, it wil only be
// read but not be modified.
const std::map<Control, std::shared_ptr<MixerControl>> mMixerControls;
};
} // namespace aidl::android::hardware::audio::core::alsa

View File

@@ -0,0 +1,67 @@
/*
* 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_ModuleAlsa"
#include <vector>
#include <android-base/logging.h>
#include "Utils.h"
#include "core-impl/ModuleAlsa.h"
extern "C" {
#include "alsa_device_profile.h"
}
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioPort;
using aidl::android::media::audio::common::AudioProfile;
namespace aidl::android::hardware::audio::core {
ndk::ScopedAStatus ModuleAlsa::populateConnectedDevicePort(AudioPort* audioPort) {
auto deviceProfile = alsa::getDeviceProfile(*audioPort);
if (!deviceProfile.has_value()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
auto profile = alsa::readAlsaDeviceInfo(*deviceProfile);
if (!profile.has_value()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
std::vector<AudioChannelLayout> channels = alsa::getChannelMasksFromProfile(&profile.value());
std::vector<int> sampleRates = alsa::getSampleRatesFromProfile(&profile.value());
for (size_t i = 0; i < std::min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) &&
profile->formats[i] != PCM_FORMAT_INVALID;
++i) {
auto audioFormatDescription =
alsa::c2aidl_pcm_format_AudioFormatDescription(profile->formats[i]);
if (audioFormatDescription.type == AudioFormatType::DEFAULT) {
LOG(WARNING) << __func__ << ": unknown pcm type=" << profile->formats[i];
continue;
}
AudioProfile audioProfile = {.format = audioFormatDescription,
.channelMasks = channels,
.sampleRates = sampleRates};
audioPort->profiles.push_back(std::move(audioProfile));
}
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,135 @@
/*
* 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_StreamAlsa"
#include <android-base/logging.h>
#include <Utils.h>
#include <audio_utils/clock.h>
#include <error/expected_utils.h>
#include "core-impl/StreamAlsa.h"
namespace aidl::android::hardware::audio::core {
StreamAlsa::StreamAlsa(const Metadata& metadata, StreamContext&& context)
: StreamCommonImpl(metadata, std::move(context)),
mFrameSizeBytes(getContext().getFrameSize()),
mIsInput(isInput(metadata)),
mConfig(alsa::getPcmConfig(getContext(), mIsInput)) {}
::android::status_t StreamAlsa::init() {
return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
}
::android::status_t StreamAlsa::standby() {
mAlsaDeviceProxies.clear();
return ::android::OK;
}
::android::status_t StreamAlsa::start() {
decltype(mAlsaDeviceProxies) alsaDeviceProxies;
for (const auto& device : getDeviceProfiles()) {
auto profile = alsa::readAlsaDeviceInfo(device);
if (!profile.has_value()) {
LOG(ERROR) << __func__ << ": unable to read device info, device address=" << device;
return ::android::UNKNOWN_ERROR;
}
auto proxy = alsa::makeDeviceProxy();
// Always ask for alsa configure as required since the configuration should be supported
// by the connected device. That is guaranteed by `setAudioPortConfig` and `setAudioPatch`.
if (int err = proxy_prepare(proxy.get(), &profile.value(),
const_cast<struct pcm_config*>(&mConfig.value()),
true /*require_exact_match*/);
err != 0) {
LOG(ERROR) << __func__ << ": fail to prepare for device address=" << device
<< " error=" << err;
return ::android::UNKNOWN_ERROR;
}
if (int err = proxy_open(proxy.get()); err != 0) {
LOG(ERROR) << __func__ << ": failed to open device, address=" << device
<< " error=" << err;
return ::android::UNKNOWN_ERROR;
}
alsaDeviceProxies.push_back(std::move(proxy));
}
mAlsaDeviceProxies = std::move(alsaDeviceProxies);
return ::android::OK;
}
::android::status_t StreamAlsa::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) {
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(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer);
maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get());
} else {
for (auto& proxy : mAlsaDeviceProxies) {
proxy_write(proxy.get(), buffer, bytesToTransfer);
maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get()));
}
}
*actualFrameCount = frameCount;
maxLatency = std::min(maxLatency, static_cast<unsigned>(std::numeric_limits<int32_t>::max()));
*latencyMs = maxLatency;
return ::android::OK;
}
::android::status_t StreamAlsa::getPosition(StreamDescriptor::Position* position) {
if (mAlsaDeviceProxies.empty()) {
LOG(FATAL) << __func__ << ": no input devices";
return ::android::NO_INIT;
}
if (mIsInput) {
if (int ret = proxy_get_capture_position(mAlsaDeviceProxies[0].get(), &position->frames,
&position->timeNs);
ret != 0) {
LOG(WARNING) << __func__ << ": failed to retrieve capture position: " << ret;
return ::android::INVALID_OPERATION;
}
} else {
uint64_t hwFrames;
struct timespec timestamp;
if (int ret = proxy_get_presentation_position(mAlsaDeviceProxies[0].get(), &hwFrames,
&timestamp);
ret == 0) {
if (hwFrames > std::numeric_limits<int64_t>::max()) {
hwFrames -= std::numeric_limits<int64_t>::max();
}
position->frames = static_cast<int64_t>(hwFrames);
position->timeNs = audio_utils_ns_from_timespec(&timestamp);
} else {
LOG(WARNING) << __func__ << ": failed to retrieve presentation position: " << ret;
return ::android::INVALID_OPERATION;
}
}
return ::android::OK;
}
void StreamAlsa::shutdown() {
mAlsaDeviceProxies.clear();
}
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,293 @@
/*
* 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 <map>
#include <set>
#define LOG_TAG "AHAL_AlsaUtils"
#include <Utils.h>
#include <aidl/android/media/audio/common/AudioFormatType.h>
#include <aidl/android/media/audio/common/PcmType.h>
#include <android-base/logging.h>
#include "Utils.h"
#include "core-impl/utils.h"
using aidl::android::hardware::audio::common::getChannelCount;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioDeviceAddress;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioIoFlags;
using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::PcmType;
namespace aidl::android::hardware::audio::core::alsa {
namespace {
using AudioChannelCountToMaskMap = std::map<unsigned int, AudioChannelLayout>;
using AudioFormatDescToPcmFormatMap = std::map<AudioFormatDescription, enum pcm_format>;
using PcmFormatToAudioFormatDescMap = std::map<enum pcm_format, AudioFormatDescription>;
AudioChannelLayout getInvalidChannelLayout() {
static const AudioChannelLayout invalidChannelLayout =
AudioChannelLayout::make<AudioChannelLayout::Tag::invalid>(0);
return invalidChannelLayout;
}
static AudioChannelCountToMaskMap make_ChannelCountToMaskMap(
const std::set<AudioChannelLayout>& channelMasks) {
AudioChannelCountToMaskMap channelMaskToCountMap;
for (const auto& channelMask : channelMasks) {
channelMaskToCountMap.emplace(getChannelCount(channelMask), channelMask);
}
return channelMaskToCountMap;
}
#define DEFINE_CHANNEL_LAYOUT_MASK(n) \
AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(AudioChannelLayout::LAYOUT_##n)
const AudioChannelCountToMaskMap& getSupportedChannelOutLayoutMap() {
static const std::set<AudioChannelLayout> supportedOutChannelLayouts = {
DEFINE_CHANNEL_LAYOUT_MASK(MONO), DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
DEFINE_CHANNEL_LAYOUT_MASK(2POINT1), DEFINE_CHANNEL_LAYOUT_MASK(QUAD),
DEFINE_CHANNEL_LAYOUT_MASK(PENTA), DEFINE_CHANNEL_LAYOUT_MASK(5POINT1),
DEFINE_CHANNEL_LAYOUT_MASK(6POINT1), DEFINE_CHANNEL_LAYOUT_MASK(7POINT1),
DEFINE_CHANNEL_LAYOUT_MASK(7POINT1POINT4), DEFINE_CHANNEL_LAYOUT_MASK(22POINT2),
};
static const AudioChannelCountToMaskMap outLayouts =
make_ChannelCountToMaskMap(supportedOutChannelLayouts);
return outLayouts;
}
const AudioChannelCountToMaskMap& getSupportedChannelInLayoutMap() {
static const std::set<AudioChannelLayout> supportedInChannelLayouts = {
DEFINE_CHANNEL_LAYOUT_MASK(MONO),
DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
};
static const AudioChannelCountToMaskMap inLayouts =
make_ChannelCountToMaskMap(supportedInChannelLayouts);
return inLayouts;
}
#undef DEFINE_CHANNEL_LAYOUT_MASK
#define DEFINE_CHANNEL_INDEX_MASK(n) \
AudioChannelLayout::make<AudioChannelLayout::Tag::indexMask>(AudioChannelLayout::INDEX_MASK_##n)
const AudioChannelCountToMaskMap& getSupportedChannelIndexLayoutMap() {
static const std::set<AudioChannelLayout> supportedIndexChannelLayouts = {
DEFINE_CHANNEL_INDEX_MASK(1), DEFINE_CHANNEL_INDEX_MASK(2),
DEFINE_CHANNEL_INDEX_MASK(3), DEFINE_CHANNEL_INDEX_MASK(4),
DEFINE_CHANNEL_INDEX_MASK(5), DEFINE_CHANNEL_INDEX_MASK(6),
DEFINE_CHANNEL_INDEX_MASK(7), DEFINE_CHANNEL_INDEX_MASK(8),
DEFINE_CHANNEL_INDEX_MASK(9), DEFINE_CHANNEL_INDEX_MASK(10),
DEFINE_CHANNEL_INDEX_MASK(11), DEFINE_CHANNEL_INDEX_MASK(12),
DEFINE_CHANNEL_INDEX_MASK(13), DEFINE_CHANNEL_INDEX_MASK(14),
DEFINE_CHANNEL_INDEX_MASK(15), DEFINE_CHANNEL_INDEX_MASK(16),
DEFINE_CHANNEL_INDEX_MASK(17), DEFINE_CHANNEL_INDEX_MASK(18),
DEFINE_CHANNEL_INDEX_MASK(19), DEFINE_CHANNEL_INDEX_MASK(20),
DEFINE_CHANNEL_INDEX_MASK(21), DEFINE_CHANNEL_INDEX_MASK(22),
DEFINE_CHANNEL_INDEX_MASK(23), DEFINE_CHANNEL_INDEX_MASK(24),
};
static const AudioChannelCountToMaskMap indexLayouts =
make_ChannelCountToMaskMap(supportedIndexChannelLayouts);
return indexLayouts;
}
#undef DEFINE_CHANNEL_INDEX_MASK
AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
AudioFormatDescription result;
result.type = type;
return result;
}
AudioFormatDescription make_AudioFormatDescription(PcmType pcm) {
auto result = make_AudioFormatDescription(AudioFormatType::PCM);
result.pcm = pcm;
return result;
}
const AudioFormatDescToPcmFormatMap& getAudioFormatDescriptorToPcmFormatMap() {
static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = {
{make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8},
{make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE},
{make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE},
{make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE},
{make_AudioFormatDescription(PcmType::INT_32_BIT), PCM_FORMAT_S32_LE},
{make_AudioFormatDescription(PcmType::FLOAT_32_BIT), PCM_FORMAT_FLOAT_LE},
};
return formatDescToPcmFormatMap;
}
static PcmFormatToAudioFormatDescMap make_PcmFormatToAudioFormatDescMap(
const AudioFormatDescToPcmFormatMap& formatDescToPcmFormatMap) {
PcmFormatToAudioFormatDescMap result;
for (const auto& formatPair : formatDescToPcmFormatMap) {
result.emplace(formatPair.second, formatPair.first);
}
return result;
}
const PcmFormatToAudioFormatDescMap& getPcmFormatToAudioFormatDescMap() {
static const PcmFormatToAudioFormatDescMap pcmFormatToFormatDescMap =
make_PcmFormatToAudioFormatDescMap(getAudioFormatDescriptorToPcmFormatMap());
return pcmFormatToFormatDescMap;
}
} // namespace
std::ostream& operator<<(std::ostream& os, const DeviceProfile& device) {
return os << "<" << device.card << "," << device.device << ">";
}
AudioChannelLayout getChannelLayoutMaskFromChannelCount(unsigned int channelCount, int isInput) {
return findValueOrDefault(
isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
channelCount, getInvalidChannelLayout());
}
AudioChannelLayout getChannelIndexMaskFromChannelCount(unsigned int channelCount) {
return findValueOrDefault(getSupportedChannelIndexLayoutMap(), channelCount,
getInvalidChannelLayout());
}
unsigned int getChannelCountFromChannelMask(const AudioChannelLayout& channelMask, bool isInput) {
switch (channelMask.getTag()) {
case AudioChannelLayout::Tag::layoutMask: {
return findKeyOrDefault(
isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
static_cast<unsigned>(getChannelCount(channelMask)), 0u /*defaultValue*/);
}
case AudioChannelLayout::Tag::indexMask: {
return findKeyOrDefault(getSupportedChannelIndexLayoutMap(),
static_cast<unsigned>(getChannelCount(channelMask)),
0u /*defaultValue*/);
}
case AudioChannelLayout::Tag::none:
case AudioChannelLayout::Tag::invalid:
case AudioChannelLayout::Tag::voiceMask:
default:
return 0;
}
}
std::vector<AudioChannelLayout> getChannelMasksFromProfile(const alsa_device_profile* profile) {
const bool isInput = profile->direction == PCM_IN;
std::vector<AudioChannelLayout> channels;
for (size_t i = 0; i < AUDIO_PORT_MAX_CHANNEL_MASKS && profile->channel_counts[i] != 0; ++i) {
auto layoutMask =
alsa::getChannelLayoutMaskFromChannelCount(profile->channel_counts[i], isInput);
if (layoutMask.getTag() == AudioChannelLayout::Tag::layoutMask) {
channels.push_back(layoutMask);
}
auto indexMask = alsa::getChannelIndexMaskFromChannelCount(profile->channel_counts[i]);
if (indexMask.getTag() == AudioChannelLayout::Tag::indexMask) {
channels.push_back(indexMask);
}
}
return channels;
}
std::optional<DeviceProfile> getDeviceProfile(
const ::aidl::android::media::audio::common::AudioDevice& audioDevice, bool isInput) {
if (audioDevice.address.getTag() != AudioDeviceAddress::Tag::alsa) {
LOG(ERROR) << __func__ << ": not alsa address: " << audioDevice.toString();
return std::nullopt;
}
auto& alsaAddress = audioDevice.address.get<AudioDeviceAddress::Tag::alsa>();
if (alsaAddress.size() != 2 || alsaAddress[0] < 0 || alsaAddress[1] < 0) {
LOG(ERROR) << __func__
<< ": malformed alsa address: " << ::android::internal::ToString(alsaAddress);
return std::nullopt;
}
return DeviceProfile{.card = alsaAddress[0],
.device = alsaAddress[1],
.direction = isInput ? PCM_IN : PCM_OUT};
}
std::optional<DeviceProfile> getDeviceProfile(
const ::aidl::android::media::audio::common::AudioPort& audioPort) {
if (audioPort.ext.getTag() != AudioPortExt::Tag::device) {
LOG(ERROR) << __func__ << ": port id " << audioPort.id << " is not a device port";
return std::nullopt;
}
auto& devicePort = audioPort.ext.get<AudioPortExt::Tag::device>();
return getDeviceProfile(devicePort.device, audioPort.flags.getTag() == AudioIoFlags::input);
}
std::optional<struct pcm_config> getPcmConfig(const StreamContext& context, bool isInput) {
struct pcm_config config;
config.channels = alsa::getChannelCountFromChannelMask(context.getChannelLayout(), isInput);
if (config.channels == 0) {
LOG(ERROR) << __func__ << ": invalid channel=" << context.getChannelLayout().toString();
return std::nullopt;
}
config.format = alsa::aidl2c_AudioFormatDescription_pcm_format(context.getFormat());
if (config.format == PCM_FORMAT_INVALID) {
LOG(ERROR) << __func__ << ": invalid format=" << context.getFormat().toString();
return std::nullopt;
}
config.rate = context.getSampleRate();
if (config.rate == 0) {
LOG(ERROR) << __func__ << ": invalid sample rate=" << config.rate;
return std::nullopt;
}
return config;
}
std::vector<int> getSampleRatesFromProfile(const alsa_device_profile* profile) {
std::vector<int> sampleRates;
for (int i = 0; i < std::min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES) &&
profile->sample_rates[i] != 0;
i++) {
sampleRates.push_back(profile->sample_rates[i]);
}
return sampleRates;
}
DeviceProxy makeDeviceProxy() {
return DeviceProxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) {
if (proxy != nullptr) {
proxy_close(proxy);
delete proxy;
}
});
}
std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& deviceProfile) {
alsa_device_profile profile;
profile_init(&profile, deviceProfile.direction);
profile.card = deviceProfile.card;
profile.device = deviceProfile.device;
if (!profile_read_device_info(&profile)) {
LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card
<< ", device=" << profile.device;
return std::nullopt;
}
return profile;
}
AudioFormatDescription c2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy) {
return findValueOrDefault(getPcmFormatToAudioFormatDescMap(), legacy, AudioFormatDescription());
}
pcm_format aidl2c_AudioFormatDescription_pcm_format(const AudioFormatDescription& aidl) {
return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID);
}
} // namespace aidl::android::hardware::audio::core::alsa

View File

@@ -0,0 +1,70 @@
/*
* 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 <functional>
#include <iostream>
#include <memory>
#include <optional>
#include <vector>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
#include <aidl/android/media/audio/common/AudioPort.h>
#include "core-impl/Stream.h"
extern "C" {
#include <tinyalsa/pcm.h>
#include "alsa_device_profile.h"
#include "alsa_device_proxy.h"
}
namespace aidl::android::hardware::audio::core::alsa {
struct DeviceProfile {
int card;
int device;
int direction; /* PCM_OUT or PCM_IN */
};
std::ostream& operator<<(std::ostream& os, const DeviceProfile& device);
using DeviceProxyDeleter = std::function<void(alsa_device_proxy*)>;
using DeviceProxy = std::unique_ptr<alsa_device_proxy, DeviceProxyDeleter>;
::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount(
unsigned int channelCount, int isInput);
::aidl::android::media::audio::common::AudioChannelLayout getChannelIndexMaskFromChannelCount(
unsigned int channelCount);
unsigned int getChannelCountFromChannelMask(
const ::aidl::android::media::audio::common::AudioChannelLayout& channelMask, bool isInput);
std::vector<::aidl::android::media::audio::common::AudioChannelLayout> getChannelMasksFromProfile(
const alsa_device_profile* profile);
std::optional<DeviceProfile> getDeviceProfile(
const ::aidl::android::media::audio::common::AudioDevice& audioDevice, bool isInput);
std::optional<DeviceProfile> getDeviceProfile(
const ::aidl::android::media::audio::common::AudioPort& audioPort);
std::optional<struct pcm_config> getPcmConfig(const StreamContext& context, bool isInput);
std::vector<int> getSampleRatesFromProfile(const alsa_device_profile* profile);
DeviceProxy makeDeviceProxy();
std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& deviceProfile);
::aidl::android::media::audio::common::AudioFormatDescription
c2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy);
pcm_format aidl2c_AudioFormatDescription_pcm_format(
const ::aidl::android::media::audio::common::AudioFormatDescription& aidl);
} // namespace aidl::android::hardware::audio::core::alsa

View File

@@ -9,6 +9,11 @@
<version>1</version>
<fqname>IModule/r_submix</fqname>
</hal>
<hal format="aidl">
<name>android.hardware.audio.core</name>
<version>1</version>
<fqname>IModule/stub</fqname>
</hal>
<hal format="aidl">
<name>android.hardware.audio.core</name>
<version>1</version>

View File

@@ -0,0 +1,48 @@
/*
* 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 <memory>
#include <utility>
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder_platform.h>
#include <system/thread_defs.h>
namespace aidl::android::hardware::audio::core {
// Helper used for interfaces that require a persistent instance. We hold them via a strong
// pointer. The binder token is retained for a call to 'setMinSchedulerPolicy'.
template <class C>
struct ChildInterface : private std::pair<std::shared_ptr<C>, ndk::SpAIBinder> {
ChildInterface() = default;
ChildInterface& operator=(const std::shared_ptr<C>& c) {
return operator=(std::shared_ptr<C>(c));
}
ChildInterface& operator=(std::shared_ptr<C>&& c) {
this->first = std::move(c);
this->second = this->first->asBinder();
AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
return *this;
}
explicit operator bool() const { return !!this->first; }
C& operator*() const { return *(this->first); }
C* operator->() const { return this->first; }
std::shared_ptr<C> getPtr() const { return this->first; }
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -45,6 +45,7 @@ struct Configuration {
std::unique_ptr<Configuration> getPrimaryConfiguration();
std::unique_ptr<Configuration> getRSubmixConfiguration();
std::unique_ptr<Configuration> getStubConfiguration();
std::unique_ptr<Configuration> getUsbConfiguration();
} // namespace aidl::android::hardware::audio::core::internal

View File

@@ -16,12 +16,14 @@
#pragma once
#include <iostream>
#include <map>
#include <memory>
#include <set>
#include <aidl/android/hardware/audio/core/BnModule.h>
#include "core-impl/ChildInterface.h"
#include "core-impl/Configuration.h"
#include "core-impl/Stream.h"
@@ -31,7 +33,7 @@ class Module : public BnModule {
public:
// This value is used for all AudioPatches and reported by all streams.
static constexpr int32_t kLatencyMs = 10;
enum Type : int { DEFAULT, R_SUBMIX, USB };
enum Type : int { DEFAULT, R_SUBMIX, STUB, USB };
static std::shared_ptr<Module> createInstance(Type type);
@@ -132,26 +134,6 @@ class Module : public BnModule {
bool forceTransientBurst = false;
bool forceSynchronousDrain = false;
};
// Helper used for interfaces that require a persistent instance. We hold them via a strong
// pointer. The binder token is retained for a call to 'setMinSchedulerPolicy'.
template <class C>
struct ChildInterface : private std::pair<std::shared_ptr<C>, ndk::SpAIBinder> {
ChildInterface() {}
ChildInterface& operator=(const std::shared_ptr<C>& c) {
return operator=(std::shared_ptr<C>(c));
}
ChildInterface& operator=(std::shared_ptr<C>&& c) {
this->first = std::move(c);
this->second = this->first->asBinder();
AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL,
ANDROID_PRIORITY_AUDIO);
return *this;
}
explicit operator bool() const { return !!this->first; }
C& operator*() const { return *(this->first); }
C* operator->() const { return this->first; }
std::shared_ptr<C> getPtr() const { return this->first; }
};
// ids of device ports created at runtime via 'connectExternalDevice'.
// Also stores a list of ids of mix ports with dynamic profiles that were populated from
// the connected port. This list can be empty, thus an int->int multimap can't be used.
@@ -164,10 +146,6 @@ class Module : public BnModule {
std::unique_ptr<internal::Configuration> mConfig;
ModuleDebug mDebug;
VendorDebug mVendorDebug;
ChildInterface<ITelephony> mTelephony;
ChildInterface<IBluetooth> mBluetooth;
ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
ChildInterface<IBluetoothLe> mBluetoothLe;
ConnectedDevicePorts mConnectedDevicePorts;
Streams mStreams;
Patches mPatches;
@@ -184,13 +162,13 @@ class Module : public BnModule {
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result);
std::shared_ptr<StreamIn>* result) = 0;
virtual ndk::ScopedAStatus createOutputStream(
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
StreamContext&& context,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result);
std::shared_ptr<StreamOut>* result) = 0;
// If the module is unable to populate the connected device port correctly, the returned error
// code must correspond to the errors of `IModule.connectedExternalDevice` method.
virtual ndk::ScopedAStatus populateConnectedDevicePort(
@@ -232,4 +210,6 @@ class Module : public BnModule {
const AudioPatch& newPatch);
};
std::ostream& operator<<(std::ostream& os, Module::Type t);
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,38 @@
/*
* 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 "core-impl/Module.h"
namespace aidl::android::hardware::audio::core {
// This class is intended to be used as a base class for implementations
// that use TinyAlsa. This can be either a primary module or a USB Audio
// module. This class does not define a complete module implementation,
// and should never be used on its own. Derived classes are expected to
// provide necessary overrides for all interface methods omitted here.
class ModuleAlsa : public Module {
public:
explicit ModuleAlsa(Module::Type type) : Module(type) {}
protected:
// Extension methods of 'Module'.
ndk::ScopedAStatus populateConnectedDevicePort(
::aidl::android::media::audio::common::AudioPort* audioPort) override;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,46 @@
/*
* 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 "core-impl/Module.h"
namespace aidl::android::hardware::audio::core {
class ModulePrimary final : public Module {
public:
ModulePrimary() : Module(Type::DEFAULT) {}
protected:
ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
ndk::ScopedAStatus createInputStream(
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) override;
ndk::ScopedAStatus createOutputStream(
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
StreamContext&& context,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
private:
ChildInterface<ITelephony> mTelephony;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -22,7 +22,7 @@ namespace aidl::android::hardware::audio::core {
class ModuleRemoteSubmix : public Module {
public:
explicit ModuleRemoteSubmix(Module::Type type) : Module(type) {}
ModuleRemoteSubmix() : Module(Type::R_SUBMIX) {}
private:
// IModule interfaces

View File

@@ -0,0 +1,50 @@
/*
* 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 "core-impl/Module.h"
namespace aidl::android::hardware::audio::core {
class ModuleStub final : public Module {
public:
ModuleStub() : Module(Type::STUB) {}
protected:
ndk::ScopedAStatus getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) override;
ndk::ScopedAStatus getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) override;
ndk::ScopedAStatus getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) override;
ndk::ScopedAStatus createInputStream(
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) override;
ndk::ScopedAStatus createOutputStream(
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
StreamContext&& context,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
private:
ChildInterface<IBluetooth> mBluetooth;
ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
ChildInterface<IBluetoothLe> mBluetoothLe;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -16,13 +16,13 @@
#pragma once
#include "core-impl/Module.h"
#include "core-impl/ModuleAlsa.h"
namespace aidl::android::hardware::audio::core {
class ModuleUsb : public Module {
class ModuleUsb final : public ModuleAlsa {
public:
explicit ModuleUsb(Module::Type type) : Module(type) {}
ModuleUsb() : ModuleAlsa(Type::USB) {}
private:
// IModule interfaces

View File

@@ -184,6 +184,12 @@ struct DriverInterface {
virtual ::android::status_t start() = 0;
virtual ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) = 0;
// No need to implement 'getPosition' unless the driver can provide more precise
// data than just total frame count. For example, the driver may correctly account
// for any intermediate buffers.
virtual ::android::status_t getPosition(StreamDescriptor::Position* /*position*/) {
return ::android::OK;
}
virtual void shutdown() = 0; // This function is only called once.
};

View File

@@ -0,0 +1,55 @@
/*
* 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 <optional>
#include <vector>
#include "Stream.h"
#include "alsa/Utils.h"
namespace aidl::android::hardware::audio::core {
// This class is intended to be used as a base class for implementations
// that use TinyAlsa.
// This class does not define a complete stream implementation,
// and should never be used on its own. Derived classes are expected to
// provide necessary overrides for all interface methods omitted here.
class StreamAlsa : public StreamCommonImpl {
public:
StreamAlsa(const Metadata& metadata, StreamContext&& context);
// Methods of 'DriverInterface'.
::android::status_t init() override;
::android::status_t standby() override;
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
::android::status_t getPosition(StreamDescriptor::Position* position) override;
void shutdown() override;
protected:
// Called from 'start' to initialize 'mAlsaDeviceProxies', the vector must be non-empty.
virtual std::vector<alsa::DeviceProfile> getDeviceProfiles() = 0;
const size_t mFrameSizeBytes;
const bool mIsInput;
const std::optional<struct pcm_config> mConfig;
// All fields below are only used on the worker thread.
std::vector<alsa::DeviceProxy> mAlsaDeviceProxies;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -39,6 +39,7 @@ class StreamRemoteSubmix : public StreamCommonImpl {
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
::android::status_t getPosition(StreamDescriptor::Position* position) override;
void shutdown() override;
// Overridden methods of 'StreamCommonImpl', called on a Binder thread.

View File

@@ -17,55 +17,34 @@
#pragma once
#include <atomic>
#include <functional>
#include <mutex>
#include <optional>
#include <vector>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include "core-impl/Stream.h"
extern "C" {
#include <tinyalsa/pcm.h>
#include "alsa_device_proxy.h"
}
#include "StreamAlsa.h"
namespace aidl::android::hardware::audio::core {
class StreamUsb : public StreamCommonImpl {
class StreamUsb : public StreamAlsa {
public:
StreamUsb(const Metadata& metadata, StreamContext&& context);
// Methods of 'DriverInterface'.
::android::status_t init() override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
::android::status_t standby() override;
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
void shutdown() override;
// Overridden methods of 'StreamCommonImpl', called on a Binder thread.
const ConnectedDevices& getConnectedDevices() const override;
ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override;
private:
using AlsaDeviceProxyDeleter = std::function<void(alsa_device_proxy*)>;
using AlsaDeviceProxy = std::unique_ptr<alsa_device_proxy, AlsaDeviceProxyDeleter>;
static std::optional<struct pcm_config> maybePopulateConfig(const StreamContext& context,
bool isInput);
protected:
std::vector<alsa::DeviceProfile> getDeviceProfiles() override;
mutable std::mutex mLock;
const size_t mFrameSizeBytes;
const bool mIsInput;
const std::optional<struct pcm_config> mConfig;
std::vector<alsa::DeviceProfile> mConnectedDeviceProfiles GUARDED_BY(mLock);
std::atomic<bool> mConnectedDevicesUpdated = false;
// All fields below are only used on the worker thread.
std::vector<AlsaDeviceProxy> mAlsaDeviceProxies;
};
class StreamInUsb final : public StreamUsb, public StreamIn {
@@ -94,7 +73,7 @@ class StreamOutUsb final : public StreamUsb, public StreamOut {
ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
int mChannelCount;
const int mChannelCount;
std::vector<float> mHwVolumes;
};

View File

@@ -16,20 +16,21 @@
#include <cstdlib>
#include <ctime>
#include <sstream>
#include <utility>
#include <vector>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include "core-impl/Config.h"
#include "core-impl/Module.h"
#include "core-impl/ModuleUsb.h"
using aidl::android::hardware::audio::core::Config;
using aidl::android::hardware::audio::core::Module;
using aidl::android::hardware::audio::core::ModuleUsb;
int main() {
// Random values are used in the implementation.
@@ -52,18 +53,19 @@ int main() {
CHECK_EQ(STATUS_OK, status);
// Make modules
auto createModule = [](Module::Type type, const std::string& instance) {
auto createModule = [](Module::Type type) {
auto module = Module::createInstance(type);
ndk::SpAIBinder moduleBinder = module->asBinder();
const std::string moduleName = std::string(Module::descriptor).append("/").append(instance);
std::stringstream moduleName;
moduleName << Module::descriptor << "/" << type;
AIBinder_setMinSchedulerPolicy(moduleBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
binder_status_t status = AServiceManager_addService(moduleBinder.get(), moduleName.c_str());
binder_status_t status =
AServiceManager_addService(moduleBinder.get(), moduleName.str().c_str());
CHECK_EQ(STATUS_OK, status);
return std::make_pair(module, moduleBinder);
};
auto modules = {createModule(Module::Type::DEFAULT, "default"),
createModule(Module::Type::R_SUBMIX, "r_submix"),
createModule(Module::Type::USB, "usb")};
auto modules = {createModule(Module::Type::DEFAULT), createModule(Module::Type::R_SUBMIX),
createModule(Module::Type::USB), createModule(Module::Type::STUB)};
(void)modules;
ABinderProcess_joinThreadPool();

View File

@@ -179,6 +179,27 @@ void StreamRemoteSubmix::shutdown() {
: outWrite(buffer, frameCount, actualFrameCount));
}
::android::status_t StreamRemoteSubmix::getPosition(StreamDescriptor::Position* position) {
sp<MonoPipeReader> source = mCurrentRoute->getSource();
if (source == nullptr) {
return ::android::NO_INIT;
}
const ssize_t framesInPipe = source->availableToRead();
if (framesInPipe < 0) {
return ::android::INVALID_OPERATION;
}
if (mIsInput) {
position->frames += framesInPipe;
} else {
if (position->frames > framesInPipe) {
position->frames -= framesInPipe;
} else {
position->frames = 0;
}
}
return ::android::OK;
}
// Calculate the maximum size of the pipe buffer in frames for the specified stream.
size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
auto pipeConfig = mCurrentRoute->mPipeConfig;

View File

@@ -0,0 +1,78 @@
/*
* 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 <vector>
#define LOG_TAG "AHAL_ModuleStub"
#include <Utils.h>
#include <android-base/logging.h>
#include "core-impl/Bluetooth.h"
#include "core-impl/ModuleStub.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::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioPort;
using aidl::android::media::audio::common::AudioPortConfig;
using aidl::android::media::audio::common::MicrophoneInfo;
namespace aidl::android::hardware::audio::core {
ndk::ScopedAStatus ModuleStub::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
if (!mBluetooth) {
mBluetooth = ndk::SharedRefBase::make<Bluetooth>();
}
*_aidl_return = mBluetooth.getPtr();
LOG(DEBUG) << __func__ << ": returning instance of IBluetooth: " << _aidl_return->get();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ModuleStub::getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
if (!mBluetoothA2dp) {
mBluetoothA2dp = ndk::SharedRefBase::make<BluetoothA2dp>();
}
*_aidl_return = mBluetoothA2dp.getPtr();
LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ModuleStub::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
if (!mBluetoothLe) {
mBluetoothLe = ndk::SharedRefBase::make<BluetoothLe>();
}
*_aidl_return = mBluetoothLe.getPtr();
LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ModuleStub::createInputStream(const SinkMetadata& sinkMetadata,
StreamContext&& context,
const std::vector<MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) {
return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
microphones);
}
ndk::ScopedAStatus ModuleStub::createOutputStream(
const SourceMetadata& sourceMetadata, StreamContext&& context,
const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
offloadInfo);
}
} // namespace aidl::android::hardware::audio::core

View File

@@ -14,68 +14,34 @@
* limitations under the License.
*/
#define LOG_TAG "AHAL_ModuleUsb"
#include <vector>
#define LOG_TAG "AHAL_ModuleUsb"
#include <Utils.h>
#include <android-base/logging.h>
#include <tinyalsa/asoundlib.h>
#include "UsbAlsaMixerControl.h"
#include "UsbAlsaUtils.h"
#include "alsa/Utils.h"
#include "core-impl/ModuleUsb.h"
#include "core-impl/StreamUsb.h"
extern "C" {
#include "alsa_device_profile.h"
}
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioDeviceAddress;
using aidl::android::media::audio::common::AudioDeviceDescription;
using aidl::android::media::audio::common::AudioDeviceType;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioIoFlags;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioPort;
using aidl::android::media::audio::common::AudioPortConfig;
using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::AudioProfile;
using aidl::android::media::audio::common::MicrophoneInfo;
namespace aidl::android::hardware::audio::core {
namespace {
std::vector<AudioChannelLayout> populateChannelMasksFromProfile(const alsa_device_profile* profile,
bool isInput) {
std::vector<AudioChannelLayout> channels;
for (size_t i = 0; i < AUDIO_PORT_MAX_CHANNEL_MASKS && profile->channel_counts[i] != 0; ++i) {
auto layoutMask =
usb::getChannelLayoutMaskFromChannelCount(profile->channel_counts[i], isInput);
if (layoutMask.getTag() == AudioChannelLayout::Tag::layoutMask) {
channels.push_back(layoutMask);
}
auto indexMask = usb::getChannelIndexMaskFromChannelCount(profile->channel_counts[i]);
if (indexMask.getTag() == AudioChannelLayout::Tag::indexMask) {
channels.push_back(indexMask);
}
}
return channels;
}
std::vector<int> populateSampleRatesFromProfile(const alsa_device_profile* profile) {
std::vector<int> sampleRates;
for (int i = 0; i < std::min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES) &&
profile->sample_rates[i] != 0;
i++) {
sampleRates.push_back(profile->sample_rates[i]);
}
return sampleRates;
bool isUsbDevicePort(const AudioPort& audioPort) {
return audioPort.ext.getTag() == AudioPortExt::Tag::device &&
audioPort.ext.get<AudioPortExt::Tag::device>().device.type.connection ==
AudioDeviceDescription::CONNECTION_USB;
}
} // namespace
@@ -122,55 +88,11 @@ ndk::ScopedAStatus ModuleUsb::createOutputStream(const SourceMetadata& sourceMet
}
ndk::ScopedAStatus ModuleUsb::populateConnectedDevicePort(AudioPort* audioPort) {
if (audioPort->ext.getTag() != AudioPortExt::Tag::device) {
LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not a device port";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
auto& devicePort = audioPort->ext.get<AudioPortExt::Tag::device>();
if (devicePort.device.type.connection != AudioDeviceDescription::CONNECTION_USB) {
if (!isUsbDevicePort(*audioPort)) {
LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not a usb device port";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (devicePort.device.address.getTag() != AudioDeviceAddress::Tag::alsa) {
LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not using alsa address";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
auto& alsaAddress = devicePort.device.address.get<AudioDeviceAddress::Tag::alsa>();
if (alsaAddress.size() != 2 || alsaAddress[0] < 0 || alsaAddress[1] < 0) {
LOG(ERROR) << __func__ << ": port id " << audioPort->id << " invalid alsa address";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
const bool isInput = audioPort->flags.getTag() == AudioIoFlags::input;
alsa_device_profile profile;
profile_init(&profile, isInput ? PCM_IN : PCM_OUT);
profile.card = alsaAddress[0];
profile.device = alsaAddress[1];
if (!profile_read_device_info(&profile)) {
LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card
<< ", device=" << profile.device;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
std::vector<AudioChannelLayout> channels = populateChannelMasksFromProfile(&profile, isInput);
std::vector<int> sampleRates = populateSampleRatesFromProfile(&profile);
for (size_t i = 0; i < std::min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) &&
profile.formats[i] != PCM_FORMAT_INVALID;
++i) {
auto audioFormatDescription =
usb::legacy2aidl_pcm_format_AudioFormatDescription(profile.formats[i]);
if (audioFormatDescription.type == AudioFormatType::DEFAULT) {
LOG(WARNING) << __func__ << ": unknown pcm type=" << profile.formats[i];
continue;
}
AudioProfile audioProfile = {.format = audioFormatDescription,
.channelMasks = channels,
.sampleRates = sampleRates};
audioPort->profiles.push_back(std::move(audioProfile));
}
return ndk::ScopedAStatus::ok();
return ModuleAlsa::populateConnectedDevicePort(audioPort);
}
ndk::ScopedAStatus ModuleUsb::checkAudioPatchEndpointsMatch(
@@ -191,15 +113,14 @@ ndk::ScopedAStatus ModuleUsb::checkAudioPatchEndpointsMatch(
void ModuleUsb::onExternalDeviceConnectionChanged(
const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected) {
if (audioPort.ext.getTag() != AudioPortExt::Tag::device) {
if (!isUsbDevicePort(audioPort)) {
return;
}
const auto& address = audioPort.ext.get<AudioPortExt::Tag::device>().device.address;
if (address.getTag() != AudioDeviceAddress::alsa) {
auto profile = alsa::getDeviceProfile(audioPort);
if (!profile.has_value()) {
return;
}
const int card = address.get<AudioDeviceAddress::alsa>()[0];
usb::UsbAlsaMixerControl::getInstance().setDeviceConnectionState(card, getMasterMute(),
usb::UsbAlsaMixerControl::getInstance().setDeviceConnectionState(profile->card, getMasterMute(),
getMasterVolume(), connected);
}

View File

@@ -23,64 +23,20 @@
#include <error/expected_utils.h>
#include "UsbAlsaMixerControl.h"
#include "UsbAlsaUtils.h"
#include "core-impl/Module.h"
#include "core-impl/StreamUsb.h"
extern "C" {
#include "alsa_device_profile.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;
using aidl::android::media::audio::common::AudioDeviceAddress;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::MicrophoneDynamicInfo;
using aidl::android::media::audio::common::MicrophoneInfo;
using android::OK;
using android::status_t;
namespace aidl::android::hardware::audio::core {
StreamUsb::StreamUsb(const Metadata& metadata, StreamContext&& context)
: StreamCommonImpl(metadata, std::move(context)),
mFrameSizeBytes(getContext().getFrameSize()),
mIsInput(isInput(metadata)),
mConfig(maybePopulateConfig(getContext(), mIsInput)) {}
// static
std::optional<struct pcm_config> StreamUsb::maybePopulateConfig(const StreamContext& context,
bool isInput) {
struct pcm_config config;
config.channels = usb::getChannelCountFromChannelMask(context.getChannelLayout(), isInput);
if (config.channels == 0) {
LOG(ERROR) << __func__ << ": invalid channel=" << context.getChannelLayout().toString();
return std::nullopt;
}
config.format = usb::aidl2legacy_AudioFormatDescription_pcm_format(context.getFormat());
if (config.format == PCM_FORMAT_INVALID) {
LOG(ERROR) << __func__ << ": invalid format=" << context.getFormat().toString();
return std::nullopt;
}
config.rate = context.getSampleRate();
if (config.rate == 0) {
LOG(ERROR) << __func__ << ": invalid sample rate=" << config.rate;
return std::nullopt;
}
return config;
}
::android::status_t StreamUsb::init() {
return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
}
const StreamCommonInterface::ConnectedDevices& StreamUsb::getConnectedDevices() const {
std::lock_guard guard(mLock);
return mConnectedDevices;
}
: StreamAlsa(metadata, std::move(context)) {}
ndk::ScopedAStatus StreamUsb::setConnectedDevices(
const std::vector<AudioDevice>& connectedDevices) {
@@ -89,14 +45,19 @@ ndk::ScopedAStatus StreamUsb::setConnectedDevices(
<< ") for input stream";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
std::vector<alsa::DeviceProfile> connectedDeviceProfiles;
for (const auto& connectedDevice : connectedDevices) {
if (connectedDevice.address.getTag() != AudioDeviceAddress::alsa) {
LOG(ERROR) << __func__ << ": bad device address" << connectedDevice.address.toString();
auto profile = alsa::getDeviceProfile(connectedDevice, mIsInput);
if (!profile.has_value()) {
LOG(ERROR) << __func__
<< ": unsupported device address=" << connectedDevice.address.toString();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
connectedDeviceProfiles.push_back(*profile);
}
std::lock_guard guard(mLock);
RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices));
std::lock_guard guard(mLock);
mConnectedDeviceProfiles = std::move(connectedDeviceProfiles);
mConnectedDevicesUpdated.store(true, std::memory_order_release);
return ndk::ScopedAStatus::ok();
}
@@ -119,87 +80,22 @@ ndk::ScopedAStatus StreamUsb::setConnectedDevices(
::android::status_t StreamUsb::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) {
if (mConnectedDevicesUpdated.load(std::memory_order_acquire)) {
// 'setConnectedDevices' has been called. I/O will be restarted.
// 'setConnectedDevices' was called. I/O will be restarted.
*actualFrameCount = 0;
*latencyMs = StreamDescriptor::LATENCY_UNKNOWN;
return ::android::OK;
}
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(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer);
maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get());
} else {
for (auto& proxy : mAlsaDeviceProxies) {
proxy_write(proxy.get(), buffer, bytesToTransfer);
maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get()));
}
}
*actualFrameCount = frameCount;
maxLatency = std::min(maxLatency, static_cast<unsigned>(std::numeric_limits<int32_t>::max()));
*latencyMs = maxLatency;
return ::android::OK;
return StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs);
}
::android::status_t StreamUsb::standby() {
mAlsaDeviceProxies.clear();
return ::android::OK;
}
void StreamUsb::shutdown() {
mAlsaDeviceProxies.clear();
}
::android::status_t StreamUsb::start() {
std::vector<AudioDeviceAddress> connectedDevices;
std::vector<alsa::DeviceProfile> StreamUsb::getDeviceProfiles() {
std::vector<alsa::DeviceProfile> connectedDevices;
{
std::lock_guard guard(mLock);
std::transform(mConnectedDevices.begin(), mConnectedDevices.end(),
std::back_inserter(connectedDevices),
[](const auto& device) { return device.address; });
connectedDevices = mConnectedDeviceProfiles;
mConnectedDevicesUpdated.store(false, std::memory_order_release);
}
decltype(mAlsaDeviceProxies) alsaDeviceProxies;
for (const auto& device : connectedDevices) {
alsa_device_profile profile;
profile_init(&profile, mIsInput ? PCM_IN : PCM_OUT);
profile.card = device.get<AudioDeviceAddress::alsa>()[0];
profile.device = device.get<AudioDeviceAddress::alsa>()[1];
if (!profile_read_device_info(&profile)) {
LOG(ERROR) << __func__
<< ": unable to read device info, device address=" << device.toString();
return ::android::UNKNOWN_ERROR;
}
AlsaDeviceProxy proxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) {
proxy_close(proxy);
free(proxy);
});
// Always ask for alsa configure as required since the configuration should be supported
// by the connected device. That is guaranteed by `setAudioPortConfig` and
// `setAudioPatch`.
if (int err = proxy_prepare(proxy.get(), &profile,
const_cast<struct pcm_config*>(&mConfig.value()),
true /*is_bit_perfect*/);
err != 0) {
LOG(ERROR) << __func__ << ": fail to prepare for device address=" << device.toString()
<< " error=" << err;
return ::android::UNKNOWN_ERROR;
}
if (int err = proxy_open(proxy.get()); err != 0) {
LOG(ERROR) << __func__ << ": failed to open device, address=" << device.toString()
<< " error=" << err;
return ::android::UNKNOWN_ERROR;
}
alsaDeviceProxies.push_back(std::move(proxy));
}
mAlsaDeviceProxies = std::move(alsaDeviceProxies);
return ::android::OK;
return connectedDevices;
}
StreamInUsb::StreamInUsb(const SinkMetadata& sinkMetadata, StreamContext&& context,
@@ -214,9 +110,9 @@ ndk::ScopedAStatus StreamInUsb::getActiveMicrophones(
StreamOutUsb::StreamOutUsb(const SourceMetadata& sourceMetadata, StreamContext&& context,
const std::optional<AudioOffloadInfo>& offloadInfo)
: StreamUsb(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {
mChannelCount = getChannelCount(getContext().getChannelLayout());
}
: StreamUsb(sourceMetadata, std::move(context)),
StreamOut(offloadInfo),
mChannelCount(getChannelCount(getContext().getChannelLayout())) {}
ndk::ScopedAStatus StreamOutUsb::getHwVolume(std::vector<float>* _aidl_return) {
*_aidl_return = mHwVolumes;
@@ -224,17 +120,17 @@ ndk::ScopedAStatus StreamOutUsb::getHwVolume(std::vector<float>* _aidl_return) {
}
ndk::ScopedAStatus StreamOutUsb::setHwVolume(const std::vector<float>& in_channelVolumes) {
// Avoid using mConnectedDeviceProfiles because it requires a lock.
for (const auto& device : getConnectedDevices()) {
if (device.address.getTag() != AudioDeviceAddress::alsa) {
LOG(DEBUG) << __func__ << ": skip as the device address is not alsa";
continue;
}
const int card = device.address.get<AudioDeviceAddress::alsa>()[0];
if (auto result =
usb::UsbAlsaMixerControl::getInstance().setVolumes(card, in_channelVolumes);
!result.isOk()) {
LOG(ERROR) << __func__ << ": failed to set volume for device, card=" << card;
return result;
if (auto deviceProfile = alsa::getDeviceProfile(device, mIsInput);
deviceProfile.has_value()) {
if (auto result = usb::UsbAlsaMixerControl::getInstance().setVolumes(
deviceProfile->card, in_channelVolumes);
!result.isOk()) {
LOG(ERROR) << __func__
<< ": failed to set volume for device address=" << *deviceProfile;
return result;
}
}
}
mHwVolumes = in_channelVolumes;

View File

@@ -17,144 +17,12 @@
#define LOG_TAG "AHAL_UsbAlsaMixerControl"
#include <android-base/logging.h>
#include <cmath>
#include <string>
#include <vector>
#include <android/binder_status.h>
#include "UsbAlsaMixerControl.h"
namespace aidl::android::hardware::audio::core::usb {
//-----------------------------------------------------------------------------
MixerControl::MixerControl(struct mixer_ctl* ctl)
: mCtl(ctl),
mNumValues(mixer_ctl_get_num_values(ctl)),
mMinValue(mixer_ctl_get_range_min(ctl)),
mMaxValue(mixer_ctl_get_range_max(ctl)) {}
unsigned int MixerControl::getNumValues() const {
return mNumValues;
}
int MixerControl::getMaxValue() const {
return mMaxValue;
}
int MixerControl::getMinValue() const {
return mMinValue;
}
int MixerControl::setArray(const void* array, size_t count) {
const std::lock_guard guard(mLock);
return mixer_ctl_set_array(mCtl, array, count);
}
//-----------------------------------------------------------------------------
// static
const std::map<AlsaMixer::Control, std::vector<AlsaMixer::ControlNamesAndExpectedCtlType>>
AlsaMixer::kPossibleControls = {
{AlsaMixer::MASTER_SWITCH, {{"Master Playback Switch", MIXER_CTL_TYPE_BOOL}}},
{AlsaMixer::MASTER_VOLUME, {{"Master Playback Volume", MIXER_CTL_TYPE_INT}}},
{AlsaMixer::HW_VOLUME,
{{"Headphone Playback Volume", MIXER_CTL_TYPE_INT},
{"Headset Playback Volume", MIXER_CTL_TYPE_INT},
{"PCM Playback Volume", MIXER_CTL_TYPE_INT}}}};
// static
std::map<AlsaMixer::Control, std::shared_ptr<MixerControl>> AlsaMixer::initializeMixerControls(
struct mixer* mixer) {
std::map<AlsaMixer::Control, std::shared_ptr<MixerControl>> mixerControls;
std::string mixerCtlNames;
for (const auto& [control, possibleCtls] : kPossibleControls) {
for (const auto& [ctlName, expectedCtlType] : possibleCtls) {
struct mixer_ctl* ctl = mixer_get_ctl_by_name(mixer, ctlName.c_str());
if (ctl != nullptr && mixer_ctl_get_type(ctl) == expectedCtlType) {
mixerControls.emplace(control, std::make_unique<MixerControl>(ctl));
if (!mixerCtlNames.empty()) {
mixerCtlNames += ",";
}
mixerCtlNames += ctlName;
break;
}
}
}
LOG(DEBUG) << __func__ << ": available mixer control names=[" << mixerCtlNames << "]";
return mixerControls;
}
AlsaMixer::AlsaMixer(struct mixer* mixer)
: mMixer(mixer), mMixerControls(initializeMixerControls(mMixer)) {}
AlsaMixer::~AlsaMixer() {
mixer_close(mMixer);
}
namespace {
int volumeFloatToInteger(float fValue, int maxValue, int minValue) {
return minValue + std::ceil((maxValue - minValue) * fValue);
}
} // namespace
ndk::ScopedAStatus AlsaMixer::setMasterMute(bool muted) {
auto it = mMixerControls.find(AlsaMixer::MASTER_SWITCH);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
const int numValues = it->second->getNumValues();
std::vector<int> values(numValues, muted ? 0 : 1);
if (int err = it->second->setArray(values.data(), numValues); err != 0) {
LOG(ERROR) << __func__ << ": failed to set master mute, err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus AlsaMixer::setMasterVolume(float volume) {
auto it = mMixerControls.find(AlsaMixer::MASTER_VOLUME);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
const int numValues = it->second->getNumValues();
std::vector<int> values(numValues, volumeFloatToInteger(volume, it->second->getMaxValue(),
it->second->getMinValue()));
if (int err = it->second->setArray(values.data(), numValues); err != 0) {
LOG(ERROR) << __func__ << ": failed to set master volume, err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus AlsaMixer::setVolumes(std::vector<float> volumes) {
auto it = mMixerControls.find(AlsaMixer::HW_VOLUME);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
const int numValues = it->second->getNumValues();
if (numValues < 0) {
LOG(FATAL) << __func__ << ": negative number of values: " << numValues;
}
const int maxValue = it->second->getMaxValue();
const int minValue = it->second->getMinValue();
std::vector<int> values;
size_t i = 0;
for (; i < static_cast<size_t>(numValues) && i < values.size(); ++i) {
values.emplace_back(volumeFloatToInteger(volumes[i], maxValue, minValue));
}
if (int err = it->second->setArray(values.data(), values.size()); err != 0) {
LOG(ERROR) << __func__ << ": failed to set volume, err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
return ndk::ScopedAStatus::ok();
}
//-----------------------------------------------------------------------------
// static
UsbAlsaMixerControl& UsbAlsaMixerControl::getInstance() {
static UsbAlsaMixerControl gInstance;
@@ -170,7 +38,7 @@ void UsbAlsaMixerControl::setDeviceConnectionState(int card, bool masterMuted, f
PLOG(ERROR) << __func__ << ": failed to open mixer for card=" << card;
return;
}
auto alsaMixer = std::make_shared<AlsaMixer>(mixer);
auto alsaMixer = std::make_shared<alsa::Mixer>(mixer);
alsaMixer->setMasterMute(masterMuted);
alsaMixer->setMasterVolume(masterVolume);
const std::lock_guard guard(mLock);
@@ -209,7 +77,7 @@ ndk::ScopedAStatus UsbAlsaMixerControl::setMasterVolume(float volume) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, std::vector<float> volumes) {
ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, const std::vector<float>& volumes) {
auto alsaMixer = getAlsaMixer(card);
if (alsaMixer == nullptr) {
LOG(ERROR) << __func__ << ": no mixer control found for card=" << card;
@@ -218,13 +86,13 @@ ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, std::vector<float>
return alsaMixer->setVolumes(volumes);
}
std::shared_ptr<AlsaMixer> UsbAlsaMixerControl::getAlsaMixer(int card) {
std::shared_ptr<alsa::Mixer> UsbAlsaMixerControl::getAlsaMixer(int card) {
const std::lock_guard guard(mLock);
const auto it = mMixerControls.find(card);
return it == mMixerControls.end() ? nullptr : it->second;
}
std::map<int, std::shared_ptr<AlsaMixer>> UsbAlsaMixerControl::getAlsaMixers() {
std::map<int, std::shared_ptr<alsa::Mixer>> UsbAlsaMixerControl::getAlsaMixers() {
const std::lock_guard guard(mLock);
return mMixerControls;
}

View File

@@ -19,67 +19,15 @@
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <vector>
#include <android-base/thread_annotations.h>
#include <android/binder_auto_utils.h>
extern "C" {
#include <tinyalsa/mixer.h>
}
#include "alsa/Mixer.h"
namespace aidl::android::hardware::audio::core::usb {
class MixerControl {
public:
explicit MixerControl(struct mixer_ctl* ctl);
unsigned int getNumValues() const;
int getMaxValue() const;
int getMinValue() const;
int setArray(const void* array, size_t count);
private:
std::mutex mLock;
// The mixer_ctl object is owned by ALSA and will be released when the mixer is closed.
struct mixer_ctl* mCtl GUARDED_BY(mLock);
const unsigned int mNumValues;
const int mMinValue;
const int mMaxValue;
};
class AlsaMixer {
public:
explicit AlsaMixer(struct mixer* mixer);
~AlsaMixer();
bool isValid() const { return mMixer != nullptr; }
ndk::ScopedAStatus setMasterMute(bool muted);
ndk::ScopedAStatus setMasterVolume(float volume);
ndk::ScopedAStatus setVolumes(std::vector<float> volumes);
private:
enum Control {
MASTER_SWITCH,
MASTER_VOLUME,
HW_VOLUME,
};
using ControlNamesAndExpectedCtlType = std::pair<std::string, enum mixer_ctl_type>;
static const std::map<Control, std::vector<ControlNamesAndExpectedCtlType>> kPossibleControls;
static std::map<Control, std::shared_ptr<MixerControl>> initializeMixerControls(
struct mixer* mixer);
// The mixer object is owned by ALSA and will be released when the mixer is closed.
struct mixer* mMixer;
// `mMixerControls` will only be initialized in constructor. After that, it wil only be
// read but not be modified.
const std::map<Control, std::shared_ptr<MixerControl>> mMixerControls;
};
class UsbAlsaMixerControl {
public:
static UsbAlsaMixerControl& getInstance();
@@ -91,16 +39,16 @@ class UsbAlsaMixerControl {
ndk::ScopedAStatus setMasterMute(bool muted);
ndk::ScopedAStatus setMasterVolume(float volume);
// The volume settings can be different on sound cards. It is controlled by streams.
ndk::ScopedAStatus setVolumes(int card, std::vector<float> volumes);
ndk::ScopedAStatus setVolumes(int card, const std::vector<float>& volumes);
private:
std::shared_ptr<AlsaMixer> getAlsaMixer(int card);
std::map<int, std::shared_ptr<AlsaMixer>> getAlsaMixers();
std::shared_ptr<alsa::Mixer> getAlsaMixer(int card);
std::map<int, std::shared_ptr<alsa::Mixer>> getAlsaMixers();
std::mutex mLock;
// A map whose key is the card number and value is a shared pointer to corresponding
// AlsaMixer object.
std::map<int, std::shared_ptr<AlsaMixer>> mMixerControls GUARDED_BY(mLock);
std::map<int, std::shared_ptr<alsa::Mixer>> mMixerControls GUARDED_BY(mLock);
};
} // namespace aidl::android::hardware::audio::core::usb

View File

@@ -1,181 +0,0 @@
/*
* 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 <map>
#include <set>
#include <Utils.h>
#include <aidl/android/media/audio/common/AudioFormatType.h>
#include <aidl/android/media/audio/common/PcmType.h>
#include "UsbAlsaUtils.h"
#include "core-impl/utils.h"
using aidl::android::hardware::audio::common::getChannelCount;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::PcmType;
namespace aidl::android::hardware::audio::core::usb {
namespace {
using AudioChannelCountToMaskMap = std::map<unsigned int, AudioChannelLayout>;
using AudioFormatDescToPcmFormatMap = std::map<AudioFormatDescription, enum pcm_format>;
using PcmFormatToAudioFormatDescMap = std::map<enum pcm_format, AudioFormatDescription>;
static const AudioChannelLayout INVALID_CHANNEL_LAYOUT =
AudioChannelLayout::make<AudioChannelLayout::Tag::invalid>(0);
#define DEFINE_CHANNEL_LAYOUT_MASK(n) \
AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(AudioChannelLayout::LAYOUT_##n)
static const std::set<AudioChannelLayout> SUPPORTED_OUT_CHANNEL_LAYOUTS = {
DEFINE_CHANNEL_LAYOUT_MASK(MONO), DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
DEFINE_CHANNEL_LAYOUT_MASK(2POINT1), DEFINE_CHANNEL_LAYOUT_MASK(QUAD),
DEFINE_CHANNEL_LAYOUT_MASK(PENTA), DEFINE_CHANNEL_LAYOUT_MASK(5POINT1),
DEFINE_CHANNEL_LAYOUT_MASK(6POINT1), DEFINE_CHANNEL_LAYOUT_MASK(7POINT1),
DEFINE_CHANNEL_LAYOUT_MASK(7POINT1POINT4), DEFINE_CHANNEL_LAYOUT_MASK(22POINT2),
};
static const std::set<AudioChannelLayout> SUPPORTED_IN_CHANNEL_LAYOUTS = {
DEFINE_CHANNEL_LAYOUT_MASK(MONO),
DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
};
#define DEFINE_CHANNEL_INDEX_MASK(n) \
AudioChannelLayout::make<AudioChannelLayout::Tag::indexMask>(AudioChannelLayout::INDEX_MASK_##n)
static const std::set<AudioChannelLayout> SUPPORTED_INDEX_CHANNEL_LAYOUTS = {
DEFINE_CHANNEL_INDEX_MASK(1), DEFINE_CHANNEL_INDEX_MASK(2), DEFINE_CHANNEL_INDEX_MASK(3),
DEFINE_CHANNEL_INDEX_MASK(4), DEFINE_CHANNEL_INDEX_MASK(5), DEFINE_CHANNEL_INDEX_MASK(6),
DEFINE_CHANNEL_INDEX_MASK(7), DEFINE_CHANNEL_INDEX_MASK(8), DEFINE_CHANNEL_INDEX_MASK(9),
DEFINE_CHANNEL_INDEX_MASK(10), DEFINE_CHANNEL_INDEX_MASK(11), DEFINE_CHANNEL_INDEX_MASK(12),
DEFINE_CHANNEL_INDEX_MASK(13), DEFINE_CHANNEL_INDEX_MASK(14), DEFINE_CHANNEL_INDEX_MASK(15),
DEFINE_CHANNEL_INDEX_MASK(16), DEFINE_CHANNEL_INDEX_MASK(17), DEFINE_CHANNEL_INDEX_MASK(18),
DEFINE_CHANNEL_INDEX_MASK(19), DEFINE_CHANNEL_INDEX_MASK(20), DEFINE_CHANNEL_INDEX_MASK(21),
DEFINE_CHANNEL_INDEX_MASK(22), DEFINE_CHANNEL_INDEX_MASK(23), DEFINE_CHANNEL_INDEX_MASK(24),
};
static AudioChannelCountToMaskMap make_ChannelCountToMaskMap(
const std::set<AudioChannelLayout>& channelMasks) {
AudioChannelCountToMaskMap channelMaskToCountMap;
for (const auto& channelMask : channelMasks) {
channelMaskToCountMap.emplace(getChannelCount(channelMask), channelMask);
}
return channelMaskToCountMap;
}
const AudioChannelCountToMaskMap& getSupportedChannelOutLayoutMap() {
static const AudioChannelCountToMaskMap outLayouts =
make_ChannelCountToMaskMap(SUPPORTED_OUT_CHANNEL_LAYOUTS);
return outLayouts;
}
const AudioChannelCountToMaskMap& getSupportedChannelInLayoutMap() {
static const AudioChannelCountToMaskMap inLayouts =
make_ChannelCountToMaskMap(SUPPORTED_IN_CHANNEL_LAYOUTS);
return inLayouts;
}
const AudioChannelCountToMaskMap& getSupportedChannelIndexLayoutMap() {
static const AudioChannelCountToMaskMap indexLayouts =
make_ChannelCountToMaskMap(SUPPORTED_INDEX_CHANNEL_LAYOUTS);
return indexLayouts;
}
AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
AudioFormatDescription result;
result.type = type;
return result;
}
AudioFormatDescription make_AudioFormatDescription(PcmType pcm) {
auto result = make_AudioFormatDescription(AudioFormatType::PCM);
result.pcm = pcm;
return result;
}
const AudioFormatDescToPcmFormatMap& getAudioFormatDescriptorToPcmFormatMap() {
static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = {
{make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8},
{make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE},
{make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE},
{make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE},
{make_AudioFormatDescription(PcmType::INT_32_BIT), PCM_FORMAT_S32_LE},
{make_AudioFormatDescription(PcmType::FLOAT_32_BIT), PCM_FORMAT_FLOAT_LE},
};
return formatDescToPcmFormatMap;
}
static PcmFormatToAudioFormatDescMap make_PcmFormatToAudioFormatDescMap(
const AudioFormatDescToPcmFormatMap& formatDescToPcmFormatMap) {
PcmFormatToAudioFormatDescMap result;
for (const auto& formatPair : formatDescToPcmFormatMap) {
result.emplace(formatPair.second, formatPair.first);
}
return result;
}
const PcmFormatToAudioFormatDescMap& getPcmFormatToAudioFormatDescMap() {
static const PcmFormatToAudioFormatDescMap pcmFormatToFormatDescMap =
make_PcmFormatToAudioFormatDescMap(getAudioFormatDescriptorToPcmFormatMap());
return pcmFormatToFormatDescMap;
}
} // namespace
AudioChannelLayout getChannelLayoutMaskFromChannelCount(unsigned int channelCount, int isInput) {
return findValueOrDefault(
isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
channelCount, INVALID_CHANNEL_LAYOUT);
}
AudioChannelLayout getChannelIndexMaskFromChannelCount(unsigned int channelCount) {
return findValueOrDefault(getSupportedChannelIndexLayoutMap(), channelCount,
INVALID_CHANNEL_LAYOUT);
}
unsigned int getChannelCountFromChannelMask(const AudioChannelLayout& channelMask, bool isInput) {
switch (channelMask.getTag()) {
case AudioChannelLayout::Tag::layoutMask: {
return findKeyOrDefault(
isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
(unsigned int)getChannelCount(channelMask), 0u /*defaultValue*/);
}
case AudioChannelLayout::Tag::indexMask: {
return findKeyOrDefault(getSupportedChannelIndexLayoutMap(),
(unsigned int)getChannelCount(channelMask),
0u /*defaultValue*/);
}
case AudioChannelLayout::Tag::none:
case AudioChannelLayout::Tag::invalid:
case AudioChannelLayout::Tag::voiceMask:
default:
return 0;
}
}
AudioFormatDescription legacy2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy) {
return findValueOrDefault(getPcmFormatToAudioFormatDescMap(), legacy, AudioFormatDescription());
}
pcm_format aidl2legacy_AudioFormatDescription_pcm_format(const AudioFormatDescription& aidl) {
return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID);
}
} // namespace aidl::android::hardware::audio::core::usb

View File

@@ -1,39 +0,0 @@
/*
* 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/media/audio/common/AudioChannelLayout.h>
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
extern "C" {
#include <tinyalsa/pcm.h>
}
namespace aidl::android::hardware::audio::core::usb {
::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount(
unsigned int channelCount, int isInput);
::aidl::android::media::audio::common::AudioChannelLayout getChannelIndexMaskFromChannelCount(
unsigned int channelCount);
unsigned int getChannelCountFromChannelMask(
const ::aidl::android::media::audio::common::AudioChannelLayout& channelMask, bool isInput);
::aidl::android::media::audio::common::AudioFormatDescription
legacy2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy);
pcm_format aidl2legacy_AudioFormatDescription_pcm_format(
const ::aidl::android::media::audio::common::AudioFormatDescription& aidl);
} // namespace aidl::android::hardware::audio::core::usb