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 }