/* * 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 // The following includes are required because we have class definitions below // for EndPoint and Patch, which precludes using a forward declaration only. #include "IAfThread.h" // IAfThreadBase IAfMmapThread IAfPlaybackThread IAfRecordThread #include "IAfTrack.h" // IAfPatchRecord IAfPatchTrack #include #include #include // ALOG used in this file #include // avoid transitive dependency #include namespace android { class IAfPatchPanel; class PatchCommandThread; class SoftwarePatch { public: SoftwarePatch( const sp& patchPanel, audio_patch_handle_t patchHandle, audio_io_handle_t playbackThreadHandle, audio_io_handle_t recordThreadHandle) : mPatchPanel(patchPanel), mPatchHandle(patchHandle), mPlaybackThreadHandle(playbackThreadHandle), mRecordThreadHandle(recordThreadHandle) {} SoftwarePatch(const SoftwarePatch&) = default; status_t getLatencyMs_l(double* latencyMs) const REQUIRES(audio_utils::AudioFlinger_Mutex); audio_patch_handle_t getPatchHandle() const { return mPatchHandle; }; audio_io_handle_t getPlaybackThreadHandle() const { return mPlaybackThreadHandle; }; audio_io_handle_t getRecordThreadHandle() const { return mRecordThreadHandle; }; private: const sp mPatchPanel; const audio_patch_handle_t mPatchHandle; const audio_io_handle_t mPlaybackThreadHandle; const audio_io_handle_t mRecordThreadHandle; }; class IAfPatchPanelCallback : public virtual RefBase { public: virtual void closeThreadInternal_l(const sp& thread) REQUIRES(mutex()) = 0; virtual void closeThreadInternal_l(const sp& thread) REQUIRES(mutex()) = 0; virtual IAfPlaybackThread* primaryPlaybackThread_l() const REQUIRES(mutex()) = 0; virtual IAfPlaybackThread* checkPlaybackThread_l(audio_io_handle_t output) const REQUIRES(mutex()) = 0; virtual IAfRecordThread* checkRecordThread_l(audio_io_handle_t input) const REQUIRES(mutex()) = 0; virtual IAfMmapThread* checkMmapThread_l(audio_io_handle_t io) const REQUIRES(mutex()) = 0; virtual sp openInput_l(audio_module_handle_t module, audio_io_handle_t* input, audio_config_t* config, audio_devices_t device, const char* address, audio_source_t source, audio_input_flags_t flags, audio_devices_t outputDevice, const String8& outputDeviceAddress) REQUIRES(mutex()) = 0; virtual sp openOutput_l(audio_module_handle_t module, audio_io_handle_t* output, audio_config_t* halConfig, audio_config_base_t* mixerConfig, audio_devices_t deviceType, const String8& address, audio_output_flags_t flags) REQUIRES(mutex()) = 0; virtual audio_utils::mutex& mutex() const RETURN_CAPABILITY(audio_utils::AudioFlinger_Mutex) = 0; virtual const DefaultKeyedVector& getAudioHwDevs_l() const REQUIRES(mutex()) = 0; virtual audio_unique_id_t nextUniqueId(audio_unique_id_use_t use) = 0; virtual const sp& getPatchCommandThread() = 0; virtual void updateDownStreamPatches_l( const struct audio_patch* patch, const std::set& streams) REQUIRES(mutex()) = 0; virtual void updateOutDevicesForRecordThreads_l(const DeviceDescriptorBaseVector& devices) REQUIRES(mutex()) = 0; }; class IAfPatchPanel : public virtual RefBase { public: static sp create(const sp& afPatchPanelCallback); // Extraction of inner Endpoint and Patch classes would require interfaces // (in the Endpoint case a templated interface) but that seems // excessive for now. We keep them as inner classes until extraction // is needed. template class Endpoint final { public: Endpoint() = default; Endpoint(const Endpoint&) = delete; Endpoint& operator=(const Endpoint& other) noexcept { mThread = other.mThread; mCloseThread = other.mCloseThread; mHandle = other.mHandle; mTrack = other.mTrack; return *this; } Endpoint(Endpoint&& other) noexcept { swap(other); } Endpoint& operator=(Endpoint&& other) noexcept { swap(other); return *this; } ~Endpoint() { ALOGE_IF( mHandle != AUDIO_PATCH_HANDLE_NONE, "A non empty Patch Endpoint leaked, handle %d", mHandle); } status_t checkTrack(TrackType* trackOrNull) const { if (trackOrNull == nullptr) return NO_MEMORY; return trackOrNull->initCheck(); } audio_patch_handle_t handle() const { return mHandle; } sp thread() const { return mThread; } sp track() const { return mTrack; } sp const_thread() const { return mThread; } sp const_track() const { return mTrack; } void closeConnections_l(const sp& panel) REQUIRES(audio_utils::AudioFlinger_Mutex) NO_THREAD_SAFETY_ANALYSIS // this is broken in clang { if (mHandle != AUDIO_PATCH_HANDLE_NONE) { panel->releaseAudioPatch_l(mHandle); mHandle = AUDIO_PATCH_HANDLE_NONE; } if (mThread != nullptr) { if (mTrack != nullptr) { mThread->deletePatchTrack(mTrack); } if (mCloseThread) { panel->closeThreadInternal_l(mThread); } } } audio_patch_handle_t* handlePtr() { return &mHandle; } void setThread(const sp& thread, bool closeThread = true) { mThread = thread; mCloseThread = closeThread; } template void setTrackAndPeer(const sp& track, const sp& peer, bool holdReference) { mTrack = track; mThread->addPatchTrack(mTrack); mTrack->setPeerProxy(peer, holdReference); mClearPeerProxy = holdReference; } void clearTrackPeer() { if (mClearPeerProxy && mTrack) mTrack->clearPeerProxy(); } void stopTrack() { if (mTrack) mTrack->stop(); } void swap(Endpoint& other) noexcept { using std::swap; swap(mThread, other.mThread); swap(mCloseThread, other.mCloseThread); swap(mClearPeerProxy, other.mClearPeerProxy); swap(mHandle, other.mHandle); swap(mTrack, other.mTrack); } friend void swap(Endpoint& a, Endpoint& b) noexcept { a.swap(b); } private: sp mThread; bool mCloseThread = true; bool mClearPeerProxy = true; audio_patch_handle_t mHandle = AUDIO_PATCH_HANDLE_NONE; sp mTrack; }; class Patch final { public: Patch(const struct audio_patch& patch, bool endpointPatch) : mAudioPatch(patch), mIsEndpointPatch(endpointPatch) {} Patch() = default; ~Patch(); Patch(const Patch& other) noexcept { mAudioPatch = other.mAudioPatch; mHalHandle = other.mHalHandle; mPlayback = other.mPlayback; mRecord = other.mRecord; mThread = other.mThread; mIsEndpointPatch = other.mIsEndpointPatch; } Patch(Patch&& other) noexcept { swap(other); } Patch& operator=(Patch&& other) noexcept { swap(other); return *this; } void swap(Patch& other) noexcept { using std::swap; swap(mAudioPatch, other.mAudioPatch); swap(mHalHandle, other.mHalHandle); swap(mPlayback, other.mPlayback); swap(mRecord, other.mRecord); swap(mThread, other.mThread); swap(mIsEndpointPatch, other.mIsEndpointPatch); } friend void swap(Patch& a, Patch& b) noexcept { a.swap(b); } status_t createConnections_l(const sp& panel) REQUIRES(audio_utils::AudioFlinger_Mutex); void clearConnections_l(const sp& panel) REQUIRES(audio_utils::AudioFlinger_Mutex); bool isSoftware() const { return mRecord.handle() != AUDIO_PATCH_HANDLE_NONE || mPlayback.handle() != AUDIO_PATCH_HANDLE_NONE; } void setThread(const sp& thread) { mThread = thread; } wp thread() const { return mThread; } // returns the latency of the patch (from record to playback). status_t getLatencyMs(double* latencyMs) const; String8 dump(audio_patch_handle_t myHandle) const; // Note that audio_patch::id is only unique within a HAL module struct audio_patch mAudioPatch; // handle for audio HAL patch handle present only when the audio HAL version is >= 3.0 audio_patch_handle_t mHalHandle = AUDIO_PATCH_HANDLE_NONE; // below members are used by a software audio patch connecting a source device from a // given audio HW module to a sink device on an other audio HW module. // the objects are created by createConnections() and released by clearConnections() // playback thread is created if no existing playback thread can be used // connects playback thread output to sink device Endpoint mPlayback; // connects source device to record thread input Endpoint mRecord; wp mThread; bool mIsEndpointPatch; }; /* List connected audio ports and their attributes */ virtual status_t listAudioPorts_l(unsigned int* num_ports, struct audio_port* ports) REQUIRES(audio_utils::AudioFlinger_Mutex) = 0; /* Get supported attributes for a given audio port */ virtual status_t getAudioPort_l(struct audio_port_v7* port) REQUIRES(audio_utils::AudioFlinger_Mutex) = 0; /* Create a patch between several source and sink ports */ virtual status_t createAudioPatch_l( const struct audio_patch* patch, audio_patch_handle_t* handle, bool endpointPatch = false) REQUIRES(audio_utils::AudioFlinger_Mutex) = 0; /* Release a patch */ virtual status_t releaseAudioPatch_l(audio_patch_handle_t handle) REQUIRES(audio_utils::AudioFlinger_Mutex) = 0; /* List connected audio devices and they attributes */ virtual status_t listAudioPatches_l(unsigned int* num_patches, struct audio_patch* patches) REQUIRES(audio_utils::AudioFlinger_Mutex) = 0; // Retrieves all currently estrablished software patches for a stream // opened on an intermediate module. virtual status_t getDownstreamSoftwarePatches( audio_io_handle_t stream, std::vector* patches) const = 0; // Notifies patch panel about all opened and closed streams. virtual void notifyStreamOpened( AudioHwDevice* audioHwDevice, audio_io_handle_t stream, struct audio_patch* patch) = 0; virtual void notifyStreamClosed(audio_io_handle_t stream) = 0; virtual void dump(int fd) const = 0; virtual const std::map& patches_l() const REQUIRES(audio_utils::AudioFlinger_Mutex) = 0; virtual status_t getLatencyMs_l(audio_patch_handle_t patchHandle, double* latencyMs) const REQUIRES(audio_utils::AudioFlinger_Mutex) = 0; virtual void closeThreadInternal_l(const sp& thread) const REQUIRES(audio_utils::AudioFlinger_Mutex) = 0; /** * Get the attributes of the mix port when connecting to the given device port. */ virtual status_t getAudioMixPort_l( const struct audio_port_v7* devicePort, struct audio_port_v7* mixPort) REQUIRES(audio_utils::AudioFlinger_Mutex) = 0; }; } // namespace android