blob: 497ba2d0a56324ae064914c4c7d0a6a61d61a895 [file] [log] [blame]
Eric Laurent1c333e22014-05-20 10:48:17 -07001/*
2**
3** Copyright 2014, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#ifndef INCLUDING_FROM_AUDIOFLINGER_H
19 #error This header file should only be included from AudioFlinger.h
20#endif
21
Andy Hung6ac17eb2023-06-20 18:56:17 -070022public: // TODO(b/288339104) extract out of AudioFlinger class
Mikhail Naganovdea53042018-04-26 13:10:21 -070023// PatchPanel is concealed within AudioFlinger, their lifetimes are the same.
24class PatchPanel {
Eric Laurent1c333e22014-05-20 10:48:17 -070025public:
Mikhail Naganovadca70f2018-07-09 12:49:25 -070026 class SoftwarePatch {
27 public:
28 SoftwarePatch(const PatchPanel &patchPanel, audio_patch_handle_t patchHandle,
29 audio_io_handle_t playbackThreadHandle, audio_io_handle_t recordThreadHandle)
30 : mPatchPanel(patchPanel), mPatchHandle(patchHandle),
31 mPlaybackThreadHandle(playbackThreadHandle),
32 mRecordThreadHandle(recordThreadHandle) {}
33 SoftwarePatch(const SoftwarePatch&) = default;
Mikhail Naganovadca70f2018-07-09 12:49:25 -070034
35 // Must be called under AudioFlinger::mLock
36 status_t getLatencyMs_l(double *latencyMs) const;
Andy Hung2dbffc22018-08-08 18:50:41 -070037 audio_patch_handle_t getPatchHandle() const { return mPatchHandle; };
Mikhail Naganovadca70f2018-07-09 12:49:25 -070038 audio_io_handle_t getPlaybackThreadHandle() const { return mPlaybackThreadHandle; };
39 audio_io_handle_t getRecordThreadHandle() const { return mRecordThreadHandle; };
40 private:
41 const PatchPanel &mPatchPanel;
42 const audio_patch_handle_t mPatchHandle;
43 const audio_io_handle_t mPlaybackThreadHandle;
44 const audio_io_handle_t mRecordThreadHandle;
45 };
46
Mikhail Naganovdea53042018-04-26 13:10:21 -070047 explicit PatchPanel(AudioFlinger* audioFlinger) : mAudioFlinger(*audioFlinger) {}
Eric Laurent1c333e22014-05-20 10:48:17 -070048
49 /* List connected audio ports and their attributes */
50 status_t listAudioPorts(unsigned int *num_ports,
51 struct audio_port *ports);
52
53 /* Get supported attributes for a given audio port */
jiabinb4fed192020-09-22 14:45:40 -070054 status_t getAudioPort(struct audio_port_v7 *port);
Eric Laurent1c333e22014-05-20 10:48:17 -070055
56 /* Create a patch between several source and sink ports */
57 status_t createAudioPatch(const struct audio_patch *patch,
Francois Gaffiee0dc36d2021-04-01 16:01:00 +020058 audio_patch_handle_t *handle,
59 bool endpointPatch = false);
Eric Laurent1c333e22014-05-20 10:48:17 -070060
61 /* Release a patch */
62 status_t releaseAudioPatch(audio_patch_handle_t handle);
63
64 /* List connected audio devices and they attributes */
65 status_t listAudioPatches(unsigned int *num_patches,
66 struct audio_patch *patches);
67
Mikhail Naganovadca70f2018-07-09 12:49:25 -070068 // Retrieves all currently estrablished software patches for a stream
69 // opened on an intermediate module.
70 status_t getDownstreamSoftwarePatches(audio_io_handle_t stream,
71 std::vector<SoftwarePatch> *patches) const;
72
73 // Notifies patch panel about all opened and closed streams.
Eric Laurent74c38dc2020-12-23 18:19:44 +010074 void notifyStreamOpened(AudioHwDevice *audioHwDevice, audio_io_handle_t stream,
75 struct audio_patch *patch);
Mikhail Naganovadca70f2018-07-09 12:49:25 -070076 void notifyStreamClosed(audio_io_handle_t stream);
77
78 void dump(int fd) const;
Mikhail Naganov201369b2018-05-16 16:52:32 -070079
Mikhail Naganov444ecc32018-05-01 17:40:05 -070080 template<typename ThreadType, typename TrackType>
Eric Laurent9b2064c2019-11-22 17:25:04 -080081 class Endpoint final {
Mikhail Naganov444ecc32018-05-01 17:40:05 -070082 public:
Mikhail Naganov9dfa2642018-05-10 10:09:19 -070083 Endpoint() = default;
Mikhail Naganovfa97d9b2019-02-05 14:08:21 -080084 Endpoint(const Endpoint&) = delete;
Eric Laurent9b2064c2019-11-22 17:25:04 -080085 Endpoint& operator=(const Endpoint& other) noexcept {
86 mThread = other.mThread;
87 mCloseThread = other.mCloseThread;
88 mHandle = other.mHandle;
89 mTrack = other.mTrack;
90 return *this;
91 }
Mikhail Naganovfa97d9b2019-02-05 14:08:21 -080092 Endpoint(Endpoint&& other) noexcept { swap(other); }
93 Endpoint& operator=(Endpoint&& other) noexcept {
94 swap(other);
95 return *this;
96 }
97 ~Endpoint() {
Mikhail Naganov9dfa2642018-05-10 10:09:19 -070098 ALOGE_IF(mHandle != AUDIO_PATCH_HANDLE_NONE,
99 "A non empty Patch Endpoint leaked, handle %d", mHandle);
Mikhail Naganov9dfa2642018-05-10 10:09:19 -0700100 }
101
Mikhail Naganov444ecc32018-05-01 17:40:05 -0700102 status_t checkTrack(TrackType *trackOrNull) const {
103 if (trackOrNull == nullptr) return NO_MEMORY;
104 return trackOrNull->initCheck();
105 }
106 audio_patch_handle_t handle() const { return mHandle; }
Eric Laurent9b2064c2019-11-22 17:25:04 -0800107 sp<ThreadType> thread() const { return mThread; }
108 sp<TrackType> track() const { return mTrack; }
Mikhail Naganovadca70f2018-07-09 12:49:25 -0700109 sp<const ThreadType> const_thread() const { return mThread; }
Andy Hungc3ab7732018-06-01 13:46:29 -0700110 sp<const TrackType> const_track() const { return mTrack; }
Mikhail Naganov444ecc32018-05-01 17:40:05 -0700111
112 void closeConnections(PatchPanel *panel) {
113 if (mHandle != AUDIO_PATCH_HANDLE_NONE) {
114 panel->releaseAudioPatch(mHandle);
115 mHandle = AUDIO_PATCH_HANDLE_NONE;
116 }
117 if (mThread != 0) {
118 if (mTrack != 0) {
119 mThread->deletePatchTrack(mTrack);
120 }
121 if (mCloseThread) {
122 panel->mAudioFlinger.closeThreadInternal_l(mThread);
123 }
124 }
125 }
126 audio_patch_handle_t* handlePtr() { return &mHandle; }
127 void setThread(const sp<ThreadType>& thread, bool closeThread = true) {
128 mThread = thread;
129 mCloseThread = closeThread;
130 }
Andy Hungabfab202019-03-07 19:45:54 -0800131 template <typename T>
Mikhail Naganovdd91ce22020-01-13 11:34:30 -0800132 void setTrackAndPeer(const sp<TrackType>& track, const sp<T> &peer, bool holdReference) {
Mikhail Naganov444ecc32018-05-01 17:40:05 -0700133 mTrack = track;
134 mThread->addPatchTrack(mTrack);
Mikhail Naganovdd91ce22020-01-13 11:34:30 -0800135 mTrack->setPeerProxy(peer, holdReference);
136 mClearPeerProxy = holdReference;
Mikhail Naganov444ecc32018-05-01 17:40:05 -0700137 }
Mikhail Naganovdd91ce22020-01-13 11:34:30 -0800138 void clearTrackPeer() { if (mClearPeerProxy && mTrack) mTrack->clearPeerProxy(); }
Mikhail Naganov444ecc32018-05-01 17:40:05 -0700139 void stopTrack() { if (mTrack) mTrack->stop(); }
140
Mikhail Naganovfa97d9b2019-02-05 14:08:21 -0800141 void swap(Endpoint &other) noexcept {
142 using std::swap;
143 swap(mThread, other.mThread);
144 swap(mCloseThread, other.mCloseThread);
Mikhail Naganovdd91ce22020-01-13 11:34:30 -0800145 swap(mClearPeerProxy, other.mClearPeerProxy);
Mikhail Naganovfa97d9b2019-02-05 14:08:21 -0800146 swap(mHandle, other.mHandle);
147 swap(mTrack, other.mTrack);
148 }
Mikhail Naganov9dfa2642018-05-10 10:09:19 -0700149
Mikhail Naganovfa97d9b2019-02-05 14:08:21 -0800150 friend void swap(Endpoint &a, Endpoint &b) noexcept {
151 a.swap(b);
152 }
153
154 private:
Mikhail Naganov444ecc32018-05-01 17:40:05 -0700155 sp<ThreadType> mThread;
156 bool mCloseThread = true;
Mikhail Naganovdd91ce22020-01-13 11:34:30 -0800157 bool mClearPeerProxy = true;
Mikhail Naganov444ecc32018-05-01 17:40:05 -0700158 audio_patch_handle_t mHandle = AUDIO_PATCH_HANDLE_NONE;
159 sp<TrackType> mTrack;
160 };
161
Eric Laurent9b2064c2019-11-22 17:25:04 -0800162 class Patch final {
Eric Laurent1c333e22014-05-20 10:48:17 -0700163 public:
Francois Gaffiee0dc36d2021-04-01 16:01:00 +0200164 Patch(const struct audio_patch &patch, bool endpointPatch) :
165 mAudioPatch(patch), mIsEndpointPatch(endpointPatch) {}
Eric Laurent9b2064c2019-11-22 17:25:04 -0800166 Patch() = default;
Mikhail Naganov444ecc32018-05-01 17:40:05 -0700167 ~Patch();
Eric Laurent9b2064c2019-11-22 17:25:04 -0800168 Patch(const Patch& other) noexcept {
169 mAudioPatch = other.mAudioPatch;
170 mHalHandle = other.mHalHandle;
171 mPlayback = other.mPlayback;
172 mRecord = other.mRecord;
173 mThread = other.mThread;
Francois Gaffiee0dc36d2021-04-01 16:01:00 +0200174 mIsEndpointPatch = other.mIsEndpointPatch;
Eric Laurent9b2064c2019-11-22 17:25:04 -0800175 }
176 Patch(Patch&& other) noexcept { swap(other); }
177 Patch& operator=(Patch&& other) noexcept {
178 swap(other);
179 return *this;
180 }
181
182 void swap(Patch &other) noexcept {
183 using std::swap;
184 swap(mAudioPatch, other.mAudioPatch);
185 swap(mHalHandle, other.mHalHandle);
186 swap(mPlayback, other.mPlayback);
187 swap(mRecord, other.mRecord);
188 swap(mThread, other.mThread);
Francois Gaffiee0dc36d2021-04-01 16:01:00 +0200189 swap(mIsEndpointPatch, other.mIsEndpointPatch);
Eric Laurent9b2064c2019-11-22 17:25:04 -0800190 }
191
192 friend void swap(Patch &a, Patch &b) noexcept {
193 a.swap(b);
194 }
Eric Laurent1c333e22014-05-20 10:48:17 -0700195
Mikhail Naganovdea53042018-04-26 13:10:21 -0700196 status_t createConnections(PatchPanel *panel);
197 void clearConnections(PatchPanel *panel);
Mikhail Naganov444ecc32018-05-01 17:40:05 -0700198 bool isSoftware() const {
199 return mRecord.handle() != AUDIO_PATCH_HANDLE_NONE ||
200 mPlayback.handle() != AUDIO_PATCH_HANDLE_NONE; }
Mikhail Naganovdea53042018-04-26 13:10:21 -0700201
Andy Hung87c693c2023-07-06 20:56:16 -0700202 void setThread(const sp<IAfThreadBase>& thread) { mThread = thread; }
203 wp<IAfThreadBase> thread() const { return mThread; }
Eric Laurent9b2064c2019-11-22 17:25:04 -0800204
Andy Hungc3ab7732018-06-01 13:46:29 -0700205 // returns the latency of the patch (from record to playback).
206 status_t getLatencyMs(double *latencyMs) const;
207
Mikhail Naganovadca70f2018-07-09 12:49:25 -0700208 String8 dump(audio_patch_handle_t myHandle) const;
Mikhail Naganov201369b2018-05-16 16:52:32 -0700209
Mikhail Naganovdea53042018-04-26 13:10:21 -0700210 // Note that audio_patch::id is only unique within a HAL module
Eric Laurent83b88082014-06-20 18:31:16 -0700211 struct audio_patch mAudioPatch;
Eric Laurentb997d3a2016-06-07 18:23:45 -0700212 // handle for audio HAL patch handle present only when the audio HAL version is >= 3.0
Mikhail Naganovdea53042018-04-26 13:10:21 -0700213 audio_patch_handle_t mHalHandle = AUDIO_PATCH_HANDLE_NONE;
Eric Laurentb997d3a2016-06-07 18:23:45 -0700214 // below members are used by a software audio patch connecting a source device from a
215 // given audio HW module to a sink device on an other audio HW module.
Mikhail Naganovdea53042018-04-26 13:10:21 -0700216 // the objects are created by createConnections() and released by clearConnections()
217 // playback thread is created if no existing playback thread can be used
Mikhail Naganov444ecc32018-05-01 17:40:05 -0700218 // connects playback thread output to sink device
Andy Hung87c693c2023-07-06 20:56:16 -0700219 Endpoint<IAfPlaybackThread, IAfPatchTrack> mPlayback;
Mikhail Naganov444ecc32018-05-01 17:40:05 -0700220 // connects source device to record thread input
Andy Hung87c693c2023-07-06 20:56:16 -0700221 Endpoint<IAfRecordThread, IAfPatchRecord> mRecord;
Eric Laurent9b2064c2019-11-22 17:25:04 -0800222
Andy Hung87c693c2023-07-06 20:56:16 -0700223 wp<IAfThreadBase> mThread;
Francois Gaffiee0dc36d2021-04-01 16:01:00 +0200224 bool mIsEndpointPatch;
Eric Laurent1c333e22014-05-20 10:48:17 -0700225 };
Eric Laurent83b88082014-06-20 18:31:16 -0700226
Eric Laurent9b2064c2019-11-22 17:25:04 -0800227 // Call with AudioFlinger mLock held
228 std::map<audio_patch_handle_t, Patch>& patches_l() { return mPatches; }
229
230private:
Mikhail Naganovadca70f2018-07-09 12:49:25 -0700231 AudioHwDevice* findAudioHwDeviceByModule(audio_module_handle_t module);
Mikhail Naganov444ecc32018-05-01 17:40:05 -0700232 sp<DeviceHalInterface> findHwDeviceByModule(audio_module_handle_t module);
Mikhail Naganovadca70f2018-07-09 12:49:25 -0700233 void addSoftwarePatchToInsertedModules(
Eric Laurent74c38dc2020-12-23 18:19:44 +0100234 audio_module_handle_t module, audio_patch_handle_t handle,
235 const struct audio_patch *patch);
Mikhail Naganovadca70f2018-07-09 12:49:25 -0700236 void removeSoftwarePatchFromInsertedModules(audio_patch_handle_t handle);
François Gaffie58e73af2023-02-15 11:47:24 +0100237 /**
238 * erase the patch referred by its handle.
239 * @param handle of the patch to be erased
240 * @param reuseExistingHalPatch if set, do not trig the callback of listeners, listener
241 * would receive instead a onUpdateAudioPatch when the patch will be recreated.
242 * It prevents for example DeviceEffectManager to spuriously remove / add a device on an already
243 * opened input / output mix.
244 */
245 void erasePatch(audio_patch_handle_t handle, bool reuseExistingHalPatch = false);
246
247 /**
248 * Returns true if the old and new patches passed as arguments describe the same
249 * connections between the first sink and the first source
250 * @param oldPatch previous patch
251 * @param newPatch new patch
252 * @return true if the route is unchanged between the old and new patch, false otherwise
253 */
254 inline bool patchesHaveSameRoute(
255 const struct audio_patch &newPatch, const struct audio_patch &oldPatch) const {
256 return (newPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE &&
257 oldPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE &&
258 newPatch.sources[0].id == oldPatch.sources[0].id &&
259 newPatch.sinks[0].type == AUDIO_PORT_TYPE_MIX &&
260 oldPatch.sinks[0].type == AUDIO_PORT_TYPE_MIX &&
261 newPatch.sinks[0].ext.mix.handle == oldPatch.sinks[0].ext.mix.handle) ||
262 (newPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE &&
263 oldPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE &&
264 newPatch.sinks[0].id == oldPatch.sinks[0].id &&
265 newPatch.sources[0].type == AUDIO_PORT_TYPE_MIX &&
266 oldPatch.sources[0].type == AUDIO_PORT_TYPE_MIX &&
267 newPatch.sources[0].ext.mix.handle == oldPatch.sources[0].ext.mix.handle);
268 }
Mikhail Naganov444ecc32018-05-01 17:40:05 -0700269
Mikhail Naganovdea53042018-04-26 13:10:21 -0700270 AudioFlinger &mAudioFlinger;
271 std::map<audio_patch_handle_t, Patch> mPatches;
Mikhail Naganovadca70f2018-07-09 12:49:25 -0700272
273 // This map allows going from a thread to "downstream" software patches
274 // when a processing module inserted in between. Example:
275 //
276 // from map value.streams map key
277 // [Mixer thread] --> [Virtual output device] --> [Processing module] ---\
278 // [Harware module] <-- [Physical output device] <-- [S/W Patch] <--/
279 // from map value.sw_patches
280 //
281 // This allows the mixer thread to look up the threads of the software patch
282 // for propagating timing info, parameters, etc.
283 //
284 // The current assumptions are:
285 // 1) The processing module acts as a mixer with several outputs which
286 // represent differently downmixed and / or encoded versions of the same
287 // mixed stream. There is no 1:1 correspondence between the input streams
288 // and the software patches, but rather a N:N correspondence between
289 // a group of streams and a group of patches.
290 // 2) There are only a couple of inserted processing modules in the system,
291 // so when looking for a stream or patch handle we can iterate over
292 // all modules.
293 struct ModuleConnections {
294 std::set<audio_io_handle_t> streams;
295 std::set<audio_patch_handle_t> sw_patches;
296 };
297 std::map<audio_module_handle_t, ModuleConnections> mInsertedModules;
Eric Laurent1c333e22014-05-20 10:48:17 -0700298};
Andy Hung6ac17eb2023-06-20 18:56:17 -0700299
300private: