1/*
2 * Copyright (C) 2016 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
18#include "BufLog.h"
19#define LOG_TAG "BufLog"
20//#define LOG_NDEBUG 0
21
22#include <errno.h>
23#include "log/log.h"
24#include <pthread.h>
25#include <stdio.h>
26#include <string.h>
27#include <audio_utils/string.h>
28
29#define MIN(a, b) ((a) < (b) ? (a) : (b))
30
31// ------------------------------
32// BufLogSingleton
33// ------------------------------
34pthread_once_t onceControl = PTHREAD_ONCE_INIT;
35
36BufLog *BufLogSingleton::mInstance = NULL;
37
38void BufLogSingleton::initOnce() {
39    mInstance = new BufLog();
40    ALOGW("=====================================\n" \
41            "Warning: BUFLOG is defined in some part of your code.\n" \
42            "This will create large audio dumps in %s.\n" \
43            "=====================================\n", BUFLOG_BASE_PATH);
44}
45
46BufLog *BufLogSingleton::instance() {
47    pthread_once(&onceControl, initOnce);
48    return mInstance;
49}
50
51bool BufLogSingleton::instanceExists() {
52    return mInstance != NULL;
53}
54
55// ------------------------------
56// BufLog
57// ------------------------------
58
59BufLog::BufLog() {
60    memset(mStreams, 0, sizeof(mStreams));
61}
62
63BufLog::~BufLog() {
64    android::Mutex::Autolock autoLock(mLock);
65
66    for (unsigned int id = 0; id < BUFLOG_MAXSTREAMS; id++) {
67        BufLogStream *pBLStream = mStreams[id];
68        if (pBLStream != NULL) {
69            delete pBLStream ;
70            mStreams[id] = NULL;
71        }
72    }
73}
74
75size_t BufLog::write(int streamid, const char *tag, int format, int channels,
76        int samplingRate, size_t maxBytes, const void *buf, size_t size) {
77    unsigned int id = streamid % BUFLOG_MAXSTREAMS;
78    android::Mutex::Autolock autoLock(mLock);
79
80    BufLogStream *pBLStream = mStreams[id];
81
82    if (pBLStream == NULL) {
83        pBLStream = mStreams[id] = new BufLogStream(id, tag, format, channels,
84                samplingRate, maxBytes);
85        ALOG_ASSERT(pBLStream != NULL, "BufLogStream Failed to be created");
86    }
87
88    return pBLStream->write(buf, size);
89}
90
91void BufLog::reset() {
92    android::Mutex::Autolock autoLock(mLock);
93    ALOGV("Resetting all BufLogs");
94    int count = 0;
95
96    for (unsigned int id = 0; id < BUFLOG_MAXSTREAMS; id++) {
97        BufLogStream *pBLStream = mStreams[id];
98        if (pBLStream != NULL) {
99            delete pBLStream;
100            mStreams[id] = NULL;
101            count++;
102        }
103    }
104    ALOGV("Reset %d BufLogs", count);
105}
106
107// ------------------------------
108// BufLogStream
109// ------------------------------
110
111BufLogStream::BufLogStream(unsigned int id,
112        const char *tag,
113        unsigned int format,
114        unsigned int channels,
115        unsigned int samplingRate,
116        size_t maxBytes = 0) : mId(id), mFormat(format), mChannels(channels),
117                mSamplingRate(samplingRate), mMaxBytes(maxBytes) {
118    mByteCount = 0l;
119    mPaused = false;
120    if (tag != NULL) {
121        (void)audio_utils_strlcpy(mTag, tag);
122    } else {
123        mTag[0] = 0;
124    }
125    ALOGV("Creating BufLogStream id:%d tag:%s format:%#x ch:%d sr:%d maxbytes:%zu", mId, mTag,
126            mFormat, mChannels, mSamplingRate, mMaxBytes);
127
128    //open file (s), info about tag, format, etc.
129    //timestamp
130    char timeStr[16];   //size 16: format %Y%m%d%H%M%S 14 chars + string null terminator
131    struct timeval tv;
132    gettimeofday(&tv, NULL);
133    struct tm tm;
134    localtime_r(&tv.tv_sec, &tm);
135    strftime(timeStr, sizeof(timeStr), "%Y%m%d%H%M%S", &tm);
136    char logPath[BUFLOG_MAX_PATH_SIZE];
137    snprintf(logPath, BUFLOG_MAX_PATH_SIZE, "%s/%s_%d_%s_%d_%d_%d.raw", BUFLOG_BASE_PATH, timeStr,
138            mId, mTag, mFormat, mChannels, mSamplingRate);
139    ALOGV("data output: %s", logPath);
140
141    mFile = fopen(logPath, "wb");
142    if (mFile != NULL) {
143        ALOGV("Success creating file at: %p", mFile);
144    } else {
145        ALOGE("Error: could not create file BufLogStream %s", strerror(errno));
146    }
147}
148
149void BufLogStream::closeStream_l() {
150    ALOGV("Closing BufLogStream id:%d tag:%s", mId, mTag);
151    if (mFile != NULL) {
152        fclose(mFile);
153        mFile = NULL;
154    }
155}
156
157BufLogStream::~BufLogStream() {
158    ALOGV("Destroying BufLogStream id:%d tag:%s", mId, mTag);
159    android::Mutex::Autolock autoLock(mLock);
160    closeStream_l();
161}
162
163size_t BufLogStream::write(const void *buf, size_t size) {
164
165    size_t bytes = 0;
166    if (!mPaused && mFile != NULL) {
167        if (size > 0 && buf != NULL) {
168            android::Mutex::Autolock autoLock(mLock);
169            if (mMaxBytes > 0) {
170                size = MIN(size, mMaxBytes - mByteCount);
171            }
172            bytes = fwrite(buf, 1, size, mFile);
173            mByteCount += bytes;
174            if (mMaxBytes > 0 && mMaxBytes == mByteCount) {
175                closeStream_l();
176            }
177        }
178        ALOGV("wrote %zu/%zu bytes to BufLogStream %d tag:%s. Total Bytes: %zu", bytes, size, mId,
179                mTag, mByteCount);
180    } else {
181        ALOGV("Warning: trying to write to %s BufLogStream id:%d tag:%s",
182                mPaused ? "paused" : "closed", mId, mTag);
183    }
184    return bytes;
185}
186
187bool BufLogStream::setPause(bool pause) {
188    bool old = mPaused;
189    mPaused = pause;
190    return old;
191}
192
193void BufLogStream::finalize() {
194    android::Mutex::Autolock autoLock(mLock);
195    closeStream_l();
196}
197