1 /*
2 * Copyright (C) 2025 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
16 #include <fstream>
17 #include <list>
18
19 #include "dfx_utils.h"
20 #include "dfx_database_utils.h"
21 #include "file_utils.h"
22 #include "medialibrary_astc_stat.h"
23 #include "medialibrary_type_const.h"
24 #include "media_file_utils.h"
25 #include "media_log.h"
26 #include "thumbnail_const.h"
27 #include "thumbnail_generate_worker.h"
28 #include "thumbnail_generate_worker_manager.h"
29
30 namespace OHOS {
31 namespace Media {
32
GetInstance()33 MediaLibraryAstcStat &MediaLibraryAstcStat::GetInstance()
34 {
35 static MediaLibraryAstcStat instance;
36 return instance;
37 }
38
39 template <typename T>
enum_to_value(T enumValue)40 auto enum_to_value(T enumValue) -> std::underlying_type_t<T>
41 {
42 return static_cast<std::underlying_type_t<T>>(enumValue);
43 }
44
ConvertSceneStatToJson(const SceneStat & sceneStat,AstcGenScene sceneType)45 static nlohmann::json ConvertSceneStatToJson(const SceneStat &sceneStat, AstcGenScene sceneType)
46 {
47 nlohmann::json jsonSceneStat;
48 jsonSceneStat["scene"] = enum_to_value(sceneType);
49 jsonSceneStat["duration"] = sceneStat.duration_;
50 jsonSceneStat["astc"] = sceneStat.astcCount_;
51 return jsonSceneStat;
52 }
53
ConvertPhaseStatToJson(const PhaseStat & phaseStat,AstcPhase phaseType)54 static nlohmann::json ConvertPhaseStatToJson(const PhaseStat &phaseStat, AstcPhase phaseType)
55 {
56 nlohmann::json jsonPhaseStat;
57 jsonPhaseStat["phase"] = enum_to_value(phaseType);
58 jsonPhaseStat["phase_start_time"] = phaseStat.startTime_;
59 jsonPhaseStat["phase_end_time"] = phaseStat.endTime_;
60 jsonPhaseStat["interruptArr"] = nlohmann::json::array();
61 for (const auto &[retValue, retCount] : phaseStat.retValues_) {
62 nlohmann::json jsonRetStat;
63 jsonRetStat[retValue] = retCount;
64 jsonPhaseStat["interruptArr"].emplace_back(jsonRetStat);
65 }
66 nlohmann::json scenesJson;
67 for (const auto &[sceneType, sceneStat] : phaseStat.scenes_) {
68 std::string key = "scene" + std::to_string(enum_to_value(sceneType));
69 jsonPhaseStat[key] = ConvertSceneStatToJson(sceneStat, sceneType);
70 }
71 return jsonPhaseStat;
72 }
73
ConvertToJson(nlohmann::json & jsonPhasesStat,const PhasesStat & phasesStat,int32_t totalAstcCount)74 bool MediaLibraryAstcStat::ConvertToJson(nlohmann::json& jsonPhasesStat, const PhasesStat& phasesStat,
75 int32_t totalAstcCount)
76 {
77 jsonPhasesStat["totalAstcCount"] = totalAstcCount;
78 for (const auto &[phaseType, phaseStat] : phasesStat.phases_) {
79 std::string key = "phase" + std::to_string(enum_to_value(phaseType));
80 jsonPhasesStat[key] = ConvertPhaseStatToJson(phaseStat, phaseType);
81 }
82 return true;
83 }
84
ConvertRetStatToStruct(const nlohmann::json & jsonPhaseStat,PhaseStat & phaseStat)85 static bool ConvertRetStatToStruct(const nlohmann::json &jsonPhaseStat, PhaseStat &phaseStat)
86 {
87 for (const auto& jsonRetStat : jsonPhaseStat["interruptArr"]) {
88 for (const auto& [key, value] : jsonRetStat.items()) {
89 phaseStat.retValues_[key] = 0;
90 if (value.is_number_integer()) {
91 phaseStat.retValues_[key] = value.get<int32_t>();
92 }
93 }
94 }
95 return true;
96 }
97
ConvertSceneStatToStruct(const nlohmann::json & jsonSceneStat,SceneStat & sceneStat,AstcGenScene & sceneType)98 static bool ConvertSceneStatToStruct(const nlohmann::json &jsonSceneStat, SceneStat &sceneStat, AstcGenScene& sceneType)
99 {
100 if (jsonSceneStat.contains("scene") && jsonSceneStat["scene"].is_number_integer()) {
101 sceneType = static_cast<AstcGenScene>(jsonSceneStat["scene"].get<int32_t>());
102 sceneStat.sceneKey_ = sceneType;
103 }
104 if (jsonSceneStat.contains("duration") && jsonSceneStat["duration"].is_number_integer()) {
105 sceneStat.duration_ = jsonSceneStat["duration"].get<int64_t>();
106 }
107 if (jsonSceneStat.contains("astc") && jsonSceneStat["astc"].is_number_integer()) {
108 sceneStat.astcCount_ = static_cast<uint32_t>(jsonSceneStat["astc"].get<int32_t>());
109 }
110
111 return true;
112 }
113
ConvertPhaseStatToStruct(const nlohmann::json & jsonPhaseStat,PhaseStat & phaseStat,AstcPhase & photoSyncPhase)114 static bool ConvertPhaseStatToStruct(const nlohmann::json &jsonPhaseStat, PhaseStat &phaseStat,
115 AstcPhase& photoSyncPhase)
116 {
117 if (jsonPhaseStat.contains("phase") && jsonPhaseStat["phase"].is_number_integer()) {
118 photoSyncPhase = static_cast<AstcPhase>(jsonPhaseStat["phase"].get<int32_t>());
119 phaseStat.phase_ = photoSyncPhase;
120 }
121 if (jsonPhaseStat.contains("phase_start_time") && jsonPhaseStat["phase_start_time"].is_number_integer()) {
122 phaseStat.startTime_ = jsonPhaseStat["phase_start_time"].get<int64_t>();
123 }
124 if (jsonPhaseStat.contains("phase_end_time") && jsonPhaseStat["phase_end_time"].is_number_integer()) {
125 phaseStat.endTime_ = jsonPhaseStat["phase_end_time"].get<int64_t>();
126 }
127 if (jsonPhaseStat.contains("interruptArr") && jsonPhaseStat["interruptArr"].is_array()) {
128 ConvertRetStatToStruct(jsonPhaseStat, phaseStat);
129 }
130 for (int i = 0; i <= static_cast<int>(AstcGenScene::CHARGING_SCREENOFF); i++) {
131 std::string key = "scene" + std::to_string(i);
132 if (jsonPhaseStat.contains(key)) {
133 const auto& jsonSceneStat = jsonPhaseStat[key];
134 SceneStat sceneStat{};
135 AstcGenScene sceneType{AstcGenScene::DEFAULT};
136 ConvertSceneStatToStruct(jsonSceneStat, sceneStat, sceneType);
137 if (phaseStat.scenes_.count(sceneType) == 0) {
138 phaseStat.scenes_[sceneType] = sceneStat;
139 } else {
140 phaseStat.scenes_[sceneType] += sceneStat;
141 }
142 }
143 }
144 return true;
145 }
146
ConvertToStruct(const nlohmann::json & jsonPhasesStat,PhasesStat & phasesStat,int32_t & totalAstcCount)147 bool MediaLibraryAstcStat::ConvertToStruct(const nlohmann::json &jsonPhasesStat, PhasesStat &phasesStat,
148 int32_t& totalAstcCount)
149 {
150 if (jsonPhasesStat.contains("totalAstcCount") && jsonPhasesStat["totalAstcCount"].is_number_integer()) {
151 totalAstcCount = jsonPhasesStat["totalAstcCount"].get<int32_t>();
152 }
153 for (int i = static_cast<int>(AstcPhase::PHASE1); i <= static_cast<int>(AstcPhase::PHASE5); i++) {
154 std::string key = "phase" + std::to_string(i);
155 if (jsonPhasesStat.contains(key)) {
156 const auto& jsonPhaseStat = jsonPhasesStat[key];
157 PhaseStat phaseStat{};
158 AstcPhase phaseType{AstcPhase::DEFAULT};
159 ConvertPhaseStatToStruct(jsonPhaseStat, phaseStat, phaseType);
160 if (phasesStat.phases_.count(phaseType) == 0) {
161 phasesStat.phases_[phaseType] = phaseStat;
162 } else {
163 phasesStat.phases_[phaseType] += phaseStat;
164 }
165 }
166 }
167 return true;
168 }
169
ReadAstcInfoFromJsonFile(PhasesStat & phasesStat,int32_t & totalAstcCount)170 bool MediaLibraryAstcStat::ReadAstcInfoFromJsonFile(PhasesStat& phasesStat, int32_t& totalAstcCount)
171 {
172 nlohmann::json jsonPhaseStat{};
173 if (not ReadJsonFile(ASTC_JSON_FILE_PATH, jsonPhaseStat)) {
174 MEDIA_ERR_LOG("ReadJsonFile failed!");
175 return false;
176 }
177 if (not ConvertToStruct(jsonPhaseStat, phasesStat, totalAstcCount)) {
178 MEDIA_ERR_LOG("convert from json to struct failed!");
179 return false;
180 }
181 return true;
182 }
183
WriteAstcInfoToJsonFile(const PhasesStat & phasesStat,int32_t totalAstcCount)184 bool MediaLibraryAstcStat::WriteAstcInfoToJsonFile(const PhasesStat& phasesStat, int32_t totalAstcCount)
185 {
186 nlohmann::json jsonPhaseStat{};
187 if (not ConvertToJson(jsonPhaseStat, phasesStat, totalAstcCount)) {
188 MEDIA_ERR_LOG("convert from struct to json failed!");
189 return false;
190 }
191 if (not WriteJsonFile(ASTC_JSON_FILE_PATH, jsonPhaseStat)) {
192 MEDIA_ERR_LOG("WriteJsonFile failed!");
193 return false;
194 }
195 return true;
196 }
197
WriteJsonFile(const std::string & filePath,const nlohmann::json & j)198 bool MediaLibraryAstcStat::WriteJsonFile(const std::string &filePath, const nlohmann::json &j)
199 {
200 const std::string parentDir = MediaFileUtils::GetParentPath(filePath);
201 if (!MediaFileUtils::CreateDirectory(parentDir)) {
202 MEDIA_ERR_LOG("CreateDirectory failed, dir = %{public}s", DfxUtils::GetSafePath(parentDir).c_str());
203 return false;
204 }
205
206 std::ofstream outFile(filePath, std::ofstream::out | std::ofstream::trunc);
207 CHECK_AND_RETURN_RET_LOG(outFile.is_open(), false, "open filePath: %{private}s failed", filePath.c_str());
208 outFile << j << std::endl;
209 outFile.close();
210 return true;
211 }
212
ReadJsonFile(const std::string & filePath,nlohmann::json & j)213 bool MediaLibraryAstcStat::ReadJsonFile(const std::string &filePath, nlohmann::json &j)
214 {
215 std::ifstream inFile(filePath);
216 CHECK_AND_RETURN_RET_LOG(inFile.is_open(), false, "open filePath: %{private}s failed", filePath.c_str());
217
218 std::string buffer = std::string((std::istreambuf_iterator<char>(inFile)), std::istreambuf_iterator<char>());
219 j = nlohmann::json::parse(buffer, nullptr, false);
220 inFile.close();
221 return !j.is_discarded();
222 }
223
GetAstcPhase(int32_t totalAstcCount,GenerateScene genScene)224 AstcPhase MediaLibraryAstcStat::GetAstcPhase(int32_t totalAstcCount, GenerateScene genScene)
225 {
226 AstcPhase phaseKey = AstcPhase::DEFAULT;
227 constexpr int32_t phase1MaxCount = 100;
228 constexpr int32_t phase2MaxCount = 2000;
229 constexpr int32_t phase3MaxCount = 20000;
230 constexpr int32_t phase4MaxCount = 200000;
231 if (totalAstcCount == phase1MaxCount || totalAstcCount == phase2MaxCount ||
232 totalAstcCount == phase3MaxCount || totalAstcCount == phase4MaxCount) {
233 GetJsonStr();
234 }
235
236 if (totalAstcCount <= phase1MaxCount) {
237 phaseKey = AstcPhase::PHASE1;
238 } else if (totalAstcCount <= phase2MaxCount && totalAstcCount > phase1MaxCount) {
239 phaseKey = AstcPhase::PHASE2;
240 } else if (totalAstcCount <= phase3MaxCount && totalAstcCount > phase2MaxCount) {
241 phaseKey = AstcPhase::PHASE3;
242 } else if (totalAstcCount <= phase4MaxCount && totalAstcCount > phase3MaxCount) {
243 phaseKey = AstcPhase::PHASE4;
244 } else {
245 phaseKey = AstcPhase::PHASE5;
246 }
247
248 return phaseKey;
249 }
250
IsBackupGroundTaskEmpty()251 static bool IsBackupGroundTaskEmpty()
252 {
253 std::shared_ptr<ThumbnailGenerateWorker> thumbnailWorker =
254 ThumbnailGenerateWorkerManager::GetInstance().GetThumbnailWorker(ThumbnailTaskType::BACKGROUND);
255 CHECK_AND_RETURN_RET_LOG(thumbnailWorker != nullptr, true, "thumbnailWorker is null");
256 return thumbnailWorker->IsLowerQueueEmpty();
257 }
258
GetInterruptInfo(bool isScreenOff,bool isCharging,bool isPowerSufficient,bool isThermalLow)259 void MediaLibraryAstcStat::GetInterruptInfo(bool isScreenOff, bool isCharging,
260 bool isPowerSufficient, bool isThermalLow)
261 {
262 if (IsBackupGroundTaskEmpty()) {
263 return;
264 }
265 std::string screenOn = "screenOn";
266 std::string notCharging = "notCharging";
267 std::string powerNotSufficient = "powerNotSufficient";
268 std::string thermalHigh = "thermalHigh";
269
270 std::lock_guard<std::mutex> lock(mutex_);
271 AstcPhase phaseKey = GetAstcPhase(totalAstcCount_ + 1, GenerateScene::BACKGROUND);
272 if (!phasesStat_.phases_.count(phaseKey)) {
273 PhaseStat phase;
274 phasesStat_.phases_[phaseKey] = phase;
275 }
276 auto &interruptStat = phasesStat_.phases_[phaseKey].retValues_;
277 if (!isScreenOff) {
278 if (interruptStat.count(screenOn)) {
279 interruptStat[screenOn]++;
280 } else {
281 interruptStat[screenOn] = 1;
282 }
283 }
284 if (!isCharging) {
285 if (interruptStat.count(notCharging)) {
286 interruptStat[notCharging]++;
287 } else {
288 interruptStat[notCharging] = 1;
289 }
290 }
291 if (!isPowerSufficient) {
292 if (interruptStat.count(powerNotSufficient)) {
293 interruptStat[powerNotSufficient]++;
294 } else {
295 interruptStat[powerNotSufficient] = 1;
296 }
297 }
298 if (!isThermalLow) {
299 if (interruptStat.count(thermalHigh)) {
300 interruptStat[thermalHigh]++;
301 } else {
302 interruptStat[thermalHigh] = 1;
303 }
304 }
305 }
306
GetScene(int64_t duration,AstcGenScene sceneKey)307 static SceneStat GetScene(int64_t duration, AstcGenScene sceneKey)
308 {
309 SceneStat scene;
310 scene.sceneKey_ = sceneKey;
311 scene.duration_ = duration;
312 scene.astcCount_ = 1;
313 return scene;
314 }
315
TryToReadAstcInfoFromJsonFile()316 void MediaLibraryAstcStat::TryToReadAstcInfoFromJsonFile()
317 {
318 if (FileUtils::IsFileExist(ASTC_JSON_FILE_PATH)) {
319 ReadAstcInfoFromJsonFile(phasesStat_, totalAstcCount_);
320 } else {
321 totalAstcCount_ = DfxDatabaseUtils::QueryASTCThumb(true) + DfxDatabaseUtils::QueryASTCThumb(false);
322 }
323 }
324
CheckId(const std::string & id)325 bool MediaLibraryAstcStat::CheckId(const std::string &id)
326 {
327 static std::list<std::string> idList;
328 constexpr int32_t maxSize = 2000;
329 constexpr int32_t removeCount = 100;
330 if (id == "") {
331 return false;
332 }
333 std::lock_guard<std::mutex> lock(mutex_);
334 if (std::find(idList.begin(), idList.end(), id) != idList.end()) {
335 MEDIA_INFO_LOG("have statted id %{public}s", id.c_str());
336 return true;
337 }
338 if (idList.size() >= maxSize) {
339 for (size_t i = 0; i < removeCount; ++i) {
340 if (idList.empty()) {
341 break;
342 }
343 idList.pop_front();
344 }
345 }
346 idList.push_back(id);
347 return false;
348 }
349
AddAstcInfo(int64_t startTime,GenerateScene genScene,AstcGenScene sceneKey,const std::string & id)350 void MediaLibraryAstcStat::AddAstcInfo(int64_t startTime, GenerateScene genScene, AstcGenScene sceneKey,
351 const std::string &id)
352 {
353 if (CheckId(id)) {
354 return;
355 }
356 int64_t endTime = MediaFileUtils::UTCTimeMilliSeconds();
357 AstcPhase phaseKey = GetAstcPhase(totalAstcCount_ + 1, genScene);
358 MEDIA_DEBUG_LOG("phaseKey %{public}d GenerateScene %{public}d sceneKey %{public}d", static_cast<int32_t>(phaseKey),
359 static_cast<int32_t>(genScene), static_cast<int32_t>(sceneKey));
360
361 PhaseStat phase;
362 phase.phase_ = phaseKey;
363 phase.scenes_[sceneKey] = GetScene(endTime - startTime, sceneKey);
364 phase.startTime_ = startTime;
365 phase.endTime_ = endTime;
366 std::lock_guard<std::mutex> lock(mutex_);
367 if (totalAstcCount_ == 0) {
368 TryToReadAstcInfoFromJsonFile();
369 }
370 totalAstcCount_++;
371 if (phasesStat_.phases_.count(phaseKey)) {
372 PhaseStat &phaseStat = phasesStat_.phases_[phaseKey];
373 phaseStat += phase;
374 } else {
375 phasesStat_.phases_[phaseKey] = phase;
376 }
377 int64_t currentTime = MediaFileUtils::UTCTimeSeconds();
378 constexpr int64_t oneHour = 3600;
379 if (currentTime - lastReportTime_ > oneHour) {
380 WriteAstcInfoToJsonFile(phasesStat_, totalAstcCount_);
381 lastReportTime_ = currentTime;
382 }
383 }
384
GetJson()385 std::string MediaLibraryAstcStat::GetJson()
386 {
387 std::lock_guard<std::mutex> lock(mutex_);
388 return GetJsonStr();
389 }
390
GetJsonStr()391 std::string MediaLibraryAstcStat::GetJsonStr()
392 {
393 nlohmann::json jsn;
394 ConvertToJson(jsn, phasesStat_, totalAstcCount_);
395 MEDIA_INFO_LOG("json %{public}s", jsn.dump().c_str());
396 return jsn.dump();
397 }
398
ClearOldData()399 void MediaLibraryAstcStat::ClearOldData()
400 {
401 std::lock_guard<std::mutex> lock(mutex_);
402 phasesStat_.phases_.clear();
403 totalAstcCount_ = 0;
404 if (FileUtils::IsFileExist(ASTC_JSON_FILE_PATH)) {
405 FileUtils::DeleteFile(ASTC_JSON_FILE_PATH);
406 }
407 }
408 } // namespace Media
409 } // namespace OHOS
410