#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 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* > mAudioDumpClipFileVector; KeyedVector* > mAudioDumpClipQueueVector; //fileinfo for dump clip struct AudioDumpClipFileInfos { String8 timeStampNunc; String8 timeStampDein; int totalSize; int clipCount; AudioDumpClipFileInfos() { totalSize = 0; clipCount = 0; } }; KeyedVector 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*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 *pDumpBufferVector = NULL; ssize_t index = mAudioDumpClipFileVector.indexOfKey(filePath); if (index < 0) { pDumpBufferVector = new Vector; 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 *pQueueBufferVector = NULL; Vector 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* 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 *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 *pQueueBufferVector = NULL; mAudioDumpClipMutex.lock(); ssize_t index = mAudioDumpClipQueueVector.indexOfKey(String8(path)); if (index < 0) { // new add pQueueBufferVector = new Vector; 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 &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 fileInfoVec; Vector halfileInfoVec; Vector totalfileInfoVec; Vector 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(); } }