blob: 6110e4ccb1c9b2a2c2fd91c59a0ba12fd0ac3b95 [file] [log] [blame]
/*
* 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 <datapath/AudioHwDevice.h>
#include <media/DeviceDescriptorBase.h>
#include <utils/Log.h> // ALOG used in this file
#include <utils/RefBase.h> // avoid transitive dependency
#include <utils/Thread.h>
namespace android {
class IAfPatchPanel;
class PatchCommandThread;
class SoftwarePatch {
public:
SoftwarePatch(
const sp<const IAfPatchPanel>& 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<const IAfPatchPanel> 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<IAfPlaybackThread>& thread) REQUIRES(mutex()) = 0;
virtual void closeThreadInternal_l(const sp<IAfRecordThread>& 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<IAfThreadBase> 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<IAfThreadBase> 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<audio_module_handle_t, AudioHwDevice*>&
getAudioHwDevs_l() const REQUIRES(mutex()) = 0;
virtual audio_unique_id_t nextUniqueId(audio_unique_id_use_t use) = 0;
virtual const sp<PatchCommandThread>& getPatchCommandThread() = 0;
virtual void updateDownStreamPatches_l(
const struct audio_patch* patch, const std::set<audio_io_handle_t>& streams)
REQUIRES(mutex()) = 0;
virtual void updateOutDevicesForRecordThreads_l(const DeviceDescriptorBaseVector& devices)
REQUIRES(mutex()) = 0;
};
class IAfPatchPanel : public virtual RefBase {
public:
static sp<IAfPatchPanel> create(const sp<IAfPatchPanelCallback>& 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 <typename ThreadType, typename TrackType>
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<ThreadType> thread() const { return mThread; }
sp<TrackType> track() const { return mTrack; }
sp<const ThreadType> const_thread() const { return mThread; }
sp<const TrackType> const_track() const { return mTrack; }
void closeConnections_l(const sp<IAfPatchPanel>& 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<ThreadType>& thread, bool closeThread = true) {
mThread = thread;
mCloseThread = closeThread;
}
template <typename T>
void setTrackAndPeer(const sp<TrackType>& track, const sp<T>& 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<ThreadType> mThread;
bool mCloseThread = true;
bool mClearPeerProxy = true;
audio_patch_handle_t mHandle = AUDIO_PATCH_HANDLE_NONE;
sp<TrackType> 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<IAfPatchPanel>& panel)
REQUIRES(audio_utils::AudioFlinger_Mutex);
void clearConnections_l(const sp<IAfPatchPanel>& 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<IAfThreadBase>& thread) { mThread = thread; }
wp<IAfThreadBase> 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<IAfPlaybackThread, IAfPatchTrack> mPlayback;
// connects source device to record thread input
Endpoint<IAfRecordThread, IAfPatchRecord> mRecord;
wp<IAfThreadBase> 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<SoftwarePatch>* 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<audio_patch_handle_t, Patch>& 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<IAfThreadBase>& 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