Merge "AOSP AIDL USB audio HAL implementation."

This commit is contained in:
Treehugger Robot 2023-02-06 17:14:13 +00:00 committed by Gerrit Code Review
commit 9275a96448
15 changed files with 937 additions and 10 deletions

View file

@ -104,6 +104,30 @@ constexpr bool isTelephonyDeviceType(
device == ::aidl::android::media::audio::common::AudioDeviceType::OUT_TELEPHONY_TX;
}
constexpr bool isUsbInputDeviceType(::aidl::android::media::audio::common::AudioDeviceType type) {
switch (type) {
case ::aidl::android::media::audio::common::AudioDeviceType::IN_DOCK:
case ::aidl::android::media::audio::common::AudioDeviceType::IN_ACCESSORY:
case ::aidl::android::media::audio::common::AudioDeviceType::IN_DEVICE:
case ::aidl::android::media::audio::common::AudioDeviceType::IN_HEADSET:
return true;
default:
return false;
}
}
constexpr bool isUsbOutputtDeviceType(::aidl::android::media::audio::common::AudioDeviceType type) {
switch (type) {
case ::aidl::android::media::audio::common::AudioDeviceType::OUT_DOCK:
case ::aidl::android::media::audio::common::AudioDeviceType::OUT_ACCESSORY:
case ::aidl::android::media::audio::common::AudioDeviceType::OUT_DEVICE:
case ::aidl::android::media::audio::common::AudioDeviceType::OUT_HEADSET:
return true;
default:
return false;
}
}
constexpr bool isValidAudioMode(::aidl::android::media::audio::common::AudioMode mode) {
return std::find(kValidAudioModes.begin(), kValidAudioModes.end(), mode) !=
kValidAudioModes.end();

View file

@ -11,12 +11,14 @@ cc_defaults {
name: "aidlaudioservice_defaults",
vendor: true,
shared_libs: [
"libalsautilsv2",
"libaudioaidlcommon",
"libbase",
"libbinder_ndk",
"libcutils",
"libfmq",
"libstagefright_foundation",
"libtinyalsav2",
"libutils",
"libxml2",
"android.hardware.common-V2-ndk",
@ -71,6 +73,9 @@ cc_library_static {
"Stream.cpp",
"StreamStub.cpp",
"Telephony.cpp",
"usb/ModuleUsb.cpp",
"usb/StreamUsb.cpp",
"usb/UsbAlsaUtils.cpp",
],
generated_sources: [
"audio_policy_configuration_aidl_default",

View file

@ -27,8 +27,10 @@
#include "core-impl/Bluetooth.h"
#include "core-impl/Module.h"
#include "core-impl/ModuleUsb.h"
#include "core-impl/SoundDose.h"
#include "core-impl/StreamStub.h"
#include "core-impl/StreamUsb.h"
#include "core-impl/Telephony.h"
#include "core-impl/utils.h"
@ -104,6 +106,42 @@ bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& forma
} // namespace
// static
std::shared_ptr<Module> Module::createInstance(Type type) {
switch (type) {
case Module::Type::USB:
return ndk::SharedRefBase::make<ModuleUsb>(type);
case Type::DEFAULT:
case Type::R_SUBMIX:
default:
return ndk::SharedRefBase::make<Module>(type);
}
}
// static
StreamIn::CreateInstance Module::getStreamInCreator(Type type) {
switch (type) {
case Type::USB:
return StreamInUsb::createInstance;
case Type::DEFAULT:
case Type::R_SUBMIX:
default:
return StreamInStub::createInstance;
}
}
// static
StreamOut::CreateInstance Module::getStreamOutCreator(Type type) {
switch (type) {
case Type::USB:
return StreamOutUsb::createInstance;
case Type::DEFAULT:
case Type::R_SUBMIX:
default:
return StreamOutStub::createInstance;
}
}
void Module::cleanUpPatch(int32_t patchId) {
erase_all_values(mPatches, std::set<int32_t>{patchId});
}
@ -153,6 +191,7 @@ ndk::ScopedAStatus Module::createStreamContext(
std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
portConfigIt->format.value(), portConfigIt->channelMask.value(),
portConfigIt->sampleRate.value().value,
std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
asyncCallback, outEventCallback, params);
if (temp.isValid()) {
@ -261,6 +300,7 @@ internal::Configuration& Module::getConfig() {
break;
case Type::USB:
mConfig = std::move(internal::getUsbConfiguration());
break;
}
}
return *mConfig;
@ -401,6 +441,8 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA
if (!mDebug.simulateDeviceConnections) {
// In a real HAL here we would attempt querying the profiles from the device.
LOG(ERROR) << __func__ << ": failed to query supported device profiles";
// TODO: Check the return value when it is ready for actual devices.
populateConnectedDevicePort(&connectedPort);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
@ -560,10 +602,9 @@ ndk::ScopedAStatus Module::openInputStream(const OpenInputStreamArguments& in_ar
}
context.fillDescriptor(&_aidl_return->desc);
std::shared_ptr<StreamIn> stream;
// TODO: Add a mapping from module instance names to a corresponding 'createInstance'.
if (auto status = StreamInStub::createInstance(in_args.sinkMetadata, std::move(context),
mConfig->microphones, &stream);
!status.isOk()) {
ndk::ScopedAStatus status = getStreamInCreator(mType)(in_args.sinkMetadata, std::move(context),
mConfig->microphones, &stream);
if (!status.isOk()) {
return status;
}
StreamWrapper streamWrapper(stream);
@ -615,10 +656,9 @@ ndk::ScopedAStatus Module::openOutputStream(const OpenOutputStreamArguments& in_
}
context.fillDescriptor(&_aidl_return->desc);
std::shared_ptr<StreamOut> stream;
// TODO: Add a mapping from module instance names to a corresponding 'createInstance'.
if (auto status = StreamOutStub::createInstance(in_args.sourceMetadata, std::move(context),
in_args.offloadInfo, &stream);
!status.isOk()) {
ndk::ScopedAStatus status = getStreamOutCreator(mType)(
in_args.sourceMetadata, std::move(context), in_args.offloadInfo, &stream);
if (!status.isOk()) {
return status;
}
StreamWrapper streamWrapper(stream);
@ -696,6 +736,10 @@ ndk::ScopedAStatus Module::setAudioPatch(const AudioPatch& in_requested, AudioPa
}
}
if (auto status = checkAudioPatchEndpointsMatch(sources, sinks); !status.isOk()) {
return status;
}
auto& patches = getConfig().patches;
auto existing = patches.end();
std::optional<decltype(mPatches)> patchesBackup;
@ -1190,4 +1234,16 @@ bool Module::isMmapSupported() {
return mIsMmapSupported.value();
}
ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort __unused) {
LOG(DEBUG) << __func__ << ": do nothing and return ok";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Module::checkAudioPatchEndpointsMatch(
const std::vector<AudioPortConfig*>& sources __unused,
const std::vector<AudioPortConfig*>& sinks __unused) {
LOG(DEBUG) << __func__ << ": do nothing and return ok";
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::audio::core

View file

@ -22,6 +22,7 @@
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::AudioOffloadInfo;
namespace aidl::android::hardware::audio::core {
@ -68,6 +69,11 @@ DriverStub::DriverStub(const StreamContext& context, bool isInput)
return ::android::OK;
}
::android::status_t DriverStub::setConnectedDevices(
const std::vector<AudioDevice>& connectedDevices __unused) {
return ::android::OK;
}
// static
ndk::ScopedAStatus StreamInStub::createInstance(const SinkMetadata& sinkMetadata,
StreamContext&& context,

View file

@ -35,6 +35,10 @@ class Module : public BnModule {
explicit Module(Type type) : mType(type) {}
static std::shared_ptr<Module> createInstance(Type type);
static StreamIn::CreateInstance getStreamInCreator(Type type);
static StreamOut::CreateInstance getStreamOutCreator(Type type);
private:
struct VendorDebug {
static const std::string kForceTransientBurstName;
@ -163,6 +167,17 @@ class Module : public BnModule {
std::shared_ptr<sounddose::ISoundDose> mSoundDose;
ndk::SpAIBinder mSoundDoseBinder;
std::optional<bool> mIsMmapSupported;
protected:
// 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(
::aidl::android::media::audio::common::AudioPort* audioPort);
// If the module finds that the patch endpoints configurations are not matched, the returned
// error code must correspond to the errors of `IModule.setAudioPatch` method.
virtual ndk::ScopedAStatus checkAudioPatchEndpointsMatch(
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources,
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks);
};
} // namespace aidl::android::hardware::audio::core

View file

@ -0,0 +1,47 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "core-impl/Module.h"
namespace aidl::android::hardware::audio::core {
class ModuleUsb : public Module {
public:
explicit ModuleUsb(Module::Type type) : Module(type) {}
private:
// IModule interfaces
ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
ndk::ScopedAStatus getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) override;
ndk::ScopedAStatus getMasterMute(bool* _aidl_return) override;
ndk::ScopedAStatus setMasterMute(bool in_mute) override;
ndk::ScopedAStatus getMasterVolume(float* _aidl_return) override;
ndk::ScopedAStatus setMasterVolume(float in_volume) override;
ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
ndk::ScopedAStatus setMicMute(bool in_mute) override;
// Module interfaces
ndk::ScopedAStatus populateConnectedDevicePort(
::aidl::android::media::audio::common::AudioPort* audioPort) override;
ndk::ScopedAStatus checkAudioPatchEndpointsMatch(
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources,
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks)
override;
};
} // namespace aidl::android::hardware::audio::core

View file

@ -77,7 +77,8 @@ class StreamContext {
StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
const ::aidl::android::media::audio::common::AudioFormatDescription& format,
const ::aidl::android::media::audio::common::AudioChannelLayout& channelLayout,
std::unique_ptr<DataMQ> dataMQ, std::shared_ptr<IStreamCallback> asyncCallback,
int sampleRate, std::unique_ptr<DataMQ> dataMQ,
std::shared_ptr<IStreamCallback> asyncCallback,
std::shared_ptr<IStreamOutEventCallback> outEventCallback,
DebugParameters debugParameters)
: mCommandMQ(std::move(commandMQ)),
@ -85,6 +86,7 @@ class StreamContext {
mReplyMQ(std::move(replyMQ)),
mFormat(format),
mChannelLayout(channelLayout),
mSampleRate(sampleRate),
mDataMQ(std::move(dataMQ)),
mAsyncCallback(asyncCallback),
mOutEventCallback(outEventCallback),
@ -95,6 +97,7 @@ class StreamContext {
mReplyMQ(std::move(other.mReplyMQ)),
mFormat(other.mFormat),
mChannelLayout(other.mChannelLayout),
mSampleRate(other.mSampleRate),
mDataMQ(std::move(other.mDataMQ)),
mAsyncCallback(std::move(other.mAsyncCallback)),
mOutEventCallback(std::move(other.mOutEventCallback)),
@ -105,6 +108,7 @@ class StreamContext {
mReplyMQ = std::move(other.mReplyMQ);
mFormat = std::move(other.mFormat);
mChannelLayout = std::move(other.mChannelLayout);
mSampleRate = other.mSampleRate;
mDataMQ = std::move(other.mDataMQ);
mAsyncCallback = std::move(other.mAsyncCallback);
mOutEventCallback = std::move(other.mOutEventCallback);
@ -131,6 +135,7 @@ class StreamContext {
}
ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; }
int getSampleRate() const { return mSampleRate; }
bool isValid() const;
void reset();
@ -140,6 +145,7 @@ class StreamContext {
std::unique_ptr<ReplyMQ> mReplyMQ;
::aidl::android::media::audio::common::AudioFormatDescription mFormat;
::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout;
int mSampleRate;
std::unique_ptr<DataMQ> mDataMQ;
std::shared_ptr<IStreamCallback> mAsyncCallback;
std::shared_ptr<IStreamOutEventCallback> mOutEventCallback; // Only used by output streams
@ -151,6 +157,11 @@ struct DriverInterface {
virtual ~DriverInterface() = default;
// This function is called once, on the main thread, before starting the worker thread.
virtual ::android::status_t init() = 0;
// This function is called from Binder pool thread. It must be done in a thread-safe manner
// if this method and other methods in this interface share data.
virtual ::android::status_t setConnectedDevices(
const std::vector<::aidl::android::media::audio::common::AudioDevice>&
connectedDevices) = 0;
// All the functions below are called on the worker thread.
virtual ::android::status_t drain(StreamDescriptor::DrainMode mode) = 0;
virtual ::android::status_t flush() = 0;
@ -370,6 +381,7 @@ class StreamCommonImpl : public StreamCommonInterface {
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
mWorker->setIsConnected(!devices.empty());
mConnectedDevices = devices;
mDriver->setConnectedDevices(devices);
}
ndk::ScopedAStatus updateMetadata(const Metadata& metadata);

View file

@ -24,6 +24,9 @@ class DriverStub : public DriverInterface {
public:
DriverStub(const StreamContext& context, bool isInput);
::android::status_t init() override;
::android::status_t setConnectedDevices(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& connectedDevices)
override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;

View file

@ -0,0 +1,94 @@
/*
* 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 <mutex>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include "core-impl/Stream.h"
extern "C" {
#include <tinyalsa/pcm.h>
#include "alsa_device_proxy.h"
}
namespace aidl::android::hardware::audio::core {
class DriverUsb : public DriverInterface {
public:
DriverUsb(const StreamContext& context, bool isInput);
::android::status_t init() override;
::android::status_t setConnectedDevices(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& connectedDevices)
override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
::android::status_t standby() override;
private:
::android::status_t exitStandby();
std::mutex mLock;
const size_t mFrameSizeBytes;
std::optional<struct pcm_config> mConfig;
const bool mIsInput;
// Cached device addresses for connected devices.
std::vector<::aidl::android::media::audio::common::AudioDeviceAddress> mConnectedDevices
GUARDED_BY(mLock);
std::vector<std::shared_ptr<alsa_device_proxy>> mAlsaDeviceProxies GUARDED_BY(mLock);
bool mIsStandby = false;
};
class StreamInUsb final : public StreamIn {
ndk::ScopedAStatus getActiveMicrophones(
std::vector<MicrophoneDynamicInfo>* _aidl_return) override;
public:
static ndk::ScopedAStatus createInstance(
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context, const std::vector<MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result);
private:
friend class ndk::SharedRefBase;
StreamInUsb(const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context, const std::vector<MicrophoneInfo>& microphones);
};
class StreamOutUsb final : public StreamOut {
public:
static ndk::ScopedAStatus createInstance(
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);
private:
friend class ndk::SharedRefBase;
StreamOutUsb(const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
StreamContext&& context,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
};
} // namespace aidl::android::hardware::audio::core

View file

@ -17,6 +17,7 @@
#pragma once
#include <algorithm>
#include <map>
#include <set>
#include <vector>
@ -101,4 +102,21 @@ std::vector<T*> selectByIds(std::vector<T>& v, const std::vector<int32_t>& ids,
return result;
}
// Assuming that M is a map whose keys' type is K and values' type is V,
// return the corresponding value of the given key from the map or default
// value if the key is not found.
template <typename M, typename K, typename V>
auto findValueOrDefault(const M& m, const K& key, V defaultValue) {
auto it = m.find(key);
return it == m.end() ? defaultValue : it->second;
}
// Assuming that M is a map whose keys' type is K, return the given key if it
// is found from the map or default value.
template <typename M, typename K>
auto findKeyOrDefault(const M& m, const K& key, K defaultValue) {
auto it = m.find(key);
return it == m.end() ? defaultValue : key;
}
} // namespace aidl::android::hardware::audio::core

View file

@ -25,9 +25,11 @@
#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.
@ -48,7 +50,7 @@ int main() {
// Make modules
auto createModule = [](Module::Type type, const std::string& instance) {
auto module = ndk::SharedRefBase::make<Module>(type);
auto module = Module::createInstance(type);
ndk::SpAIBinder moduleBinder = module->asBinder();
const std::string moduleName = std::string(Module::descriptor).append("/").append(instance);
AIBinder_setMinSchedulerPolicy(moduleBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);

View file

@ -0,0 +1,183 @@
/*
* 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_ModuleUsb"
#include <vector>
#include <Utils.h>
#include <android-base/logging.h>
#include <tinyalsa/asoundlib.h>
#include "UsbAlsaUtils.h"
#include "core-impl/ModuleUsb.h"
extern "C" {
#include "alsa_device_profile.h"
}
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::AudioPort;
using aidl::android::media::audio::common::AudioPortConfig;
using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::AudioProfile;
using android::hardware::audio::common::isUsbInputDeviceType;
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;
}
} // namespace
ndk::ScopedAStatus ModuleUsb::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
*_aidl_return = nullptr;
LOG(DEBUG) << __func__ << ": returning null";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ModuleUsb::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
*_aidl_return = nullptr;
LOG(DEBUG) << __func__ << ": returning null";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ModuleUsb::getMasterMute(bool* _aidl_return __unused) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus ModuleUsb::setMasterMute(bool in_mute __unused) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus ModuleUsb::getMasterVolume(float* _aidl_return __unused) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus ModuleUsb::setMasterVolume(float in_volume __unused) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus ModuleUsb::getMicMute(bool* _aidl_return __unused) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus ModuleUsb::setMicMute(bool in_mute __unused) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
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) {
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 = isUsbInputDeviceType(devicePort.device.type.type);
alsa_device_profile profile;
profile_init(&profile, isInput ? PCM_IN : PCM_OUT);
if (!profile_read_device_info(&profile)) {
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] != 0;
++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();
}
ndk::ScopedAStatus ModuleUsb::checkAudioPatchEndpointsMatch(
const std::vector<AudioPortConfig*>& sources, const std::vector<AudioPortConfig*>& sinks) {
for (const auto& source : sources) {
for (const auto& sink : sinks) {
if (source->sampleRate != sink->sampleRate ||
source->channelMask != sink->channelMask || source->format != sink->format) {
LOG(ERROR) << __func__
<< ": mismatch port configuration, source=" << source->toString()
<< ", sink=" << sink->toString();
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
}
}
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::audio::core

View file

@ -0,0 +1,242 @@
/*
* 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_StreamUsb"
#include <android-base/logging.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::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;
namespace aidl::android::hardware::audio::core {
DriverUsb::DriverUsb(const StreamContext& context, bool isInput)
: mFrameSizeBytes(context.getFrameSize()), mIsInput(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;
}
config.format = usb::aidl2legacy_AudioFormatDescription_pcm_format(context.getFormat());
if (config.format == PCM_FORMAT_INVALID) {
LOG(ERROR) << __func__ << ": invalid format=" << context.getFormat().toString();
return;
}
config.rate = context.getSampleRate();
if (config.rate == 0) {
LOG(ERROR) << __func__ << ": invalid sample rate=" << config.rate;
return;
}
mConfig = config;
}
::android::status_t DriverUsb::init() {
return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
}
::android::status_t DriverUsb::setConnectedDevices(
const std::vector<AudioDevice>& connectedDevices) {
if (mIsInput && connectedDevices.size() > 1) {
LOG(ERROR) << __func__ << ": wrong device size(" << connectedDevices.size()
<< ") for input stream";
return ::android::BAD_VALUE;
}
for (const auto& connectedDevice : connectedDevices) {
if (connectedDevice.address.getTag() != AudioDeviceAddress::alsa) {
LOG(ERROR) << __func__ << ": bad device address" << connectedDevice.address.toString();
return ::android::BAD_VALUE;
}
}
std::lock_guard guard(mLock);
mAlsaDeviceProxies.clear();
mConnectedDevices.clear();
for (const auto& connectedDevice : connectedDevices) {
mConnectedDevices.push_back(connectedDevice.address);
}
return ::android::OK;
}
::android::status_t DriverUsb::drain(StreamDescriptor::DrainMode) {
usleep(1000);
return ::android::OK;
}
::android::status_t DriverUsb::flush() {
usleep(1000);
return ::android::OK;
}
::android::status_t DriverUsb::pause() {
usleep(1000);
return ::android::OK;
}
::android::status_t DriverUsb::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) {
if (!mConfig.has_value() || mConnectedDevices.empty()) {
return ::android::NO_INIT;
}
if (mIsStandby) {
if (::android::status_t status = exitStandby(); status != ::android::OK) {
return status;
}
}
std::vector<std::shared_ptr<alsa_device_proxy>> alsaDeviceProxies;
{
std::lock_guard guard(mLock);
alsaDeviceProxies = mAlsaDeviceProxies;
}
const size_t bytesToTransfer = frameCount * mFrameSizeBytes;
if (mIsInput) {
// For input case, only support single device.
proxy_read(alsaDeviceProxies[0].get(), buffer, bytesToTransfer);
} else {
for (auto& proxy : alsaDeviceProxies) {
proxy_write(proxy.get(), buffer, bytesToTransfer);
}
}
*actualFrameCount = frameCount;
*latencyMs = Module::kLatencyMs;
return ::android::OK;
}
::android::status_t DriverUsb::standby() {
if (!mIsStandby) {
std::lock_guard guard(mLock);
mAlsaDeviceProxies.clear();
mIsStandby = true;
}
return ::android::OK;
}
::android::status_t DriverUsb::exitStandby() {
std::vector<AudioDeviceAddress> connectedDevices;
{
std::lock_guard guard(mLock);
connectedDevices = mConnectedDevices;
}
std::vector<std::shared_ptr<alsa_device_proxy>> alsaDeviceProxies;
for (const auto& device : connectedDevices) {
alsa_device_profile profile;
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;
}
auto proxy = std::shared_ptr<alsa_device_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, &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;
}
alsaDeviceProxies.push_back(std::move(proxy));
}
{
std::lock_guard guard(mLock);
mAlsaDeviceProxies = alsaDeviceProxies;
}
mIsStandby = false;
return ::android::OK;
}
// static
ndk::ScopedAStatus StreamInUsb::createInstance(const SinkMetadata& sinkMetadata,
StreamContext&& context,
const std::vector<MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) {
std::shared_ptr<StreamIn> stream =
ndk::SharedRefBase::make<StreamInUsb>(sinkMetadata, std::move(context), microphones);
if (auto status = initInstance(stream); !status.isOk()) {
return status;
}
*result = std::move(stream);
return ndk::ScopedAStatus::ok();
}
StreamInUsb::StreamInUsb(const SinkMetadata& sinkMetadata, StreamContext&& context,
const std::vector<MicrophoneInfo>& microphones)
: StreamIn(
sinkMetadata, std::move(context),
[](const StreamContext& ctx) -> DriverInterface* {
return new DriverUsb(ctx, true /*isInput*/);
},
[](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
// The default worker implementation is used.
return new StreamInWorker(ctx, driver);
},
microphones) {}
ndk::ScopedAStatus StreamInUsb::getActiveMicrophones(
std::vector<MicrophoneDynamicInfo>* _aidl_return __unused) {
LOG(DEBUG) << __func__ << ": not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
// static
ndk::ScopedAStatus StreamOutUsb::createInstance(const SourceMetadata& sourceMetadata,
StreamContext&& context,
const std::optional<AudioOffloadInfo>& offloadInfo,
std::shared_ptr<StreamOut>* result) {
if (offloadInfo.has_value()) {
LOG(ERROR) << __func__ << ": offload is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
std::shared_ptr<StreamOut> stream =
ndk::SharedRefBase::make<StreamOutUsb>(sourceMetadata, std::move(context), offloadInfo);
if (auto status = initInstance(stream); !status.isOk()) {
return status;
}
*result = std::move(stream);
return ndk::ScopedAStatus::ok();
}
StreamOutUsb::StreamOutUsb(const SourceMetadata& sourceMetadata, StreamContext&& context,
const std::optional<AudioOffloadInfo>& offloadInfo)
: StreamOut(
sourceMetadata, std::move(context),
[](const StreamContext& ctx) -> DriverInterface* {
return new DriverUsb(ctx, false /*isInput*/);
},
[](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
// The default worker implementation is used.
return new StreamOutWorker(ctx, driver);
},
offloadInfo) {}
} // namespace aidl::android::hardware::audio::core

View file

@ -0,0 +1,181 @@
/*
* 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::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;
using android::hardware::audio::common::getChannelCount;
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::INT_24_BIT), PCM_FORMAT_S24_LE},
{make_AudioFormatDescription(PcmType::FIXED_Q_8_24), 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

@ -0,0 +1,39 @@
/*
* 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