1583 lines
60 KiB
C++
1583 lines
60 KiB
C++
#define LOG_TAG "AudioDumpExt"
|
|
|
|
#include "AudioDumpExt.h"
|
|
|
|
#define FWK_DUMP_PATH "/data/debuglogger/audio_dump/"
|
|
|
|
#define MAX_FILE_NAME 256
|
|
#define SIZE_MB (1024 * 1024)
|
|
#define SIZE_GB (1024 * 1024 * 1024)
|
|
#define PROPERTY_DUMP_VER_DISABLE "persist.vendor.audiohal.dump.ver.disable"
|
|
|
|
#define DUMP_VERSION_OLD 1
|
|
#define DUMP_VERSION_NEW 2
|
|
|
|
namespace android {
|
|
|
|
AudioDumpExt::AudioDumpExt() {
|
|
}
|
|
|
|
const char * const AudioDumpExt::keyAudioDumpWriter = "vendor.af.writer.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpTrack = "vendor.af.track.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpOffload = "vendor.af.offload.raw";
|
|
const char * const AudioDumpExt::keyAudioDumpResampler = "vendor.af.resampler.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpMixer = "vendor.af.mixer.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpRecord = "vendor.af.record.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpEffect = "vendor.af.effect.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpDrc = "vendor.af.drc.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpLog = "vendor.af.dumplog";
|
|
const char * const AudioDumpExt::keyAudioDumpAAudio = "vendor.af.aaudio.pcm";
|
|
|
|
const char * AudioDumpExt::audioDumpPropertyStr[] = {
|
|
AudioDumpExt::keyAudioDumpWriter,
|
|
AudioDumpExt::keyAudioDumpTrack,
|
|
AudioDumpExt::keyAudioDumpOffload,
|
|
AudioDumpExt::keyAudioDumpResampler,
|
|
AudioDumpExt::keyAudioDumpMixer,
|
|
AudioDumpExt::keyAudioDumpRecord,
|
|
AudioDumpExt::keyAudioDumpEffect,
|
|
AudioDumpExt::keyAudioDumpDrc,
|
|
AudioDumpExt::keyAudioDumpLog,
|
|
AudioDumpExt::keyAudioDumpAAudio,
|
|
};
|
|
|
|
const char * const AudioDumpExt::keyAudioDumpLegacyWriter = "vendor.af.mixer.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpLegacyTrack = "vendor.af.track.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpLegacyOffload = "vendor.af.offload.write.raw";
|
|
const char * const AudioDumpExt::keyAudioDumpLegacyResampler = "vendor.af.resampler.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpLegacyMixer = "vendor.af.mixer.end.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpLegacyRecord = "vendor.af.record.dump.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpLegacyEffect = "vendor.af.effect.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpLegacyDrc = "vendor.af.mixer.drc.pcm";
|
|
const char * const AudioDumpExt::keyAudioDumpLegacyLog = "vendor.af.dumplog";
|
|
const char * const AudioDumpExt::keyAudioDumpLegacyAAudio = "vendor.aaudio.pcm";
|
|
|
|
const char * AudioDumpExt::audioDumpLegacyPropertyStr[] = {
|
|
AudioDumpExt::keyAudioDumpLegacyWriter,
|
|
AudioDumpExt::keyAudioDumpLegacyTrack,
|
|
AudioDumpExt::keyAudioDumpLegacyOffload,
|
|
AudioDumpExt::keyAudioDumpLegacyResampler,
|
|
AudioDumpExt::keyAudioDumpLegacyMixer,
|
|
AudioDumpExt::keyAudioDumpLegacyRecord,
|
|
AudioDumpExt::keyAudioDumpLegacyEffect,
|
|
AudioDumpExt::keyAudioDumpLegacyDrc,
|
|
AudioDumpExt::keyAudioDumpLegacyLog,
|
|
AudioDumpExt::keyAudioDumpLegacyAAudio,
|
|
};
|
|
|
|
const char * const AudioDumpExt::af_track = "af_track";
|
|
const char * const AudioDumpExt::af_resampler = "af_resampler";
|
|
const char * const AudioDumpExt::af_drc = "af_drc";
|
|
const char * const AudioDumpExt::af_mixer = "af_mixer";
|
|
const char * const AudioDumpExt::af_effect = "af_effect";
|
|
const char * const AudioDumpExt::af_writer = "af_writer";
|
|
const char * const AudioDumpExt::af_record = "af_record";
|
|
const char * const AudioDumpExt::af_ulconvert = "af_ulconvert";
|
|
const char * const AudioDumpExt::af_reader = "af_reader";
|
|
const char * const AudioDumpExt::aaudio_share_dl = "aaudio_share_dl";
|
|
const char * const AudioDumpExt::aaudio_share_ul = "aaudio_share_ul";
|
|
const char * const AudioDumpExt::aaudio_exclusive_dl = "aaudio_exclusive_dl";
|
|
const char * const AudioDumpExt::aaudio_exclusive_ul = "aaudio_exclusive_ul";
|
|
|
|
int AudioDumpExt::mAPILevel = 0;
|
|
int AudioDumpExt::mDumpVersion = 0;
|
|
|
|
//test
|
|
void AudioDumpExt::dump() {
|
|
}
|
|
|
|
void AudioDumpExt::threadDump(const char * path, void * buffer, int count, audio_format_t format,
|
|
uint32_t sampleRate, uint32_t channelCount) {
|
|
int dumpVersion = checkDumpVersion();
|
|
if (dumpVersion == DUMP_VERSION_NEW) {
|
|
AudioDumpClip::threadDump(path, buffer, count, format, sampleRate, channelCount);
|
|
} else {
|
|
AudioDump::threadDump(path, buffer, count, format, sampleRate, channelCount);
|
|
}
|
|
}
|
|
|
|
bool AudioDumpExt::getProperty(AudioDumpExt::PROP_AUDIO_DUMP key) {
|
|
int dumpVersion = checkDumpVersion();
|
|
if (dumpVersion == DUMP_VERSION_NEW) {
|
|
AudioDumpExt::PROP_AUDIO_DUMP keyClip = (AudioDumpExt::PROP_AUDIO_DUMP)key;
|
|
return AudioDumpClip::getProperty(keyClip);
|
|
} else {
|
|
AudioDump::PROP_AUDIO_DUMP keyOld = (AudioDump::PROP_AUDIO_DUMP)key;
|
|
if (checkVendorVersion() >= __ANDROID_API_U__) {
|
|
return AudioDump::getProperty(keyOld);
|
|
} else {
|
|
return AudioDump::getPropertyLegacy(keyOld);
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned long AudioDumpExt::getDumpSize(const char * path) {
|
|
int dumpVersion = checkDumpVersion();
|
|
if (dumpVersion == DUMP_VERSION_NEW) {
|
|
return AudioDumpClip::getDumpSize(path);
|
|
} else {
|
|
return AudioDump::getDumpSize(path);
|
|
}
|
|
}
|
|
|
|
String8 AudioDumpExt::getDumpName(const char * path) {
|
|
int dumpVersion = checkDumpVersion();
|
|
if (dumpVersion == DUMP_VERSION_NEW) {
|
|
return AudioDumpClip::getDumpName(path);
|
|
} else {
|
|
return String8(path);
|
|
}
|
|
}
|
|
|
|
void AudioDumpExt::updateKeys(int key) {
|
|
int dumpVersion = checkDumpVersion();
|
|
if (dumpVersion == DUMP_VERSION_NEW) {
|
|
AudioDumpClip::updateKeys(key);
|
|
} else{
|
|
if (checkVendorVersion() >= __ANDROID_API_U__) {
|
|
AudioDump::updateKeys(key);
|
|
} else {
|
|
AudioDump::updateKeysLegacy(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AudioDumpExt::checkIfNeedToDump(audio_format_t format) {
|
|
if (format == AUDIO_FORMAT_AAC_LC) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void AudioDumpExt::startDumpManagerThread() {
|
|
int dumpVersion = checkDumpVersion();
|
|
if (dumpVersion == DUMP_VERSION_NEW) {
|
|
ALOGD("DumpManagerThread: triggered by hal dump enable");
|
|
AudioDumpClip::startDumpManagerThread();
|
|
} else {
|
|
ALOGE("DumpManagerThread: won't triggered by hal dump enable - dump version fail");
|
|
}
|
|
}
|
|
|
|
int AudioDumpExt::checkVendorVersion() {
|
|
if (mAPILevel != 0) {
|
|
return mAPILevel;
|
|
} else {
|
|
mAPILevel = property_get_int32("ro.vndk.version", __ANDROID_API_U__);
|
|
return mAPILevel;
|
|
}
|
|
}
|
|
|
|
int AudioDumpExt::checkDumpVersion() {
|
|
if (mDumpVersion != 0) {
|
|
return mDumpVersion;
|
|
} else {
|
|
int btemp = (int)property_get_bool(PROPERTY_DUMP_VER_DISABLE, false);
|
|
if (btemp != 0) {
|
|
ALOGW("%s(), dumpVersionDisabled, fall back legacy flow", __func__);
|
|
mDumpVersion = DUMP_VERSION_OLD;
|
|
} else {
|
|
if (mAPILevel != 0) {
|
|
if (mAPILevel >= __ANDROID_API_U__) {
|
|
ALOGW("%s(), mAPILevel valid, vendor >= U, new flow", __func__);
|
|
mDumpVersion = DUMP_VERSION_NEW;
|
|
} else {
|
|
ALOGW("%s(), mAPILevel valid, vendor < U, legacy flow", __func__);
|
|
mDumpVersion = DUMP_VERSION_OLD;
|
|
}
|
|
} else {
|
|
mAPILevel = property_get_int32("ro.vndk.version", __ANDROID_API_U__);
|
|
if (mAPILevel != 0) {
|
|
if (mAPILevel >= __ANDROID_API_U__) {
|
|
ALOGW("%s(), mAPILevel invalid, get vendor >= U, new flow", __func__);
|
|
mDumpVersion = DUMP_VERSION_NEW;
|
|
} else {
|
|
ALOGW("%s(), mAPILevel invalid, get vendor < U, legacy flow", __func__);
|
|
mDumpVersion = DUMP_VERSION_OLD;
|
|
}
|
|
} else {
|
|
ALOGE("%s(), mAPILevel = 0, error", __func__);
|
|
}
|
|
}
|
|
}
|
|
return mDumpVersion;
|
|
}
|
|
}
|
|
|
|
//AudioDumpEmptyThread defination
|
|
pthread_t hAudioDumpEmptyThread = 0;
|
|
pthread_cond_t AudioDumpEmptyNotifyEvent;
|
|
pthread_mutex_t AudioDumpEmptyMutex;
|
|
|
|
int deleteDirFiles(const char *dirpath)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *dirp;
|
|
struct stat dirs;
|
|
char file_name[MAX_FILE_NAME] = {0};
|
|
int ret = -1;
|
|
|
|
if (dirpath == NULL) {
|
|
ALOGE("dirpath is empty!\n");
|
|
return -1;
|
|
}
|
|
|
|
if (stat(dirpath, &dirs) != 0) {
|
|
ALOGE("stat: %s failed!", dirpath);
|
|
return -1;
|
|
}
|
|
|
|
if (S_ISDIR(dirs.st_mode)) {
|
|
dir = opendir(dirpath);
|
|
if (dir == NULL) {
|
|
ALOGE("opendir: %s failed!", dirpath);
|
|
return -1;
|
|
}
|
|
while ((dirp = readdir(dir)) != NULL) {
|
|
if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0)
|
|
continue;
|
|
ret = snprintf(file_name, sizeof(file_name), "%s/%s",
|
|
dirpath, dirp->d_name);
|
|
if (ret < 0 || ret >= sizeof(file_name)) {
|
|
ALOGE("%s(), snprintf fail!!: line: %d", __FUNCTION__, __LINE__);
|
|
continue;
|
|
}
|
|
deleteDirFiles(file_name);
|
|
}
|
|
closedir(dir);
|
|
ret = remove(dirpath);
|
|
if (ret != 0) {
|
|
ALOGW("remove: %s failed!, line: %d", dirpath, __LINE__);
|
|
}
|
|
} else {
|
|
if (remove(dirpath) != 0) {
|
|
ALOGW("remove: %s failed!, line: %d", dirpath, __LINE__);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void *AudioDumpEmptyThread(void *arg __attribute__((unused)))
|
|
{
|
|
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DEFAULT);
|
|
|
|
while (1) {
|
|
//do delete action
|
|
ALOGE("%s(), do delete action", __func__);
|
|
deleteDirFiles(FWK_DUMP_PATH);
|
|
ALOGE("%s(), delete action done", __func__);
|
|
|
|
pthread_mutex_lock(&AudioDumpEmptyMutex);
|
|
pthread_cond_wait(&AudioDumpEmptyNotifyEvent, &AudioDumpEmptyMutex);
|
|
pthread_mutex_unlock(&AudioDumpEmptyMutex);
|
|
}
|
|
|
|
ALOGD("AudioDumpEmptyThread exit hAudioDumpThread=%ld", hAudioDumpEmptyThread);
|
|
hAudioDumpEmptyThread = 0;
|
|
pthread_exit(NULL);
|
|
return 0;
|
|
}
|
|
|
|
void AudioDumpExt::startAudioDumpEmptyThread() {
|
|
if (!hAudioDumpEmptyThread) {
|
|
int ret = 0;
|
|
ret = pthread_create(&hAudioDumpEmptyThread, NULL, AudioDumpEmptyThread, NULL);
|
|
if (ret != 0) {
|
|
ALOGE("hAudioDumpEmptyThread create fail!!!");
|
|
} else {
|
|
ALOGD("hAudioDumpEmptyThread created");
|
|
}
|
|
|
|
ret = pthread_cond_init(&AudioDumpEmptyNotifyEvent, NULL);
|
|
if (ret != 0) {
|
|
ALOGE("AudioDumpEmptyNotifyEvent create fail!!!");
|
|
}
|
|
ret = pthread_mutex_init(&AudioDumpEmptyMutex, NULL);
|
|
if (ret != 0) {
|
|
ALOGE("AudioDumpEmptyMutex create fail!!!");
|
|
}
|
|
} else {
|
|
pthread_mutex_lock(&AudioDumpEmptyMutex);
|
|
pthread_cond_signal(&AudioDumpEmptyNotifyEvent);
|
|
pthread_mutex_unlock(&AudioDumpEmptyMutex);
|
|
}
|
|
}
|
|
|
|
/*AudioDumpClip*/
|
|
/****************/
|
|
|
|
AudioDumpExt::AudioDumpClip::AudioDumpClip() {
|
|
}
|
|
|
|
int64_t AudioDumpExt::AudioDumpClip::mClipSize = -1;
|
|
|
|
KeyedVector<AudioDumpExt::PROP_AUDIO_DUMP, int> mAudioDumpClipKeyEnableVector; //Vector for property
|
|
|
|
const char * const AudioDumpExt::AudioDumpClip::keyAudioDumpWriter = "vendor.af.writer.pcm";
|
|
const char * const AudioDumpExt::AudioDumpClip::keyAudioDumpTrack = "vendor.af.track.pcm";
|
|
const char * const AudioDumpExt::AudioDumpClip::keyAudioDumpOffload = "vendor.af.offload.raw";
|
|
const char * const AudioDumpExt::AudioDumpClip::keyAudioDumpResampler = "vendor.af.resampler.pcm";
|
|
const char * const AudioDumpExt::AudioDumpClip::keyAudioDumpMixer = "vendor.af.mixer.pcm";
|
|
const char * const AudioDumpExt::AudioDumpClip::keyAudioDumpRecord = "vendor.af.record.pcm";
|
|
const char * const AudioDumpExt::AudioDumpClip::keyAudioDumpEffect = "vendor.af.effect.pcm";
|
|
const char * const AudioDumpExt::AudioDumpClip::keyAudioDumpDrc = "vendor.af.drc.pcm";
|
|
const char * const AudioDumpExt::AudioDumpClip::keyAudioDumpLog = "vendor.af.dumplog";
|
|
const char * const AudioDumpExt::AudioDumpClip::keyAudioDumpAAudio = "vendor.af.aaudio.pcm";
|
|
|
|
const char * AudioDumpExt::AudioDumpClip::audioDumpPropertyStr[] = {
|
|
AudioDumpExt::AudioDumpClip::keyAudioDumpWriter,
|
|
AudioDumpExt::AudioDumpClip::keyAudioDumpTrack,
|
|
AudioDumpExt::AudioDumpClip::keyAudioDumpOffload,
|
|
AudioDumpExt::AudioDumpClip::keyAudioDumpResampler,
|
|
AudioDumpExt::AudioDumpClip::keyAudioDumpMixer,
|
|
AudioDumpExt::AudioDumpClip::keyAudioDumpRecord,
|
|
AudioDumpExt::AudioDumpClip::keyAudioDumpEffect,
|
|
AudioDumpExt::AudioDumpClip::keyAudioDumpDrc,
|
|
AudioDumpExt::AudioDumpClip::keyAudioDumpLog,
|
|
AudioDumpExt::AudioDumpClip::keyAudioDumpAAudio,
|
|
};
|
|
|
|
struct WAVEFORMATEX {
|
|
uint16_t wFormatTag;
|
|
uint16_t nChannels;
|
|
uint32_t nSamplesPerSec;
|
|
uint32_t nAvgBytesPerSec;
|
|
uint16_t nBlockAlign;
|
|
uint16_t wBitsPerSample;
|
|
uint16_t cbSize;
|
|
|
|
WAVEFORMATEX() {
|
|
wFormatTag = 0;
|
|
nChannels = 0;
|
|
nSamplesPerSec = 0;
|
|
nAvgBytesPerSec = 0;
|
|
nBlockAlign = 0;
|
|
wBitsPerSample = 0;
|
|
cbSize = 0;
|
|
}
|
|
};
|
|
|
|
struct WavFormatHeader {
|
|
char ckID[5]; // 4 : Chunk ID: "RIFF"
|
|
uint32_t cksize; // 4: Chunk size: File length - 8
|
|
char WAVEID[5]; // 4: WAVE ID: "WAVE"
|
|
// Format Chunk
|
|
char FormatckID[5]; // 4: "fmt "
|
|
uint32_t Formatcksize; // 4: Chunk size: 16 or 18 or 40 ( We will use 18, no extensiable format. )
|
|
char DataID[5]; // 4: "data"
|
|
uint32_t Datacksize; // 4: Chunk size: Data Size
|
|
WAVEFORMATEX WaveFormatEx;
|
|
|
|
WavFormatHeader()
|
|
: cksize(0),
|
|
Formatcksize(18),
|
|
Datacksize(0)
|
|
{
|
|
strncpy(ckID, "RIFF", sizeof(ckID));
|
|
strncpy(WAVEID, "WAVE", sizeof(WAVEID));
|
|
strncpy(FormatckID, "fmt ", sizeof(FormatckID));
|
|
strncpy(DataID, "data", sizeof(DataID));
|
|
}
|
|
};
|
|
|
|
struct AudioDumpClipBufferInfo {
|
|
audio_format_t format;
|
|
uint32_t sampleRate;
|
|
uint32_t channelCount;
|
|
int overSizeFlag;
|
|
int size;
|
|
|
|
AudioDumpClipBufferInfo()
|
|
{
|
|
format = AUDIO_FORMAT_INVALID;
|
|
sampleRate = 0;
|
|
channelCount = 0;
|
|
overSizeFlag = 0;
|
|
size = 0;
|
|
}
|
|
};
|
|
|
|
struct AudioDumpClipBuffer {
|
|
void *pBufBase;
|
|
int changeCount;
|
|
int allocBufSize;
|
|
AudioDumpClipBufferInfo bufferInfo;
|
|
|
|
AudioDumpClipBuffer()
|
|
{
|
|
pBufBase = NULL;
|
|
changeCount = 0;
|
|
allocBufSize = 0;
|
|
}
|
|
};
|
|
|
|
#define AUDIO_DUMP_CLIP_BUFFER_COUNT_MAX 32 // How many buffer will combine
|
|
#define AUDIO_DUMP_FILE_WAV_HEADER_SIZE 46 //46 is wav header
|
|
#define AUDIO_DUMP_CLIP_SIZE_DEFAULT (100 * SIZE_MB) //clipsize default:100MB
|
|
|
|
#define PROPERTY_DUMP_CLIP_LIMIT "persist.vendor.audiohal.dump.clip.size" // unit MB
|
|
#define PROPERTY_DUMP_STORAGE_LIMIT "persist.vendor.audiohal.dump.storage.size" // percent
|
|
#define PROPERTY_DUMP_CLEAR_LIMIT "persist.vendor.audiohal.dump.clear.size" // percent
|
|
|
|
pthread_t hAudioDumpClipThread = 0;
|
|
pthread_cond_t AudioDumpClipAudioDataNotifyEvent; //thread cond
|
|
pthread_mutex_t AudioDumpClipAudioDataNotifyMutex; //thread mutex
|
|
Mutex mAudioDumpClipKeyMutex;
|
|
Mutex mAudioDumpClipMutex;
|
|
Mutex mAudioDumpClipFileMutex;
|
|
int mAudioDumpClipSleepTime = 0;
|
|
uint32_t mAudioDumpFileIoDelayTime = 0;
|
|
|
|
KeyedVector<String8, Vector<AudioDumpClipBuffer *>* > mAudioDumpClipFileVector;
|
|
KeyedVector<String8, Vector<AudioDumpClipBuffer *>* > mAudioDumpClipQueueVector;
|
|
|
|
//fileinfo for dump clip
|
|
struct AudioDumpClipFileInfos
|
|
{
|
|
String8 timeStampNunc;
|
|
String8 timeStampDein;
|
|
int totalSize;
|
|
int clipCount;
|
|
|
|
AudioDumpClipFileInfos()
|
|
{
|
|
totalSize = 0;
|
|
clipCount = 0;
|
|
}
|
|
};
|
|
KeyedVector<String8, AudioDumpClipFileInfos * > mAudioDumpClipFileInfosVector;
|
|
|
|
//dumpManager for clear
|
|
pthread_t hDumpManagerThread = 0;
|
|
bool mAudioDumpManagerSleepMode = false;
|
|
int64_t mDumpFilesSize = 0;
|
|
static String8 KEY_GET_HAL_DUMP_SIZE = String8("MTK_GET_HAL_DUMP_PATH_SIZE");
|
|
static String8 KEY_GET_HAL_DUMP_NUM = String8("MTK_GET_HAL_DUMP_FILE_NUM");
|
|
static String8 KEY_GET_HAL_DUMP_LIST = String8("MTK_GET_HAL_DUMP_FILE_LIST");
|
|
static String8 KEY_DEL_HAL_DUMP_LIST = String8("MTK_SET_HAL_DUMP_FILE_LIST");
|
|
static String8 KEY_GET_HAL_DUMP_LIVE = String8("MTK_GET_HAL_DUMP_THRE_LIVE");
|
|
|
|
struct FileTimeInfo {
|
|
char path[MAX_FILE_NAME];
|
|
int64_t lastChangeTime;
|
|
int64_t size;
|
|
bool isHal;
|
|
};
|
|
|
|
FILE* fopen_rb(String8 filePath) {
|
|
FILE * fp = fopen(filePath.string(), "rb+");
|
|
if (mAudioDumpFileIoDelayTime > 0) {
|
|
usleep(mAudioDumpFileIoDelayTime * 1000);
|
|
}
|
|
|
|
if (fp == NULL) {
|
|
ALOGV("fopen_rb() file(%s) rb+ fail, open with ab+", filePath.string());
|
|
fp= fopen(filePath.string(), "ab+");
|
|
if (mAudioDumpFileIoDelayTime > 0) {
|
|
usleep(mAudioDumpFileIoDelayTime * 1000);
|
|
}
|
|
}
|
|
|
|
if (fp == NULL) {
|
|
// ALOGE("fopen_rb() file(%s) fail", filePath.string());
|
|
return NULL;
|
|
}
|
|
|
|
return fp;
|
|
}
|
|
|
|
size_t fwrite_s(const void * ptr, size_t size, size_t count, FILE * stream) {
|
|
size_t ret = fwrite(ptr, size, count, stream);
|
|
if (ret > 0 && mAudioDumpFileIoDelayTime > 0) {
|
|
usleep(mAudioDumpFileIoDelayTime * 1000);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void fseek_s(FILE * stream, long int offset, int origin) {
|
|
int ret = fseek(stream, offset, origin);
|
|
if (ret == 0 && mAudioDumpFileIoDelayTime > 0) {
|
|
usleep(mAudioDumpFileIoDelayTime * 1000);
|
|
}
|
|
}
|
|
|
|
size_t fread_s (void * ptr, size_t size, size_t count, FILE * stream) {
|
|
size_t ret = fread(ptr, size, count, stream);
|
|
if (ret > 0 && mAudioDumpFileIoDelayTime > 0) {
|
|
usleep(mAudioDumpFileIoDelayTime * 1000);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool checkIfNeedHeader(audio_format_t format) {
|
|
if (format == AUDIO_FORMAT_MP3) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void WriteNewWaveHeader(FILE *fp, AudioDumpClipBuffer audioBuffer) {
|
|
if (!checkIfNeedHeader(audioBuffer.bufferInfo.format)) {
|
|
return;
|
|
}
|
|
WavFormatHeader wavHeader;
|
|
void* tmpBuffer = malloc(46);
|
|
if(!tmpBuffer) {
|
|
ALOGE("%s(): malloc fail!", __FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
if (audioBuffer.bufferInfo.format == AUDIO_FORMAT_PCM_FLOAT) {
|
|
wavHeader.WaveFormatEx.wFormatTag = 3; // IEEE Float
|
|
wavHeader.WaveFormatEx.wBitsPerSample = 32;
|
|
} else {
|
|
wavHeader.WaveFormatEx.wFormatTag = 1; // PCM
|
|
|
|
if (audioBuffer.bufferInfo.format == AUDIO_FORMAT_PCM_8_BIT) {
|
|
wavHeader.WaveFormatEx.wBitsPerSample = 8;
|
|
} else if (audioBuffer.bufferInfo.format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
wavHeader.WaveFormatEx.wBitsPerSample = 16;
|
|
} else if (audioBuffer.bufferInfo.format == AUDIO_FORMAT_PCM_24_BIT_PACKED) {
|
|
wavHeader.WaveFormatEx.wBitsPerSample = 24;
|
|
} else if (audioBuffer.bufferInfo.format == AUDIO_FORMAT_PCM_8_24_BIT ||
|
|
audioBuffer.bufferInfo.format == AUDIO_FORMAT_PCM_32_BIT) {
|
|
wavHeader.WaveFormatEx.wBitsPerSample = 32;
|
|
}
|
|
}
|
|
wavHeader.cksize = audioBuffer.bufferInfo.size + 38; // 46 - 8
|
|
wavHeader.Datacksize = audioBuffer.bufferInfo.size;
|
|
wavHeader.WaveFormatEx.nChannels = audioBuffer.bufferInfo.channelCount;
|
|
wavHeader.WaveFormatEx.nSamplesPerSec = audioBuffer.bufferInfo.sampleRate;
|
|
wavHeader.WaveFormatEx.nAvgBytesPerSec = wavHeader.WaveFormatEx.nSamplesPerSec * wavHeader.WaveFormatEx.nChannels
|
|
* wavHeader.WaveFormatEx.wBitsPerSample / 8;
|
|
wavHeader.WaveFormatEx.nBlockAlign = wavHeader.WaveFormatEx.nChannels * wavHeader.WaveFormatEx.wBitsPerSample / 8;
|
|
wavHeader.WaveFormatEx.cbSize = 0;
|
|
|
|
int pos = 0;
|
|
memcpy((char*)tmpBuffer + pos, &wavHeader.ckID, 4); pos += 4;
|
|
memcpy((char*)tmpBuffer + pos, &wavHeader.cksize, 4); pos += 4;
|
|
memcpy((char*)tmpBuffer + pos, &wavHeader.WAVEID, 4); pos += 4;
|
|
memcpy((char*)tmpBuffer + pos, &wavHeader.FormatckID, 4); pos += 4;
|
|
memcpy((char*)tmpBuffer + pos, &wavHeader.Formatcksize, 4); pos += 4;
|
|
memcpy((char*)tmpBuffer + pos, &wavHeader.WaveFormatEx, 18); pos += 18;
|
|
memcpy((char*)tmpBuffer + pos, &wavHeader.DataID, 4); pos += 4;
|
|
memcpy((char*)tmpBuffer + pos, &wavHeader.Datacksize, 4); pos += 4;
|
|
|
|
fwrite_s(tmpBuffer, 46, 1, fp);
|
|
free(tmpBuffer);
|
|
tmpBuffer = NULL;
|
|
}
|
|
|
|
void UpdateWaveHeader(FILE *fp, AudioDumpClipBuffer audioBuffer) {
|
|
fseek_s(fp, 0, SEEK_END);
|
|
if (ftell(fp) == 0) {
|
|
WriteNewWaveHeader(fp, audioBuffer);
|
|
} else {
|
|
WavFormatHeader wavHeader;
|
|
void* tmpBuffer = malloc(46);
|
|
if(!tmpBuffer) {
|
|
ALOGE("%s(): malloc fail!", __FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
fseek_s(fp, 0, SEEK_SET);
|
|
if (fread_s((char*)tmpBuffer, 46, 1, fp) <= 0) {
|
|
free(tmpBuffer);
|
|
tmpBuffer = NULL;
|
|
return;
|
|
}
|
|
|
|
memcpy((char*)(&wavHeader.cksize), (char*)tmpBuffer + 4, 4);
|
|
wavHeader.cksize += audioBuffer.bufferInfo.size;
|
|
memcpy((char*)tmpBuffer + 4, &wavHeader.cksize, 4);
|
|
|
|
memcpy((char*)(&wavHeader.Datacksize), (char*)tmpBuffer + 42, 4);
|
|
wavHeader.Datacksize += audioBuffer.bufferInfo.size;
|
|
memcpy((char*)tmpBuffer + 42, &wavHeader.Datacksize, 4);
|
|
|
|
fseek_s(fp, 0, SEEK_SET);
|
|
fwrite_s((char*)tmpBuffer, 46, 1, fp);
|
|
|
|
free(tmpBuffer);
|
|
tmpBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
void UpdateWaveHeader_f(String8 filePath, AudioDumpClipBuffer audioBuffer) {
|
|
if (!checkIfNeedHeader(audioBuffer.bufferInfo.format)) {
|
|
return;
|
|
}
|
|
FILE *fp = fopen_rb(filePath);
|
|
if (fp != NULL) {
|
|
//ALOGD("UpdateWaveHeader_f START filePath(%s)", filePath.string());
|
|
UpdateWaveHeader(fp, audioBuffer);
|
|
if (fclose(fp) != 0) {
|
|
ALOGE("%s(): Couldn't close dump file", __FUNCTION__);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WriteAudioBuffer(FILE *fp, AudioDumpClipBuffer audioBuffer) {
|
|
fwrite_s(audioBuffer.pBufBase, audioBuffer.bufferInfo.size, 1, fp);
|
|
//ALOGD("free %p, %d", audioBuffer.pBufBase, audioBuffer.bufferInfo.size);
|
|
free(audioBuffer.pBufBase);
|
|
audioBuffer.pBufBase = NULL;
|
|
}
|
|
|
|
void getAudioDumpCurrentTimestamp(char *timep_str, uint32_t timep_str_size) {
|
|
time_t rawtime;
|
|
struct tm *time_info;
|
|
|
|
if (time(&rawtime) == ((time_t)-1)) {
|
|
ALOGE("%s(), get timep failed\n", __FUNCTION__);
|
|
} else {
|
|
time_info = localtime(&rawtime);
|
|
|
|
if (time_info != NULL) {
|
|
if (!strftime(timep_str, timep_str_size, "%Y%m%d-%H%M%S", time_info)) {
|
|
ALOGE("%s(), strftime failed, timep_str : %s\n", __FUNCTION__, timep_str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void updateInfosByDumpName(String8 name, AudioDumpClipBuffer* pBuf) {
|
|
//dump name + timestamp
|
|
ssize_t indexInNameVector = mAudioDumpClipFileInfosVector.indexOfKey(name);
|
|
if (indexInNameVector < 0) {
|
|
String8 dumpName;
|
|
char timep_str[32] = {0};
|
|
getAudioDumpCurrentTimestamp(timep_str, sizeof(timep_str));
|
|
dumpName = String8::format("%s%s_%s", FWK_DUMP_PATH, name.string(), timep_str);
|
|
AudioDumpClipFileInfos* dumpFileInfos = new AudioDumpClipFileInfos();
|
|
|
|
dumpFileInfos->timeStampNunc = dumpName;
|
|
dumpFileInfos->totalSize += pBuf->bufferInfo.size;
|
|
|
|
mAudioDumpClipFileInfosVector.add(name, dumpFileInfos);
|
|
|
|
ALOGD("new threadDump() %s", dumpFileInfos->timeStampNunc.string()); // For dump analysis
|
|
} else {
|
|
//update size
|
|
AudioDumpClipFileInfos* dumpFileInfos = mAudioDumpClipFileInfosVector.valueAt(indexInNameVector);
|
|
dumpFileInfos->totalSize += pBuf->bufferInfo.size;
|
|
|
|
//clip
|
|
int64_t clipLimit = AudioDumpExt::AudioDumpClip::getClipSize();
|
|
|
|
if (clipLimit == -1) {
|
|
char value[PROPERTY_VALUE_MAX] = {0};
|
|
property_get(PROPERTY_DUMP_CLIP_LIMIT, value, "-1");
|
|
int64_t clipLimitValue = atoi(value);
|
|
if (clipLimitValue >= 0) {
|
|
clipLimitValue *= SIZE_MB;
|
|
} else {
|
|
clipLimitValue = AUDIO_DUMP_CLIP_SIZE_DEFAULT;
|
|
}
|
|
AudioDumpExt::AudioDumpClip::setClipSize(clipLimitValue);
|
|
|
|
clipLimit = AudioDumpExt::AudioDumpClip::getClipSize();
|
|
}
|
|
|
|
if (clipLimit <= 0) {
|
|
return; //bypass clip
|
|
}
|
|
|
|
if (dumpFileInfos->totalSize >= (((dumpFileInfos->clipCount)+1)* clipLimit)) {
|
|
//donot clip MP3 offload dump
|
|
if (!checkIfNeedHeader(pBuf->bufferInfo.format)) {
|
|
return;
|
|
}
|
|
|
|
//update infos for clip
|
|
dumpFileInfos->clipCount += 1;
|
|
pBuf->bufferInfo.overSizeFlag = 1;
|
|
|
|
String8 dumpName;
|
|
char timep_str[32] = {0};
|
|
getAudioDumpCurrentTimestamp(timep_str, sizeof(timep_str));
|
|
dumpName = String8::format("%s%s_%s", FWK_DUMP_PATH, name.string(), timep_str);
|
|
dumpFileInfos->timeStampDein = dumpName;
|
|
|
|
ALOGD("new threadDump() %s", dumpFileInfos->timeStampDein.string()); // For dump analysis
|
|
|
|
//flush data size and clip count
|
|
//1.achieve dynamic clip size apply
|
|
//2.avoid totalSize variable upper bound
|
|
dumpFileInfos->totalSize = 0;
|
|
dumpFileInfos->clipCount -= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClearFileVector() {
|
|
//int FileVectorSize = mAudioDumpClipFileVector.size();
|
|
for (int i = 0; i < mAudioDumpClipFileVector.size(); i++) {
|
|
String8 fileNamePCM = mAudioDumpClipFileVector.keyAt(i);
|
|
Vector<AudioDumpClipBuffer *>*pvector = (mAudioDumpClipFileVector.valueAt(i));
|
|
int BufferVectorSize = (*pvector).size();
|
|
|
|
if (BufferVectorSize == 1) {
|
|
String8 filePathPCMWithTs = String8::format("%s%s", FWK_DUMP_PATH, fileNamePCM.string());
|
|
String8 filePathWav = filePathPCMWithTs;
|
|
ssize_t indexName = mAudioDumpClipFileInfosVector.indexOfKey(fileNamePCM);
|
|
if (indexName >= 0) {
|
|
filePathPCMWithTs = mAudioDumpClipFileInfosVector.valueAt(indexName)->timeStampNunc;
|
|
} else {
|
|
ALOGE("%s(), mAudioDumpClipFileInfosVector mismatch, cannot get timestamp", __FUNCTION__);
|
|
}
|
|
|
|
AudioDumpClipBuffer *pLastBufferInfo = (*pvector)[0];
|
|
|
|
if (pLastBufferInfo->changeCount == 0) {
|
|
filePathWav = String8::format("%s.wav", filePathPCMWithTs.string());
|
|
} else {
|
|
filePathWav = String8::format("%s.%d.wav", filePathPCMWithTs.string(), pLastBufferInfo->changeCount);
|
|
}
|
|
|
|
UpdateWaveHeader_f(filePathWav, *pLastBufferInfo);
|
|
|
|
bool bDeleted = false;
|
|
if ((*pvector).size() == 1) {
|
|
(*pvector).removeAt(0);
|
|
delete pLastBufferInfo;
|
|
pLastBufferInfo = NULL;
|
|
|
|
mAudioDumpClipFileInfosVector.removeItem(fileNamePCM);
|
|
mAudioDumpClipFileVector.removeItem(fileNamePCM);
|
|
delete pvector;
|
|
pvector = NULL;
|
|
|
|
bDeleted = true;
|
|
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PushBufferFromQueueToVectorClip(String8 filePath, AudioDumpClipBuffer *newDumpBuffer) {
|
|
AudioDumpClipBuffer *newInBuffer = newDumpBuffer;
|
|
Vector<AudioDumpClipBuffer *> *pDumpBufferVector = NULL;
|
|
|
|
ssize_t index = mAudioDumpClipFileVector.indexOfKey(filePath);
|
|
if (index < 0) {
|
|
pDumpBufferVector = new Vector<AudioDumpClipBuffer *>;
|
|
mAudioDumpClipFileVector.add(filePath, pDumpBufferVector);
|
|
} else {
|
|
pDumpBufferVector = mAudioDumpClipFileVector.valueAt(index);
|
|
}
|
|
|
|
if (pDumpBufferVector->size() == 0) { // No previous buffer info, create a new one.
|
|
AudioDumpClipBuffer *lastInfoBuffer = new AudioDumpClipBuffer();
|
|
memcpy(lastInfoBuffer, newInBuffer, sizeof(AudioDumpClipBuffer));
|
|
lastInfoBuffer->pBufBase = NULL; // We don't care this parameter in last info.
|
|
lastInfoBuffer->bufferInfo.size = 0; // The size that had writtn to file. Set to 0 here since we havn't write anything.
|
|
pDumpBufferVector->add(lastInfoBuffer);
|
|
}
|
|
pDumpBufferVector->add(newInBuffer);
|
|
}
|
|
|
|
void ProcessBufferQueueClip(bool bFlush) {
|
|
if (AUDIO_DUMP_CLIP_BUFFER_COUNT_MAX == 1) {
|
|
bFlush = true;
|
|
}
|
|
Vector<AudioDumpClipBuffer *> *pQueueBufferVector = NULL;
|
|
Vector<String8> keyVector;
|
|
mAudioDumpClipMutex.lock();
|
|
int size = mAudioDumpClipQueueVector.size();
|
|
for (int i = 0; i < size; i++) {
|
|
String8 key = mAudioDumpClipQueueVector.keyAt(i);
|
|
keyVector.add(key);
|
|
}
|
|
mAudioDumpClipMutex.unlock();
|
|
|
|
for (size_t i = 0; i < keyVector.size(); i++) {
|
|
int allocBufSize = 0;
|
|
int currBufSize = 0;
|
|
int mergeBufSize = 0;
|
|
|
|
mAudioDumpClipMutex.lock();
|
|
pQueueBufferVector = mAudioDumpClipQueueVector.valueFor(keyVector[i]);
|
|
int queueSize = (*pQueueBufferVector).size();
|
|
mAudioDumpClipMutex.unlock();
|
|
|
|
while (queueSize >= 2) {
|
|
mAudioDumpClipMutex.lock();
|
|
AudioDumpClipBuffer *newQueueBuffer = (*pQueueBufferVector)[0];
|
|
AudioDumpClipBuffer *newQueueBuffer2 = (*pQueueBufferVector)[1];
|
|
mAudioDumpClipMutex.unlock();
|
|
|
|
if (memcmp(&(newQueueBuffer->bufferInfo),
|
|
&(newQueueBuffer2->bufferInfo),
|
|
sizeof(AudioDumpClipBufferInfo) - sizeof(int)) != 0) { // Compare format/samplerate/channel/overSizeFlag not including size
|
|
|
|
PushBufferFromQueueToVectorClip(keyVector[i], newQueueBuffer);
|
|
|
|
mAudioDumpClipMutex.lock();
|
|
pQueueBufferVector->removeAt(0);
|
|
mAudioDumpClipMutex.unlock();
|
|
} else {
|
|
currBufSize = newQueueBuffer->bufferInfo.size;
|
|
mergeBufSize = newQueueBuffer2->bufferInfo.size;
|
|
|
|
// allocate a big buffer to merge data
|
|
if (newQueueBuffer->allocBufSize == 0) {
|
|
void *pBufTemp = newQueueBuffer->pBufBase;
|
|
|
|
allocBufSize = currBufSize * AUDIO_DUMP_CLIP_BUFFER_COUNT_MAX;
|
|
newQueueBuffer->allocBufSize = allocBufSize;
|
|
newQueueBuffer->pBufBase = (char*)malloc(allocBufSize);
|
|
|
|
|
|
if (newQueueBuffer->pBufBase != NULL) {
|
|
memcpy((char*)newQueueBuffer->pBufBase, pBufTemp, currBufSize);
|
|
} else {
|
|
newQueueBuffer->allocBufSize = 0;
|
|
//error handle?
|
|
ALOGE("%s(): Error! Not enough memory for audio dump buffer size=%d", __FUNCTION__, allocBufSize);
|
|
}
|
|
free(pBufTemp);
|
|
pBufTemp = NULL;
|
|
}
|
|
|
|
if ((newQueueBuffer->pBufBase != NULL) && (newQueueBuffer->allocBufSize >= currBufSize + mergeBufSize)) {
|
|
memcpy((char*)(newQueueBuffer->pBufBase) + currBufSize,
|
|
(char*)(newQueueBuffer2->pBufBase), mergeBufSize);
|
|
newQueueBuffer->bufferInfo.size += mergeBufSize;
|
|
|
|
mAudioDumpClipMutex.lock();
|
|
pQueueBufferVector->removeAt(1);
|
|
mAudioDumpClipMutex.unlock();
|
|
|
|
free(newQueueBuffer2->pBufBase);
|
|
newQueueBuffer2->pBufBase = NULL;
|
|
|
|
delete newQueueBuffer2;
|
|
newQueueBuffer2 = NULL;
|
|
} else {
|
|
PushBufferFromQueueToVectorClip(keyVector[i], newQueueBuffer);
|
|
|
|
mAudioDumpClipMutex.lock();
|
|
pQueueBufferVector->removeAt(0);
|
|
mAudioDumpClipMutex.unlock();
|
|
}
|
|
}
|
|
queueSize--;
|
|
}
|
|
|
|
if (bFlush && queueSize == 1) {
|
|
//ALOGD("Flush data");
|
|
mAudioDumpClipMutex.lock();
|
|
AudioDumpClipBuffer *newQueueBuffer = (*pQueueBufferVector)[0];
|
|
mAudioDumpClipMutex.unlock();
|
|
|
|
PushBufferFromQueueToVectorClip(keyVector[i], newQueueBuffer);
|
|
|
|
mAudioDumpClipMutex.lock();
|
|
pQueueBufferVector->removeItemsAt(0);
|
|
mAudioDumpClipMutex.unlock();
|
|
|
|
queueSize--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void *AudioDumpClipThread(void *arg __attribute__((unused))) {
|
|
|
|
pthread_detach(pthread_self());
|
|
bool bHasdata = false;
|
|
bool bFlush = false;
|
|
int iNoDataCount = 0;
|
|
int iLoopCount = 0;
|
|
ALOGV("AudioDumpClipThread(%ld) start", hAudioDumpClipThread);
|
|
|
|
id_t tid = gettid();
|
|
int priority = -10;
|
|
int ret;
|
|
|
|
ret = setpriority(PRIO_PROCESS, tid, priority);
|
|
if (ret != 0) {
|
|
ALOGE("AudioDumpClipThread setpriority fail. tid(%d), priority(%d) ",
|
|
tid, priority);
|
|
}
|
|
|
|
while (1) {
|
|
ProcessBufferQueueClip(bFlush);
|
|
bFlush = false;
|
|
bHasdata = false;
|
|
size_t FileVectorSize = mAudioDumpClipFileVector.size();
|
|
|
|
for (size_t i = 0; i < FileVectorSize; i++) {
|
|
Vector<AudioDumpClipBuffer *>* pvector = (mAudioDumpClipFileVector.valueAt(i));
|
|
int BufferVectorSize = (*pvector).size();
|
|
|
|
if (BufferVectorSize > 1) {
|
|
bHasdata = true;
|
|
FILE * fpWav = NULL;
|
|
String8 fileNamePCM = mAudioDumpClipFileVector.keyAt(i);
|
|
String8 filePathPCMWithTs = String8::format("%s%s", FWK_DUMP_PATH, fileNamePCM.string()); //not timestamp here, just init for checkPath
|
|
String8 filePathWav = String8::format("%s.wav", filePathPCMWithTs.string()); //just init
|
|
|
|
AudioDumpClipBuffer *pLastBufferInfo = (*pvector)[0];
|
|
if (pLastBufferInfo == NULL) {
|
|
ALOGE("Array index error!!![%s]", fileNamePCM.string());
|
|
continue;
|
|
}
|
|
|
|
int ret = AudioDump::checkPath(filePathWav.string());
|
|
if (ret < 0) {
|
|
ALOGE("dump %s fail!!!", filePathWav.string());
|
|
continue;
|
|
}
|
|
|
|
while (BufferVectorSize > 1) {
|
|
AudioDumpClipBuffer *pAudioBuffer = (*pvector)[1];
|
|
if (pAudioBuffer == NULL || pLastBufferInfo == NULL) {
|
|
ALOGE("AudioDumpClipThread null buffer error!!!!");
|
|
break;
|
|
}
|
|
|
|
//query path with timestamp
|
|
ssize_t indexName = mAudioDumpClipFileInfosVector.indexOfKey(fileNamePCM);
|
|
if (indexName >= 0) {
|
|
filePathPCMWithTs = mAudioDumpClipFileInfosVector.valueAt(indexName)->timeStampNunc;
|
|
} else {
|
|
ALOGE("%s(), mAudioDumpClipFileInfosVector mismatch, cannot get timestamp", __FUNCTION__);
|
|
}
|
|
|
|
//add changeCount
|
|
if (pLastBufferInfo->changeCount == 0) {
|
|
filePathWav = String8::format("%s.wav", filePathPCMWithTs.string());
|
|
} else {
|
|
filePathWav = String8::format("%s.%d.wav", filePathPCMWithTs.string(), pLastBufferInfo->changeCount);
|
|
}
|
|
|
|
//handle oversize clip
|
|
if (pAudioBuffer->bufferInfo.overSizeFlag == 1) {
|
|
if (indexName >= 0) {
|
|
//++handle old file
|
|
if (fpWav != NULL) {
|
|
if (fclose(fpWav) != 0) {
|
|
ALOGE("%s(): Couldn't close dump file", __FUNCTION__);
|
|
}
|
|
fpWav = NULL;
|
|
}
|
|
UpdateWaveHeader_f(filePathWav, *pLastBufferInfo);
|
|
//--handle old file
|
|
|
|
//++handle new file
|
|
memcpy(pLastBufferInfo, pAudioBuffer, sizeof(AudioDumpClipBuffer));
|
|
pLastBufferInfo->bufferInfo.size = 0;
|
|
pLastBufferInfo->bufferInfo.overSizeFlag = 0;
|
|
pLastBufferInfo->changeCount = 0; //dump file clip by timestamp, flush changeCount
|
|
|
|
mAudioDumpClipFileInfosVector.valueAt(indexName)->timeStampNunc =
|
|
mAudioDumpClipFileInfosVector.valueAt(indexName)->timeStampDein;
|
|
mAudioDumpClipFileInfosVector.valueAt(indexName)->timeStampDein.clear();
|
|
filePathPCMWithTs = mAudioDumpClipFileInfosVector.valueAt(indexName)->timeStampNunc;
|
|
|
|
filePathWav = String8::format("%s.wav", filePathPCMWithTs.string());
|
|
//--handle new file
|
|
}
|
|
}
|
|
|
|
//handle audio parameters change, this will be bypassed if oversize clip comes above
|
|
if (memcmp(&(pAudioBuffer->bufferInfo), &(pLastBufferInfo->bufferInfo),
|
|
sizeof(AudioDumpClipBufferInfo) - sizeof(int) - sizeof(int)) != 0) {
|
|
if (fpWav != NULL) {
|
|
if (fclose(fpWav) != 0) {
|
|
ALOGE("%s(): Couldn't close dump file", __FUNCTION__);
|
|
}
|
|
fpWav = NULL;
|
|
}
|
|
UpdateWaveHeader_f(filePathWav, *pLastBufferInfo);
|
|
int changeCount = pLastBufferInfo->changeCount + 1;
|
|
memcpy(pLastBufferInfo, pAudioBuffer, sizeof(AudioDumpClipBuffer));
|
|
pLastBufferInfo->changeCount = changeCount;
|
|
pLastBufferInfo->bufferInfo.size = 0;
|
|
pLastBufferInfo->bufferInfo.overSizeFlag = 0;
|
|
filePathWav = String8::format("%s.%d.wav", filePathPCMWithTs.string(), pLastBufferInfo->changeCount);
|
|
}
|
|
|
|
if (fpWav == NULL) {
|
|
fpWav = fopen(filePathWav.string(), "ab+");
|
|
if (fpWav != NULL) {
|
|
fseek_s(fpWav, 0, SEEK_END);
|
|
if (ftell(fpWav) == 0) {
|
|
// Write Header
|
|
WriteNewWaveHeader(fpWav, *pLastBufferInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
// write audio buffer
|
|
if (fpWav != NULL) {
|
|
WriteAudioBuffer(fpWav, *pAudioBuffer);
|
|
pLastBufferInfo->bufferInfo.size += pAudioBuffer->bufferInfo.size;
|
|
(*pvector).removeAt(1);
|
|
delete pAudioBuffer;
|
|
pAudioBuffer = NULL;
|
|
BufferVectorSize--;
|
|
}
|
|
}
|
|
|
|
if (fpWav != NULL) {
|
|
if (fclose(fpWav) != 0) {
|
|
ALOGE("%s(): Couldn't close dump file.", __FUNCTION__);
|
|
}
|
|
fpWav = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bHasdata) {
|
|
iNoDataCount++;
|
|
if (iNoDataCount >= 2 * AUDIO_DUMP_CLIP_BUFFER_COUNT_MAX) { // wait 640ms
|
|
bFlush = true;
|
|
iLoopCount = 0;
|
|
}
|
|
if (iNoDataCount >= 2 * AUDIO_DUMP_CLIP_BUFFER_COUNT_MAX + 1) { // wait 641ms
|
|
ClearFileVector();
|
|
}
|
|
if (iNoDataCount >= 300) { // 3s
|
|
mAudioDumpClipMutex.lock();
|
|
mAudioDumpClipSleepTime = -1;
|
|
mAudioDumpClipMutex.unlock();
|
|
pthread_mutex_lock(&AudioDumpClipAudioDataNotifyMutex);
|
|
pthread_cond_wait(&AudioDumpClipAudioDataNotifyEvent, &AudioDumpClipAudioDataNotifyMutex);
|
|
pthread_mutex_unlock(&AudioDumpClipAudioDataNotifyMutex);
|
|
} else {
|
|
mAudioDumpClipMutex.lock();
|
|
mAudioDumpClipSleepTime = 10;
|
|
mAudioDumpClipMutex.unlock();
|
|
usleep(mAudioDumpClipSleepTime * 1000);
|
|
}
|
|
} else {
|
|
iNoDataCount = 0;
|
|
mAudioDumpClipMutex.lock();
|
|
mAudioDumpClipSleepTime = 2;
|
|
mAudioDumpClipMutex.unlock();
|
|
usleep(mAudioDumpClipSleepTime * 1000);
|
|
}
|
|
|
|
if (++iLoopCount >= 4 * AUDIO_DUMP_CLIP_BUFFER_COUNT_MAX) {
|
|
bFlush = true;
|
|
iLoopCount = 0;
|
|
}
|
|
}
|
|
|
|
ALOGD("AudioDumpClipThread exit hAudioDumpClipThread=%ld", hAudioDumpClipThread);
|
|
hAudioDumpClipThread = 0;
|
|
pthread_exit(NULL);
|
|
return 0;
|
|
}
|
|
|
|
//TODO:need cover?
|
|
void AudioDumpExt::AudioDumpClip::dump() {
|
|
}
|
|
//TODO:need
|
|
|
|
void AudioDumpExt::AudioDumpClip::threadDump(const char * path, void * buffer, int count, audio_format_t format,
|
|
uint32_t sampleRate, uint32_t channelCount) {
|
|
{
|
|
Mutex::Autolock _l(mAudioDumpClipMutex);
|
|
if (hAudioDumpClipThread == 0) {
|
|
int ret = pthread_create(&hAudioDumpClipThread, NULL, AudioDumpClipThread, NULL);
|
|
if (ret != 0) {
|
|
ALOGE("hAudioDumpClipThread create fail!!!");
|
|
} else {
|
|
ALOGD("hAudioDumpClipThread=%ld created", hAudioDumpClipThread);
|
|
}
|
|
|
|
ret = pthread_cond_init(&AudioDumpClipAudioDataNotifyEvent, NULL);
|
|
if (ret != 0) {
|
|
ALOGE("AudioDumpClipAudioDataNotifyEvent create fail!!!");
|
|
}
|
|
|
|
ret = pthread_mutex_init(&AudioDumpClipAudioDataNotifyMutex, NULL);
|
|
if (ret != 0) {
|
|
ALOGE("AudioDumpClipAudioDataNotifyMutex create fail!!!");
|
|
}
|
|
|
|
startDumpManagerThreadInt();
|
|
}
|
|
}
|
|
|
|
pushBufferInfo(path, buffer, count, format, sampleRate, channelCount);
|
|
}
|
|
|
|
|
|
bool AudioDumpExt::AudioDumpClip::getProperty(AudioDumpExt::PROP_AUDIO_DUMP key) {
|
|
Mutex::Autolock _l(mAudioDumpClipKeyMutex);
|
|
|
|
ssize_t index = mAudioDumpClipKeyEnableVector.indexOfKey(key);
|
|
if (index < 0) {
|
|
int bflag = (int)property_get_bool(AudioDumpExt::AudioDumpClip::audioDumpPropertyStr[key], false);
|
|
mAudioDumpClipKeyEnableVector.add(key, bflag);
|
|
return bflag;
|
|
}
|
|
return mAudioDumpClipKeyEnableVector[index];
|
|
}
|
|
|
|
int AudioDumpExt::AudioDumpClip::checkPath(const char * path) {
|
|
char tmp[PATH_MAX] = {0};
|
|
unsigned i = 0;
|
|
|
|
while (*path) {
|
|
tmp[i] = *path;
|
|
|
|
if (*path == '/' && i) {
|
|
tmp[i] = '\0';
|
|
Mutex::Autolock _l(mAudioDumpClipFileMutex);
|
|
if (access(tmp, F_OK) != 0) {
|
|
if (mkdir(tmp, 0770) == -1) {
|
|
// ALOGE("mkdir error! %s",(char*)strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
tmp[i] = '/';
|
|
}
|
|
i++;
|
|
path++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned long AudioDumpExt::AudioDumpClip::getDumpSize(const char * path) {
|
|
unsigned long dump_size_in_queue = 0;
|
|
|
|
Vector<AudioDumpClipBuffer *> *pQueueBufferVector = NULL;
|
|
mAudioDumpClipMutex.lock();
|
|
ssize_t index = mAudioDumpClipQueueVector.indexOfKey(String8(path));
|
|
if (index >= 0) {
|
|
pQueueBufferVector = mAudioDumpClipQueueVector.valueAt(index);
|
|
int queueSize = (*pQueueBufferVector).size();
|
|
for (int i = 0; i < queueSize; i++) {
|
|
AudioDumpClipBuffer *newQueueBuffer = (*pQueueBufferVector)[i];
|
|
dump_size_in_queue += newQueueBuffer->bufferInfo.size;
|
|
}
|
|
}
|
|
mAudioDumpClipMutex.unlock();
|
|
|
|
struct stat dump_stat;
|
|
String8 fileNamePCM = String8(path);
|
|
String8 filePathPCMWithTs = String8::format("%s%s", FWK_DUMP_PATH, fileNamePCM.string());
|
|
ssize_t indexName = mAudioDumpClipFileInfosVector.indexOfKey(fileNamePCM);
|
|
if (indexName >= 0) {
|
|
filePathPCMWithTs = mAudioDumpClipFileInfosVector.valueAt(indexName)->timeStampNunc;
|
|
} else {
|
|
ALOGE("%s(), mAudioDumpClipFileInfosVector mismatch, cannot get timestamp", __FUNCTION__);
|
|
}
|
|
String8 wavpath = String8::format("%s.wav", filePathPCMWithTs.string());
|
|
if (stat(wavpath, &dump_stat) == 0) {
|
|
return dump_stat.st_size > AUDIO_DUMP_FILE_WAV_HEADER_SIZE ?
|
|
(dump_size_in_queue + dump_stat.st_size - AUDIO_DUMP_FILE_WAV_HEADER_SIZE) : dump_size_in_queue;
|
|
}
|
|
return dump_size_in_queue;
|
|
}
|
|
|
|
String8 AudioDumpExt::AudioDumpClip::getDumpName(const char * path) {
|
|
String8 fileNamePCM = String8(path);
|
|
String8 filePathPCMWithTs = String8::format("%s%s", FWK_DUMP_PATH, fileNamePCM.string());
|
|
ssize_t indexName = mAudioDumpClipFileInfosVector.indexOfKey(fileNamePCM);
|
|
if (indexName >= 0) {
|
|
filePathPCMWithTs = mAudioDumpClipFileInfosVector.valueAt(indexName)->timeStampNunc;
|
|
} else {
|
|
ALOGV("%s(), mAudioDumpClipFileInfosVector mismatch, cannot get timestamp", __FUNCTION__);
|
|
}
|
|
return filePathPCMWithTs;
|
|
}
|
|
|
|
void AudioDumpExt::AudioDumpClip::updateKeys(int key) {
|
|
Mutex::Autolock _l(mAudioDumpClipKeyMutex);
|
|
|
|
if (key < 0 || key >= AudioDumpExt::PROP_AUDIO_DUMP_MAXNUM) {
|
|
mAudioDumpClipKeyEnableVector.clear();
|
|
} else {
|
|
mAudioDumpClipKeyEnableVector.removeItem((AudioDumpExt::PROP_AUDIO_DUMP)key);
|
|
}
|
|
}
|
|
|
|
|
|
void AudioDumpExt::AudioDumpClip::pushBufferInfo(const char * path, void * buffer, int count,
|
|
audio_format_t format, uint32_t sampleRate, uint32_t channelCount) {
|
|
if (buffer != NULL && count > 0) {
|
|
AudioDumpClipBuffer *newQueueBuffer = new AudioDumpClipBuffer();
|
|
|
|
newQueueBuffer->pBufBase = (char*)malloc(count);
|
|
if (newQueueBuffer->pBufBase != NULL) {
|
|
memcpy(newQueueBuffer->pBufBase, buffer, count);
|
|
} else {
|
|
//error handle?
|
|
ALOGE("%s(): Error! Not enough memory for audio dump buffer size=%d", __FUNCTION__, count);
|
|
}
|
|
newQueueBuffer->bufferInfo.format = format;
|
|
newQueueBuffer->bufferInfo.sampleRate = sampleRate;
|
|
newQueueBuffer->bufferInfo.channelCount = channelCount;
|
|
newQueueBuffer->bufferInfo.size = count;
|
|
|
|
Vector<AudioDumpClipBuffer *> *pQueueBufferVector = NULL;
|
|
|
|
mAudioDumpClipMutex.lock();
|
|
ssize_t index = mAudioDumpClipQueueVector.indexOfKey(String8(path));
|
|
if (index < 0) { // new add
|
|
pQueueBufferVector = new Vector<AudioDumpClipBuffer *>;
|
|
mAudioDumpClipQueueVector.add(String8(path), pQueueBufferVector);
|
|
} else {
|
|
pQueueBufferVector = mAudioDumpClipQueueVector.valueAt(index);
|
|
}
|
|
if (pQueueBufferVector) {
|
|
pQueueBufferVector->add(newQueueBuffer);
|
|
}
|
|
bool needWakeup = (mAudioDumpClipSleepTime == -1);
|
|
updateInfosByDumpName(String8(path), newQueueBuffer);
|
|
mAudioDumpClipMutex.unlock();
|
|
if (needWakeup) { //need to send event
|
|
pthread_mutex_lock(&AudioDumpClipAudioDataNotifyMutex);
|
|
pthread_cond_signal(&AudioDumpClipAudioDataNotifyEvent);
|
|
pthread_mutex_unlock(&AudioDumpClipAudioDataNotifyMutex);
|
|
}
|
|
}
|
|
}
|
|
|
|
int cmp(const FileTimeInfo *a, const FileTimeInfo *b) {
|
|
int ret = 0;
|
|
int64_t temp = a->lastChangeTime - b->lastChangeTime;
|
|
|
|
if (temp > 0)
|
|
ret = 1;
|
|
else if (temp < 0)
|
|
ret = -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int totalSize(const char *fpath __unused, const struct stat *sb, int typeflag __unused) {
|
|
mDumpFilesSize += sb->st_size;
|
|
return 0;
|
|
}
|
|
|
|
void getHalFileInfoVec(String8 input, Vector<FileTimeInfo> &output) {
|
|
char* pFileList = strdup(input.string());
|
|
char* restOfStr = NULL;
|
|
char* fileName = strtok_r(pFileList, ",", &restOfStr);
|
|
FileTimeInfo fileTimeInfo;
|
|
while (fileName != NULL) {
|
|
if (sprintf(fileTimeInfo.path, "%s", fileName) < 0) {
|
|
ALOGE("%s(), sprintf fail: line: %d", __func__, __LINE__);
|
|
}
|
|
|
|
char* fileTimeValue = strtok_r(NULL, ",", &restOfStr);
|
|
int64_t fileTime = atoll(fileTimeValue);
|
|
fileTimeInfo.lastChangeTime = fileTime;
|
|
|
|
char* fileSizeValue = strtok_r(NULL, ",", &restOfStr);
|
|
int64_t fileSize = atoll(fileSizeValue);
|
|
fileTimeInfo.size = fileSize;
|
|
|
|
strtok_r(NULL, ":", &restOfStr);
|
|
fileTimeInfo.isHal = true;
|
|
|
|
output.add(fileTimeInfo);
|
|
|
|
fileName = strtok_r(NULL, ",", &restOfStr);
|
|
}
|
|
free(pFileList);
|
|
pFileList = NULL;
|
|
free(restOfStr);
|
|
restOfStr = NULL;
|
|
free(fileName);
|
|
fileName = NULL;
|
|
}
|
|
|
|
bool isFwkOrHalAudioDumpThreadAlive() {
|
|
bool isHalAudioDumpThreadAlive;
|
|
String8 ret = AudioSystem::getParameters(KEY_GET_HAL_DUMP_LIVE);
|
|
AudioParameter param = AudioParameter(ret);
|
|
String8 value;
|
|
if (param.get(KEY_GET_HAL_DUMP_LIVE, value) == NO_ERROR) {
|
|
if (value == String8("true")) {
|
|
isHalAudioDumpThreadAlive = true;
|
|
} else {
|
|
isHalAudioDumpThreadAlive = false;
|
|
}
|
|
} else {
|
|
ALOGE("error, getHalDumpLive");
|
|
isHalAudioDumpThreadAlive = false;
|
|
}
|
|
|
|
bool isFwkAudioDumpThreadAlive =
|
|
(mAudioDumpClipSleepTime != -1) && (mAudioDumpClipSleepTime != 0);
|
|
ALOGV("DumpManagerThread: %s() fwkDumpThreadAlive: %d, halDumpThreadAlive: %d",
|
|
__func__, isFwkAudioDumpThreadAlive, isHalAudioDumpThreadAlive);
|
|
return isHalAudioDumpThreadAlive || isFwkAudioDumpThreadAlive;
|
|
}
|
|
|
|
void *DumpManagerThread(void *arg __unused) {
|
|
struct statfs data_st;
|
|
int64_t totalStorage = 4 * 1024; //default min value
|
|
int64_t storageLimit = 0;
|
|
int loopCount = 0;
|
|
int LOOP_MAX;
|
|
char value[PROPERTY_VALUE_MAX] = {0};
|
|
timespec timePre;
|
|
timespec timePost;
|
|
unsigned timeSpent;
|
|
String8 halDumpPathSizeRet = String8("");
|
|
String8 halDumpPathSizeValue = String8("");
|
|
int64_t halDumpPathSize = 0;
|
|
String8 halDumpFileNumRet = String8("");
|
|
int halDumpFileNumValue = 0;
|
|
String8 halDumpFileListRet = String8("");
|
|
String8 halDumpFileListValue = String8("");
|
|
String8 halDumpFileDelListRet = String8("");
|
|
String8 halDumpFileDelListValue = String8("");
|
|
AudioParameter param;
|
|
|
|
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DEFAULT);
|
|
|
|
if (statfs("/data", &data_st) == 0) {
|
|
totalStorage = (int64_t)data_st.f_bavail * data_st.f_bsize;
|
|
mDumpFilesSize = 0;
|
|
// add the storage has used by audio dump
|
|
if (ftw(FWK_DUMP_PATH, &totalSize, 1) == 0) {
|
|
totalStorage += mDumpFilesSize;
|
|
}
|
|
// add the storage has used by audio dump hal
|
|
halDumpPathSizeRet = AudioSystem::getParameters(KEY_GET_HAL_DUMP_SIZE);
|
|
param = AudioParameter(halDumpPathSizeRet);
|
|
if (param.get(KEY_GET_HAL_DUMP_SIZE, halDumpPathSizeValue) == NO_ERROR && halDumpPathSizeValue.string() != NULL) {
|
|
halDumpPathSize = atoll(halDumpPathSizeValue.string());
|
|
} else {
|
|
ALOGE("DumpManagerThread: error, getHalDumpSize");
|
|
halDumpPathSize = 0;
|
|
}
|
|
ALOGD("DumpManagerThread: totalDumpSize: %lldMB, fwkDumpPathSize: %lldMB, halDumpPathSize: %lldMB",
|
|
(long long)(mDumpFilesSize + halDumpPathSize) / SIZE_MB,
|
|
(long long)mDumpFilesSize / SIZE_MB,
|
|
(long long)halDumpPathSize / SIZE_MB);
|
|
totalStorage += halDumpPathSize;
|
|
ALOGD("DumpManagerThread: totalStorage:%lldMB", (long long)totalStorage / SIZE_MB);
|
|
}
|
|
|
|
while (1) {
|
|
if (mAudioDumpManagerSleepMode) {
|
|
ALOGD("DumpManagerThread: [SleepMode]: bypass all flow");
|
|
goto wait;
|
|
}
|
|
memset(&timePre, 0, sizeof(struct timespec));
|
|
memset(&timePost, 0, sizeof(struct timespec));
|
|
clock_gettime(CLOCK_MONOTONIC, &timePre);
|
|
|
|
// get clean storage size
|
|
property_get(PROPERTY_DUMP_STORAGE_LIMIT, value, "-1");
|
|
storageLimit = atoi(value);
|
|
if (storageLimit >= 0) {
|
|
storageLimit = totalStorage * storageLimit / 100;
|
|
} else {
|
|
storageLimit = totalStorage / 2; // default size 50% totalStorage
|
|
}
|
|
|
|
mDumpFilesSize = 0;
|
|
LOOP_MAX = (storageLimit / (500 * SIZE_MB) > 20) ? 20 : (storageLimit / (500 * SIZE_MB));
|
|
ALOGD("DumpManagerThread: loopCount = %d, LOOP_MAX = %d", loopCount, LOOP_MAX);
|
|
if (loopCount >= LOOP_MAX && storageLimit > 0) { // check need clean storage or not
|
|
loopCount = 0;
|
|
if (ftw(FWK_DUMP_PATH, &totalSize, 1)) {
|
|
ALOGE("DumpManagerThread: get size error");
|
|
}
|
|
// update totalStorage since other module may comsume the data storage
|
|
if (statfs("/data", &data_st) == 0) {
|
|
totalStorage = (int64_t)data_st.f_bavail * data_st.f_bsize;
|
|
totalStorage += mDumpFilesSize;
|
|
}
|
|
// update the storage has used by audio dump hal
|
|
halDumpPathSizeRet = AudioSystem::getParameters(KEY_GET_HAL_DUMP_SIZE);
|
|
param = AudioParameter(halDumpPathSizeRet);
|
|
if (param.get(KEY_GET_HAL_DUMP_SIZE, halDumpPathSizeValue) == NO_ERROR && halDumpPathSizeValue.string() != NULL) {
|
|
halDumpPathSize = atoll(halDumpPathSizeValue.string());
|
|
} else {
|
|
ALOGE("DumpManagerThread: error, getHalDumpSize");
|
|
halDumpPathSize = 0;
|
|
}
|
|
ALOGD("DumpManagerThread: totalDumpSize: %lldMB, fwkDumpPathSize: %lldMB, halDumpPathSize: %lldMB",
|
|
(long long)(mDumpFilesSize + halDumpPathSize) / SIZE_MB,
|
|
(long long)mDumpFilesSize / SIZE_MB,
|
|
(long long)halDumpPathSize / SIZE_MB);
|
|
totalStorage += halDumpPathSize;
|
|
ALOGD("DumpManagerThread: totalStorage:%lldMB", (long long)totalStorage / SIZE_MB);
|
|
|
|
//update storageLimit
|
|
property_get(PROPERTY_DUMP_STORAGE_LIMIT, value, "-1");
|
|
storageLimit = atoi(value);
|
|
if (storageLimit >= 0) {
|
|
storageLimit = totalStorage * storageLimit / 100;
|
|
} else {
|
|
storageLimit = totalStorage / 2; // default size 50% totalStorage
|
|
}
|
|
|
|
if (halDumpPathSize + mDumpFilesSize >= storageLimit) {
|
|
DIR *dir;
|
|
struct dirent *dirp;
|
|
Vector<FileTimeInfo> fileInfoVec;
|
|
Vector<FileTimeInfo> halfileInfoVec;
|
|
Vector<FileTimeInfo> totalfileInfoVec;
|
|
Vector<String8> deleteListFwkVec;
|
|
String8 deleteListHalVec;
|
|
struct stat dump_stat;
|
|
int64_t deleteSize = 0;
|
|
int64_t clearLimit = 0;
|
|
int ret = -1;
|
|
|
|
ALOGW("DumpManagerThread: storage need clean");
|
|
//fwk path query
|
|
dir = opendir(FWK_DUMP_PATH);
|
|
if (dir == NULL) {
|
|
ALOGE("DumpManagerThread: opendir: %s failed!", FWK_DUMP_PATH);
|
|
} else {
|
|
while ((dirp = readdir(dir)) != NULL) {
|
|
if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0) {
|
|
continue;
|
|
}
|
|
FileTimeInfo info;
|
|
ret = snprintf(info.path, sizeof(info.path), "%s%s", FWK_DUMP_PATH, dirp->d_name);
|
|
if (ret < 0 || ret >= sizeof(info.path)) {
|
|
ALOGE("%s(), snprintf fail!!: line: %d", __FUNCTION__, __LINE__);
|
|
}
|
|
if (stat(info.path, &dump_stat) != 0) {
|
|
ALOGE("DumpManagerThread: stat time: %s failed!", info.path);
|
|
continue;
|
|
}
|
|
info.lastChangeTime = (int64_t)dump_stat.st_mtime;
|
|
info.size = (int64_t)dump_stat.st_size;
|
|
info.isHal = false;
|
|
//ALOGD("DumpManagerThread: %s|%lld|%lld|%d",
|
|
// info.path, (long long)info.lastChangeTime, (long long)info.size, info.isHal);
|
|
fileInfoVec.add(info);
|
|
}
|
|
closedir(dir);
|
|
}
|
|
|
|
//hal path query
|
|
halDumpFileNumRet = AudioSystem::getParameters(KEY_GET_HAL_DUMP_NUM);
|
|
param = AudioParameter(halDumpFileNumRet);
|
|
if (param.getInt(KEY_GET_HAL_DUMP_NUM, halDumpFileNumValue) == NO_ERROR) {
|
|
ALOGD("DumpManagerThread: queryHalDumpFileList: halDumpFileNum: %d", halDumpFileNumValue);
|
|
} else {
|
|
ALOGE("DumpManagerThread: error, getHalDumpList");
|
|
halDumpFileNumValue = 0;
|
|
continue;
|
|
}
|
|
if (halDumpFileNumValue > 0) {
|
|
for (int i=0; i<(halDumpFileNumValue/100+1); i++) {
|
|
ALOGD("DumpManagerThread: queryHalDumpFileList: loop: %d", i);
|
|
halDumpFileListRet = AudioSystem::getParameters(KEY_GET_HAL_DUMP_LIST);
|
|
param = AudioParameter(halDumpFileListRet);
|
|
if (param.get(KEY_GET_HAL_DUMP_LIST, halDumpFileListValue) == NO_ERROR) {
|
|
//ALOGD("DumpManagerThread: halDumpFileListValue: %s", halDumpFileListValue.string());
|
|
} else {
|
|
ALOGE("DumpManagerThread: error, getHalDumpList");
|
|
halDumpFileListValue = String8("");
|
|
}
|
|
getHalFileInfoVec(halDumpFileListValue, halfileInfoVec);
|
|
fileInfoVec.appendVector(halfileInfoVec);
|
|
}
|
|
}
|
|
|
|
//sort hal dump file list
|
|
fileInfoVec.sort(cmp);
|
|
|
|
//get delete list
|
|
property_get(PROPERTY_DUMP_CLEAR_LIMIT, value, "-1");
|
|
clearLimit = atoi(value);
|
|
if (clearLimit >= 0) {
|
|
clearLimit = clearLimit * storageLimit / 100;
|
|
} else {
|
|
clearLimit = storageLimit / 5;
|
|
}
|
|
if (clearLimit == 0) { //bypass clear
|
|
continue;
|
|
}
|
|
for (int i = 0; i < fileInfoVec.size(); ++i) {
|
|
deleteSize += fileInfoVec[i].size;
|
|
if (fileInfoVec[i].isHal) {
|
|
deleteListHalVec = deleteListHalVec + String8(fileInfoVec[i].path) + String8(",");
|
|
} else {
|
|
deleteListFwkVec.add(String8(fileInfoVec[i].path));
|
|
}
|
|
|
|
if (deleteSize >= clearLimit) {
|
|
ALOGD("DumpManagerThread: free storage size:%lldMB",
|
|
(long long)deleteSize / SIZE_MB);
|
|
break;
|
|
}
|
|
if (i == fileInfoVec.size() - 1) {
|
|
ALOGD("DumpManagerThread: free storage size:%lldMB",
|
|
(long long)deleteSize / SIZE_MB);
|
|
}
|
|
}
|
|
|
|
//delete fwk dump
|
|
for (int i = 0; i < deleteListFwkVec.size(); i++) {
|
|
//ALOGD("DumpManagerThread: delete |%s|", deleteListFwkVec[i].string());
|
|
ret = remove(deleteListFwkVec[i].string());
|
|
if (ret != 0) {
|
|
ALOGW("remove: %s failed!, line: %d", deleteListFwkVec[i].string(), __LINE__);
|
|
}
|
|
}
|
|
|
|
//delete hal dump
|
|
String8 keyValuePar = KEY_DEL_HAL_DUMP_LIST + String8("=") + deleteListHalVec;
|
|
AudioSystem::setParameters(keyValuePar);
|
|
}
|
|
}
|
|
wait:
|
|
if (isFwkOrHalAudioDumpThreadAlive()) { //switch sleep interval
|
|
if (mAudioDumpManagerSleepMode) {
|
|
ALOGW("DumpManagerThread: [SleepMode]: Leave");
|
|
mAudioDumpManagerSleepMode = false;
|
|
}
|
|
loopCount++;
|
|
clock_gettime(CLOCK_MONOTONIC, &timePost);
|
|
timeSpent = timePost.tv_sec - timePre.tv_sec;
|
|
if (10 > timeSpent) {
|
|
sleep(10 - timeSpent);
|
|
}
|
|
} else {
|
|
if (!mAudioDumpManagerSleepMode) {
|
|
ALOGW("DumpManagerThread: [SleepMode]: Enter");
|
|
loopCount++;
|
|
mAudioDumpManagerSleepMode = true;
|
|
}
|
|
sleep(30);
|
|
}
|
|
}
|
|
|
|
ALOGD("DumpManagerThread exit hDumpManagerThread=%ld", hDumpManagerThread);
|
|
hDumpManagerThread = 0;
|
|
pthread_exit(NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void startDumpManagerThreadInt(void) {
|
|
if (!hDumpManagerThread) {
|
|
//create PCM data dump thread here
|
|
int ret;
|
|
ret = pthread_create(&hDumpManagerThread, NULL, DumpManagerThread, NULL);
|
|
if (ret != 0) {
|
|
ALOGE("hDumpManagerThread create fail!!!");
|
|
} else {
|
|
ALOGD("hDumpManagerThread created");
|
|
}
|
|
}
|
|
}
|
|
|
|
void AudioDumpExt::AudioDumpClip::startDumpManagerThread() {
|
|
startDumpManagerThreadInt();
|
|
}
|
|
|
|
}
|