blob: 11336713430a83958560ca677db340622801d774 [file] [log] [blame]
Andy Hung8946a282018-04-19 20:04:56 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// Enabled with TEE_SINK in Configuration.h
Andy Hungd6699ce2023-06-08 14:13:54 -070018
19#pragma once
Andy Hung8946a282018-04-19 20:04:56 -070020
21#ifdef TEE_SINK
22
23#include <atomic>
24#include <mutex>
25#include <set>
26
27#include <cutils/properties.h>
28#include <media/nbaio/NBAIO.h>
29
30namespace android {
31
32/**
33 * The NBAIO_Tee uses the NBAIO Pipe and PipeReader for nonblocking
34 * data collection, for eventual dump to log files.
35 * See https://source.android.com/devices/audio/debugging for how to
36 * enable by ro.debuggable and af.tee properties.
37 *
38 * The write() into the NBAIO_Tee is therefore nonblocking,
39 * but changing NBAIO_Tee formats with set() cannot be done during a write();
40 * usually the caller already implements this mutual exclusion.
41 *
42 * All other calls except set() vs write() may occur at any time.
43 *
44 * dump() disruption is minimized to the caller since system calls are executed
45 * in an asynchronous thread (when possible).
46 *
47 * Currently the NBAIO_Tee is "hardwired" for AudioFlinger support.
48 *
49 * Some AudioFlinger specific notes:
50 *
Dean Wheatleyace1eeb2023-11-06 21:58:14 +110051 * 1) Tees capture only linear PCM or IEC61937 data.
Andy Hung8946a282018-04-19 20:04:56 -070052 * 2) Tees without any data written are considered empty and do not generate
53 * any output files.
54 * 2) Once a Tee dumps data, it is considered "emptied" and new data
55 * needs to be written before another Tee file is generated.
56 * 3) Tee file format is
57 * WAV integer PCM 16 bit for AUDIO_FORMAT_PCM_8_BIT, AUDIO_FORMAT_PCM_16_BIT.
58 * WAV integer PCM 32 bit for AUDIO_FORMAT_PCM_8_24_BIT, AUDIO_FORMAT_PCM_24_BIT_PACKED
59 * AUDIO_FORMAT_PCM_32_BIT.
60 * WAV float PCM 32 bit for AUDIO_FORMAT_PCM_FLOAT.
Dean Wheatleyace1eeb2023-11-06 21:58:14 +110061 * RAW for AUDIO_FORMAT_IEC61937.
Andy Hung8946a282018-04-19 20:04:56 -070062 *
63 * Input_Thread:
64 * 1) Capture buffer is teed when read from the HAL, before resampling for the AudioRecord
65 * client.
66 *
67 * Output_Thread:
68 * 1) MixerThreads will tee at the FastMixer output (if it has one) or at the
69 * NormalMixer output (if no FastMixer).
70 * 2) DuplicatingThreads do not tee any mixed data. Apply a tee on the downstream OutputTrack
71 * or on the upstream playback Tracks.
Dean Wheatleyace1eeb2023-11-06 21:58:14 +110072 * 3) DirectThreads and OffloadThreads with SpdifStreamOut will tee IEC61937 wrapped data.
73 * Otherwise, the upstream track (if linear PCM format) may be teed to discover data.
Andy Hung8946a282018-04-19 20:04:56 -070074 * 4) MmapThreads are not supported.
75 *
76 * Tracks:
77 * 1) RecordTracks and playback Tracks tee as data is being written to or
78 * read from the shared client-server track buffer by the associated Threads.
79 * 2) The mechanism is on the AudioBufferProvider release() so large static Track
80 * playback may not show any Tee data depending on when it is released.
81 * 3) When a track becomes inactive, the Thread will trigger a dump.
82 */
83
84class NBAIO_Tee {
85public:
86 /* TEE_FLAG is used in set() and must match the flags for the af.tee property
87 given in https://source.android.com/devices/audio/debugging
88 */
89 enum TEE_FLAG {
90 TEE_FLAG_NONE = 0,
91 TEE_FLAG_INPUT_THREAD = (1 << 0), // treat as a Tee for input (Capture) Threads
92 TEE_FLAG_OUTPUT_THREAD = (1 << 1), // treat as a Tee for output (Playback) Threads
93 TEE_FLAG_TRACK = (1 << 2), // treat as a Tee for tracks (Record and Playback)
94 };
95
96 NBAIO_Tee()
97 : mTee(std::make_shared<NBAIO_TeeImpl>())
98 {
99 getRunningTees().add(mTee);
100 }
101
102 ~NBAIO_Tee() {
103 getRunningTees().remove(mTee);
104 dump(-1, "_DTOR"); // log any data remaining in Tee.
105 }
106
107 /**
108 * \brief set is used for deferred configuration of Tee.
109 *
110 * May be called anytime except concurrently with write().
111 *
112 * \param format NBAIO_Format used to open NBAIO pipes
113 * \param flags (https://source.android.com/devices/audio/debugging)
114 * - TEE_FLAG_NONE to bypass af.tee property checks (default);
115 * - TEE_FLAG_INPUT_THREAD to check af.tee if input thread logging set;
116 * - TEE_FLAG_OUTPUT_THREAD to check af.tee if output thread logging set;
117 * - TEE_FLAG_TRACK to check af.tee if track logging set.
118 * \param frames number of frames to open the NBAIO pipe (set to 0 to use default).
119 *
120 * \return
121 * - NO_ERROR on success (or format unchanged)
122 * - BAD_VALUE if format or flags invalid.
123 * - PERMISSION_DENIED if flags not allowed by af.tee
124 */
125
126 status_t set(const NBAIO_Format &format,
127 TEE_FLAG flags = TEE_FLAG_NONE, size_t frames = 0) const {
128 return mTee->set(format, flags, frames);
129 }
130
131 status_t set(uint32_t sampleRate, uint32_t channelCount, audio_format_t format,
132 TEE_FLAG flags = TEE_FLAG_NONE, size_t frames = 0) const {
133 return mTee->set(Format_from_SR_C(sampleRate, channelCount, format), flags, frames);
134 }
135
136 /**
137 * \brief write data to the tee.
138 *
139 * This call is lock free (as shared pointer and NBAIO is lock free);
140 * may be called simultaneous to all methods except set().
141 *
142 * \param buffer to write to pipe.
143 * \param frameCount in frames as specified by the format passed to set()
144 */
145
146 void write(const void *buffer, size_t frameCount) const {
147 mTee->write(buffer, frameCount);
148 }
149
150 /** sets Tee id string which identifies the generated file (should be unique). */
151 void setId(const std::string &id) const {
152 mTee->setId(id);
153 }
154
155 /**
156 * \brief dump the audio content written to the Tee.
157 *
158 * \param fd file descriptor to write dumped filename for logging, use -1 to ignore.
159 * \param reason string suffix to append to the generated file.
160 */
161 void dump(int fd, const std::string &reason = "") const {
162 mTee->dump(fd, reason);
163 }
164
165 /**
166 * \brief dump all Tees currently alive.
167 *
168 * \param fd file descriptor to write dumped filename for logging, use -1 to ignore.
169 * \param reason string suffix to append to the generated file.
170 */
171 static void dumpAll(int fd, const std::string &reason = "") {
172 getRunningTees().dump(fd, reason);
173 }
174
175private:
176
177 /** The underlying implementation of the Tee - the lifetime is through
178 a shared pointer so destruction of the NBAIO_Tee container may proceed
179 even though dumping is occurring. */
180 class NBAIO_TeeImpl {
181 public:
182 status_t set(const NBAIO_Format &format, TEE_FLAG flags, size_t frames) {
183 static const int teeConfig = property_get_bool("ro.debuggable", false)
184 ? property_get_int32("af.tee", 0) : 0;
185
186 // check the type of Tee
187 const TEE_FLAG type = TEE_FLAG(
188 flags & (TEE_FLAG_INPUT_THREAD | TEE_FLAG_OUTPUT_THREAD | TEE_FLAG_TRACK));
189
190 // parameter flags can't select multiple types.
191 if (__builtin_popcount(type) > 1) {
192 return BAD_VALUE;
193 }
194
195 // if type is set, we check to see if it is permitted by configuration.
196 if (type != 0 && (type & teeConfig) == 0) {
197 return PERMISSION_DENIED;
198 }
199
200 // determine number of frames for Tee
201 if (frames == 0) {
202 // TODO: consider varying frame count based on type.
203 frames = DEFAULT_TEE_FRAMES;
204 }
205
206 // TODO: should we check minimum number of frames?
207
208 // don't do anything if format and frames are the same.
209 if (Format_isEqual(format, mFormat) && frames == mFrames) {
210 return NO_ERROR;
211 }
212
213 bool enabled = false;
214 auto sinksource = makeSinkSource(format, frames, &enabled);
215
216 // enabled is set if makeSinkSource is successful.
217 // Note: as mentioned in NBAIO_Tee::set(), don't call set() while write() is
218 // ongoing.
219 if (enabled) {
Andy Hung6cc98c62023-06-09 12:37:23 -0700220 const std::lock_guard<std::mutex> _l(mLock);
Andy Hung8946a282018-04-19 20:04:56 -0700221 mFlags = flags;
222 mFormat = format; // could get this from the Sink.
223 mFrames = frames;
224 mSinkSource = std::move(sinksource);
225 mEnabled.store(true);
226 return NO_ERROR;
227 }
228 return BAD_VALUE;
229 }
230
231 void setId(const std::string &id) {
Andy Hung6cc98c62023-06-09 12:37:23 -0700232 const std::lock_guard<std::mutex> _l(mLock);
Andy Hung8946a282018-04-19 20:04:56 -0700233 mId = id;
234 }
235
236 void dump(int fd, const std::string &reason) {
237 if (!mDataReady.exchange(false)) return;
238 std::string suffix;
239 NBAIO_SinkSource sinkSource;
240 {
Andy Hung6cc98c62023-06-09 12:37:23 -0700241 const std::lock_guard<std::mutex> _l(mLock);
Andy Hung8946a282018-04-19 20:04:56 -0700242 suffix = mId + reason;
243 sinkSource = mSinkSource;
244 }
245 dumpTee(fd, sinkSource, suffix);
246 }
247
248 void write(const void *buffer, size_t frameCount) {
249 if (!mEnabled.load() || frameCount == 0) return;
250 (void)mSinkSource.first->write(buffer, frameCount);
251 mDataReady.store(true);
252 }
253
254 private:
255 // TRICKY: We need to keep the NBAIO_Sink and NBAIO_Source both alive at the same time
256 // because PipeReader holds a naked reference (not a strong or weak pointer) to Pipe.
257 using NBAIO_SinkSource = std::pair<sp<NBAIO_Sink>, sp<NBAIO_Source>>;
258
259 static void dumpTee(int fd, const NBAIO_SinkSource& sinkSource, const std::string& suffix);
260
261 static NBAIO_SinkSource makeSinkSource(
262 const NBAIO_Format &format, size_t frames, bool *enabled);
263
264 // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes
265 static constexpr size_t DEFAULT_TEE_FRAMES = 0x200000;
266
267 // atomic status checking
268 std::atomic<bool> mEnabled{false};
269 std::atomic<bool> mDataReady{false};
270
271 // locked dump information
272 mutable std::mutex mLock;
273 std::string mId; // GUARDED_BY(mLock)
274 TEE_FLAG mFlags = TEE_FLAG_NONE; // GUARDED_BY(mLock)
275 NBAIO_Format mFormat = Format_Invalid; // GUARDED_BY(mLock)
276 size_t mFrames = 0; // GUARDED_BY(mLock)
277 NBAIO_SinkSource mSinkSource; // GUARDED_BY(mLock)
278 };
279
280 /** RunningTees tracks current running tees for dump purposes.
281 It is implemented to have minimal locked regions, to be transparent to the caller. */
282 class RunningTees {
283 public:
284 void add(const std::shared_ptr<NBAIO_TeeImpl> &tee) {
Andy Hung6cc98c62023-06-09 12:37:23 -0700285 const std::lock_guard<std::mutex> _l(mLock);
Andy Hung8946a282018-04-19 20:04:56 -0700286 ALOGW_IF(!mTees.emplace(tee).second,
287 "%s: %p already exists in mTees", __func__, tee.get());
288 }
289
290 void remove(const std::shared_ptr<NBAIO_TeeImpl> &tee) {
Andy Hung6cc98c62023-06-09 12:37:23 -0700291 const std::lock_guard<std::mutex> _l(mLock);
Andy Hung8946a282018-04-19 20:04:56 -0700292 ALOGW_IF(mTees.erase(tee) != 1,
293 "%s: %p doesn't exist in mTees", __func__, tee.get());
294 }
295
296 void dump(int fd, const std::string &reason) {
297 std::vector<std::shared_ptr<NBAIO_TeeImpl>> tees; // safe snapshot of tees
298 {
Andy Hung6cc98c62023-06-09 12:37:23 -0700299 const std::lock_guard<std::mutex> _l(mLock);
Andy Hung8946a282018-04-19 20:04:56 -0700300 tees.insert(tees.end(), mTees.begin(), mTees.end());
301 }
302 for (const auto &tee : tees) {
303 tee->dump(fd, reason);
304 }
305 }
306
307 private:
308 std::mutex mLock;
309 std::set<std::shared_ptr<NBAIO_TeeImpl>> mTees; // GUARDED_BY(mLock)
310 };
311
312 // singleton
313 static RunningTees &getRunningTees() {
314 static RunningTees runningTees;
315 return runningTees;
316 }
317
318 // The NBAIO TeeImpl may have lifetime longer than NBAIO_Tee if
319 // RunningTees::dump() is being called simultaneous to ~NBAIO_Tee().
320 // This is allowed for maximum concurrency.
321 const std::shared_ptr<NBAIO_TeeImpl> mTee;
322}; // NBAIO_Tee
323
Andy Hung6cc98c62023-06-09 12:37:23 -0700324} // namespace android
325
Andy Hung8946a282018-04-19 20:04:56 -0700326#endif // TEE_SINK