1 /*
2  * Copyright (C) 2020 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.systemui.statusbar.notification.collection.listbuilder
18 
19 import com.android.systemui.log.dagger.NotificationLog
20 import com.android.systemui.log.LogBuffer
21 import com.android.systemui.log.core.LogLevel.DEBUG
22 import com.android.systemui.log.core.LogLevel.INFO
23 import com.android.systemui.log.core.LogLevel.WARNING
24 import com.android.systemui.statusbar.notification.NotifPipelineFlags
25 import com.android.systemui.statusbar.notification.collection.GroupEntry
26 import com.android.systemui.statusbar.notification.collection.ListEntry
27 import com.android.systemui.statusbar.notification.collection.NotificationEntry
28 import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.StateName
29 import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.getStateName
30 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
31 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
32 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
33 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
34 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
35 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager
36 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
37 import com.android.systemui.statusbar.notification.logKey
38 import com.android.systemui.util.Compile
39 import javax.inject.Inject
40 
41 class ShadeListBuilderLogger @Inject constructor(
42     notifPipelineFlags: NotifPipelineFlags,
43     @NotificationLog private val buffer: LogBuffer
44 ) {
logOnBuildListnull45     fun logOnBuildList(reason: String?) {
46         buffer.log(TAG, INFO, {
47             str1 = reason
48         }, {
49             "Request received from NotifCollection for $str1"
50         })
51     }
52 
logEndBuildListnull53     fun logEndBuildList(
54         buildId: Int,
55         topLevelEntries: Int,
56         numChildren: Int,
57         enforcedVisualStability: Boolean
58     ) {
59         buffer.log(TAG, INFO, {
60             long1 = buildId.toLong()
61             int1 = topLevelEntries
62             int2 = numChildren
63             bool1 = enforcedVisualStability
64         }, {
65             "(Build $long1) Build complete ($int1 top-level entries, $int2 children)" +
66                     " enforcedVisualStability=$bool1"
67         })
68     }
69 
logPluggableInvalidatednull70     private fun logPluggableInvalidated(
71         type: String,
72         pluggable: Pluggable<*>,
73         @StateName pipelineState: Int,
74         reason: String?
75     ) {
76         buffer.log(TAG, DEBUG, {
77             str1 = type
78             str2 = pluggable.name
79             int1 = pipelineState
80             str3 = reason
81         }, {
82             """Invalidated while ${getStateName(int1)} by $str1 "$str2" because $str3"""
83         })
84     }
85 
logPreRenderInvalidatednull86     fun logPreRenderInvalidated(
87         invalidator: Invalidator,
88         @StateName pipelineState: Int,
89         reason: String?
90     ) = logPluggableInvalidated("Pre-render Invalidator", invalidator, pipelineState, reason)
91 
92     fun logPreGroupFilterInvalidated(
93         filter: NotifFilter,
94         @StateName pipelineState: Int,
95         reason: String?
96     ) = logPluggableInvalidated("Pre-group NotifFilter", filter, pipelineState, reason)
97 
98     fun logReorderingAllowedInvalidated(
99         stabilityManager: NotifStabilityManager,
100         @StateName pipelineState: Int,
101         reason: String?
102     ) = logPluggableInvalidated("ReorderingNowAllowed", stabilityManager, pipelineState, reason)
103 
104     fun logPromoterInvalidated(
105         promoter: NotifPromoter,
106         @StateName pipelineState: Int,
107         reason: String?
108     ) = logPluggableInvalidated("NotifPromoter", promoter, pipelineState, reason)
109 
110     fun logNotifSectionInvalidated(
111         sectioner: NotifSectioner,
112         @StateName pipelineState: Int,
113         reason: String?
114     ) = logPluggableInvalidated("NotifSection", sectioner, pipelineState, reason)
115 
116     fun logNotifComparatorInvalidated(
117         comparator: NotifComparator,
118         @StateName pipelineState: Int,
119         reason: String?
120     ) = logPluggableInvalidated("NotifComparator", comparator, pipelineState, reason)
121 
122     fun logFinalizeFilterInvalidated(
123         filter: NotifFilter,
124         @StateName pipelineState: Int,
125         reason: String?
126     ) = logPluggableInvalidated("Finalize NotifFilter", filter, pipelineState, reason)
127 
128     fun logDuplicateSummary(
129         buildId: Int,
130         group: GroupEntry,
131         existingSummary: NotificationEntry,
132         newSummary: NotificationEntry
133     ) {
134         buffer.log(TAG, WARNING, {
135             long1 = buildId.toLong()
136             str1 = group.logKey
137             str2 = existingSummary.logKey
138             str3 = newSummary.logKey
139         }, {
140             """(Build $long1) Duplicate summary for group "$str1": "$str2" vs. "$str3""""
141         })
142     }
143 
144     fun logDuplicateTopLevelKey(buildId: Int, topLevelKey: String) {
145         buffer.log(TAG, WARNING, {
146             long1 = buildId.toLong()
147             str1 = logKey(topLevelKey)
148         }, {
149             "(Build $long1) Duplicate top-level key: $str1"
150         })
151     }
152 
153     fun logEntryAttachStateChanged(
154         buildId: Int,
155         entry: ListEntry,
156         prevParent: GroupEntry?,
157         newParent: GroupEntry?
158     ) {
159         buffer.log(TAG, INFO, {
160             long1 = buildId.toLong()
161             str1 = entry.logKey
162             str2 = prevParent?.logKey
163             str3 = newParent?.logKey
164         }, {
165 
166             val action = if (str2 == null && str3 != null) {
167                 "ATTACHED"
168             } else if (str2 != null && str3 == null) {
169                 "DETACHED"
170             } else if (str2 == null && str3 == null) {
171                 "MODIFIED (DETACHED)"
172             } else {
173                 "MODIFIED (ATTACHED)"
174             }
175 
176             "(Build $long1) $action {$str1}"
177         })
178     }
179 
180     fun logParentChanged(buildId: Int, prevParent: GroupEntry?, newParent: GroupEntry?) {
181         buffer.log(TAG, INFO, {
182             long1 = buildId.toLong()
183             str1 = prevParent?.logKey
184             str2 = newParent?.logKey
185         }, {
186             if (str1 == null && str2 != null) {
187                 "(Build $long1)     Parent is {$str2}"
188             } else if (str1 != null && str2 == null) {
189                 "(Build $long1)     Parent was {$str1}"
190             } else {
191                 "(Build $long1)     Reparent: {$str1} -> {$str2}"
192             }
193         })
194     }
195 
196     fun logParentChangeSuppressedStarted(
197         buildId: Int,
198         suppressedParent: GroupEntry?,
199         keepingParent: GroupEntry?
200     ) {
201         buffer.log(TAG, INFO, {
202             long1 = buildId.toLong()
203             str1 = suppressedParent?.logKey
204             str2 = keepingParent?.logKey
205         }, {
206             "(Build $long1)     Change of parent to '$str1' suppressed; keeping parent '$str2'"
207         })
208     }
209 
210     fun logParentChangeSuppressedStopped(
211             buildId: Int,
212             previouslySuppressedParent: GroupEntry?,
213             previouslyKeptParent: GroupEntry?
214     ) {
215         buffer.log(TAG, INFO, {
216             long1 = buildId.toLong()
217             str1 = previouslySuppressedParent?.logKey
218             str2 = previouslyKeptParent?.logKey
219         }, {
220             "(Build $long1)     Change of parent to '$str1' no longer suppressed; " +
221                     "replaced parent '$str2'"
222         })
223     }
224 
225     fun logGroupPruningSuppressed(
226         buildId: Int,
227         keepingParent: GroupEntry?
228     ) {
229         buffer.log(TAG, INFO, {
230             long1 = buildId.toLong()
231             str1 = keepingParent?.logKey
232         }, {
233             "(Build $long1)     Group pruning suppressed; keeping parent '$str1'"
234         })
235     }
236 
237     fun logPrunedReasonChanged(
238         buildId: Int,
239         prevReason: String?,
240         newReason: String?
241     ) {
242         buffer.log(TAG, INFO, {
243             long1 = buildId.toLong()
244             str1 = prevReason
245             str2 = newReason
246         }, {
247             "(Build $long1)     Pruned reason changed: $str1 -> $str2"
248         })
249     }
250 
251     fun logFilterChanged(
252         buildId: Int,
253         prevFilter: NotifFilter?,
254         newFilter: NotifFilter?
255     ) {
256         buffer.log(TAG, INFO, {
257             long1 = buildId.toLong()
258             str1 = prevFilter?.name
259             str2 = newFilter?.name
260         }, {
261             "(Build $long1)     Filter changed: $str1 -> $str2"
262         })
263     }
264 
265     fun logPromoterChanged(
266         buildId: Int,
267         prevPromoter: NotifPromoter?,
268         newPromoter: NotifPromoter?
269     ) {
270         buffer.log(TAG, INFO, {
271             long1 = buildId.toLong()
272             str1 = prevPromoter?.name
273             str2 = newPromoter?.name
274         }, {
275             "(Build $long1)     Promoter changed: $str1 -> $str2"
276         })
277     }
278 
279     fun logSectionChanged(
280         buildId: Int,
281         prevSection: NotifSection?,
282         newSection: NotifSection?
283     ) {
284         buffer.log(TAG, INFO, {
285             long1 = buildId.toLong()
286             str1 = prevSection?.label
287             str2 = newSection?.label
288         }, {
289             if (str1 == null) {
290                 "(Build $long1)     Section assigned: $str2"
291             } else {
292                 "(Build $long1)     Section changed: $str1 -> $str2"
293             }
294         })
295     }
296 
297     fun logSectionChangeSuppressed(
298         buildId: Int,
299         suppressedSection: NotifSection?,
300         assignedSection: NotifSection?
301     ) {
302         buffer.log(TAG, INFO, {
303             long1 = buildId.toLong()
304             str1 = suppressedSection?.label
305             str2 = assignedSection?.label
306         }, {
307             "(Build $long1)     Suppressing section change to $str1 (staying at $str2)"
308         })
309     }
310 
311     val logRankInFinalList = Compile.IS_DEBUG && notifPipelineFlags.isDevLoggingEnabled()
312 
313     fun logFinalList(entries: List<ListEntry>) {
314         if (entries.isEmpty()) {
315             buffer.log(TAG, DEBUG, {}, { "(empty list)" })
316         }
317         for (i in entries.indices) {
318             val entry = entries[i]
319             buffer.log(TAG, DEBUG, {
320                 int1 = i
321                 str1 = entry.logKey
322                 bool1 = logRankInFinalList
323                 int2 = entry.representativeEntry!!.ranking.rank
324             }, {
325                 "[$int1] $str1".let { if (bool1) "$it rank=$int2" else it }
326             })
327 
328             if (entry is GroupEntry) {
329                 entry.summary?.let {
330                     buffer.log(TAG, DEBUG, {
331                         str1 = it.logKey
332                         bool1 = logRankInFinalList
333                         int2 = it.ranking.rank
334                     }, {
335                         "  [*] $str1 (summary)".let { if (bool1) "$it rank=$int2" else it }
336                     })
337                 }
338                 for (j in entry.children.indices) {
339                     val child = entry.children[j]
340                     buffer.log(TAG, DEBUG, {
341                         int1 = j
342                         str1 = child.logKey
343                         bool1 = logRankInFinalList
344                         int2 = child.ranking.rank
345                     }, {
346                         "  [$int1] $str1".let { if (bool1) "$it rank=$int2" else it }
347                     })
348                 }
349             }
350         }
351     }
352 
353     fun logPipelineRunSuppressed() =
354         buffer.log(TAG, INFO, {}, { "Suppressing pipeline run during animation." })
355 }
356 
357 private const val TAG = "ShadeListBuilder"
358