/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ConversionHelperAidl.h" #include "StreamPowerLog.h" using ::aidl::android::hardware::audio::common::AudioOffloadMetadata; using ::aidl::android::hardware::audio::core::MmapBufferDescriptor; namespace android { class StreamContextAidl { public: typedef AidlMessageQueue<::aidl::android::hardware::audio::core::StreamDescriptor::Command, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite> CommandMQ; typedef AidlMessageQueue<::aidl::android::hardware::audio::core::StreamDescriptor::Reply, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite> ReplyMQ; typedef AidlMessageQueue DataMQ; StreamContextAidl( ::aidl::android::hardware::audio::core::StreamDescriptor& descriptor, bool isAsynchronous) : mFrameSizeBytes(descriptor.frameSizeBytes), mCommandMQ(new CommandMQ(descriptor.command)), mReplyMQ(new ReplyMQ(descriptor.reply)), mBufferSizeFrames(descriptor.bufferSizeFrames), mDataMQ(maybeCreateDataMQ(descriptor)), mIsAsynchronous(isAsynchronous), mIsMmapped(isMmapped(descriptor)), mMmapBufferDescriptor(maybeGetMmapBuffer(descriptor)) {} StreamContextAidl(StreamContextAidl&& other) : mFrameSizeBytes(other.mFrameSizeBytes), mCommandMQ(std::move(other.mCommandMQ)), mReplyMQ(std::move(other.mReplyMQ)), mBufferSizeFrames(other.mBufferSizeFrames), mDataMQ(std::move(other.mDataMQ)), mIsAsynchronous(other.mIsAsynchronous), mIsMmapped(other.mIsMmapped), mMmapBufferDescriptor(std::move(other.mMmapBufferDescriptor)) {} StreamContextAidl& operator=(StreamContextAidl&& other) { mFrameSizeBytes = other.mFrameSizeBytes; mCommandMQ = std::move(other.mCommandMQ); mReplyMQ = std::move(other.mReplyMQ); mBufferSizeFrames = other.mBufferSizeFrames; mDataMQ = std::move(other.mDataMQ); mIsAsynchronous = other.mIsAsynchronous; mIsMmapped = other.mIsMmapped; mMmapBufferDescriptor = std::move(other.mMmapBufferDescriptor); return *this; } bool isValid() const { return mFrameSizeBytes != 0 && mCommandMQ != nullptr && mCommandMQ->isValid() && mReplyMQ != nullptr && mReplyMQ->isValid() && (mDataMQ == nullptr || ( mDataMQ->isValid() && mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() >= mFrameSizeBytes * mBufferSizeFrames)) && (!mIsMmapped || mMmapBufferDescriptor.sharedMemory.fd.get() >= 0); } size_t getBufferSizeBytes() const { return mFrameSizeBytes * mBufferSizeFrames; } size_t getBufferSizeFrames() const { return mBufferSizeFrames; } size_t getBufferDurationMs(int32_t sampleRate) const { auto bufferSize = mIsMmapped ? getMmapBurstSize() : mBufferSizeFrames; return sampleRate != 0 ? bufferSize * MILLIS_PER_SECOND / sampleRate : 0; } CommandMQ* getCommandMQ() const { return mCommandMQ.get(); } DataMQ* getDataMQ() const { return mDataMQ.get(); } size_t getFrameSizeBytes() const { return mFrameSizeBytes; } ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); } bool isAsynchronous() const { return mIsAsynchronous; } bool isMmapped() const { return mIsMmapped; } const MmapBufferDescriptor& getMmapBufferDescriptor() const { return mMmapBufferDescriptor; } size_t getMmapBurstSize() const { return mMmapBufferDescriptor.burstSizeFrames;} private: static std::unique_ptr maybeCreateDataMQ( const ::aidl::android::hardware::audio::core::StreamDescriptor& descriptor) { using Tag = ::aidl::android::hardware::audio::core::StreamDescriptor::AudioBuffer::Tag; if (descriptor.audio.getTag() == Tag::fmq) { return std::make_unique(descriptor.audio.get()); } return nullptr; } static bool isMmapped( const ::aidl::android::hardware::audio::core::StreamDescriptor& descriptor) { using Tag = ::aidl::android::hardware::audio::core::StreamDescriptor::AudioBuffer::Tag; return descriptor.audio.getTag() == Tag::mmap; } static MmapBufferDescriptor maybeGetMmapBuffer( ::aidl::android::hardware::audio::core::StreamDescriptor& descriptor) { using Tag = ::aidl::android::hardware::audio::core::StreamDescriptor::AudioBuffer::Tag; if (descriptor.audio.getTag() == Tag::mmap) { return std::move(descriptor.audio.get()); } return {}; } size_t mFrameSizeBytes; std::unique_ptr mCommandMQ; std::unique_ptr mReplyMQ; size_t mBufferSizeFrames; std::unique_ptr mDataMQ; bool mIsAsynchronous; bool mIsMmapped; MmapBufferDescriptor mMmapBufferDescriptor; }; class StreamHalAidl : public virtual StreamHalInterface, public ConversionHelperAidl { public: // Return size of input/output buffer in bytes for this stream - eg. 4800. status_t getBufferSize(size_t *size) override; // Return the base configuration of the stream: // - channel mask; // - format - e.g. AUDIO_FORMAT_PCM_16_BIT; // - sampling rate in Hz - eg. 44100. status_t getAudioProperties(audio_config_base_t *configBase) override; // Set audio stream parameters. status_t setParameters(const String8& kvPairs) override; // Get audio stream parameters. status_t getParameters(const String8& keys, String8 *values) override; // Return the frame size (number of bytes per sample) of a stream. status_t getFrameSize(size_t *size) override; // Add or remove the effect on the stream. status_t addEffect(sp effect) override; status_t removeEffect(sp effect) override; // Put the audio hardware input/output into standby mode. status_t standby() override; status_t dump(int fd, const Vector& args) override; // Start a stream operating in mmap mode. status_t start() override; // Stop a stream operating in mmap mode. status_t stop() override; // Retrieve information on the data buffer in mmap mode. status_t createMmapBuffer(int32_t minSizeFrames, struct audio_mmap_buffer_info *info) override; // Get current read/write position in the mmap buffer status_t getMmapPosition(struct audio_mmap_position *position) override; // Set the priority of the thread that interacts with the HAL // (must match the priority of the audioflinger's thread that calls 'read' / 'write') status_t setHalThreadPriority(int priority) override; status_t legacyCreateAudioPatch(const struct audio_port_config& port, std::optional source, audio_devices_t type) override; status_t legacyReleaseAudioPatch() override; protected: // For tests. friend class sp; struct StatePositions { int64_t framesAtFlushOrDrain; int64_t framesAtStandby; }; template static std::shared_ptr<::aidl::android::hardware::audio::core::IStreamCommon> getStreamCommon( const std::shared_ptr& stream); // Subclasses can not be constructed directly by clients. StreamHalAidl(std::string_view className, bool isInput, const audio_config& config, int32_t nominalLatency, StreamContextAidl&& context, const std::shared_ptr<::aidl::android::hardware::audio::core::IStreamCommon>& stream, const std::shared_ptr<::aidl::android::media::audio::IHalAdapterVendorExtension>& vext); ~StreamHalAidl() override; status_t getLatency(uint32_t *latency); // Always returns non-negative values. status_t getObservablePosition(int64_t* frames, int64_t* timestamp, StatePositions* statePositions = nullptr); // Always returns non-negative values. status_t getHardwarePosition(int64_t *frames, int64_t *timestamp); // Always returns non-negative values. status_t getXruns(int32_t *frames); status_t transfer(void *buffer, size_t bytes, size_t *transferred); status_t pause( ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply = nullptr); status_t resume( ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply = nullptr); status_t drain(bool earlyNotify, ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply = nullptr); status_t flush( ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply = nullptr); status_t exit(); void onAsyncTransferReady(); void onAsyncDrainReady(); void onAsyncError(); const bool mIsInput; const audio_config_base_t mConfig; const StreamContextAidl mContext; // This lock is used to make sending of a command and receiving a reply an atomic // operation. Otherwise, when two threads are trying to send a command, they may both advance to // reading of the reply once the HAL has consumed the command from the MQ, and that creates a // race condition between them. // // Note that only access to command and reply MQs needs to be protected because the data MQ is // only accessed by the I/O thread. Also, there is no need to protect lookup operations on the // queues as they are thread-safe, only send/receive operation must be protected. std::mutex mCommandReplyLock; private: static audio_config_base_t configToBase(const audio_config& config) { audio_config_base_t result = AUDIO_CONFIG_BASE_INITIALIZER; result.sample_rate = config.sample_rate; result.channel_mask = config.channel_mask; result.format = config.format; return result; } ::aidl::android::hardware::audio::core::StreamDescriptor::State getState() { std::lock_guard l(mLock); return mLastReply.state; } // Note: Since `sendCommand` takes mLock while holding mCommandReplyLock, never call // it with `mLock` being held. status_t sendCommand( const ::aidl::android::hardware::audio::core::StreamDescriptor::Command& command, ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply = nullptr, bool safeFromNonWorkerThread = false, StatePositions* statePositions = nullptr); status_t updateCountersIfNeeded( ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply = nullptr, StatePositions* statePositions = nullptr); const std::shared_ptr<::aidl::android::hardware::audio::core::IStreamCommon> mStream; const std::shared_ptr<::aidl::android::media::audio::IHalAdapterVendorExtension> mVendorExt; const int64_t mLastReplyLifeTimeNs; std::mutex mLock; ::aidl::android::hardware::audio::core::StreamDescriptor::Reply mLastReply GUARDED_BY(mLock); int64_t mLastReplyExpirationNs GUARDED_BY(mLock) = 0; // Cached values of observable positions when the stream last entered certain state. // Updated for output streams only. StatePositions mStatePositions GUARDED_BY(mLock) = {}; // mStreamPowerLog is used for audio signal power logging. StreamPowerLog mStreamPowerLog; std::atomic mWorkerTid = -1; }; class CallbackBroker; class StreamOutHalAidl : public virtual StreamOutHalInterface, public virtual StreamOutHalInterfaceCallback, public StreamHalAidl { public: // Extract the output stream parameters and set by AIDL APIs. status_t setParameters(const String8& kvPairs) override; // Return the audio hardware driver estimated latency in milliseconds. status_t getLatency(uint32_t *latency) override; // Use this method in situations where audio mixing is done in the hardware. status_t setVolume(float left, float right) override; // Selects the audio presentation (if available). status_t selectPresentation(int presentationId, int programId) override; // Write audio buffer to driver. status_t write(const void *buffer, size_t bytes, size_t *written) override; // Return the number of audio frames written by the audio dsp to DAC since // the output has exited standby. status_t getRenderPosition(uint64_t *dspFrames) override; // Set the callback for notifying completion of non-blocking write and drain. status_t setCallback(wp callback) override; // Returns whether pause and resume operations are supported. status_t supportsPauseAndResume(bool *supportsPause, bool *supportsResume) override; // Notifies to the audio driver to resume playback following a pause. status_t pause() override; // Notifies to the audio driver to resume playback following a pause. status_t resume() override; // Returns whether drain operation is supported. status_t supportsDrain(bool *supportsDrain) override; // Requests notification when data buffered by the driver/hardware has been played. status_t drain(bool earlyNotify) override; // Notifies to the audio driver to flush (that is, drop) the queued data. Stream must // already be paused before calling 'flush'. status_t flush() override; // Return a recent count of the number of audio frames presented to an external observer. // This excludes frames which have been written but are still in the pipeline. See the // table at the start of the 'StreamOutHalInterface' for the specification of the frame // count behavior w.r.t. 'flush', 'drain' and 'standby' operations. status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp) override; // Notifies the HAL layer that the framework considers the current playback as completed. status_t presentationComplete() override; // Called when the metadata of the stream's source has been changed. status_t updateSourceMetadata(const SourceMetadata& sourceMetadata) override; // Returns the Dual Mono mode presentation setting. status_t getDualMonoMode(audio_dual_mono_mode_t* mode) override; // Sets the Dual Mono mode presentation on the output device. status_t setDualMonoMode(audio_dual_mono_mode_t mode) override; // Returns the Audio Description Mix level in dB. status_t getAudioDescriptionMixLevel(float* leveldB) override; // Sets the Audio Description Mix level in dB. status_t setAudioDescriptionMixLevel(float leveldB) override; // Retrieves current playback rate parameters. status_t getPlaybackRateParameters(audio_playback_rate_t* playbackRate) override; // Sets the playback rate parameters that control playback behavior. status_t setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) override; status_t setEventCallback(const sp& callback) override; status_t setLatencyMode(audio_latency_mode_t mode) override; status_t getRecommendedLatencyModes(std::vector *modes) override; status_t setLatencyModeCallback( const sp& callback) override; status_t exit() override; // StreamOutHalInterfaceCallback void onWriteReady() override; void onDrainReady() override; void onError(bool isHardError) override; private: friend class sp; static ConversionResult<::aidl::android::hardware::audio::common::SourceMetadata> legacy2aidl_SourceMetadata(const StreamOutHalInterface::SourceMetadata& legacy); const std::shared_ptr<::aidl::android::hardware::audio::core::IStreamOut> mStream; const wp mCallbackBroker; mediautils::atomic_wp mClientCallback; AudioOffloadMetadata mOffloadMetadata; // Can not be constructed directly by clients. StreamOutHalAidl( const audio_config& config, StreamContextAidl&& context, int32_t nominalLatency, const std::shared_ptr<::aidl::android::hardware::audio::core::IStreamOut>& stream, const std::shared_ptr<::aidl::android::media::audio::IHalAdapterVendorExtension>& vext, const sp& callbackBroker); ~StreamOutHalAidl() override; // Filter and update the offload metadata. The parameters which are related to the offload // metadata will be removed after filtering. status_t filterAndUpdateOffloadMetadata(AudioParameter ¶meters); }; class MicrophoneInfoProvider; class StreamInHalAidl : public StreamInHalInterface, public StreamHalAidl { public: // Set the input gain for the audio driver. status_t setGain(float gain) override; // Read audio buffer in from driver. status_t read(void *buffer, size_t bytes, size_t *read) override; // Return the amount of input frames lost in the audio driver. status_t getInputFramesLost(uint32_t *framesLost) override; // Return a recent count of the number of audio frames received and // the clock time associated with that frame count. // The count must not reset to zero when a PCM input enters standby. status_t getCapturePosition(int64_t *frames, int64_t *time) override; // Get active microphones status_t getActiveMicrophones(std::vector *microphones) override; // Set microphone direction (for processing) status_t setPreferredMicrophoneDirection( audio_microphone_direction_t direction) override; // Set microphone zoom (for processing) status_t setPreferredMicrophoneFieldDimension(float zoom) override; // Called when the metadata of the stream's sink has been changed. status_t updateSinkMetadata(const SinkMetadata& sinkMetadata) override; private: friend class sp; static ConversionResult<::aidl::android::hardware::audio::common::SinkMetadata> legacy2aidl_SinkMetadata(const StreamInHalInterface::SinkMetadata& legacy); const std::shared_ptr<::aidl::android::hardware::audio::core::IStreamIn> mStream; const wp mMicInfoProvider; // Can not be constructed directly by clients. StreamInHalAidl( const audio_config& config, StreamContextAidl&& context, int32_t nominalLatency, const std::shared_ptr<::aidl::android::hardware::audio::core::IStreamIn>& stream, const std::shared_ptr<::aidl::android::media::audio::IHalAdapterVendorExtension>& vext, const sp& micInfoProvider); ~StreamInHalAidl() override = default; }; } // namespace android