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