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 
16 #include "AppLaunchSceneDataProcessor.h"
17 #include "AppLaunchConverter.h"
18 
19 namespace OHOS {
20 namespace HiviewDFX {
21 static const std::string EVENT_INTERACTION_RESPONSE_LATENCY = "INTERACTION_RESPONSE_LATENCY";
22 static const std::string EVENT_START_ABILITY = "START_ABILITY";
23 static const std::string EVENT_APP_STARTUP_TYPE = "APP_STARTUP_TYPE";
24 static const std::string EVENT_PROCESS_START = "PROCESS_START";
25 static const std::string EVENT_APP_ATTACH = "APP_ATTACH";
26 static const std::string EVENT_APP_FOREGROUND = "APP_FOREGROUND";
27 static const std::string EVENT_ABILITY_ONFOREGROUND = "ABILITY_ONFOREGROUND";
28 static const std::string EVENT_START_WINDOW = "START_WINDOW";
29 static const std::string EVENT_DRAWN_COMPLETED = "DRAWN_COMPLETED";
30 static const std::string EVENT_FIRST_FRAME_DRAWN = "FIRST_FRAME_DRAWN";
31 static const std::string EVENT_INTERACTION_COMPLETED_LATENCY = "INTERACTION_COMPLETED_LATENCY";
32 
33 static const std::string SCENE_LAUNCHER_APP_LAUNCH_FROM_ICON = "LAUNCHER_APP_LAUNCH_FROM_ICON";
34 static const std::string SCENE_LAUNCHER_APP_LAUNCH_FROM_NOTIFICATIONBAR = "LAUNCHER_APP_LAUNCH_FROM_NOTIFICATIONBAR";
35 static const std::string SCENE_LAUNCHER_APP_LAUNCH_FROM_DOCK = "LAUNCHER_APP_LAUNCH_FROM_DOCK";
36 static const std::string SCENE_LAUNCHER_APP_LAUNCH_FROM_APPCENTER = "LAUNCHER_APP_LAUNCH_FROM_APPCENTER";
37 static const std::string SCENE_LAUNCHER_APP_LAUNCH_FROM_RECENT = "LAUNCHER_APP_LAUNCH_FROM_RECENT";
38 
AppLaunchSceneDataProcessor(IAppLaunchSceneDb* db, ITimeoutExecutor* exec, IAppLaunchSceneDataProcessor::MetricReporter* metricReporter, IAppTimer* timer)39 AppLaunchSceneDataProcessor::AppLaunchSceneDataProcessor(IAppLaunchSceneDb* db, ITimeoutExecutor* exec,
40                                                          IAppLaunchSceneDataProcessor::MetricReporter* metricReporter,
41                                                          IAppTimer* timer)
42 {
43     this->db = db;
44     this->exec = exec;
45     this->metricReporter = metricReporter;
46     this->timer = timer;
47 }
48 
AppLaunchSceneDataProcessor(IAppLaunchSceneDb* db, ITimeoutExecutor* exec, IAppTimer* timer)49 AppLaunchSceneDataProcessor::AppLaunchSceneDataProcessor(IAppLaunchSceneDb* db, ITimeoutExecutor* exec,
50                                                          IAppTimer* timer)
51 {
52     this->db = db;
53     this->exec = exec;
54     this->timer = timer;
55 }
56 
GetBundleName(const AppStartCheckPointData& data)57 std::string AppLaunchSceneDataProcessor::GetBundleName(const AppStartCheckPointData& data)
58 {
59     std::string bundleName = data.bundleName;
60     if (data.eventName == EVENT_FIRST_FRAME_DRAWN) {
61         if (pidBundleMap.find(data.appPid) != pidBundleMap.end()) {
62             bundleName = pidBundleMap[data.appPid];
63         }
64     } else if ((data.eventName == EVENT_INTERACTION_RESPONSE_LATENCY) ||
65                (data.eventName == EVENT_INTERACTION_COMPLETED_LATENCY)) {
66         bundleName = data.note;
67     }
68     return bundleName;
69 }
70 
ProcessSceneData(const AppStartCheckPointData& data)71 void AppLaunchSceneDataProcessor::ProcessSceneData(const AppStartCheckPointData& data)
72 {
73     if (!CheckValidCheckPoint(data)) {
74         return;
75     }
76     ValidateDuplication(data);
77     std::string bundleName = GetBundleName(data);
78     if (bundleName.empty()) {
79         return;
80     }
81     AppStartRecord record = GetRecord(bundleName);
82     if (IsStartPoint(data)) {
83         CheckOutExistStartPoint(data);
84         CreateRecord(bundleName, data);
85         StartTimer(bundleName);
86         return;
87     }
88     // Filter out non-user-triggered application startup. This event does not have the start ability.
89     if (record.bundleName.empty()) {
90         return;
91     }
92     SaveCheckPoint(record, data);
93     if (AllPointsReceived(bundleName)) {
94         StopTimer(bundleName);
95         AppStartMetrics metrics = CalcMetrics(bundleName);
96         Report(metrics);
97         DeleteRecord(bundleName);
98         ClearMapByBundleName(bundleName);
99     }
100 }
101 
Expired(std::string bundleName)102 void AppLaunchSceneDataProcessor::Expired(std::string bundleName)
103 {
104     exec->ExecuteTimeoutInMainThr(this, bundleName);
105 }
106 
HandleTimeoutInMainThr(std::string name)107 void AppLaunchSceneDataProcessor::HandleTimeoutInMainThr(std::string name)
108 {
109     if (ReportConditionMet(name)) {
110         AppStartMetrics metrics = CalcMetrics(name);
111         Report(metrics);
112     }
113     DeleteRecord(name);
114     ClearMapByBundleName(name);
115 }
116 
CheckValidCheckPoint(const AppStartCheckPointData& data)117 bool AppLaunchSceneDataProcessor::CheckValidCheckPoint(const AppStartCheckPointData& data)
118 {
119     if (data.domain.empty() || data.eventName.empty()) {
120         return false;
121     }
122     bool checkBundle;
123     if (data.eventName == EVENT_FIRST_FRAME_DRAWN) {
124         checkBundle = (data.appPid > 0);
125     } else {
126         checkBundle = !data.bundleName.empty();
127     }
128     bool checkSceneId;
129     if (!data.sceneId.empty()) {
130         checkSceneId = ((data.sceneId == SCENE_LAUNCHER_APP_LAUNCH_FROM_ICON)
131             || (data.sceneId == SCENE_LAUNCHER_APP_LAUNCH_FROM_NOTIFICATIONBAR)
132             || (data.sceneId == SCENE_LAUNCHER_APP_LAUNCH_FROM_DOCK)
133             || (data.sceneId == SCENE_LAUNCHER_APP_LAUNCH_FROM_RECENT)
134             || (data.sceneId == SCENE_LAUNCHER_APP_LAUNCH_FROM_APPCENTER));
135     } else {
136         checkSceneId = true;
137     }
138 
139     return (checkBundle && checkSceneId);
140 }
141 
ValidateDuplication(const AppStartCheckPointData& data)142 void AppLaunchSceneDataProcessor::ValidateDuplication(const AppStartCheckPointData& data)
143 {
144     // if duplicate, throw a logic error
145 }
146 
SaveCheckPoint(const std::string& bundleName, const AppStartCheckPointData& data)147 void AppLaunchSceneDataProcessor::SaveCheckPoint(const std::string& bundleName, const AppStartCheckPointData& data)
148 {
149     AppStartRecord record = GetRecord(bundleName);
150     if (record.bundleName.empty()) {
151         return;
152     }
153     SaveCheckPoint(record, data);
154 }
155 
SaveCheckPoint(AppStartRecord& record, const AppStartCheckPointData& data)156 void AppLaunchSceneDataProcessor::SaveCheckPoint(AppStartRecord& record, const AppStartCheckPointData& data)
157 {
158     if (data.eventName == EVENT_INTERACTION_RESPONSE_LATENCY) {
159         InteractionResponse interactionResponse = AppLaunchConverter::ConvertToInteractionResponse(data);
160         record.interactionResponse = interactionResponse;
161     } else if (data.eventName == EVENT_START_ABILITY) {
162         StartAbility startAbility = AppLaunchConverter::ConvertToStartAbility(data);
163         record.startAbility = startAbility;
164     } else if (data.eventName == EVENT_APP_STARTUP_TYPE) {
165         if (!record.appStartupType.bundleName.empty()) {
166             return;
167         }
168         record.appStartupType = AppLaunchConverter::ConvertToAppStartupType(data);
169         SaveAppIdIntoMap(record.appStartupType.appPid, record.appStartupType.bundleName);
170         record.appPid = record.appStartupType.appPid;
171     } else if (data.eventName == EVENT_PROCESS_START) {
172         ProcessStart processStart = AppLaunchConverter::ConvertToProcessStart(data);
173         record.processStart = processStart;
174     } else if (data.eventName == EVENT_APP_ATTACH) {
175         record.appAttach = AppLaunchConverter::ConvertToAppAttach(data);
176         SaveAppIdIntoMap(record.appAttach.appPid, record.appAttach.bundleName);
177         record.appPid = record.appAttach.appPid;
178     } else if (data.eventName == EVENT_APP_FOREGROUND) {
179         record.appForeground = AppLaunchConverter::ConvertToAppForeground(data);
180         SaveAppIdIntoMap(record.appForeground.appPid, record.appForeground.bundleName);
181         record.appPid = record.appForeground.appPid;
182     } else if (data.eventName == EVENT_ABILITY_ONFOREGROUND) {
183         AbilityForeground abilityForeground = AppLaunchConverter::ConvertToAbilityForeground(data);
184         record.abilityForeground = abilityForeground;
185     } else if (data.eventName == EVENT_START_WINDOW) {
186         record.startWindow = AppLaunchConverter::ConvertToStartWindow(data);
187         SaveAppIdIntoMap(record.startWindow.appPid, record.startWindow.bundleName);
188         record.appPid = record.startWindow.appPid;
189     } else if (data.eventName == EVENT_DRAWN_COMPLETED) {
190         record.drawnCompleted = AppLaunchConverter::ConvertToDrawnCompleted(data);
191         SaveAppIdIntoMap(record.drawnCompleted.appPid, record.drawnCompleted.bundleName);
192         record.appPid = record.drawnCompleted.appPid;
193     } else if (data.eventName == EVENT_FIRST_FRAME_DRAWN) {
194         FirstFrameDrawn firstFrameDrawn = AppLaunchConverter::ConvertToFirstFrameDrawn(data);
195         record.firstFrameDrawn = firstFrameDrawn;
196     } else if (data.eventName == EVENT_INTERACTION_COMPLETED_LATENCY) {
197         InteractionCompleted interactionCompleted = AppLaunchConverter::ConvertToInteractionCompleted(data);
198         record.interactionCompleted = interactionCompleted;
199     } else {
200         return;
201     }
202     db->UpdateRecord(record);
203 }
204 
IsStartPoint(const AppStartCheckPointData& data)205 bool AppLaunchSceneDataProcessor::IsStartPoint(const AppStartCheckPointData& data)
206 {
207     bool isStartAbilityPoint = ((data.domain == "AAFWK") && (data.eventName == EVENT_START_ABILITY));
208     return isStartAbilityPoint;
209 }
210 
HaveStartPoint(const AppStartRecord& record)211 bool AppLaunchSceneDataProcessor::HaveStartPoint(const AppStartRecord& record)
212 {
213     return (!record.interactionResponse.bundleName.empty() || !record.startAbility.bundleName.empty());
214 }
215 
HaveAllEndPoints(const AppStartRecord& record)216 bool AppLaunchSceneDataProcessor::HaveAllEndPoints(const AppStartRecord& record)
217 {
218     return (record.firstFrameDrawn.appPid > 0) && (!record.interactionCompleted.bundleName.empty());
219 }
220 
HaveEndPoint(const AppStartRecord& record)221 bool AppLaunchSceneDataProcessor::HaveEndPoint(const AppStartRecord& record)
222 {
223     return (record.firstFrameDrawn.appPid > 0) || (!record.interactionCompleted.bundleName.empty());
224 }
225 
HaveResponseOrCompletedPoint(const AppStartRecord& record)226 bool AppLaunchSceneDataProcessor::HaveResponseOrCompletedPoint(const AppStartRecord& record)
227 {
228     return (!record.interactionResponse.bundleName.empty() || !record.interactionCompleted.bundleName.empty());
229 }
230 
AllPointsReceived(const std::string& bundleName)231 bool AppLaunchSceneDataProcessor::AllPointsReceived(const std::string& bundleName)
232 {
233     AppStartRecord record = GetRecord(bundleName);
234     bool hasTypePoint = !record.appStartupType.bundleName.empty();
235     bool hasDrawnCompletedPoint = !record.drawnCompleted.bundleName.empty();
236     return (HaveStartPoint(record) && hasTypePoint && hasDrawnCompletedPoint && HaveAllEndPoints(record));
237 }
238 
239 
GetProcessName(const AppStartRecord& record)240 std::string AppLaunchSceneDataProcessor::GetProcessName(const AppStartRecord& record)
241 {
242     if (!record.appAttach.processName.empty()) {
243         return record.appAttach.processName;
244     }
245     if (!record.appForeground.processName.empty()) {
246         return record.appForeground.processName;
247     }
248     if (!record.startWindow.processName.empty()) {
249         return record.startWindow.processName;
250     }
251     return "";
252 }
253 
GetAppPid(const AppStartRecord& record)254 int32_t AppLaunchSceneDataProcessor::GetAppPid(const AppStartRecord& record)
255 {
256     if (record.drawnCompleted.appPid > 0) {
257         return record.drawnCompleted.appPid;
258     }
259     if (record.firstFrameDrawn.appPid > 0) {
260         return record.firstFrameDrawn.appPid;
261     }
262     return 0;
263 }
264 
GetInputTime(const AppStartRecord& record)265 uint64_t AppLaunchSceneDataProcessor::GetInputTime(const AppStartRecord& record)
266 {
267     if (!record.interactionCompleted.bundleName.empty()) {
268         return record.interactionCompleted.inputTime;
269     } else {
270         return record.interactionResponse.inputTime;
271     }
272 }
273 
CalcMetrics(const std::string& bundleName)274 AppStartMetrics AppLaunchSceneDataProcessor::CalcMetrics(const std::string& bundleName)
275 {
276     AppStartRecord record = GetRecord(bundleName);
277     AppStartMetrics appStartMetrics;
278     appStartMetrics.appPid = GetAppPid(record);
279     appStartMetrics.versionCode = record.appStartupType.versionCode;
280     appStartMetrics.versionName = record.appStartupType.versionName;
281     appStartMetrics.processName = GetProcessName(record);
282     appStartMetrics.bundleName = record.appStartupType.bundleName;
283     appStartMetrics.abilityName = record.appStartupType.abilityName;
284     appStartMetrics.startType = record.appStartupType.startType;
285     appStartMetrics.happenTime = record.startAbility.time;
286     appStartMetrics.responseLatency = (!record.interactionResponse.note.empty()) ?
287         record.interactionResponse.responseLatency : record.interactionCompleted.animationStartLatency;
288     uint64_t inputTime = GetInputTime(record);
289     if (!record.startAbility.bundleName.empty() && (inputTime > 0)) {
290         appStartMetrics.launcherToAmsStartAbilityDur = (record.startAbility.time - inputTime);
291     }
292     if ((!record.startAbility.bundleName.empty()) && (!record.processStart.bundleName.empty())) {
293         appStartMetrics.amsStartAbilityToProcessStartDuration = (record.processStart.time - record.startAbility.time);
294     }
295     if ((!record.processStart.bundleName.empty()) && (!record.appAttach.bundleName.empty())) {
296         appStartMetrics.amsProcessStartToAppAttachDuration = (record.appAttach.time - record.processStart.time);
297     }
298     if ((!record.appAttach.bundleName.empty()) && (!record.appForeground.bundleName.empty())) {
299         appStartMetrics.amsAppAttachToAppForegroundDuration = (record.appForeground.time - record.appAttach.time);
300     }
301     if ((!record.startAbility.bundleName.empty()) && (!record.appForeground.bundleName.empty())) {
302         appStartMetrics.amsStartAbilityToAppForegroundDuration = (record.appForeground.time - record.startAbility.time);
303     }
304     if ((!record.appForeground.bundleName.empty()) && (!record.abilityForeground.bundleName.empty())) {
305         appStartMetrics.amsAppFgToAbilityFgDur = (record.abilityForeground.time - record.appForeground.time);
306     }
307     if ((!record.abilityForeground.bundleName.empty()) && (!record.startWindow.bundleName.empty())) {
308         appStartMetrics.amsAbilityFgToWmsStartWinDur = (record.abilityForeground.time - record.startWindow.time);
309     }
310     if (!record.drawnCompleted.bundleName.empty()  && (inputTime > 0)) {
311         appStartMetrics.drawnLatency = (record.drawnCompleted.time - inputTime);
312     }
313     CalcLatency(appStartMetrics, record, inputTime);
314     return appStartMetrics;
315 }
316 
CalcLatency(AppStartMetrics& appStartMetrics, const AppStartRecord& record, const uint64_t inputTime)317 void AppLaunchSceneDataProcessor::CalcLatency(AppStartMetrics& appStartMetrics, const AppStartRecord& record,
318                                               const uint64_t inputTime)
319 {
320     uint64_t e2eLatency = 0;
321     if ((record.firstFrameDrawn.appPid > 0)  && (inputTime > 0)) {
322         appStartMetrics.firstFrameDrawnLatency = (record.firstFrameDrawn.time - inputTime);
323         if (appStartMetrics.firstFrameDrawnLatency > e2eLatency) {
324             e2eLatency = appStartMetrics.firstFrameDrawnLatency;
325         }
326     }
327     if (!record.interactionCompleted.bundleName.empty()) {
328         appStartMetrics.sceneId = record.interactionCompleted.sceneId;
329         appStartMetrics.sourceType = record.interactionCompleted.sourceType;
330         appStartMetrics.inputTime = record.interactionCompleted.inputTime;
331         appStartMetrics.pageUrl = record.interactionCompleted.pageUrl;
332         appStartMetrics.animationEndLatency = record.interactionCompleted.animationEndLatency;
333         appStartMetrics.animationLatency = record.interactionCompleted.e2eLatency;
334         uint64_t latency = (record.interactionCompleted.time - inputTime);
335         if (latency > e2eLatency) {
336             e2eLatency = latency;
337         }
338     } else if (!record.interactionResponse.bundleName.empty()) {
339         appStartMetrics.sceneId = record.interactionResponse.sceneId;
340         appStartMetrics.sourceType = record.interactionResponse.sourceType;
341         appStartMetrics.inputTime = record.interactionResponse.inputTime;
342         appStartMetrics.pageUrl = record.interactionResponse.pageUrl;
343     }
344     appStartMetrics.e2eLatency = e2eLatency;
345 }
346 
Report(const AppStartMetrics& metrics)347 void AppLaunchSceneDataProcessor::Report(const AppStartMetrics& metrics)
348 {
349     metricReporter->ReportMetrics(metrics);
350 }
351 
ReportConditionMet(const std::string& bundleName)352 bool AppLaunchSceneDataProcessor::ReportConditionMet(const std::string& bundleName)
353 {
354     AppStartRecord record = GetRecord(bundleName);
355     bool hasTypePoint = !record.appStartupType.bundleName.empty();
356     return (HaveStartPoint(record) && hasTypePoint && HaveEndPoint(record) && HaveResponseOrCompletedPoint(record));
357 }
358 
GetRecord(const std::string& bundleName)359 AppStartRecord AppLaunchSceneDataProcessor::GetRecord(const std::string& bundleName)
360 {
361     return db->QueryRecord(bundleName);
362 }
363 
DeleteRecord(const std::string& bundleName)364 void AppLaunchSceneDataProcessor::DeleteRecord(const std::string& bundleName)
365 {
366     db->DeleteRecord(bundleName);
367 }
368 
CreateRecord(const AppStartCheckPointData& data)369 void AppLaunchSceneDataProcessor::CreateRecord(const AppStartCheckPointData& data)
370 {
371     CreateRecord(data.bundleName, data);
372 }
373 
CreateRecord(const std::string& bundleName, const AppStartCheckPointData& data)374 void AppLaunchSceneDataProcessor::CreateRecord(const std::string& bundleName, const AppStartCheckPointData& data)
375 {
376     AppStartRecord record;
377     record.bundleName = bundleName;
378     if (data.eventName == EVENT_INTERACTION_RESPONSE_LATENCY) {
379         InteractionResponse interactionResponse = AppLaunchConverter::ConvertToInteractionResponse(data);
380         record.interactionResponse = interactionResponse;
381     } else if (data.eventName == EVENT_START_ABILITY) {
382         IAppLaunchSceneDb::StartAbility startAbility = AppLaunchConverter::ConvertToStartAbility(data);
383         record.startAbility = startAbility;
384     } else {
385         return;
386     }
387     db->CreateRecord(record);
388 }
389 
StartTimer(std::string bundle)390 void AppLaunchSceneDataProcessor::StartTimer(std::string bundle)
391 {
392     try {
393         timer->Start(bundle);
394     } catch (const std::invalid_argument& ex) {
395         throw std::logic_error("AppLaunchSceneDataProcessor error " + std::string(ex.what()));
396     }
397 }
398 
StopTimer(std::string bundle)399 void AppLaunchSceneDataProcessor::StopTimer(std::string bundle)
400 {
401     try {
402         timer->Stop(bundle);
403     } catch (const std::invalid_argument& ex) {
404         throw std::logic_error("AppLaunchSceneDataProcessor error " + std::string(ex.what()));
405     }
406 }
407 
SetMetricReporter(MetricReporter* metricReporter)408 void AppLaunchSceneDataProcessor::SetMetricReporter(MetricReporter* metricReporter)
409 {
410     this->metricReporter = metricReporter;
411 }
412 
CheckOutExistStartPoint(const AppStartCheckPointData& data)413 void AppLaunchSceneDataProcessor::CheckOutExistStartPoint(const AppStartCheckPointData& data)
414 {
415     AppStartRecord record = GetRecord(data.bundleName);
416     if (!record.interactionResponse.bundleName.empty()) {
417         // stop old timer
418         StopTimer(data.bundleName);
419         if (ReportConditionMet(data.bundleName)) {
420             AppStartMetrics metrics = CalcMetrics(data.bundleName);
421             Report(metrics);
422         }
423         db->DeleteRecord(data.bundleName);
424         ClearMapByBundleName(data.bundleName);
425     }
426 }
427 } // HiviewDFX
428 } // OHOS