| Andy Hung | 8946a28 | 2018-04-19 20:04:56 -0700 | [diff] [blame] | 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 |  | 
 | 95 |     NBAIO_Tee() | 
 | 96 |         : mTee(std::make_shared<NBAIO_TeeImpl>()) | 
 | 97 |     { | 
 | 98 |         getRunningTees().add(mTee); | 
 | 99 |     } | 
 | 100 |  | 
 | 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 |  | 
 | 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). */ | 
 | 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: | 
 | 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 |  | 
 | 230 |         void setId(const std::string &id) { | 
 | 231 |             std::lock_guard<std::mutex> _l(mLock); | 
 | 232 |             mId = id; | 
 | 233 |         } | 
 | 234 |  | 
 | 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 |  | 
 | 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: | 
 | 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 |  | 
 | 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 |  | 
 | 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 | 
 | 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 |