/* * Copyright (C) 2022 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 "UsecaseValidator" // #define LOG_NDEBUG 0 #include #include #include "media/UsecaseValidator.h" #include "media/UsecaseLookup.h" namespace android { namespace media { namespace { class UsecaseValidatorImpl : public UsecaseValidator { public: UsecaseValidatorImpl() {} /** * Register a new mixer/stream. * Called when the stream is opened at the HAL and communicates * immutable stream attributes like flags, sampling rate, format. */ status_t registerStream(audio_io_handle_t streamId, const audio_config_base_t& audioConfig __attribute__((unused)), const audio_output_flags_t outputFlags) override { ALOGV("%s output: %d flags: %#x", __func__, streamId, outputFlags); // Check if FAST or MMAP output flag has been set. bool outputFlagGame = outputFlags & (AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_MMAP_NOIRQ); m_lookup.addStream(streamId, outputFlagGame); return OK; }; /** * Unregister a stream/mixer. * Called when the stream is closed. */ status_t unregisterStream(audio_io_handle_t streamId) override { ALOGV("%s output: %d", __func__, streamId); m_lookup.removeStream(streamId); return OK; }; /** * Indicates that some playback activity started on the stream. * Called each time an audio track starts or resumes. */ error::Result startClient(audio_io_handle_t streamId, audio_port_handle_t portId, const content::AttributionSourceState& attributionSource, const audio_attributes_t& attributes, const AttributesChangedCallback *callback __attribute__((unused))) override { ALOGV("%s output: %d portId: %d usage: %d pid: %d package: %s", __func__, streamId, portId, attributes.usage, attributionSource.pid, attributionSource.packageName.value_or("").c_str()); m_lookup.addTrack(streamId, portId); return verifyAudioAttributes(streamId, attributionSource, attributes); }; /** * Indicates that some playback activity stopped on the stream. * Called each time an audio track stops or pauses. */ status_t stopClient(audio_io_handle_t streamId, audio_port_handle_t portId) override { ALOGV("%s output: %d portId: %d", __func__, streamId, portId); m_lookup.removeTrack(streamId, portId); return OK; }; /** * Called to verify and update audio attributes for a track that is connected * to the specified stream. */ error::Result verifyAudioAttributes(audio_io_handle_t streamId, const content::AttributionSourceState& attributionSource, const audio_attributes_t& attributes) override { ALOGV("%s output: %d usage: %d pid: %d package: %s", __func__, streamId, attributes.usage, attributionSource.pid, attributionSource.packageName.value_or("").c_str()); audio_attributes_t attrRet = attributes; if (isUsageValid(attributes.usage) && isContentTypeValid(attributes.content_type) && areFlagsValid(attributes.flags) && m_lookup.isGameStream(streamId)) { ALOGI("%s update usage: %d to AUDIO_USAGE_GAME for output: %d pid: %d package: %s", __func__, attributes.usage, streamId, attributionSource.pid, attributionSource.packageName.value_or("").c_str()); // Set attribute usage Game. attrRet.usage = AUDIO_USAGE_GAME; } return {attrRet}; }; protected: /** * Check if attribute usage valid. */ bool isUsageValid(audio_usage_t usage) { ALOGV("isUsageValid usage: %d", usage); switch (usage) { case AUDIO_USAGE_MEDIA: case AUDIO_USAGE_UNKNOWN: return true; default: break; } return false; } bool isContentTypeValid(audio_content_type_t contentType) { ALOGV("isContentTypeValid contentType: %d", contentType); switch (contentType) { case AUDIO_CONTENT_TYPE_MUSIC: case AUDIO_CONTENT_TYPE_MOVIE: case AUDIO_CONTENT_TYPE_UNKNOWN: return true; default: break; } return false; } bool areFlagsValid(audio_flags_mask_t flags) { ALOGV("areFlagsValid flags: %#x", flags); if ((flags & (AUDIO_FLAG_SCO|AUDIO_FLAG_AUDIBILITY_ENFORCED|AUDIO_FLAG_BEACON)) != 0) { return false; } if ((flags & AUDIO_FLAG_LOW_LATENCY) != 0) { return true; } return false; } protected: UsecaseLookup m_lookup; }; } // namespace std::unique_ptr createUsecaseValidator() { return std::make_unique(); } } // namespace media } // namespace android