Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3da2c43e authored by Andy Hung's avatar Andy Hung Committed by Android (Google) Code Review
Browse files

Merge "AudioFlinger: Fix Tee logging"

parents 1f90537e 8946a28a
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -13,7 +13,8 @@ LOCAL_SRC_FILES:= \
    PatchPanel.cpp              \
    StateQueue.cpp              \
    BufLog.cpp                  \
    TypedLogger.cpp
    TypedLogger.cpp             \
    NBAIO_Tee.cpp               \

LOCAL_C_INCLUDES := \
    frameworks/av/services/audiopolicy \
@@ -41,6 +42,7 @@ LOCAL_SHARED_LIBRARIES := \

LOCAL_STATIC_LIBRARIES := \
    libcpustats \
    libsndfile \

LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)

+5 −222
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@
#include <system/audio.h>

#include "AudioFlinger.h"
#include "NBAIO_Tee.h"

#include <media/AudioResamplerPublic.h>

@@ -55,7 +56,6 @@
#include <system/audio_effects/effect_aec.h>

#include <audio_utils/primitives.h>
#include <audio_utils/string.h>

#include <powermanager/PowerManager.h>

@@ -100,17 +100,6 @@ nsecs_t AudioFlinger::mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs;

uint32_t AudioFlinger::mScreenState;


#ifdef TEE_SINK
bool AudioFlinger::mTeeSinkInputEnabled = false;
bool AudioFlinger::mTeeSinkOutputEnabled = false;
bool AudioFlinger::mTeeSinkTrackEnabled = false;

size_t AudioFlinger::mTeeSinkInputFrames = kTeeSinkInputFramesDefault;
size_t AudioFlinger::mTeeSinkOutputFrames = kTeeSinkOutputFramesDefault;
size_t AudioFlinger::mTeeSinkTrackFrames = kTeeSinkTrackFramesDefault;
#endif

// In order to avoid invalidating offloaded tracks each time a Visualizer is turned on and off
// we define a minimum time during which a global effect is considered enabled.
static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200);
@@ -186,27 +175,6 @@ AudioFlinger::AudioFlinger()
    mEffectsFactoryHal = EffectsFactoryHalInterface::create();

    mMediaLogNotifier->run("MediaLogNotifier");

#ifdef TEE_SINK
    char value[PROPERTY_VALUE_MAX];
    (void) property_get("ro.debuggable", value, "0");
    int debuggable = atoi(value);
    int teeEnabled = 0;
    if (debuggable) {
        (void) property_get("af.tee", value, "0");
        teeEnabled = atoi(value);
    }
    // FIXME symbolic constants here
    if (teeEnabled & 1) {
        mTeeSinkInputEnabled = true;
    }
    if (teeEnabled & 2) {
        mTeeSinkOutputEnabled = true;
    }
    if (teeEnabled & 4) {
        mTeeSinkTrackEnabled = true;
    }
#endif
}

void AudioFlinger::onFirstRef()
@@ -535,19 +503,16 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args)

        mPatchPanel.dump(fd);

#ifdef TEE_SINK
        // dump the serially shared record tee sink
        if (mRecordTeeSource != 0) {
            dumpTee(fd, mRecordTeeSource, AUDIO_IO_HANDLE_NONE, 'C');
        }
#endif

        BUFLOG_RESET;

        if (locked) {
            mLock.unlock();
        }

#ifdef TEE_SINK
        // NBAIO_Tee dump is safe to call outside of AF lock.
        NBAIO_Tee::dumpAll(fd, "_DUMP");
#endif
        // append a copy of media.log here by forwarding fd to it, but don't attempt
        // to lookup the service if it's not running, as it will block for a second
        if (sMediaLogServiceAsBinder != 0) {
@@ -2430,55 +2395,6 @@ sp<AudioFlinger::ThreadBase> AudioFlinger::openInput_l(audio_module_handle_t mod
                    thread.get());
            return thread;
        } else {
#ifdef TEE_SINK
            // Try to re-use most recently used Pipe to archive a copy of input for dumpsys,
            // or (re-)create if current Pipe is idle and does not match the new format
            sp<NBAIO_Sink> teeSink;
            enum {
                TEE_SINK_NO,    // don't copy input
                TEE_SINK_NEW,   // copy input using a new pipe
                TEE_SINK_OLD,   // copy input using an existing pipe
            } kind;
            NBAIO_Format format = Format_from_SR_C(halconfig.sample_rate,
                    audio_channel_count_from_in_mask(halconfig.channel_mask), halconfig.format);
            if (!mTeeSinkInputEnabled) {
                kind = TEE_SINK_NO;
            } else if (!Format_isValid(format)) {
                kind = TEE_SINK_NO;
            } else if (mRecordTeeSink == 0) {
                kind = TEE_SINK_NEW;
            } else if (mRecordTeeSink->getStrongCount() != 1) {
                kind = TEE_SINK_NO;
            } else if (Format_isEqual(format, mRecordTeeSink->format())) {
                kind = TEE_SINK_OLD;
            } else {
                kind = TEE_SINK_NEW;
            }
            switch (kind) {
            case TEE_SINK_NEW: {
                Pipe *pipe = new Pipe(mTeeSinkInputFrames, format);
                size_t numCounterOffers = 0;
                const NBAIO_Format offers[1] = {format};
                ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
                ALOG_ASSERT(index == 0);
                PipeReader *pipeReader = new PipeReader(*pipe);
                numCounterOffers = 0;
                index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
                ALOG_ASSERT(index == 0);
                mRecordTeeSink = pipe;
                mRecordTeeSource = pipeReader;
                teeSink = pipe;
                }
                break;
            case TEE_SINK_OLD:
                teeSink = mRecordTeeSink;
                break;
            case TEE_SINK_NO:
            default:
                break;
            }
#endif

            // Start record thread
            // RecordThread requires both input and output device indication to forward to audio
            // pre processing modules
@@ -2488,9 +2404,6 @@ sp<AudioFlinger::ThreadBase> AudioFlinger::openInput_l(audio_module_handle_t mod
                                      primaryOutputDevice_l(),
                                      devices,
                                      mSystemReady
#ifdef TEE_SINK
                                      , teeSink
#endif
                                      );
            mRecordThreads.add(*input, thread);
            ALOGV("openInput_l() created record thread: ID %d thread %p", *input, thread.get());
@@ -3393,136 +3306,6 @@ bool AudioFlinger::updateOrphanEffectChains(const sp<AudioFlinger::EffectModule>
}


struct Entry {
#define TEE_MAX_FILENAME 32 // %Y%m%d%H%M%S_%d.wav = 4+2+2+2+2+2+1+1+4+1 = 21
    char mFileName[TEE_MAX_FILENAME];
};

int comparEntry(const void *p1, const void *p2)
{
    return strcmp(((const Entry *) p1)->mFileName, ((const Entry *) p2)->mFileName);
}

#ifdef TEE_SINK
void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id, char suffix)
{
    NBAIO_Source *teeSource = source.get();
    if (teeSource != NULL) {
        // .wav rotation
        // There is a benign race condition if 2 threads call this simultaneously.
        // They would both traverse the directory, but the result would simply be
        // failures at unlink() which are ignored.  It's also unlikely since
        // normally dumpsys is only done by bugreport or from the command line.
        char teePath[PATH_MAX] = "/data/misc/audioserver";
        size_t teePathLen = strlen(teePath);
        DIR *dir = opendir(teePath);
        teePath[teePathLen++] = '/';
        if (dir != NULL) {
#define TEE_MAX_SORT 20 // number of entries to sort
#define TEE_MAX_KEEP 10 // number of entries to keep
            struct Entry entries[TEE_MAX_SORT];
            size_t entryCount = 0;
            while (entryCount < TEE_MAX_SORT) {
                errno = 0; // clear errno before readdir() to track potential errors.
                const struct dirent *result = readdir(dir);
                if (result == nullptr) {
                    ALOGW_IF(errno != 0, "tee readdir() failure %s", strerror(errno));
                    break;
                }
                // ignore non .wav file entries
                const size_t nameLen = strlen(result->d_name);
                if (nameLen <= 4 || nameLen >= TEE_MAX_FILENAME ||
                        strcmp(&result->d_name[nameLen - 4], ".wav")) {
                    continue;
                }
                (void)audio_utils_strlcpy(entries[entryCount++].mFileName, result->d_name);
            }
            (void) closedir(dir);
            if (entryCount > TEE_MAX_KEEP) {
                qsort(entries, entryCount, sizeof(Entry), comparEntry);
                for (size_t i = 0; i < entryCount - TEE_MAX_KEEP; ++i) {
                    strcpy(&teePath[teePathLen], entries[i].mFileName);
                    (void) unlink(teePath);
                }
            }
        } else {
            if (fd >= 0) {
                dprintf(fd, "unable to rotate tees in %.*s: %s\n", (int) teePathLen, teePath,
                        strerror(errno));
            }
        }
        char teeTime[16];
        struct timeval tv;
        gettimeofday(&tv, NULL);
        struct tm tm;
        localtime_r(&tv.tv_sec, &tm);
        strftime(teeTime, sizeof(teeTime), "%Y%m%d%H%M%S", &tm);
        snprintf(&teePath[teePathLen], sizeof(teePath) - teePathLen, "%s_%d_%c.wav", teeTime, id,
                suffix);
        // if 2 dumpsys are done within 1 second, and rotation didn't work, then discard 2nd
        int teeFd = open(teePath, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR);
        if (teeFd >= 0) {
            // FIXME use libsndfile
            char wavHeader[44];
            memcpy(wavHeader,
                "RIFF\0\0\0\0WAVEfmt \20\0\0\0\1\0\2\0\104\254\0\0\0\0\0\0\4\0\20\0data\0\0\0\0",
                sizeof(wavHeader));
            NBAIO_Format format = teeSource->format();
            unsigned channelCount = Format_channelCount(format);
            uint32_t sampleRate = Format_sampleRate(format);
            size_t frameSize = Format_frameSize(format);
            wavHeader[22] = channelCount;       // number of channels
            wavHeader[24] = sampleRate;         // sample rate
            wavHeader[25] = sampleRate >> 8;
            wavHeader[32] = frameSize;          // block alignment
            wavHeader[33] = frameSize >> 8;
            write(teeFd, wavHeader, sizeof(wavHeader));
            size_t total = 0;
            bool firstRead = true;
#define TEE_SINK_READ 1024                      // frames per I/O operation
            void *buffer = malloc(TEE_SINK_READ * frameSize);
            for (;;) {
                size_t count = TEE_SINK_READ;
                ssize_t actual = teeSource->read(buffer, count);
                bool wasFirstRead = firstRead;
                firstRead = false;
                if (actual <= 0) {
                    if (actual == (ssize_t) OVERRUN && wasFirstRead) {
                        continue;
                    }
                    break;
                }
                ALOG_ASSERT(actual <= (ssize_t)count);
                write(teeFd, buffer, actual * frameSize);
                total += actual;
            }
            free(buffer);
            lseek(teeFd, (off_t) 4, SEEK_SET);
            uint32_t temp = 44 + total * frameSize - 8;
            // FIXME not big-endian safe
            write(teeFd, &temp, sizeof(temp));
            lseek(teeFd, (off_t) 40, SEEK_SET);
            temp =  total * frameSize;
            // FIXME not big-endian safe
            write(teeFd, &temp, sizeof(temp));
            close(teeFd);
            // TODO Should create file with temporary name and then rename to final if non-empty.
            if (total > 0) {
                if (fd >= 0) {
                    dprintf(fd, "tee copied to %s\n", teePath);
                }
            } else {
                unlink(teePath);
            }
        } else {
            if (fd >= 0) {
                dprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno));
            }
        }
    }
}
#endif

// ----------------------------------------------------------------------------

status_t AudioFlinger::onTransact(
+1 −28
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@
#include "AudioStreamOut.h"
#include "SpdifStreamOut.h"
#include "AudioHwDevice.h"
#include "NBAIO_Tee.h"

#include <powermanager/IPowerManager.h>

@@ -800,35 +801,7 @@ private:

    void        filterReservedParameters(String8& keyValuePairs, uid_t callingUid);

#ifdef TEE_SINK
    // all record threads serially share a common tee sink, which is re-created on format change
    sp<NBAIO_Sink>   mRecordTeeSink;
    sp<NBAIO_Source> mRecordTeeSource;
#endif

public:

#ifdef TEE_SINK
    // tee sink, if enabled by property, allows dumpsys to write most recent audio to .wav file
    static void dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id, char suffix);

    // whether tee sink is enabled by property
    static bool mTeeSinkInputEnabled;
    static bool mTeeSinkOutputEnabled;
    static bool mTeeSinkTrackEnabled;

    // runtime configured size of each tee sink pipe, in frames
    static size_t mTeeSinkInputFrames;
    static size_t mTeeSinkOutputFrames;
    static size_t mTeeSinkTrackFrames;

    // compile-time default size of tee sink pipes, in frames
    // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes
    static const size_t kTeeSinkInputFramesDefault = 0x200000;
    static const size_t kTeeSinkOutputFramesDefault = 0x200000;
    static const size_t kTeeSinkTrackFramesDefault = 0x200000;
#endif

    // These methods read variables atomically without mLock,
    // though the variables are updated with mLock.
    bool    isLowRamDevice() const { return mIsLowRamDevice; }
+13 −6
Original line number Diff line number Diff line
@@ -47,7 +47,8 @@ namespace android {

/*static*/ const FastMixerState FastMixer::sInitial;

FastMixer::FastMixer() : FastThread("cycle_ms", "load_us"),
FastMixer::FastMixer(audio_io_handle_t parentIoHandle)
    : FastThread("cycle_ms", "load_us"),
    // mFastTrackNames
    // mGenerations
    mOutputSink(NULL),
@@ -66,8 +67,11 @@ FastMixer::FastMixer() : FastThread("cycle_ms", "load_us"),
    mTotalNativeFramesWritten(0),
    // timestamp
    mNativeFramesWrittenButNotPresented(0),   // the = 0 is to silence the compiler
    mMasterMono(false)
    mMasterMono(false),
    mThreadIoHandle(parentIoHandle)
{
    (void)mThreadIoHandle; // prevent unused warning, see C++17 [[maybe_unused]]

    // FIXME pass sInitial as parameter to base class constructor, and make it static local
    mPrevious = &sInitial;
    mCurrent = &sInitial;
@@ -220,6 +224,10 @@ void FastMixer::onStateChange()
        previousTrackMask = 0;
        mFastTracksGen = current->mFastTracksGen - 1;
        dumpState->mFrameCount = frameCount;
#ifdef TEE_SINK
        mTee.set(mFormat, NBAIO_Tee::TEE_FLAG_OUTPUT_THREAD);
        mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + "_F");
#endif
    } else {
        previousTrackMask = previous->mTrackMask;
    }
@@ -446,10 +454,9 @@ void FastMixer::onWork()
                    frameCount * Format_channelCount(mFormat));
        }
        // if non-NULL, then duplicate write() to this non-blocking sink
        NBAIO_Sink* teeSink;
        if ((teeSink = current->mTeeSink) != NULL) {
            (void) teeSink->write(buffer, frameCount);
        }
#ifdef TEE_SINK
        mTee.write(buffer, frameCount);
#endif
        // FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink,
        //       but this code should be modified to handle both non-blocking and blocking sinks
        dumpState->mWriteSequence++;
+9 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include "StateQueue.h"
#include "FastMixerState.h"
#include "FastMixerDumpState.h"
#include "NBAIO_Tee.h"

namespace android {

@@ -32,7 +33,9 @@ typedef StateQueue<FastMixerState> FastMixerStateQueue;
class FastMixer : public FastThread {

public:
            FastMixer();
    /** FastMixer constructor takes as param the parent MixerThread's io handle (id)
        for purposes of identification. */
    explicit FastMixer(audio_io_handle_t threadIoHandle);
    virtual ~FastMixer();

            FastMixerStateQueue* sq();
@@ -87,6 +90,11 @@ private:
    // accessed without lock between multiple threads.
    std::atomic_bool mMasterMono;
    std::atomic_int_fast64_t mBoottimeOffset;

    const audio_io_handle_t mThreadIoHandle; // parent thread id for debugging purposes
#ifdef TEE_SINK
    NBAIO_Tee       mTee;
#endif
};  // class FastMixer

}   // namespace android
Loading