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 #define MLOG_TAG "DfxWorker"
16 
17 #include "dfx_worker.h"
18 
19 #include <pthread.h>
20 
21 #include "media_file_utils.h"
22 #include "media_log.h"
23 #include "dfx_manager.h"
24 #include "preferences.h"
25 #include "preferences_helper.h"
26 #include "parameters.h"
27 
28 using namespace std;
29 namespace OHOS {
30 namespace Media {
31 shared_ptr<DfxWorker> DfxWorker::dfxWorkerInstance_{nullptr};
32 
GetInstance()33 shared_ptr<DfxWorker> DfxWorker::GetInstance()
34 {
35     if (dfxWorkerInstance_ == nullptr) {
36         dfxWorkerInstance_ = make_shared<DfxWorker>();
37     }
38     return dfxWorkerInstance_;
39 }
40 
DfxWorker()41 DfxWorker::DfxWorker() : isThreadRunning_(false)
42 {
43 }
44 
~DfxWorker()45 DfxWorker::~DfxWorker()
46 {
47     MEDIA_INFO_LOG("DfxWorker deconstructor");
48     isThreadRunning_ = false;
49     workCv_.notify_all();
50     if (delayThread_.joinable()) {
51         delayThread_.join();
52     }
53     dfxWorkerInstance_ = nullptr;
54 }
55 
Init()56 void DfxWorker::Init()
57 {
58     MEDIA_INFO_LOG("init");
59     isThreadRunning_ = true;
60     delayThread_ = thread([this] { this->InitDelayThread(); });
61 }
62 
HandleLoopTask(DfxData * data)63 static void HandleLoopTask(DfxData *data)
64 {
65     MEDIA_DEBUG_LOG("HandleLoopTask");
66     int32_t errCode;
67     shared_ptr<NativePreferences::Preferences> prefs =
68        NativePreferences::PreferencesHelper::GetPreferences(DFX_COMMON_XML, errCode);
69     if (!prefs) {
70         MEDIA_ERR_LOG("get preferences error: %{public}d", errCode);
71         return;
72     }
73     int64_t lastReportTime = prefs->GetLong(LAST_REPORT_TIME, 0);
74     int64_t lastMiddleReportTime = prefs->GetLong(LAST_MIDDLE_REPORT_TIME, 0);
75     DfxManager::GetInstance()->HandleFiveMinuteTask();
76     if (MediaFileUtils::UTCTimeSeconds() - lastMiddleReportTime >= SIX_HOUR) {
77         MEDIA_INFO_LOG("Report Middle Xml");
78         lastMiddleReportTime = DfxManager::GetInstance()->HandleMiddleReport();
79         prefs->PutLong(LAST_MIDDLE_REPORT_TIME, lastMiddleReportTime);
80         prefs->FlushSync();
81     }
82     if (MediaFileUtils::UTCTimeSeconds() - lastReportTime >= ONE_DAY) {
83         MEDIA_INFO_LOG("Report one day Xml");
84         lastReportTime = DfxManager::GetInstance()->HandleOneDayReport();
85         prefs->PutLong(LAST_REPORT_TIME, lastReportTime);
86         prefs->FlushSync();
87     }
88 }
89 
Prepare()90 void DfxWorker::Prepare()
91 {
92     int32_t errCode;
93     shared_ptr<NativePreferences::Preferences> prefs =
94         NativePreferences::PreferencesHelper::GetPreferences(DFX_COMMON_XML, errCode);
95     if (!prefs) {
96         MEDIA_ERR_LOG("get preferences error: %{public}d", errCode);
97         return;
98     }
99     thumbnailVersion_ = prefs->GetInt(THUMBNAIL_ERROR_VERSION, 0);
100     deleteStatisticVersion_ = prefs->GetInt(DELETE_STATISTIC_VERSION, 0);
101     if (IsThumbnailUpdate()) {
102         thumbnailVersion_ = LATEST_THUMBNAIL_ERROR_VERSION;
103         prefs->PutInt(THUMBNAIL_ERROR_VERSION, LATEST_THUMBNAIL_ERROR_VERSION);
104     }
105     if (IsDeleteStatisticUpdate()) {
106         deleteStatisticVersion_ = LATEST_DELETE_STATISTIC_VERSION;
107         prefs->PutInt(DELETE_STATISTIC_VERSION, LATEST_DELETE_STATISTIC_VERSION);
108     }
109     prefs->FlushSync();
110 }
111 
IsThumbnailUpdate()112 bool DfxWorker::IsThumbnailUpdate()
113 {
114     if (thumbnailVersion_ < LATEST_THUMBNAIL_ERROR_VERSION) {
115         MEDIA_INFO_LOG("update thumbnail version from %{public}d to %{public}d", thumbnailVersion_,
116             LATEST_THUMBNAIL_ERROR_VERSION);
117         int32_t errCode;
118         shared_ptr<NativePreferences::Preferences> prefs =
119             NativePreferences::PreferencesHelper::GetPreferences(THUMBNAIL_ERROR_XML, errCode);
120         if (!prefs) {
121             MEDIA_ERR_LOG("get preferences error: %{public}d", errCode);
122             return false;
123         }
124         prefs->Clear();
125         prefs->FlushSync();
126         return true;
127     }
128     return false;
129 }
130 
IsDeleteStatisticUpdate()131 bool DfxWorker::IsDeleteStatisticUpdate()
132 {
133     if (deleteStatisticVersion_ < LATEST_DELETE_STATISTIC_VERSION) {
134         MEDIA_INFO_LOG("update delete statistic version from %{public}d to %{public}d", deleteStatisticVersion_,
135             LATEST_DELETE_STATISTIC_VERSION);
136         int32_t errCode;
137         shared_ptr<NativePreferences::Preferences> prefs =
138             NativePreferences::PreferencesHelper::GetPreferences(DELETE_BEHAVIOR_XML, errCode);
139         if (!prefs) {
140             MEDIA_ERR_LOG("get preferences error: %{public}d", errCode);
141             return false;
142         }
143         prefs->Clear();
144         prefs->FlushSync();
145         return true;
146     }
147     return false;
148 }
149 
InitDelayThread()150 void DfxWorker::InitDelayThread()
151 {
152     Prepare();
153     bool isStartLoopTask = true;
154     MEDIA_INFO_LOG("InitDelayThread");
155     string name("DfxDelayThread");
156     pthread_setname_np(pthread_self(), name.c_str());
157     while (isThreadRunning_) {
158         if (isStartLoopTask) {
159             HandleLoopTask(nullptr);
160             StartLoopTaskDelay();
161             isStartLoopTask = false;
162         }
163         WaitForTask();
164         if (!isThreadRunning_) {
165             break;
166         }
167         if (IsTaskQueueEmpty()) {
168             continue;
169         }
170         auto now = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
171         auto executeTime = std::chrono::time_point_cast<std::chrono::milliseconds>(GetWaitTime());
172         auto delay = now.time_since_epoch().count() - executeTime.time_since_epoch().count();
173         if (delay < 0) {
174             continue;
175         }
176         shared_ptr<DfxTask> task = GetTask();
177         if (task == nullptr) {
178             continue;
179         }
180         task->executor_(task->data_);
181         if (task->isDelayTask_) {
182             StartLoopTaskDelay();
183         }
184         task = nullptr;
185     }
186 }
187 
StartLoopTaskDelay()188 void DfxWorker::StartLoopTaskDelay()
189 {
190     auto loopTask = make_shared<DfxTask>(HandleLoopTask, nullptr);
191     AddTask(loopTask, FIVE_MINUTE);
192 }
193 
compare(const shared_ptr<DfxTask> & taskOne,const shared_ptr<DfxTask> & taskTwo)194 static bool compare(const shared_ptr<DfxTask> &taskOne, const shared_ptr<DfxTask> &taskTwo)
195 {
196     auto firstTime = std::chrono::time_point_cast<std::chrono::milliseconds>(taskOne->executeTime_);
197     auto secondTime = std::chrono::time_point_cast<std::chrono::milliseconds>(taskTwo->executeTime_);
198     return firstTime.time_since_epoch().count() > secondTime.time_since_epoch().count();
199 }
200 
AddTask(const shared_ptr<DfxTask> & task,int64_t delayTime)201 void DfxWorker::AddTask(const shared_ptr<DfxTask> &task, int64_t delayTime)
202 {
203     lock_guard<mutex> lockGuard(taskLock_);
204     if (delayTime > 0) {
205         task->executeTime_ = std::chrono::system_clock::now() + std::chrono::milliseconds(delayTime);
206         task->isDelayTask_ = true;
207     }
208     taskList_.push_back(task);
209     sort(taskList_.begin(), taskList_.end(), compare);
210     workCv_.notify_one();
211 }
212 
IsTaskQueueEmpty()213 bool DfxWorker::IsTaskQueueEmpty()
214 {
215     lock_guard<mutex> lock_Guard(taskLock_);
216     return taskList_.empty();
217 }
218 
WaitForTask()219 void DfxWorker::WaitForTask()
220 {
221     std::unique_lock<std::mutex> lock(workLock_);
222     if (IsTaskQueueEmpty()) {
223         workCv_.wait(lock,
224             [this]() { return !isThreadRunning_ || !IsTaskQueueEmpty(); });
225     } else {
226         workCv_.wait_until(lock, GetWaitTime(),
227             [this]() { return !isThreadRunning_ || !IsDelayTask(); });
228     }
229 }
230 
GetTask()231 shared_ptr<DfxTask> DfxWorker::GetTask()
232 {
233     lock_guard<mutex> lockGuard(taskLock_);
234     if (taskList_.empty()) {
235         return nullptr;
236     }
237     shared_ptr<DfxTask> task = taskList_.back();
238     taskList_.pop_back();
239     return task;
240 }
241 
242 
IsDelayTask()243 bool DfxWorker::IsDelayTask()
244 {
245     lock_guard<mutex> lockGuard(taskLock_);
246     shared_ptr<DfxTask> task = taskList_.back();
247     return task->isDelayTask_;
248 }
249 
GetWaitTime()250 std::chrono::system_clock::time_point DfxWorker::GetWaitTime()
251 {
252     lock_guard<mutex> lockGuard(taskLock_);
253     shared_ptr<DfxTask> task = taskList_.back();
254     return task->executeTime_;
255 }
256 
End()257 void DfxWorker::End()
258 {
259     isEnd_ = true;
260 }
261 } // namespace Media
262 } // namespace OHOS