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 19 #pragma once 20 21 #ifdef TEE_SINK 22 23 #include <atomic> 24 #include <mutex> 25 #include <set> 26 27 #include <audio_utils/clock.h> 28 #include <cutils/properties.h> 29 #include <media/nbaio/NBAIO.h> 30 31 namespace android { 32 33 /** 34 * The NBAIO_Tee uses the NBAIO Pipe and PipeReader for nonblocking 35 * data collection, for eventual dump to log files. 36 * See https://source.android.com/devices/audio/debugging for how to 37 * enable by ro.debuggable and af.tee properties. 38 * 39 * The write() into the NBAIO_Tee is therefore nonblocking, 40 * but changing NBAIO_Tee formats with set() cannot be done during a write(); 41 * usually the caller already implements this mutual exclusion. 42 * 43 * All other calls except set() vs write() may occur at any time. 44 * 45 * dump() disruption is minimized to the caller since system calls are executed 46 * in an asynchronous thread (when possible). 47 * 48 * Currently the NBAIO_Tee is "hardwired" for AudioFlinger support. 49 * 50 * Some AudioFlinger specific notes: 51 * 52 * 1) Tees capture only linear PCM or IEC61937 data. 53 * 2) Tees without any data written are considered empty and do not generate 54 * any output files. 55 * 2) Once a Tee dumps data, it is considered "emptied" and new data 56 * needs to be written before another Tee file is generated. 57 * 3) Tee file format is 58 * WAV integer PCM 16 bit for AUDIO_FORMAT_PCM_8_BIT, AUDIO_FORMAT_PCM_16_BIT. 59 * WAV integer PCM 32 bit for AUDIO_FORMAT_PCM_8_24_BIT, AUDIO_FORMAT_PCM_24_BIT_PACKED 60 * AUDIO_FORMAT_PCM_32_BIT. 61 * WAV float PCM 32 bit for AUDIO_FORMAT_PCM_FLOAT. 62 * RAW for AUDIO_FORMAT_IEC61937. 63 * 64 * Input_Thread: 65 * 1) Capture buffer is teed when read from the HAL, before resampling for the AudioRecord 66 * client. 67 * 68 * Output_Thread: 69 * 1) MixerThreads will tee at the FastMixer output (if it has one) or at the 70 * NormalMixer output (if no FastMixer). 71 * 2) DuplicatingThreads do not tee any mixed data. Apply a tee on the downstream OutputTrack 72 * or on the upstream playback Tracks. 73 * 3) DirectThreads and OffloadThreads with SpdifStreamOut will tee IEC61937 wrapped data. 74 * Otherwise, the upstream track (if linear PCM format) may be teed to discover data. 75 * 4) MmapThreads are not supported. 76 * 77 * Tracks: 78 * 1) RecordTracks and playback Tracks tee as data is being written to or 79 * read from the shared client-server track buffer by the associated Threads. 80 * 2) The mechanism is on the AudioBufferProvider release() so large static Track 81 * playback may not show any Tee data depending on when it is released. 82 * 3) When a track becomes inactive, the Thread will trigger a dump. 83 */ 84 85 class NBAIO_Tee { 86 public: 87 /* TEE_FLAG is used in set() and must match the flags for the af.tee property 88 given in https://source.android.com/devices/audio/debugging 89 */ 90 enum TEE_FLAG { 91 TEE_FLAG_NONE = 0, 92 TEE_FLAG_INPUT_THREAD = (1 << 0), // treat as a Tee for input (Capture) Threads 93 TEE_FLAG_OUTPUT_THREAD = (1 << 1), // treat as a Tee for output (Playback) Threads 94 TEE_FLAG_TRACK = (1 << 2), // treat as a Tee for tracks (Record and Playback) 95 }; 96 NBAIO_Tee()97 NBAIO_Tee() 98 : mTee(std::make_shared<NBAIO_TeeImpl>()) 99 { 100 getRunningTees().add(mTee); 101 } 102 ~NBAIO_Tee()103 ~NBAIO_Tee() { 104 getRunningTees().remove(mTee); 105 dump(-1, "_DTOR"); // log any data remaining in Tee. 106 } 107 108 /** 109 * \brief set is used for deferred configuration of Tee. 110 * 111 * May be called anytime except concurrently with write(). 112 * 113 * \param format NBAIO_Format used to open NBAIO pipes 114 * \param flags (https://source.android.com/devices/audio/debugging) 115 * - TEE_FLAG_NONE to bypass af.tee property checks (default); 116 * - TEE_FLAG_INPUT_THREAD to check af.tee if input thread logging set; 117 * - TEE_FLAG_OUTPUT_THREAD to check af.tee if output thread logging set; 118 * - TEE_FLAG_TRACK to check af.tee if track logging set. 119 * \param frames number of frames to open the NBAIO pipe (set to 0 to use default). 120 * 121 * \return 122 * - NO_ERROR on success (or format unchanged) 123 * - BAD_VALUE if format or flags invalid. 124 * - PERMISSION_DENIED if flags not allowed by af.tee 125 */ 126 127 status_t set(const NBAIO_Format &format, 128 TEE_FLAG flags = TEE_FLAG_NONE, size_t frames = 0) const { 129 return mTee->set(format, flags, frames); 130 } 131 132 status_t set(uint32_t sampleRate, uint32_t channelCount, audio_format_t format, 133 TEE_FLAG flags = TEE_FLAG_NONE, size_t frames = 0) const { 134 return mTee->set(Format_from_SR_C(sampleRate, channelCount, format), flags, frames); 135 } 136 137 /** 138 * \brief write data to the tee. 139 * 140 * This call is lock free (as shared pointer and NBAIO is lock free); 141 * may be called simultaneous to all methods except set(). 142 * 143 * \param buffer to write to pipe. 144 * \param frameCount in frames as specified by the format passed to set() 145 */ 146 write(const void * buffer,size_t frameCount)147 void write(const void *buffer, size_t frameCount) const { 148 mTee->write(buffer, frameCount); 149 } 150 151 /** sets Tee id string which identifies the generated file (should be unique). */ setId(const std::string & id)152 void setId(const std::string &id) const { 153 mTee->setId(id); 154 } 155 156 /** 157 * \brief dump the audio content written to the Tee. 158 * 159 * \param fd file descriptor to write dumped filename for logging, use -1 to ignore. 160 * \param reason string suffix to append to the generated file. 161 */ 162 void dump(int fd, const std::string &reason = "") const { 163 mTee->dump(fd, reason); 164 } 165 166 /** 167 * \brief dump all Tees currently alive. 168 * 169 * \param fd file descriptor to write dumped filename for logging, use -1 to ignore. 170 * \param reason string suffix to append to the generated file. 171 */ 172 static void dumpAll(int fd, const std::string &reason = "") { 173 getRunningTees().dump(fd, reason); 174 } 175 176 private: 177 178 /** The underlying implementation of the Tee - the lifetime is through 179 a shared pointer so destruction of the NBAIO_Tee container may proceed 180 even though dumping is occurring. */ 181 class NBAIO_TeeImpl { 182 public: set(const NBAIO_Format & format,TEE_FLAG flags,size_t frames)183 status_t set(const NBAIO_Format &format, TEE_FLAG flags, size_t frames) { 184 static const int teeConfig = property_get_bool("ro.debuggable", false) 185 ? property_get_int32("af.tee", 0) : 0; 186 187 // check the type of Tee 188 const TEE_FLAG type = TEE_FLAG( 189 flags & (TEE_FLAG_INPUT_THREAD | TEE_FLAG_OUTPUT_THREAD | TEE_FLAG_TRACK)); 190 191 // parameter flags can't select multiple types. 192 if (__builtin_popcount(type) > 1) { 193 return BAD_VALUE; 194 } 195 196 // if type is set, we check to see if it is permitted by configuration. 197 if (type != 0 && (type & teeConfig) == 0) { 198 return PERMISSION_DENIED; 199 } 200 201 // determine number of frames for Tee 202 if (frames == 0) { 203 frames = (static_cast<long long>(DEFAULT_TEE_DURATION_MS) * format.mSampleRate) 204 / MILLIS_PER_SECOND; 205 } 206 207 // TODO: should we check minimum number of frames? 208 209 // don't do anything if format and frames are the same. 210 if (Format_isEqual(format, mFormat) && frames == mFrames) { 211 return NO_ERROR; 212 } 213 214 bool enabled = false; 215 auto sinksource = makeSinkSource(format, frames, &enabled); 216 217 // enabled is set if makeSinkSource is successful. 218 // Note: as mentioned in NBAIO_Tee::set(), don't call set() while write() is 219 // ongoing. 220 if (enabled) { 221 const std::lock_guard<std::mutex> _l(mLock); 222 mFlags = flags; 223 mFormat = format; // could get this from the Sink. 224 mFrames = frames; 225 mSinkSource = std::move(sinksource); 226 mEnabled.store(true); 227 return NO_ERROR; 228 } 229 return BAD_VALUE; 230 } 231 setId(const std::string & id)232 void setId(const std::string &id) { 233 const std::lock_guard<std::mutex> _l(mLock); 234 mId = id; 235 } 236 dump(int fd,const std::string & reason)237 void dump(int fd, const std::string &reason) { 238 if (!mDataReady.exchange(false)) return; 239 std::string suffix; 240 NBAIO_SinkSource sinkSource; 241 { 242 const std::lock_guard<std::mutex> _l(mLock); 243 suffix = mId + reason; 244 sinkSource = mSinkSource; 245 } 246 dumpTee(fd, sinkSource, suffix); 247 } 248 write(const void * buffer,size_t frameCount)249 void write(const void *buffer, size_t frameCount) { 250 if (!mEnabled.load() || frameCount == 0) return; 251 (void)mSinkSource.first->write(buffer, frameCount); 252 mDataReady.store(true); 253 } 254 255 private: 256 // TRICKY: We need to keep the NBAIO_Sink and NBAIO_Source both alive at the same time 257 // because PipeReader holds a naked reference (not a strong or weak pointer) to Pipe. 258 using NBAIO_SinkSource = std::pair<sp<NBAIO_Sink>, sp<NBAIO_Source>>; 259 260 static void dumpTee(int fd, const NBAIO_SinkSource& sinkSource, const std::string& suffix); 261 262 static NBAIO_SinkSource makeSinkSource( 263 const NBAIO_Format &format, size_t frames, bool *enabled); 264 265 static constexpr size_t DEFAULT_TEE_DURATION_MS = 60'000; 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: add(const std::shared_ptr<NBAIO_TeeImpl> & tee)284 void add(const std::shared_ptr<NBAIO_TeeImpl> &tee) { 285 const std::lock_guard<std::mutex> _l(mLock); 286 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) { 291 const std::lock_guard<std::mutex> _l(mLock); 292 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 { 299 const std::lock_guard<std::mutex> _l(mLock); 300 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 315 // The NBAIO TeeImpl may have lifetime longer than NBAIO_Tee if 316 // RunningTees::dump() is being called simultaneous to ~NBAIO_Tee(). 317 // This is allowed for maximum concurrency. 318 const std::shared_ptr<NBAIO_TeeImpl> mTee; 319 }; // NBAIO_Tee 320 321 } // namespace android 322 323 #endif // TEE_SINK 324