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