1 /*
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
18 #ifndef ANDROID_NBAIO_TEE_H
19 #define ANDROID_NBAIO_TEE_H
20 
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 
30 namespace 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  *
51  * 1) Tees capture only linear PCM data.
52  * 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.
61  *
62  * Input_Thread:
63  * 1) Capture buffer is teed when read from the HAL, before resampling for the AudioRecord
64  *    client.
65  *
66  * Output_Thread:
67  * 1) MixerThreads will tee at the FastMixer output (if it has one) or at the
68  *    NormalMixer output (if no FastMixer).
69  * 2) DuplicatingThreads do not tee any mixed data. Apply a tee on the downstream OutputTrack
70  *    or on the upstream playback Tracks.
71  * 3) DirectThreads and OffloadThreads do not tee any data. The upstream track
72  *    (if linear PCM format) may be teed to discover data.
73  * 4) MmapThreads are not supported.
74  *
75  * Tracks:
76  * 1) RecordTracks and playback Tracks tee as data is being written to or
77  *    read from the shared client-server track buffer by the associated Threads.
78  * 2) The mechanism is on the AudioBufferProvider release() so large static Track
79  *    playback may not show any Tee data depending on when it is released.
80  * 3) When a track becomes inactive, the Thread will trigger a dump.
81  */
82 
83 class NBAIO_Tee {
84 public:
85     /* TEE_FLAG is used in set() and must match the flags for the af.tee property
86        given in https://source.android.com/devices/audio/debugging
87     */
88     enum TEE_FLAG {
89         TEE_FLAG_NONE = 0,
90         TEE_FLAG_INPUT_THREAD = (1 << 0),  // treat as a Tee for input (Capture) Threads
91         TEE_FLAG_OUTPUT_THREAD = (1 << 1), // treat as a Tee for output (Playback) Threads
92         TEE_FLAG_TRACK = (1 << 2),         // treat as a Tee for tracks (Record and Playback)
93     };
94 
NBAIO_Tee()95     NBAIO_Tee()
96         : mTee(std::make_shared<NBAIO_TeeImpl>())
97     {
98         getRunningTees().add(mTee);
99     }
100 
~NBAIO_Tee()101     ~NBAIO_Tee() {
102         getRunningTees().remove(mTee);
103         dump(-1, "_DTOR"); // log any data remaining in Tee.
104     }
105 
106     /**
107      * \brief set is used for deferred configuration of Tee.
108      *
109      *  May be called anytime except concurrently with write().
110      *
111      * \param format NBAIO_Format used to open NBAIO pipes
112      * \param flags (https://source.android.com/devices/audio/debugging)
113      *              - TEE_FLAG_NONE to bypass af.tee property checks (default);
114      *              - TEE_FLAG_INPUT_THREAD to check af.tee if input thread logging set;
115      *              - TEE_FLAG_OUTPUT_THREAD to check af.tee if output thread logging set;
116      *              - TEE_FLAG_TRACK to check af.tee if track logging set.
117      * \param frames number of frames to open the NBAIO pipe (set to 0 to use default).
118      *
119      * \return
120      *         - NO_ERROR on success (or format unchanged)
121      *         - BAD_VALUE if format or flags invalid.
122      *         - PERMISSION_DENIED if flags not allowed by af.tee
123      */
124 
125     status_t set(const NBAIO_Format &format,
126             TEE_FLAG flags = TEE_FLAG_NONE, size_t frames = 0) const {
127         return mTee->set(format, flags, frames);
128     }
129 
130     status_t set(uint32_t sampleRate, uint32_t channelCount, audio_format_t format,
131             TEE_FLAG flags = TEE_FLAG_NONE, size_t frames = 0) const {
132         return mTee->set(Format_from_SR_C(sampleRate, channelCount, format), flags, frames);
133     }
134 
135     /**
136      * \brief write data to the tee.
137      *
138      * This call is lock free (as shared pointer and NBAIO is lock free);
139      * may be called simultaneous to all methods except set().
140      *
141      * \param buffer to write to pipe.
142      * \param frameCount in frames as specified by the format passed to set()
143      */
144 
write(const void * buffer,size_t frameCount)145     void write(const void *buffer, size_t frameCount) const {
146         mTee->write(buffer, frameCount);
147     }
148 
149     /** sets Tee id string which identifies the generated file (should be unique). */
setId(const std::string & id)150     void setId(const std::string &id) const {
151         mTee->setId(id);
152     }
153 
154     /**
155      * \brief dump the audio content written to the Tee.
156      *
157      * \param fd file descriptor to write dumped filename for logging, use -1 to ignore.
158      * \param reason string suffix to append to the generated file.
159      */
160     void dump(int fd, const std::string &reason = "") const {
161         mTee->dump(fd, reason);
162     }
163 
164     /**
165      * \brief dump all Tees currently alive.
166      *
167      * \param fd file descriptor to write dumped filename for logging, use -1 to ignore.
168      * \param reason string suffix to append to the generated file.
169      */
170     static void dumpAll(int fd, const std::string &reason = "") {
171         getRunningTees().dump(fd, reason);
172     }
173 
174 private:
175 
176     /** The underlying implementation of the Tee - the lifetime is through
177         a shared pointer so destruction of the NBAIO_Tee container may proceed
178         even though dumping is occurring. */
179     class NBAIO_TeeImpl {
180     public:
set(const NBAIO_Format & format,TEE_FLAG flags,size_t frames)181         status_t set(const NBAIO_Format &format, TEE_FLAG flags, size_t frames) {
182             static const int teeConfig = property_get_bool("ro.debuggable", false)
183                    ? property_get_int32("af.tee", 0) : 0;
184 
185             // check the type of Tee
186             const TEE_FLAG type = TEE_FLAG(
187                     flags & (TEE_FLAG_INPUT_THREAD | TEE_FLAG_OUTPUT_THREAD | TEE_FLAG_TRACK));
188 
189             // parameter flags can't select multiple types.
190             if (__builtin_popcount(type) > 1) {
191                 return BAD_VALUE;
192             }
193 
194             // if type is set, we check to see if it is permitted by configuration.
195             if (type != 0 && (type & teeConfig) == 0) {
196                 return PERMISSION_DENIED;
197             }
198 
199             // determine number of frames for Tee
200             if (frames == 0) {
201                 // TODO: consider varying frame count based on type.
202                 frames = DEFAULT_TEE_FRAMES;
203             }
204 
205             // TODO: should we check minimum number of frames?
206 
207             // don't do anything if format and frames are the same.
208             if (Format_isEqual(format, mFormat) && frames == mFrames) {
209                 return NO_ERROR;
210             }
211 
212             bool enabled = false;
213             auto sinksource = makeSinkSource(format, frames, &enabled);
214 
215             // enabled is set if makeSinkSource is successful.
216             // Note: as mentioned in NBAIO_Tee::set(), don't call set() while write() is
217             // ongoing.
218             if (enabled) {
219                 std::lock_guard<std::mutex> _l(mLock);
220                 mFlags = flags;
221                 mFormat = format; // could get this from the Sink.
222                 mFrames = frames;
223                 mSinkSource = std::move(sinksource);
224                 mEnabled.store(true);
225                 return NO_ERROR;
226             }
227             return BAD_VALUE;
228         }
229 
setId(const std::string & id)230         void setId(const std::string &id) {
231             std::lock_guard<std::mutex> _l(mLock);
232             mId = id;
233         }
234 
dump(int fd,const std::string & reason)235         void dump(int fd, const std::string &reason) {
236             if (!mDataReady.exchange(false)) return;
237             std::string suffix;
238             NBAIO_SinkSource sinkSource;
239             {
240                 std::lock_guard<std::mutex> _l(mLock);
241                 suffix = mId + reason;
242                 sinkSource = mSinkSource;
243             }
244             dumpTee(fd, sinkSource, suffix);
245         }
246 
write(const void * buffer,size_t frameCount)247         void write(const void *buffer, size_t frameCount) {
248             if (!mEnabled.load() || frameCount == 0) return;
249             (void)mSinkSource.first->write(buffer, frameCount);
250             mDataReady.store(true);
251         }
252 
253     private:
254         // TRICKY: We need to keep the NBAIO_Sink and NBAIO_Source both alive at the same time
255         // because PipeReader holds a naked reference (not a strong or weak pointer) to Pipe.
256         using NBAIO_SinkSource = std::pair<sp<NBAIO_Sink>, sp<NBAIO_Source>>;
257 
258         static void dumpTee(int fd, const NBAIO_SinkSource& sinkSource, const std::string& suffix);
259 
260         static NBAIO_SinkSource makeSinkSource(
261                 const NBAIO_Format &format, size_t frames, bool *enabled);
262 
263         // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes
264         static constexpr size_t DEFAULT_TEE_FRAMES = 0x200000;
265 
266         // atomic status checking
267         std::atomic<bool> mEnabled{false};
268         std::atomic<bool> mDataReady{false};
269 
270         // locked dump information
271         mutable std::mutex mLock;
272         std::string mId;                                         // GUARDED_BY(mLock)
273         TEE_FLAG mFlags = TEE_FLAG_NONE;                         // GUARDED_BY(mLock)
274         NBAIO_Format mFormat = Format_Invalid;                   // GUARDED_BY(mLock)
275         size_t mFrames = 0;                                      // GUARDED_BY(mLock)
276         NBAIO_SinkSource mSinkSource;                            // GUARDED_BY(mLock)
277     };
278 
279     /** RunningTees tracks current running tees for dump purposes.
280         It is implemented to have minimal locked regions, to be transparent to the caller. */
281     class RunningTees {
282     public:
add(const std::shared_ptr<NBAIO_TeeImpl> & tee)283         void add(const std::shared_ptr<NBAIO_TeeImpl> &tee) {
284             std::lock_guard<std::mutex> _l(mLock);
285             ALOGW_IF(!mTees.emplace(tee).second,
286                     "%s: %p already exists in mTees", __func__, tee.get());
287         }
288 
remove(const std::shared_ptr<NBAIO_TeeImpl> & tee)289         void remove(const std::shared_ptr<NBAIO_TeeImpl> &tee) {
290             std::lock_guard<std::mutex> _l(mLock);
291             ALOGW_IF(mTees.erase(tee) != 1,
292                     "%s: %p doesn't exist in mTees", __func__, tee.get());
293         }
294 
dump(int fd,const std::string & reason)295         void dump(int fd, const std::string &reason) {
296             std::vector<std::shared_ptr<NBAIO_TeeImpl>> tees; // safe snapshot of tees
297             {
298                 std::lock_guard<std::mutex> _l(mLock);
299                 tees.insert(tees.end(), mTees.begin(), mTees.end());
300             }
301             for (const auto &tee : tees) {
302                 tee->dump(fd, reason);
303             }
304         }
305 
306     private:
307         std::mutex mLock;
308         std::set<std::shared_ptr<NBAIO_TeeImpl>> mTees; // GUARDED_BY(mLock)
309     };
310 
311     // singleton
getRunningTees()312     static RunningTees &getRunningTees() {
313         static RunningTees runningTees;
314         return runningTees;
315     }
316 
317     // The NBAIO TeeImpl may have lifetime longer than NBAIO_Tee if
318     // RunningTees::dump() is being called simultaneous to ~NBAIO_Tee().
319     // This is allowed for maximum concurrency.
320     const std::shared_ptr<NBAIO_TeeImpl> mTee;
321 }; // NBAIO_Tee
322 
323 } // namespace android
324 
325 #endif // TEE_SINK
326 #endif // !ANDROID_NBAIO_TEE_H
327