1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #ifndef LOG_TAG
16 #define LOG_TAG "AudioDumpPCM"
17 #endif
18 
19 #include "audio_dump_pcm.h"
20 #include "audio_dump_pcm_private.h"
21 
22 #include <utility>
23 
24 #include "media_monitor_manager.h"
25 #include "callback_handler.h"
26 #include "audio_log.h"
27 #include "audio_errors.h"
28 #include "audio_utils.h"
29 
30 namespace OHOS {
31 namespace AudioStandard {
32 
33 constexpr uint64_t MAX_LIMIT_BUFFER_SIZE = 100 * 1024 * 1024;   // 100M
34 constexpr size_t EACH_CHUNK_SIZE = 1024 * 1024;                 // 1M
35 constexpr int64_t MEMBLOCK_RELEASE_TIME = 5 * 60 * 1000000;     // release pcm 5min ago
36 constexpr int64_t MEMBLOCK_CHECK_TIME_MS = 30 * 1000;           // check cached data's time every 30s
37 constexpr int64_t MEMORY_PRINT_TIME_MS = 60 * 1000;             // print memory info every 60s
38 constexpr int64_t MEMORY_PRINT_MININUM_SIZE = 5 * 1024 * 1024;  // print memory info only when used excceds 5M
39 constexpr uint16_t FILENAME_ID_MAX_INDEX = 65530;               // FileNameId 0 - 65530
40 constexpr size_t FILENAME_AND_ID_SIZE = 128;                    // estimate each entry size in map
41 constexpr size_t NAME_MAP_NUM = 2;                              // FileNameIdMap and idFileNameMap
42 
MemChunk()43 MemChunk::MemChunk() : totalBufferSize_(EACH_CHUNK_SIZE), pointerOffset_(0), curFileNameId_(0)
44 {
45     Trace trace("MemChunk::MemChunk");
46     firstMemBlockTime_ = ClockTime::GetRealNano();
47     lastMemBlockTime_ = firstMemBlockTime_;
48     bufferPointer_ = new (std::nothrow) uint8_t[EACH_CHUNK_SIZE];
49     if (bufferPointer_ == nullptr) {
50         AUDIO_ERR_LOG("failed to get memory!");
51     }
52     memBlockDeque_ = std::make_shared<std::deque<MemBlock>>();
53 }
54 
~MemChunk()55 MemChunk::~MemChunk()
56 {
57     Trace trace("MemChunk::~MemChunk");
58     if (bufferPointer_ != nullptr) {
59         delete bufferPointer_;
60     }
61     bufferPointer_ = nullptr;
62 }
63 
GetMemBlock(size_t dataLength,std::string & dumpFileName,MemBlock & curMemBlock)64 int32_t MemChunk::GetMemBlock(size_t dataLength, std::string &dumpFileName, MemBlock &curMemBlock)
65 {
66     CHECK_AND_RETURN_RET(totalBufferSize_ - pointerOffset_ >= dataLength, ERROR);
67     Trace trace("MemChunk::GetMemBlock");
68 
69     if (fileNameIdMap_.find(dumpFileName) != fileNameIdMap_.end()) {
70         curMemBlock.dumpFileNameId_ = fileNameIdMap_[dumpFileName];
71     } else {
72         curMemBlock.dumpFileNameId_ = (curFileNameId_ + 1 >= FILENAME_ID_MAX_INDEX) ? 0 : ++curFileNameId_;
73         fileNameIdMap_[dumpFileName] = curFileNameId_;
74         idFileNameMap_[curFileNameId_] = dumpFileName;
75     }
76 
77     curMemBlock.dataPointer_ = bufferPointer_ + pointerOffset_;
78     curMemBlock.dataLength_ = dataLength;
79     pointerOffset_ += dataLength;
80     lastMemBlockTime_ = ClockTime::GetRealNano();
81     memBlockDeque_->push_back(curMemBlock);
82     return SUCCESS;
83 }
84 
GetMemBlockDeque()85 std::shared_ptr<std::deque<MemBlock>> MemChunk::GetMemBlockDeque()
86 {
87     return memBlockDeque_;
88 }
89 
GetMemChunkDuration(int64_t & startTime,int64_t & endTime)90 void MemChunk::GetMemChunkDuration(int64_t &startTime, int64_t &endTime)
91 {
92     startTime = firstMemBlockTime_;
93     endTime = lastMemBlockTime_;
94 }
95 
GetCurUsedMemory(size_t & dataLength,size_t & bufferLength,size_t & structLength)96 int32_t MemChunk::GetCurUsedMemory(size_t &dataLength, size_t &bufferLength, size_t &structLength)
97 {
98     dataLength = pointerOffset_;
99     bufferLength = totalBufferSize_;
100     structLength = sizeof(MemBlock) * memBlockDeque_->size() + sizeof(MemChunk) +
101         FILENAME_AND_ID_SIZE * idFileNameMap_.size() * NAME_MAP_NUM; // roughly estimate the size of the map structure
102     return SUCCESS;
103 }
104 
Reset()105 void MemChunk::Reset()
106 {
107     Trace trace("MemChunk::Reset");
108     pointerOffset_ = 0;
109     curFileNameId_ = 0;
110     firstMemBlockTime_ = ClockTime::GetRealNano();
111     lastMemBlockTime_ = firstMemBlockTime_;
112     idFileNameMap_ = {};
113     fileNameIdMap_ = {};
114     memBlockDeque_->clear();
115 }
116 
GetInstance()117 AudioCacheMgr& AudioCacheMgr::GetInstance()
118 {
119     static AudioCacheMgrInner mgr;
120     return mgr;
121 }
122 
AudioCacheMgrInner()123 AudioCacheMgrInner::AudioCacheMgrInner()
124 {
125     Trace trace("AudioCacheMgrInner::AudioCacheMgrInner");
126     totalMemChunkNums_ = MAX_LIMIT_BUFFER_SIZE / EACH_CHUNK_SIZE;
127 }
128 
~AudioCacheMgrInner()129 AudioCacheMgrInner::~AudioCacheMgrInner()
130 {
131     std::lock_guard<std::mutex> runnerlock(runnerMutex_);
132     if (callbackHandler_ != nullptr) {
133         AUDIO_INFO_LOG("runner move");
134         callbackHandler_->ReleaseEventRunner();
135         callbackHandler_ = nullptr;
136     }
137 }
138 
Init()139 bool AudioCacheMgrInner::Init()
140 {
141     if (isInited_ == false) {
142         InitCallbackHandler();
143         isInited_ = true;
144         return true;
145     }
146     AUDIO_WARNING_LOG("AudioCacheMgr is Inited!");
147     return true;
148 }
149 
DeInit()150 bool AudioCacheMgrInner::DeInit()
151 {
152     Trace trace("AudioCacheMgrInner::DeInit");
153     std::unique_lock<std::mutex> lock(runnerMutex_);
154     if (callbackHandler_ != nullptr) {
155         callbackHandler_->ReleaseEventRunner();
156         callbackHandler_ = nullptr;
157         handler_ = nullptr;
158 
159         // clear all cached pcm
160         memChunkDeque_ = {};
161         AUDIO_INFO_LOG("deinit handler success");
162     }
163     lock.unlock();
164     isInited_ = false;
165     AUDIO_WARNING_LOG("AudioCacheMgr is DeInited!");
166     return true;
167 }
168 
CacheData(std::string & dumpFileName,void * srcDataPointer,size_t dataLength)169 void AudioCacheMgrInner::CacheData(std::string &dumpFileName, void* srcDataPointer, size_t dataLength)
170 {
171     if (!isInited_.load()) {
172         Trace trace("AudioCacheMgrInner::CacheData::NotInited");
173         return;
174     }
175     Trace trace("AudioCacheMgrInner::CacheData");
176     if (isDumpingData_.load()) {
177         Trace trace("AudioCacheMgrInner::CacheData::ReturnWhenDumpingData");
178         return;
179     }
180     if (!g_Mutex.try_lock()) {
181         // condition 1: cacheData thread and dumpData thread in Concurrency
182         // if dumpData thread gets mutex, discard cacheData and return directly
183         // if cacheData threads gets mutex, dumpData thread waits for cacheData finish,
184         // cause cacheData excutes very fast.
185         if (isDumpingData_.load()) {
186             Trace trace("AudioCacheMgrInner::CacheData::TryLockFailedWhenDumpingData");
187             return;
188         }
189         // condition 2: two cacheData threads in Concurrency, one gets mutex
190         // the other thread waits for mutex.
191         g_Mutex.lock();
192     }
193 
194     MemBlock curMemBlock {nullptr, 0, 0};
195     int ret = GetAvailableMemBlock(dataLength, dumpFileName, curMemBlock);
196     if (ret != SUCCESS) {
197         AUDIO_ERR_LOG("GetAvailableMemBlock failed. Unable to cacheData!");
198         g_Mutex.unlock();
199         return;
200     }
201     g_Mutex.unlock();
202 
203     ret = memcpy_s(curMemBlock.dataPointer_, dataLength, srcDataPointer, dataLength);
204     CHECK_AND_RETURN_LOG(ret == SUCCESS, "memcpy_s failed. Unable to cacheData!");
205 }
206 
GetAvailableMemBlock(size_t dataLength,std::string & dumpFileName,MemBlock & curMemBlock)207 int32_t AudioCacheMgrInner::GetAvailableMemBlock(size_t dataLength, std::string &dumpFileName, MemBlock &curMemBlock)
208 {
209     if (!memChunkDeque_.empty()) {
210         Trace trace1("AudioCacheMgrInner::GetAvailableMemBlock::hasNotFillMemChunk");
211         std::shared_ptr<MemChunk> lastMemChunk = memChunkDeque_.back();
212         if (lastMemChunk->GetMemBlock(dataLength, dumpFileName, curMemBlock) == SUCCESS) {
213             return SUCCESS;
214         }
215     }
216 
217     if (memChunkDeque_.size() < totalMemChunkNums_) {
218         Trace trace2("AudioCacheMgrInner::getAvailMemBlock::GetMemBlock");
219         std::shared_ptr<MemChunk> newMemChunk = std::make_shared<MemChunk>();
220         Trace::Count("UsedMemChunk", memChunkDeque_.size());
221         memChunkDeque_.push_back(newMemChunk);
222         if (newMemChunk->GetMemBlock(dataLength, dumpFileName, curMemBlock) == SUCCESS) {
223             return SUCCESS;
224         }
225     }
226 
227     if (!memChunkDeque_.empty()) {
228         Trace trace3("AudioCacheMgrInner::GetAvailableMemBlock::RecycleOneMemChunk");
229         std::shared_ptr<MemChunk> recycleMemChunk = memChunkDeque_.front();
230         memChunkDeque_.pop_front();
231         recycleMemChunk->Reset();
232         memChunkDeque_.push_back(recycleMemChunk);
233         if (recycleMemChunk->GetMemBlock(dataLength, dumpFileName, curMemBlock) == SUCCESS) {
234             return SUCCESS;
235         }
236     }
237 
238     AUDIO_ERR_LOG("failed to get available memBlock");
239     return ERROR;
240 }
241 
DumpAllMemBlock()242 int32_t AudioCacheMgrInner::DumpAllMemBlock()
243 {
244     if (!isInited_.load()) {
245         Trace trace("AudioCacheMgrInner::DumpAllMemBlock::NotInited");
246         AUDIO_WARNING_LOG("not inited!");
247         return ERR_ILLEGAL_STATE;
248     }
249 
250     Trace trace("AudioCacheMgrInner::DumpAllMemBlock");
251     bool targetStatus = false;
252     if (!isDumpingData_.compare_exchange_strong(targetStatus, true)) {
253         AUDIO_WARNING_LOG("Already in dumping data!");
254     }
255 
256     std::lock_guard<std::mutex> processsLock(g_Mutex);
257 
258     std::vector<std::pair<std::string, std::string>> paramStart;
259     paramStart.push_back({"BETA", "true"});
260     Media::MediaMonitor::MediaMonitorManager::GetInstance().SetMediaParameters(paramStart);
261 
262     while (!memChunkDeque_.empty()) {
263         std::shared_ptr<MemChunk> curMemChunk = memChunkDeque_.front();
264         memChunkDeque_.pop_front();
265 
266         std::shared_ptr<std::deque<MemBlock>> curMemBlockDeque = curMemChunk->GetMemBlockDeque();
267         for (auto it = curMemBlockDeque->begin(); it != curMemBlockDeque->end(); ++it) {
268             Media::MediaMonitor::MediaMonitorManager::GetInstance().WriteAudioBuffer("pcm_dump_" +
269                 curMemChunk->idFileNameMap_[it->dumpFileNameId_], it->dataPointer_, it->dataLength_);
270         }
271         Trace::Count("UsedMemChunk", memChunkDeque_.size());
272     }
273 
274     std::vector<std::pair<std::string, std::string>> paramEnd;
275     paramEnd.push_back({"BETA", "false"});
276     Media::MediaMonitor::MediaMonitorManager::GetInstance().SetMediaParameters(paramEnd);
277 
278     isDumpingData_.store(false);
279     return SUCCESS;
280 }
281 
GetCachedDuration(int64_t & startTime,int64_t & endTime)282 void AudioCacheMgrInner::GetCachedDuration(int64_t &startTime, int64_t &endTime)
283 {
284     Trace trace("AudioCacheMgrInner::GetCacheDuration");
285     if (!isInited_.load()) {
286         Trace trace("AudioCacheMgrInner::GetCachedDuration::NotInited");
287         AUDIO_WARNING_LOG("not inited!");
288         return;
289     }
290     std::lock_guard<std::mutex> processLock(g_Mutex);
291     // init but no data in memchunk
292     if (memChunkDeque_.size() == 0) {
293         startTime = ClockTime::GetRealNano();
294         endTime = startTime;
295         AUDIO_WARNING_LOG("GetCachedDuration while memChunkDeque_ is empty!");
296         return;
297     }
298 
299     int64_t temp;
300     if (memChunkDeque_.front() != nullptr) {
301         memChunkDeque_.front()->GetMemChunkDuration(startTime, temp);
302     }
303     if (memChunkDeque_.back() != nullptr) {
304         memChunkDeque_.back()->GetMemChunkDuration(temp, endTime);
305     }
306     AUDIO_INFO_LOG("startTime:%{public}s, endTime:%{public}s.",
307         ClockTime::NanoTimeToString(startTime).c_str(), ClockTime::NanoTimeToString(endTime).c_str());
308 }
309 
PrintCurMemoryCondition()310 void AudioCacheMgrInner::PrintCurMemoryCondition()
311 {
312     Trace trace("AudioCacheMgrInner::PrintCurMemoryCondition");
313     SafeSendCallBackEvent(PRINT_MEMORY_CONDITION, 0, MEMORY_PRINT_TIME_MS);
314 
315     size_t dataLength = 0;
316     size_t bufferLength = 0;
317     size_t structLength = 0;
318     GetCurMemoryCondition(dataLength, bufferLength, structLength);
319     if (bufferLength >= MEMORY_PRINT_MININUM_SIZE) {
320         AUDIO_INFO_LOG("dataLength: %{public}zu KB, bufferLength: %{public}zu KB, structLength: %{public}zu KB",
321             dataLength / BYTE_TO_KB_SIZE, bufferLength / BYTE_TO_KB_SIZE, structLength / BYTE_TO_KB_SIZE);
322     }
323 }
324 
GetCurMemoryCondition(size_t & dataLength,size_t & bufferLength,size_t & structLength)325 void AudioCacheMgrInner::GetCurMemoryCondition(size_t &dataLength, size_t &bufferLength, size_t &structLength)
326 {
327     Trace trace("AudioCacheMgrInner::GetCurMemoryCondition");
328     std::lock_guard<std::mutex> processLock(g_Mutex);
329 
330     size_t curDataLength = 0;
331     size_t curBufferLength = 0;
332     size_t curStructLength = 0;
333     if (memChunkDeque_.empty()) {
334         AUDIO_INFO_LOG("cache memory is empty");
335         return;
336     }
337 
338     for (auto it = memChunkDeque_.begin(); it != memChunkDeque_.end(); ++it) {
339         (*it)->GetCurUsedMemory(curDataLength, curBufferLength, curStructLength);
340         dataLength += curDataLength;
341         bufferLength += curBufferLength;
342         structLength += curStructLength;
343     }
344 }
345 
ReleaseOverTimeMemBlock()346 void AudioCacheMgrInner::ReleaseOverTimeMemBlock()
347 {
348     Trace trace("AudioCacheMgrInner::ReleaseOverTimeMemBlock");
349     SafeSendCallBackEvent(RELEASE_OVERTIME_MEMBLOCK, 0, MEMBLOCK_CHECK_TIME_MS);
350     std::lock_guard<std::mutex> processLock(g_Mutex);
351     if (isDumpingData_.load()) {
352         AUDIO_INFO_LOG("now dumping memblock, no need ReleaseOverTimeMemBlock");
353         return;
354     }
355 
356     int32_t recycleNums = 0;
357     int64_t curTime = ClockTime::GetRealNano();
358     int64_t startTime, endTime;
359 
360     while (!memChunkDeque_.empty()) {
361         memChunkDeque_.front()->GetMemChunkDuration(startTime, endTime);
362         if (curTime - endTime < MEMBLOCK_RELEASE_TIME * AUDIO_MS_PER_SECOND) {
363             break;
364         }
365         memChunkDeque_.pop_front();
366         ++recycleNums;
367         Trace::Count("UsedMemChunk", memChunkDeque_.size());
368     }
369     if (recycleNums != 0) {
370         AUDIO_INFO_LOG("CheckMemBlock Recycle %{public}d memBlocks", recycleNums);
371     }
372 }
373 
GetDumpParameter(const std::vector<std::string> & subKeys,std::vector<std::pair<std::string,std::string>> & result)374 bool AudioCacheMgrInner::GetDumpParameter(const std::vector<std::string> &subKeys,
375     std::vector<std::pair<std::string, std::string>> &result)
376 {
377     // vector size check had done before call this function
378     // audioCacheState 0:close, 1:open, 2:init
379     if (subKeys[0] == GET_STATUS_KEY) {
380         int32_t audioCacheState = 0;
381         GetSysPara("persist.multimedia.audio.audioCacheState", audioCacheState);
382         CHECK_AND_RETURN_RET_LOG(audioCacheState >= 0 && audioCacheState < AUDIO_CACHE_STATE.size(),
383             false, "get invalid audioCacheState %{public}d", audioCacheState);
384         result.push_back({"STATUS", AUDIO_CACHE_STATE[audioCacheState]});
385     } else if (subKeys[0] == GET_TIME_KEY) {
386         int64_t startTime = 0;
387         int64_t endTime = 0;
388         GetCachedDuration(startTime, endTime);
389         result.push_back({ClockTime::NanoTimeToString(startTime), ClockTime::NanoTimeToString(endTime)});
390     } else if (subKeys[0] == GET_MEMORY_KEY) {
391         size_t dataLength = 0;
392         size_t bufferLength = 0;
393         size_t structLength = 0;
394         GetCurMemoryCondition(dataLength, bufferLength, structLength);
395         result.push_back({std::to_string(dataLength), std::to_string(bufferLength + structLength)});
396     } else {
397         AUDIO_ERR_LOG("invalid param %{public}s", subKeys[0].c_str());
398         return false;
399     }
400     return true;
401 }
402 
SetDumpParameter(const std::vector<std::pair<std::string,std::string>> & params)403 bool AudioCacheMgrInner::SetDumpParameter(const std::vector<std::pair<std::string, std::string>> &params)
404 {
405     // vector size check had done before call this function
406     // audioCacheState 0:close, 1:open, 2:init
407     int32_t audioCacheState = 0;
408     GetSysPara("persist.multimedia.audio.audioCacheState", audioCacheState);
409     if (params[0].first == SET_OPEN_KEY) {
410         Init();
411         SetSysPara("persist.multimedia.audio.audioCacheState", 1);
412     } else if (params[0].first == SET_CLOSE_KEY) {
413         DeInit();
414         SetSysPara("persist.multimedia.audio.audioCacheState", 0);
415     } else if (params[0].first == SET_UPLOAD_KEY) {
416         // only when user argees to cachedata, audioCacheState will change to 1(open),.
417         CHECK_AND_RETURN_RET_LOG(audioCacheState == 1, false,
418             "cannot upload, curAudioCacheState is %{public}d, not code 1! ", audioCacheState);
419         CHECK_AND_RETURN_RET_LOG(DumpAllMemBlock() == SUCCESS, false,
420             "upload allMemBlock failed!");
421     } else {
422         AUDIO_ERR_LOG("invalid param %{public}s", params[0].first.c_str());
423         return false;
424     }
425     return true;
426 }
427 
InitCallbackHandler()428 void AudioCacheMgrInner::InitCallbackHandler()
429 {
430     Trace trace("AudioCacheMgrInner::InitCallbackHandler");
431     std::unique_lock<std::mutex> lock(runnerMutex_);
432     if (callbackHandler_ == nullptr) {
433         handler_ = std::make_shared<AudioCacheHandler>(this);
434         callbackHandler_ = CallbackHandler::GetInstance(handler_, "OS_AUDIODumpCB");
435         AUDIO_INFO_LOG("init handler success");
436     }
437     lock.unlock();
438     SafeSendCallBackEvent(RELEASE_OVERTIME_MEMBLOCK, 0, MEMBLOCK_CHECK_TIME_MS);
439     SafeSendCallBackEvent(PRINT_MEMORY_CONDITION, 0, MEMORY_PRINT_TIME_MS);
440 }
441 
SafeSendCallBackEvent(uint32_t eventCode,int64_t data,int64_t delayTime)442 void AudioCacheMgrInner::SafeSendCallBackEvent(uint32_t eventCode, int64_t data, int64_t delayTime)
443 {
444     Trace trace("AudioCacheMgrInner::SafeSendCallBackEvent");
445     std::lock_guard<std::mutex> lock(runnerMutex_);
446     CHECK_AND_RETURN_LOG(callbackHandler_ != nullptr, "Runner is Release");
447 
448     callbackHandler_->SendCallbackEvent(eventCode, data, delayTime);
449 }
450 
OnHandle(uint32_t code,int64_t data)451 void AudioCacheMgrInner::OnHandle(uint32_t code, int64_t data)
452 {
453     switch (code) {
454         case RELEASE_OVERTIME_MEMBLOCK:
455             ReleaseOverTimeMemBlock();
456             break;
457         case PRINT_MEMORY_CONDITION:
458             PrintCurMemoryCondition();
459             break;
460         default:
461             break;
462     }
463 }
464 
AudioCacheHandler(IHandler * handler)465 AudioCacheHandler::AudioCacheHandler(IHandler* handler) : handler_(handler) {}
466 
OnHandle(uint32_t code,int64_t data)467 void AudioCacheHandler::OnHandle(uint32_t code, int64_t data)
468 {
469     CHECK_AND_RETURN_LOG(handler_ != nullptr, "handler is nullptr");
470     handler_->OnHandle(code, data);
471 }
472 
473 } // namespace AudioStandard
474 } // namespace OHOS
475