1 /*
2  * Copyright (C) 2022-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 #include "usage_event_report.h"
16 
17 #include <sys/wait.h>
18 
19 #include "ffrt.h"
20 #include "hiview_event_report.h"
21 #include "hiview_event_cacher.h"
22 #ifdef POWER_MANAGER_ENABLE
23 #include "hiview_shutdown_callback.h"
24 #endif
25 #include "display_manager.h"
26 #include "sys_event.h"
27 #include "hiview_logger.h"
28 #include "plugin_factory.h"
29 #include "securec.h"
30 #ifdef POWER_MANAGER_ENABLE
31 #include "shutdown/shutdown_client.h"
32 #endif
33 #include "string_util.h"
34 #include "time_util.h"
35 #include "usage_event_cacher.h"
36 #include "usage_event_common.h"
37 
38 namespace OHOS {
39 namespace HiviewDFX {
40 REGISTER(UsageEventReport);
41 DEFINE_LOG_TAG("HiView-UsageEventReport");
42 uint64_t UsageEventReport::lastReportTime_ = 0;
43 uint64_t UsageEventReport::lastSysReportTime_ = 0;
44 uint64_t UsageEventReport::nextReportTime_ = 0;
45 std::string UsageEventReport::workPath_ = "";
46 namespace {
47 constexpr int TRIGGER_CYCLE = 5 * 60 * 1000 * 1000; // 5 min
48 constexpr uint32_t TRIGGER_ONE_HOUR = 12; // 1h = 5min * 12
49 }
50 using namespace OHOS::Rosen;
51 using namespace SysUsageDbSpace;
52 using namespace SysUsageEventSpace;
53 
UsageEventReport()54 UsageEventReport::UsageEventReport() : callback_(nullptr), timeOutCnt_(0), isRunning_(false)
55 {}
56 
OnLoad()57 void UsageEventReport::OnLoad()
58 {
59     HIVIEW_LOGI("start to init the env");
60     Init();
61     Start();
62 }
63 
OnEvent(std::shared_ptr<Event> & event)64 bool UsageEventReport::OnEvent(std::shared_ptr<Event>& event)
65 {
66     if (event == nullptr || event->messageType_ != Event::MessageType::SYS_EVENT) {
67         HIVIEW_LOGI("event is invalid");
68         return false;
69     }
70     if (foldEventCacher_ != nullptr) {
71         auto sysEvent = Event::DownCastTo<SysEvent>(event);
72         foldEventCacher_->ProcessEvent(sysEvent);
73     }
74     return true;
75 }
76 
OnUnload()77 void UsageEventReport::OnUnload()
78 {
79     HIVIEW_LOGI("start to clean up the env");
80     if (callback_ != nullptr) {
81 #ifdef POWER_MANAGER_ENABLE
82         PowerMgr::ShutdownClient::GetInstance().UnRegisterShutdownCallback(callback_);
83 #endif
84         callback_ = nullptr;
85     }
86     Stop();
87 }
88 
IsRunning()89 bool UsageEventReport::IsRunning()
90 {
91     std::lock_guard lock(runningMutex_);
92     return isRunning_;
93 }
94 
Init()95 void UsageEventReport::Init()
96 {
97     auto nowTime = TimeUtil::GetMilliseconds();
98     if (auto context = GetHiviewContext(); context != nullptr) {
99         workPath_ = context->GetHiViewDirectory(HiviewContext::DirectoryType::WORK_DIRECTORY);
100 
101         // get plugin stats event from db if any
102         UsageEventCacher cacher(workPath_);
103         std::vector<std::shared_ptr<LoggerEvent>> pluginStatEvents;
104         cacher.GetPluginStatsEvents(pluginStatEvents);
105         HiviewEventCacher::GetInstance().AddPluginStatsEvent(pluginStatEvents);
106         HIVIEW_LOGI("get plugin stats event=%{public}zu", pluginStatEvents.size());
107 
108         // get last report time from db if any
109         if (auto event = cacher.GetSysUsageEvent(LAST_SYS_USAGE_TABLE); event != nullptr) {
110             HIVIEW_LOGI("get cache sys usage event=%{public}s", event->ToJsonString().c_str());
111             lastReportTime_ = event->GetValue(KEY_OF_START).GetUint64();
112         } else {
113             lastReportTime_ = nowTime;
114         }
115 
116         // get last sys report time from db if any
117         if (auto event = cacher.GetSysUsageEvent(); event != nullptr) {
118             HIVIEW_LOGI("get last sys usage event=%{public}s", event->ToJsonString().c_str());
119             lastSysReportTime_ = event->GetValue(KEY_OF_START).GetUint64();
120         } else {
121             lastSysReportTime_ = nowTime;
122         }
123         BindWorkLoop(context->GetSharedWorkLoop());
124         InitFoldEventCacher(workPath_);
125     }
126     nextReportTime_ = static_cast<uint64_t>(TimeUtil::Get0ClockStampMs()) + TimeUtil::MILLISECS_PER_DAY;
127 
128     // more than one day since the last report
129     if (nowTime >= (lastReportTime_ + TimeUtil::MILLISECS_PER_DAY)) {
130         HIVIEW_LOGI("lastReportTime=%{public}" PRIu64 ", need to report daily event now", lastReportTime_);
131         ReportDailyEvent();
132     }
133 
134     // more than one hours since the shutdown time
135     if (nowTime >= (lastSysReportTime_ + 3600000)) { // 3600000ms: 1h
136         HIVIEW_LOGI("lastSysReportTime=%{public}" PRIu64 ", need to report sys usage event now", lastReportTime_);
137         ReportSysUsageEvent();
138     }
139 }
140 
InitFoldEventCacher(const std::string & workPath)141 void UsageEventReport::InitFoldEventCacher(const std::string& workPath)
142 {
143     if (!DisplayManager::GetInstance().IsFoldable()) {
144         HIVIEW_LOGI("unFoldable device");
145         return;
146     }
147     foldEventCacher_ = std::make_unique<FoldEventCacher>(workPath);
148     foldAppUsageFactory_ = std::make_unique<FoldAppUsageEventFactory>(workPath);
149 }
150 
InitCallback()151 void UsageEventReport::InitCallback()
152 {
153     HIVIEW_LOGI("start to init shutdown callback");
154 #ifdef POWER_MANAGER_ENABLE
155     callback_ = new (std::nothrow) HiViewShutdownCallback();
156     PowerMgr::ShutdownClient::GetInstance().RegisterShutdownCallback(callback_,
157         PowerMgr::ShutdownPriority::HIGH);
158 #endif
159 }
160 
Start()161 void UsageEventReport::Start()
162 {
163     {
164         std::lock_guard lock(runningMutex_);
165         isRunning_  = true;
166     }
167     RunTask();
168 }
169 
Stop()170 void UsageEventReport::Stop()
171 {
172     std::lock_guard lock(runningMutex_);
173     isRunning_ = false;
174 }
175 
RunTask()176 void UsageEventReport::RunTask()
177 {
178     auto task = bind(&UsageEventReport::TimeOut, this);
179     ffrt::submit(task, {}, {}, ffrt::task_attr().name("dft_ue_report").delay(TRIGGER_CYCLE));
180 }
181 
TimeOut()182 void UsageEventReport::TimeOut()
183 {
184     if (!IsRunning()) {
185         HIVIEW_LOGI("task exit");
186         return;
187     }
188 
189     HIVIEW_LOGD("start checking whether events need to be reported");
190     ReportTimeOutEvent();
191     ReportDailyEvent();
192 
193     // init shutdown callback if necessary
194     if (callback_ == nullptr) {
195         InitCallback();
196     }
197 
198     RunTask();
199     if (foldEventCacher_ != nullptr) {
200         foldEventCacher_->TimeOut();
201     }
202 }
203 
ReportDailyEvent()204 void UsageEventReport::ReportDailyEvent()
205 {
206     // check whether time step occurs. If yes, update the next report time
207     auto nowTime = TimeUtil::GetMilliseconds();
208     if (nowTime > (nextReportTime_ + TimeUtil::MILLISECS_PER_DAY)
209         || nowTime < (nextReportTime_ - TimeUtil::MILLISECS_PER_DAY)) {
210         HIVIEW_LOGW("start to update the next daily report time");
211         nextReportTime_ = static_cast<uint64_t>(TimeUtil::Get0ClockStampMs()) + TimeUtil::MILLISECS_PER_DAY;
212     } else if (nowTime >= nextReportTime_) {
213         // report plugin stats event
214         HIVIEW_LOGI("start to report daily event");
215         HiviewEventReport::ReportPluginStats();
216         DeletePluginStatsEvents();
217 
218         // report app usage event
219         StartServiceByOption("-A");
220         ReportFoldAppUsageEvent();
221 
222         // update report time
223         lastReportTime_ = TimeUtil::GetMilliseconds();
224         nextReportTime_ += TimeUtil::MILLISECS_PER_DAY;
225     }
226 }
227 
ReportTimeOutEvent()228 void UsageEventReport::ReportTimeOutEvent()
229 {
230     ++timeOutCnt_;
231     SaveSysUsageEvent();
232     if (timeOutCnt_ >= TRIGGER_ONE_HOUR) {
233         ReportSysUsageEvent();
234         timeOutCnt_ = 0;
235     }
236 }
237 
ReportSysUsageEvent()238 void UsageEventReport::ReportSysUsageEvent()
239 {
240     StartServiceByOption("-S");
241     lastSysReportTime_ = TimeUtil::GetMilliseconds();
242 }
243 
ReportFoldAppUsageEvent()244 void UsageEventReport::ReportFoldAppUsageEvent()
245 {
246     if (foldAppUsageFactory_ == nullptr) {
247         HIVIEW_LOGI("foldAppUsageFactory is nullptr");
248         return;
249     }
250     std::vector<std::unique_ptr<LoggerEvent>> foldAppUsageEvents;
251     foldAppUsageFactory_->Create(foldAppUsageEvents);
252     HIVIEW_LOGI("report fold app usage event num: %{public}zu", foldAppUsageEvents.size());
253     for (size_t i = 0; i < foldAppUsageEvents.size(); ++i) {
254         foldAppUsageEvents[i]->Report();
255     }
256 }
257 
SaveEventToDb()258 void UsageEventReport::SaveEventToDb()
259 {
260     HIVIEW_LOGI("start to save the event to db");
261     SavePluginStatsEvents();
262     SaveSysUsageEvent();
263 }
264 
SavePluginStatsEvents()265 void UsageEventReport::SavePluginStatsEvents()
266 {
267     std::vector<std::shared_ptr<LoggerEvent>> events;
268     HiviewEventCacher::GetInstance().GetPluginStatsEvents(events);
269     if (events.empty()) {
270         return;
271     }
272     UsageEventCacher cacher(workPath_);
273     cacher.SavePluginStatsEventsToDb(events);
274 }
275 
DeletePluginStatsEvents()276 void UsageEventReport::DeletePluginStatsEvents()
277 {
278     UsageEventCacher cacher(workPath_);
279     cacher.DeletePluginStatsEventsFromDb();
280 }
281 
SaveSysUsageEvent()282 void UsageEventReport::SaveSysUsageEvent()
283 {
284     StartServiceByOption("-s");
285 }
286 
StartServiceByOption(const std::string & opt)287 void UsageEventReport::StartServiceByOption(const std::string& opt)
288 {
289     HIVIEW_LOGI("start service, opt=%{public}s", opt.c_str());
290     if (pid_t pid = fork(); pid < 0) {
291         HIVIEW_LOGE("failed to fork child process");
292         return;
293     } else if (pid == 0) {
294         const size_t len = 20; // 20: max_len(uint64_t) + '\0'
295         char lastRTBuf[len] = {0};
296         if (sprintf_s(lastRTBuf, len, "%" PRIu64, lastReportTime_) < 0) {
297             HIVIEW_LOGE("failed to convert lastReportTime_=%{public}" PRIu64 " to string", lastReportTime_);
298             _exit(-1);
299         }
300         char lastSRTBuf[len] = {0};
301         if (sprintf_s(lastSRTBuf, len, "%" PRIu64, lastSysReportTime_) < 0) {
302             HIVIEW_LOGE("failed to convert lastSysReportTime_=%{public}" PRIu64 " to string", lastSysReportTime_);
303             _exit(-1);
304         }
305         const std::string serviceName = "usage_report";
306         const std::string servicePath = "/system/bin/usage_report";
307         if (execl(servicePath.c_str(), serviceName.c_str(),
308             "-p", workPath_.c_str(),
309             "-t", lastRTBuf,
310             "-T", lastSRTBuf,
311             opt.c_str(), nullptr) < 0) {
312             HIVIEW_LOGE("failed to execute %{public}s", serviceName.c_str());
313             _exit(-1);
314         }
315     } else {
316         if (waitpid(pid, nullptr, 0) != pid) {
317             HIVIEW_LOGE("failed to waitpid, pid=%{public}d, err=%{public}d", pid, errno);
318         } else {
319             HIVIEW_LOGI("succ to waitpid, pid=%{public}d", pid);
320         }
321     }
322 }
323 }  // namespace HiviewDFX
324 }  // namespace OHOS
325