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