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