1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.tools.monitors.events
18 
19 import android.tools.io.TraceType
20 import android.tools.monitors.TraceMonitorTest
21 import android.tools.testutils.CleanFlickerEnvironmentRule
22 import android.tools.testutils.newTestResultWriter
23 import android.tools.traces.TRACE_CONFIG_REQUIRE_CHANGES
24 import android.tools.traces.events.CujEvent
25 import android.tools.traces.events.CujType
26 import android.tools.traces.events.EventLog.Companion.MAGIC_NUMBER
27 import android.tools.traces.events.FocusEvent
28 import android.tools.traces.events.UnknownCuj
29 import android.tools.traces.io.ResultReader
30 import android.tools.traces.monitors.events.EventLogMonitor
31 import android.tools.traces.now
32 import android.util.EventLog
33 import com.android.internal.jank.EventLogTags
34 import com.google.common.truth.Truth
35 import org.junit.Assert.assertEquals
36 import org.junit.Assert.assertTrue
37 import org.junit.ClassRule
38 import org.junit.Test
39 
40 /**
41  * Contains [EventLogMonitor] tests. To run this test: {@code atest
42  * FlickerLibTest:EventLogMonitorTest}
43  */
44 class EventLogMonitorTest : TraceMonitorTest<EventLogMonitor>() {
45     override val traceType = TraceType.EVENT_LOG
46 
getMonitornull47     override fun getMonitor(): EventLogMonitor = EventLogMonitor()
48 
49     override fun assertTrace(traceData: ByteArray) {
50         Truth.assertThat(traceData.size).isAtLeast(MAGIC_NUMBER.toByteArray().size)
51         Truth.assertThat(traceData.slice(0 until MAGIC_NUMBER.toByteArray().size))
52             .isEqualTo(MAGIC_NUMBER.toByteArray().asList())
53     }
54 
55     @Test
canCaptureFocusEventLogsnull56     fun canCaptureFocusEventLogs() {
57         val monitor = EventLogMonitor()
58         EventLog.writeEvent(
59             INPUT_FOCUS_TAG,
60             "Focus entering 111 com.android.phone/" +
61                 "com.android.phone.settings.fdn.FdnSetting (server)",
62             "reason=test",
63         )
64         EventLog.writeEvent(
65             INPUT_FOCUS_TAG,
66             "Focus leaving 222 com.google.android.apps.nexuslauncher/" +
67                 "com.google.android.apps.nexuslauncher.NexusLauncherActivity (server)",
68             "reason=test",
69         )
70         EventLog.writeEvent(
71             INPUT_FOCUS_TAG,
72             "Focus entering 333 com.android.phone/" +
73                 "com.android.phone.settings.fdn.FdnSetting (server)",
74             "reason=test",
75         )
76         monitor.start()
77         EventLog.writeEvent(
78             INPUT_FOCUS_TAG,
79             "Focus leaving 4749f88 com.android.phone/" +
80                 "com.android.phone.settings.fdn.FdnSetting (server)",
81             "reason=test",
82         )
83         EventLog.writeEvent(
84             INPUT_FOCUS_TAG,
85             "Focus entering 7c01447 com.android.phone/" +
86                 "com.android.phone.settings.fdn.FdnSetting (server)",
87             "reason=test",
88         )
89         val writer = newTestResultWriter()
90         monitor.stop(writer)
91         EventLog.writeEvent(
92             INPUT_FOCUS_TAG,
93             "Focus entering 2aa30cd com.android.phone/" +
94                 "com.android.phone.settings.fdn.FdnSetting (server)",
95             "reason=test",
96         )
97         val result = writer.write()
98 
99         val reader = ResultReader(result, TRACE_CONFIG_REQUIRE_CHANGES)
100         val eventLog = reader.readEventLogTrace()
101         requireNotNull(eventLog) { "EventLog was null" }
102 
103         assertEquals(2, eventLog.focusEvents.size)
104         assertEquals(
105             "4749f88 com.android.phone/com.android.phone.settings.fdn.FdnSetting (server)",
106             eventLog.focusEvents.first().window,
107         )
108         assertEquals(FocusEvent.Type.LOST, eventLog.focusEvents.first().type)
109         assertEquals(
110             "7c01447 com.android.phone/com.android.phone.settings.fdn.FdnSetting (server)",
111             eventLog.focusEvents.drop(1).first().window,
112         )
113         assertEquals(FocusEvent.Type.GAINED, eventLog.focusEvents.drop(1).first().type)
114         assertTrue(
115             eventLog.focusEvents.first().timestamp <= eventLog.focusEvents.drop(1).first().timestamp
116         )
117         assertEquals(eventLog.focusEvents.first().reason, "test")
118     }
119 
120     @Test
onlyCapturesLastTransitionnull121     fun onlyCapturesLastTransition() {
122         val monitor = EventLogMonitor()
123         monitor.start()
124         EventLog.writeEvent(
125             INPUT_FOCUS_TAG,
126             "Focus leaving 11111 com.android.phone/" +
127                 "com.android.phone.settings.fdn.FdnSetting (server)",
128             "reason=test",
129         )
130         EventLog.writeEvent(
131             INPUT_FOCUS_TAG,
132             "Focus entering 22222 com.android.phone/" +
133                 "com.android.phone.settings.fdn.FdnSetting (server)",
134             "reason=test",
135         )
136         monitor.stop(newTestResultWriter())
137 
138         monitor.start()
139         EventLog.writeEvent(
140             INPUT_FOCUS_TAG,
141             "Focus leaving 479f88 com.android.phone/" +
142                 "com.android.phone.settings.fdn.FdnSetting (server)",
143             "reason=test",
144         )
145         EventLog.writeEvent(
146             INPUT_FOCUS_TAG,
147             "Focus entering 7c01447 com.android.phone/" +
148                 "com.android.phone.settings.fdn.FdnSetting (server)",
149             "reason=test",
150         )
151         val writer = newTestResultWriter()
152         monitor.stop(writer)
153         val result = writer.write()
154 
155         val reader = ResultReader(result, TRACE_CONFIG_REQUIRE_CHANGES)
156         val eventLog = reader.readEventLogTrace()
157         requireNotNull(eventLog) { "EventLog was null" }
158 
159         assertEquals(2, eventLog.focusEvents.size)
160         assertEquals(
161             "479f88 com.android.phone/com.android.phone.settings.fdn.FdnSetting (server)",
162             eventLog.focusEvents.first().window,
163         )
164         assertEquals(FocusEvent.Type.LOST, eventLog.focusEvents.first().type)
165         assertEquals(
166             "7c01447 com.android.phone/com.android.phone.settings.fdn.FdnSetting (server)",
167             eventLog.focusEvents.drop(1).first().window,
168         )
169         assertEquals(FocusEvent.Type.GAINED, eventLog.focusEvents.drop(1).first().type)
170         assertTrue(
171             eventLog.focusEvents.first().timestamp <= eventLog.focusEvents.drop(1).first().timestamp
172         )
173     }
174 
175     @Test
ignoreFocusRequestLogsnull176     fun ignoreFocusRequestLogs() {
177         val monitor = EventLogMonitor()
178         monitor.start()
179         EventLog.writeEvent(
180             INPUT_FOCUS_TAG,
181             "Focus leaving 4749f88 com.android.phone/" +
182                 "com.android.phone.settings.fdn.FdnSetting (server)",
183             "reason=test",
184         )
185         EventLog.writeEvent(
186             INPUT_FOCUS_TAG,
187             "Focus request 111 com.android.phone/" +
188                 "com.android.phone.settings.fdn.FdnSetting (server)",
189             "reason=test",
190         )
191         EventLog.writeEvent(
192             INPUT_FOCUS_TAG,
193             "Focus entering 7c01447 com.android.phone/" +
194                 "com.android.phone.settings.fdn.FdnSetting (server)",
195             "reason=test",
196         )
197         val writer = newTestResultWriter()
198         monitor.stop(writer)
199         val result = writer.write()
200 
201         val reader = ResultReader(result, TRACE_CONFIG_REQUIRE_CHANGES)
202         val eventLog = reader.readEventLogTrace()
203         requireNotNull(eventLog) { "EventLog was null" }
204 
205         assertEquals(2, eventLog.focusEvents.size)
206         assertEquals(
207             "4749f88 com.android.phone/com.android.phone.settings.fdn.FdnSetting (server)",
208             eventLog.focusEvents.first().window,
209         )
210         assertEquals(FocusEvent.Type.LOST, eventLog.focusEvents.first().type)
211         assertEquals(
212             "7c01447 com.android.phone/com.android.phone.settings.fdn.FdnSetting (server)",
213             eventLog.focusEvents.drop(1).first().window,
214         )
215         assertEquals(FocusEvent.Type.GAINED, eventLog.focusEvents.drop(1).first().type)
216         assertTrue(
217             eventLog.focusEvents.first().timestamp <= eventLog.focusEvents.drop(1).first().timestamp
218         )
219         assertEquals(eventLog.focusEvents.first().reason, "test")
220     }
221 
222     @Test
savesEventLogsToFilenull223     fun savesEventLogsToFile() {
224         val monitor = EventLogMonitor()
225         monitor.start()
226         EventLog.writeEvent(
227             INPUT_FOCUS_TAG,
228             "Focus leaving 4749f88 com.android.phone/" +
229                 "com.android.phone.settings.fdn.FdnSetting (server)",
230             "reason=test",
231         )
232         EventLog.writeEvent(
233             INPUT_FOCUS_TAG,
234             "Focus request 111 com.android.phone/" +
235                 "com.android.phone.settings.fdn.FdnSetting (server)",
236             "reason=test",
237         )
238         EventLog.writeEvent(
239             INPUT_FOCUS_TAG,
240             "Focus entering 7c01447 com.android.phone/" +
241                 "com.android.phone.settings.fdn.FdnSetting (server)",
242             "reason=test",
243         )
244         val writer = newTestResultWriter()
245         monitor.stop(writer)
246         val result = writer.write()
247         val reader = ResultReader(result, TRACE_CONFIG_REQUIRE_CHANGES)
248 
249         Truth.assertWithMessage("Trace not found")
250             .that(reader.hasTraceFile(TraceType.EVENT_LOG))
251             .isTrue()
252     }
253 
254     @Test
cropsEventsFromBeforeMonitorStartnull255     fun cropsEventsFromBeforeMonitorStart() {
256         val monitor = EventLogMonitor()
257 
258         EventLog.writeEvent(
259             INPUT_FOCUS_TAG,
260             "Focus leaving 4749f88 com.android.phone/" +
261                 "com.android.phone.settings.fdn.FdnSetting (server)",
262             "reason=test",
263         )
264 
265         monitor.start()
266 
267         EventLog.writeEvent(
268             INPUT_FOCUS_TAG,
269             "Focus entering 7c01447 com.android.phone/" +
270                 "com.android.phone.settings.fdn.FdnSetting (server)",
271             "reason=test",
272         )
273 
274         val writer = newTestResultWriter()
275         monitor.stop(writer)
276         val result = writer.write()
277 
278         val reader = ResultReader(result, TRACE_CONFIG_REQUIRE_CHANGES)
279         val eventLog = reader.readEventLogTrace() ?: error("EventLog should have been created")
280 
281         Truth.assertThat(eventLog.focusEvents).hasSize(1)
282         Truth.assertThat(eventLog.focusEvents.first().type).isEqualTo(FocusEvent.Type.GAINED)
283     }
284 
285     @Test
cropsEventsOutsideOfTransitionTimesnull286     fun cropsEventsOutsideOfTransitionTimes() {
287         val monitor = EventLogMonitor()
288         val writer = newTestResultWriter()
289         monitor.start()
290 
291         EventLog.writeEvent(
292             INPUT_FOCUS_TAG,
293             "Focus leaving 4749f88 com.android.phone/" +
294                 "com.android.phone.settings.fdn.FdnSetting (server)",
295             "reason=test",
296         )
297 
298         writer.setTransitionStartTime(now())
299 
300         EventLog.writeEvent(
301             INPUT_FOCUS_TAG,
302             "Focus entering 7c01447 com.android.phone/" +
303                 "com.android.phone.settings.fdn.FdnSetting (server)",
304             "reason=test",
305         )
306 
307         writer.setTransitionEndTime(now())
308 
309         EventLog.writeEvent(
310             INPUT_FOCUS_TAG,
311             "Focus entering 7c01447 com.android.phone/" +
312                 "com.android.phone.settings.fdn.FdnSetting (server)",
313             "reason=test",
314         )
315 
316         monitor.stop(writer)
317         val result = writer.write()
318 
319         val reader = ResultReader(result, TRACE_CONFIG_REQUIRE_CHANGES)
320         val eventLog = reader.readEventLogTrace() ?: error("EventLog should have been created")
321 
322         Truth.assertThat(eventLog.focusEvents).hasSize(1)
323         Truth.assertThat(eventLog.focusEvents.first().hasFocus()).isTrue()
324     }
325 
326     @Test
canCaptureCujEventsnull327     fun canCaptureCujEvents() {
328         val monitor = EventLogMonitor()
329         val writer = newTestResultWriter()
330         monitor.start()
331         var now = now()
332         EventLogTags.writeJankCujEventsBeginRequest(
333             CujType.CUJ_NOTIFICATION_APP_START.ordinal,
334             now.unixNanos,
335             now.elapsedNanos,
336             now.systemUptimeNanos,
337             "",
338         )
339         now = now()
340         EventLogTags.writeJankCujEventsEndRequest(
341             CujType.CUJ_NOTIFICATION_APP_START.ordinal,
342             now.unixNanos,
343             now.elapsedNanos,
344             now.systemUptimeNanos,
345         )
346         monitor.stop(writer)
347         val result = writer.write()
348 
349         val reader = ResultReader(result, TRACE_CONFIG_REQUIRE_CHANGES)
350         val eventLog = reader.readEventLogTrace() ?: error("EventLog should have been created")
351 
352         assertEquals(2, eventLog.cujEvents.size)
353     }
354 
355     @Test
collectsCujEventDatanull356     fun collectsCujEventData() {
357         val monitor = EventLogMonitor()
358         val writer = newTestResultWriter()
359         monitor.start()
360         val now = now()
361         EventLogTags.writeJankCujEventsBeginRequest(
362             CujType.CUJ_LAUNCHER_QUICK_SWITCH.id,
363             now.unixNanos,
364             now.elapsedNanos,
365             now.systemUptimeNanos,
366             "",
367         )
368         EventLogTags.writeJankCujEventsEndRequest(
369             CujType.CUJ_LAUNCHER_ALL_APPS_SCROLL.id,
370             now.unixNanos + 1,
371             now.elapsedNanos + 1,
372             now.systemUptimeNanos + 1,
373         )
374         EventLogTags.writeJankCujEventsCancelRequest(
375             CujType.CUJ_LOCKSCREEN_LAUNCH_CAMERA.id,
376             now.unixNanos + 2,
377             now.elapsedNanos + 2,
378             now.systemUptimeNanos + 2,
379         )
380         monitor.stop(writer)
381         val result = writer.write()
382 
383         val reader = ResultReader(result, TRACE_CONFIG_REQUIRE_CHANGES)
384         val eventLog = reader.readEventLogTrace() ?: error("EventLog should have been created")
385 
386         // There maybe be some random CUJ events triggered in the background
387         val cujEvents =
388             eventLog.cujEvents.filter {
389                 now.unixNanos <= it.timestamp.unixNanos &&
390                     it.timestamp.unixNanos <= (now.unixNanos + 2)
391             }
392 
393         Truth.assertThat(cujEvents).hasSize(3)
394 
395         Truth.assertThat(cujEvents.first().type).isEqualTo(CujEvent.Companion.Type.START)
396         Truth.assertThat(cujEvents.first().cuj).isEqualTo(CujType.CUJ_LAUNCHER_QUICK_SWITCH)
397 
398         Truth.assertThat(cujEvents.drop(1).first().type).isEqualTo(CujEvent.Companion.Type.END)
399         Truth.assertThat(cujEvents.drop(1).first().cuj)
400             .isEqualTo(CujType.CUJ_LAUNCHER_ALL_APPS_SCROLL)
401 
402         Truth.assertThat(cujEvents.drop(2).first().type).isEqualTo(CujEvent.Companion.Type.CANCEL)
403         Truth.assertThat(cujEvents.drop(2).first().cuj)
404             .isEqualTo(CujType.CUJ_LOCKSCREEN_LAUNCH_CAMERA)
405     }
406 
407     @Test
canParseHandleUnknownCujTypesnull408     fun canParseHandleUnknownCujTypes() {
409         val unknownCujId = Int.MAX_VALUE
410         val monitor = EventLogMonitor()
411         val writer = newTestResultWriter()
412         monitor.start()
413         var now = now()
414         EventLogTags.writeJankCujEventsBeginRequest(
415             unknownCujId,
416             now.unixNanos,
417             now.elapsedNanos,
418             now.systemUptimeNanos,
419             "",
420         )
421         now = now()
422         EventLogTags.writeJankCujEventsEndRequest(
423             unknownCujId,
424             now.unixNanos,
425             now.elapsedNanos,
426             now.systemUptimeNanos,
427         )
428         now = now()
429         EventLogTags.writeJankCujEventsCancelRequest(
430             unknownCujId,
431             now.unixNanos,
432             now.elapsedNanos,
433             now.systemUptimeNanos,
434         )
435         monitor.stop(writer)
436         val result = writer.write()
437 
438         val reader = ResultReader(result, TRACE_CONFIG_REQUIRE_CHANGES)
439         val eventLog = reader.readEventLogTrace()
440         requireNotNull(eventLog) { "EventLog should have been created" }
441 
442         assertEquals(3, eventLog.cujEvents.size)
443         Truth.assertThat(eventLog.cujEvents.first().cuj).isEqualTo(UnknownCuj(unknownCujId))
444         Truth.assertThat(eventLog.cujEvents.drop(1).first().cuj).isEqualTo(UnknownCuj(unknownCujId))
445         Truth.assertThat(eventLog.cujEvents.drop(2).first().cuj).isEqualTo(UnknownCuj(unknownCujId))
446     }
447 
448     private companion object {
449         const val INPUT_FOCUS_TAG = 62001
450 
451         @ClassRule @JvmField val ENV_CLEANUP = CleanFlickerEnvironmentRule()
452     }
453 }
454