// Copyright (C) 2020 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 "DeviceImpl.h" #include #include #include #include #include #include "AidlTypes.h" #include "AudioUtil.h" #include "BusOutputStream.h" #include "BusStreamProvider.h" #include "ServiceConfig.h" #include "StreamOutImpl.h" using namespace ::android::hardware::audio::common::CPP_VERSION; using namespace ::android::hardware::audio::CPP_VERSION; using ::android::wp; namespace audio_proxy { namespace service { namespace { AudioPatchHandle gNextAudioPatchHandle = 1; #if MAJOR_VERSION >= 7 std::optional toAidlAudioConfig( const AudioConfigBase& hidl_config) { audio_format_t format = AUDIO_FORMAT_INVALID; if (!audio_format_from_string(hidl_config.format.c_str(), &format)) { return std::nullopt; } audio_channel_mask_t channelMask = AUDIO_CHANNEL_INVALID; if (!audio_channel_mask_from_string(hidl_config.channelMask.c_str(), &channelMask)) { return std::nullopt; } AidlAudioConfig aidlConfig = { .format = static_cast(format), .sampleRateHz = static_cast(hidl_config.sampleRateHz), .channelMask = static_cast(channelMask), .bufferSizeBytes = 0, .latencyMs = 0}; return aidlConfig; } std::optional toAidlAudioOutputFlags( const hidl_vec& flags) { int32_t outputFlags = static_cast(AUDIO_OUTPUT_FLAG_NONE); for (const auto& flag : flags) { audio_output_flags_t outputFlag = AUDIO_OUTPUT_FLAG_NONE; if (audio_output_flag_from_string(flag.c_str(), &outputFlag)) { outputFlags |= static_cast(outputFlag); } else { return std::nullopt; } } return outputFlags; } bool checkSourceMetadata(const SourceMetadata& metadata) { for (const auto& track : metadata.tracks) { audio_usage_t usage; if (!audio_usage_from_string(track.usage.c_str(), &usage)) { return false; } audio_content_type_t contentType; if (!audio_content_type_from_string(track.contentType.c_str(), &contentType)) { return false; } audio_channel_mask_t channelMask; if (!audio_channel_mask_from_string(track.channelMask.c_str(), &channelMask)) { return false; } // From types.hal: // Tags are set by vendor specific applications and must be prefixed by // "VX_". Vendor must namespace their tag names to avoid conflicts. See // 'vendorExtension' in audio_policy_configuration.xsd for a formal // definition. // // From audio_policy_configuration.xsd: // Vendor extension names must be prefixed by "VX_" to distinguish them from // AOSP values. Vendors must namespace their names to avoid conflicts. The // namespace part must only use capital latin characters and decimal digits // and consist of at least 3 characters. for (const auto& tag : track.tags) { if (!android::base::StartsWith(tag.c_str(), "VX_")) { return false; } } } return true; } bool checkAudioPortConfig(const AudioPortConfig& config) { if (config.base.format.getDiscriminator() == AudioConfigBaseOptional::Format::hidl_discriminator::value) { audio_format_t format; if (!audio_format_from_string(config.base.format.value().c_str(), &format)) { return false; } } if (config.base.channelMask.getDiscriminator() == AudioConfigBaseOptional::ChannelMask::hidl_discriminator::value) { audio_channel_mask_t channelMask; if (!audio_channel_mask_from_string(config.base.channelMask.value().c_str(), &channelMask)) { return false; } } if (config.gain.getDiscriminator() == AudioPortConfig::OptionalGain::hidl_discriminator::config) { for (const auto& mode : config.gain.config().mode) { audio_gain_mode_t gainMode; if (!audio_gain_mode_from_string(mode.c_str(), &gainMode)) { return false; } } audio_channel_mask_t channelMask; if (!audio_channel_mask_from_string( config.gain.config().channelMask.c_str(), &channelMask)) { return false; } } if (config.ext.getDiscriminator() == AudioPortExtendedInfo::hidl_discriminator::device) { audio_devices_t deviceType; if (!audio_device_from_string(config.ext.device().deviceType.c_str(), &deviceType)) { return false; } } if (config.ext.getDiscriminator() == AudioPortExtendedInfo::hidl_discriminator::mix) { const auto& useCase = config.ext.mix().useCase; if (useCase.getDiscriminator() == AudioPortExtendedInfo::AudioPortMixExt:: UseCase::hidl_discriminator::stream) { audio_stream_type_t audioStreamType; if (!audio_stream_type_from_string(useCase.stream().c_str(), &audioStreamType)) { return false; } } else { audio_source_t audioSource; if (!audio_source_from_string(useCase.source().c_str(), &audioSource)) { return false; } } } return true; } #else AidlAudioConfig toAidlAudioConfig(const AudioConfig& hidl_config) { AidlAudioConfig aidlConfig = { .format = static_cast(hidl_config.format), .sampleRateHz = static_cast(hidl_config.sampleRateHz), .channelMask = static_cast(hidl_config.channelMask), .bufferSizeBytes = 0, .latencyMs = 0}; return aidlConfig; } // Before 7.0, the fields are using enum instead of string. There's no need to // validate them. bool checkAudioPortConfig(const AudioPortConfig& config) { return true; } #endif } // namespace DeviceImpl::DeviceImpl(BusStreamProvider& busStreamProvider, const ServiceConfig& serviceConfig) : mBusStreamProvider(busStreamProvider), mServiceConfig(serviceConfig) {} // Methods from ::android::hardware::audio::V5_0::IDevice follow. Return DeviceImpl::initCheck() { return Result::OK; } Return DeviceImpl::setMasterVolume(float volume) { // software mixer will emulate this ability return Result::NOT_SUPPORTED; } Return DeviceImpl::getMasterVolume(getMasterVolume_cb _hidl_cb) { _hidl_cb(Result::NOT_SUPPORTED, 0.f); return Void(); } Return DeviceImpl::setMicMute(bool mute) { return Result::NOT_SUPPORTED; } Return DeviceImpl::getMicMute(getMicMute_cb _hidl_cb) { _hidl_cb(Result::NOT_SUPPORTED, false); return Void(); } Return DeviceImpl::setMasterMute(bool mute) { return Result::NOT_SUPPORTED; } Return DeviceImpl::getMasterMute(getMasterMute_cb _hidl_cb) { _hidl_cb(Result::NOT_SUPPORTED, false); return Void(); } Return DeviceImpl::getInputBufferSize(const AudioConfig& config, getInputBufferSize_cb _hidl_cb) { _hidl_cb(Result::NOT_SUPPORTED, 0); return Void(); } #if MAJOR_VERSION >= 7 template Return DeviceImpl::openOutputStreamImpl( int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config, const hidl_vec& flags, const SourceMetadata& sourceMetadata, CallbackType _hidl_cb) { std::optional aidlConfig = toAidlAudioConfig(config.base); if (!aidlConfig) { _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, {}); return Void(); } std::optional outputFlags = toAidlAudioOutputFlags(flags); if (!outputFlags) { _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, {}); return Void(); } if (!checkSourceMetadata(sourceMetadata)) { _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, {}); return Void(); } std::string address; // Default device is used for VTS test. if (device.deviceType == "AUDIO_DEVICE_OUT_DEFAULT") { address = "default"; } else if (device.deviceType == "AUDIO_DEVICE_OUT_BUS") { address = device.address.id(); } else { _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, {}); return Void(); } const auto configIt = mServiceConfig.streams.find(address); if (configIt == mServiceConfig.streams.end()) { _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, {}); return Void(); } std::shared_ptr busOutputStream = mBusStreamProvider.openOutputStream( address, *aidlConfig, *outputFlags, computeBufferSizeBytes(*aidlConfig, configIt->second.bufferSizeMs), configIt->second.latencyMs); DCHECK(busOutputStream); auto streamOut = sp::make(std::move(busOutputStream), config.base); mBusStreamProvider.onStreamOutCreated(streamOut); _hidl_cb(Result::OK, streamOut, config); return Void(); } Return DeviceImpl::openOutputStream(int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config, const hidl_vec& flags, const SourceMetadata& sourceMetadata, openOutputStream_cb _hidl_cb) { return openOutputStreamImpl(ioHandle, device, config, flags, sourceMetadata, _hidl_cb); } Return DeviceImpl::openInputStream(int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config, const hidl_vec& flags, const SinkMetadata& sinkMetadata, openInputStream_cb _hidl_cb) { _hidl_cb(Result::NOT_SUPPORTED, sp(), config); return Void(); } #else Return DeviceImpl::openOutputStream(int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config, hidl_bitfield flags, const SourceMetadata& sourceMetadata, openOutputStream_cb _hidl_cb) { std::string address; if (device.device == AudioDevice::OUT_DEFAULT) { address = "default"; } else if (device.device == AudioDevice::OUT_BUS) { address = device.busAddress; } else { _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, {}); return Void(); } const auto configIt = mServiceConfig.streams.find(address); if (configIt == mServiceConfig.streams.end()) { _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, {}); return Void(); } auto aidlConfig = toAidlAudioConfig(config); std::shared_ptr busOutputStream = mBusStreamProvider.openOutputStream( address, aidlConfig, static_cast(flags), computeBufferSizeBytes(aidlConfig, configIt->second.bufferSizeMs), configIt->second.latencyMs); DCHECK(busOutputStream); auto streamOut = sp::make(std::move(busOutputStream), config); mBusStreamProvider.onStreamOutCreated(streamOut); _hidl_cb(Result::OK, streamOut, config); return Void(); } Return DeviceImpl::openInputStream(int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config, hidl_bitfield flags, const SinkMetadata& sinkMetadata, openInputStream_cb _hidl_cb) { _hidl_cb(Result::NOT_SUPPORTED, sp(), config); return Void(); } #endif Return DeviceImpl::supportsAudioPatches() { return true; } // Create a do-nothing audio patch. Return DeviceImpl::createAudioPatch( const hidl_vec& sources, const hidl_vec& sinks, createAudioPatch_cb _hidl_cb) { for (const auto& config : sources) { if (!checkAudioPortConfig(config)) { _hidl_cb(Result::INVALID_ARGUMENTS, 0); return Void(); } } for (const auto& config : sinks) { if (!checkAudioPortConfig(config)) { _hidl_cb(Result::INVALID_ARGUMENTS, 0); return Void(); } } AudioPatchHandle handle = gNextAudioPatchHandle++; mAudioPatchHandles.insert(handle); _hidl_cb(Result::OK, handle); return Void(); } Return DeviceImpl::releaseAudioPatch(AudioPatchHandle patch) { size_t removed = mAudioPatchHandles.erase(patch); return removed > 0 ? Result::OK : Result::INVALID_ARGUMENTS; } Return DeviceImpl::getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) { _hidl_cb(Result::NOT_SUPPORTED, port); return Void(); } Return DeviceImpl::setAudioPortConfig(const AudioPortConfig& config) { return Result::NOT_SUPPORTED; } Return DeviceImpl::getHwAvSync(getHwAvSync_cb _hidl_cb) { _hidl_cb(Result::NOT_SUPPORTED, 0); return Void(); } Return DeviceImpl::setScreenState(bool turnedOn) { return Result::NOT_SUPPORTED; } Return DeviceImpl::getParameters(const hidl_vec& context, const hidl_vec& keys, getParameters_cb _hidl_cb) { _hidl_cb(Result::NOT_SUPPORTED, hidl_vec()); return Void(); } Return DeviceImpl::setParameters( const hidl_vec& context, const hidl_vec& parameters) { return Result::NOT_SUPPORTED; } Return DeviceImpl::getMicrophones(getMicrophones_cb _hidl_cb) { _hidl_cb(Result::NOT_SUPPORTED, hidl_vec()); return Void(); } Return DeviceImpl::setConnectedState(const DeviceAddress& address, bool connected) { #if MAJOR_VERSION >= 7 audio_devices_t deviceType = AUDIO_DEVICE_NONE; if (!audio_device_from_string(address.deviceType.c_str(), &deviceType)) { return Result::INVALID_ARGUMENTS; } if (deviceType != AUDIO_DEVICE_OUT_BUS) { return Result::NOT_SUPPORTED; } const auto& busAddress = address.address.id(); #else if (address.device != AudioDevice::OUT_BUS) { return Result::NOT_SUPPORTED; } const auto& busAddress = address.busAddress; #endif return mServiceConfig.streams.count(busAddress) > 0 ? Result::OK : Result::NOT_SUPPORTED; } #if MAJOR_VERSION >= 6 Return DeviceImpl::updateAudioPatch( AudioPatchHandle previousPatch, const hidl_vec& sources, const hidl_vec& sinks, updateAudioPatch_cb _hidl_cb) { if (mAudioPatchHandles.erase(previousPatch) == 0) { _hidl_cb(Result::INVALID_ARGUMENTS, 0); return Void(); } AudioPatchHandle newPatch = gNextAudioPatchHandle++; mAudioPatchHandles.insert(newPatch); _hidl_cb(Result::OK, newPatch); return Void(); } Return DeviceImpl::close() { return mBusStreamProvider.cleanAndCountStreamOuts() == 0 ? Result::OK : Result::INVALID_STATE; } Return DeviceImpl::addDeviceEffect(AudioPortHandle device, uint64_t effectId) { return Result::NOT_SUPPORTED; } Return DeviceImpl::removeDeviceEffect(AudioPortHandle device, uint64_t effectId) { return Result::NOT_SUPPORTED; } #endif #if MAJOR_VERSION == 7 && MINOR_VERSION == 1 Return DeviceImpl::openOutputStream_7_1( int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config, const hidl_vec& flags, const SourceMetadata& sourceMetadata, openOutputStream_7_1_cb _hidl_cb) { return openOutputStreamImpl(ioHandle, device, config, flags, sourceMetadata, _hidl_cb); } Return DeviceImpl::setConnectedState_7_1(const AudioPort& devicePort, bool connected) { return Result::OK; } #endif } // namespace service } // namespace audio_proxy