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 com.android.launcher3.util
18 
19 import java.io.PrintWriter
20 import java.text.SimpleDateFormat
21 import java.util.Date
22 import java.util.Locale
23 
24 /**
25  * A utility class to record and log events. Events are stored in a fixed size array and old logs
26  * are purged as new events come.
27  */
28 class EventLogArray(private val name: String, size: Int) {
29 
30     companion object {
31         private const val TYPE_ONE_OFF = 0
32         private const val TYPE_FLOAT = 1
33         private const val TYPE_INTEGER = 2
34         private const val TYPE_BOOL_TRUE = 3
35         private const val TYPE_BOOL_FALSE = 4
isEntrySamenull36         private fun isEntrySame(entry: EventEntry?, type: Int, event: String): Boolean {
37             return entry != null && entry.type == type && entry.event == event
38         }
39     }
40 
41     private val logs: Array<EventEntry?>
42     private var nextIndex = 0
43 
44     init {
45         logs = arrayOfNulls(size)
46     }
47 
addLognull48     fun addLog(event: String) {
49         addLog(TYPE_ONE_OFF, event, 0f)
50     }
51 
addLognull52     fun addLog(event: String, extras: Int) {
53         addLog(TYPE_INTEGER, event, extras.toFloat())
54     }
55 
addLognull56     fun addLog(event: String, extras: Float) {
57         addLog(TYPE_FLOAT, event, extras)
58     }
59 
addLognull60     fun addLog(event: String, extras: Boolean) {
61         addLog(if (extras) TYPE_BOOL_TRUE else TYPE_BOOL_FALSE, event, 0f)
62     }
63 
addLognull64     private fun addLog(type: Int, event: String, extras: Float) {
65         // Merge the logs if it's a duplicate
66         val last = (nextIndex + logs.size - 1) % logs.size
67         val secondLast = (nextIndex + logs.size - 2) % logs.size
68         if (isEntrySame(logs[last], type, event) && isEntrySame(logs[secondLast], type, event)) {
69             logs[last]!!.update(type, event, extras)
70             logs[secondLast]!!.duplicateCount++
71             return
72         }
73         if (logs[nextIndex] == null) {
74             logs[nextIndex] = EventEntry()
75         }
76         logs[nextIndex]!!.update(type, event, extras)
77         nextIndex = (nextIndex + 1) % logs.size
78     }
79 
dumpnull80     fun dump(prefix: String, writer: PrintWriter) {
81         writer.println("$prefix$name event history:")
82         val sdf = SimpleDateFormat("  HH:mm:ss.SSSZ  ", Locale.US)
83         val date = Date()
84         for (i in logs.indices) {
85             val log = logs[(nextIndex + logs.size - i - 1) % logs.size] ?: continue
86             date.time = log.time
87             val msg = StringBuilder(prefix).append(sdf.format(date)).append(log.event)
88             when (log.type) {
89                 TYPE_BOOL_FALSE -> msg.append(": false")
90                 TYPE_BOOL_TRUE -> msg.append(": true")
91                 TYPE_FLOAT -> msg.append(": ").append(log.extras)
92                 TYPE_INTEGER -> msg.append(": ").append(log.extras.toInt())
93                 else -> {}
94             }
95             if (log.duplicateCount > 0) {
96                 msg.append(" & ").append(log.duplicateCount).append(" similar events")
97             }
98             writer.println(msg)
99         }
100     }
101 
102     /** A single event entry. */
103     private class EventEntry {
104         var type = 0
105         var event: String? = null
106         var extras = 0f
107         var time: Long = 0
108         var duplicateCount = 0
updatenull109         fun update(type: Int, event: String, extras: Float) {
110             this.type = type
111             this.event = event
112             this.extras = extras
113             time = System.currentTimeMillis()
114             duplicateCount = 0
115         }
116     }
117 }
118