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.notifcollection
18
19 import android.os.RemoteException
20 import android.service.notification.NotificationListenerService
21 import android.service.notification.NotificationListenerService.RankingMap
22 import android.service.notification.StatusBarNotification
23 import com.android.systemui.log.dagger.NotificationLog
24 import com.android.systemui.log.LogBuffer
25 import com.android.systemui.log.core.LogLevel.DEBUG
26 import com.android.systemui.log.core.LogLevel.ERROR
27 import com.android.systemui.log.core.LogLevel.INFO
28 import com.android.systemui.log.core.LogLevel.WARNING
29 import com.android.systemui.log.core.LogLevel.WTF
30 import com.android.systemui.statusbar.notification.collection.GroupEntry
31 import com.android.systemui.statusbar.notification.collection.NotifCollection
32 import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason
33 import com.android.systemui.statusbar.notification.collection.NotifCollection.FutureDismissal
34 import com.android.systemui.statusbar.notification.collection.NotificationEntry
35 import com.android.systemui.statusbar.notification.logKey
36 import javax.inject.Inject
37
cancellationReasonDebugStringnull38 fun cancellationReasonDebugString(@CancellationReason reason: Int) =
39 "$reason:" + when (reason) {
40 -1 -> "REASON_NOT_CANCELED" // NotifCollection.REASON_NOT_CANCELED
41 NotifCollection.REASON_UNKNOWN -> "REASON_UNKNOWN"
42 NotificationListenerService.REASON_CLICK -> "REASON_CLICK"
43 NotificationListenerService.REASON_CANCEL -> "REASON_CANCEL"
44 NotificationListenerService.REASON_CANCEL_ALL -> "REASON_CANCEL_ALL"
45 NotificationListenerService.REASON_ERROR -> "REASON_ERROR"
46 NotificationListenerService.REASON_PACKAGE_CHANGED -> "REASON_PACKAGE_CHANGED"
47 NotificationListenerService.REASON_USER_STOPPED -> "REASON_USER_STOPPED"
48 NotificationListenerService.REASON_PACKAGE_BANNED -> "REASON_PACKAGE_BANNED"
49 NotificationListenerService.REASON_APP_CANCEL -> "REASON_APP_CANCEL"
50 NotificationListenerService.REASON_APP_CANCEL_ALL -> "REASON_APP_CANCEL_ALL"
51 NotificationListenerService.REASON_LISTENER_CANCEL -> "REASON_LISTENER_CANCEL"
52 NotificationListenerService.REASON_LISTENER_CANCEL_ALL -> "REASON_LISTENER_CANCEL_ALL"
53 NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED -> "REASON_GROUP_SUMMARY_CANCELED"
54 NotificationListenerService.REASON_GROUP_OPTIMIZATION -> "REASON_GROUP_OPTIMIZATION"
55 NotificationListenerService.REASON_PACKAGE_SUSPENDED -> "REASON_PACKAGE_SUSPENDED"
56 NotificationListenerService.REASON_PROFILE_TURNED_OFF -> "REASON_PROFILE_TURNED_OFF"
57 NotificationListenerService.REASON_UNAUTOBUNDLED -> "REASON_UNAUTOBUNDLED"
58 NotificationListenerService.REASON_CHANNEL_BANNED -> "REASON_CHANNEL_BANNED"
59 NotificationListenerService.REASON_SNOOZED -> "REASON_SNOOZED"
60 NotificationListenerService.REASON_TIMEOUT -> "REASON_TIMEOUT"
61 NotificationListenerService.REASON_CHANNEL_REMOVED -> "REASON_CHANNEL_REMOVED"
62 NotificationListenerService.REASON_CLEAR_DATA -> "REASON_CLEAR_DATA"
63 NotificationListenerService.REASON_ASSISTANT_CANCEL -> "REASON_ASSISTANT_CANCEL"
64 else -> "unknown"
65 }
66
67 class NotifCollectionLogger @Inject constructor(
68 @NotificationLog private val buffer: LogBuffer
69 ) {
logNotifPostednull70 fun logNotifPosted(entry: NotificationEntry) {
71 buffer.log(TAG, INFO, {
72 str1 = entry.logKey
73 }, {
74 "POSTED $str1"
75 })
76 }
77
logNotifGroupPostednull78 fun logNotifGroupPosted(groupKey: String, batchSize: Int) {
79 buffer.log(TAG, INFO, {
80 str1 = logKey(groupKey)
81 int1 = batchSize
82 }, {
83 "POSTED GROUP $str1 ($int1 events)"
84 })
85 }
86
logNotifUpdatednull87 fun logNotifUpdated(entry: NotificationEntry) {
88 buffer.log(TAG, INFO, {
89 str1 = entry.logKey
90 }, {
91 "UPDATED $str1"
92 })
93 }
94
logNotifRemovednull95 fun logNotifRemoved(sbn: StatusBarNotification, @CancellationReason reason: Int) {
96 buffer.log(TAG, INFO, {
97 str1 = sbn.logKey
98 int1 = reason
99 }, {
100 "REMOVED $str1 reason=${cancellationReasonDebugString(int1)}"
101 })
102 }
103
logNotifReleasednull104 fun logNotifReleased(entry: NotificationEntry) {
105 buffer.log(TAG, INFO, {
106 str1 = entry.logKey
107 }, {
108 "RELEASED $str1"
109 })
110 }
111
logLocallyDismissednull112 fun logLocallyDismissed(entry: NotificationEntry, index: Int, count: Int) {
113 buffer.log(TAG, INFO, {
114 str1 = entry.logKey
115 int1 = index
116 int2 = count
117 }, {
118 "LOCALLY DISMISSED $str1 ($int1/$int2)"
119 })
120 }
121
logDismissNonExistentNotifnull122 fun logDismissNonExistentNotif(entryKey: String, index: Int, count: Int) {
123 buffer.log(TAG, INFO, {
124 str1 = logKey(entryKey)
125 int1 = index
126 int2 = count
127 }, {
128 "DISMISS Non Existent $str1 ($int1/$int2)"
129 })
130 }
131
logLocallyDismissedChildnull132 fun logLocallyDismissedChild(
133 child: NotificationEntry,
134 parent: NotificationEntry,
135 parentIndex: Int,
136 parentCount: Int
137 ) {
138 buffer.log(TAG, DEBUG, {
139 str1 = child.logKey
140 str2 = parent.logKey
141 int1 = parentIndex
142 int2 = parentCount
143 }, {
144 "LOCALLY DISMISSED CHILD (inferred): $str1 of parent $str2 ($int1/$int2)"
145 })
146 }
147
logDismissAllnull148 fun logDismissAll(userId: Int) {
149 buffer.log(TAG, INFO, {
150 int1 = userId
151 }, {
152 "DISMISS ALL notifications for user $int1"
153 })
154 }
155
logLocallyDismissedAlreadyCanceledEntrynull156 fun logLocallyDismissedAlreadyCanceledEntry(entry: NotificationEntry) {
157 buffer.log(TAG, DEBUG, {
158 str1 = entry.logKey
159 }, {
160 "LOCALLY DISMISSED Already Canceled $str1. Trying to remove."
161 })
162 }
163
logNotifDismissedInterceptednull164 fun logNotifDismissedIntercepted(entry: NotificationEntry, index: Int, count: Int) {
165 buffer.log(TAG, INFO, {
166 str1 = entry.logKey
167 int1 = index
168 int2 = count
169 }, {
170 "DISMISS INTERCEPTED $str1 ($int1/$int2)"
171 })
172 }
173
logNotifClearAllDismissalInterceptednull174 fun logNotifClearAllDismissalIntercepted(entry: NotificationEntry, index: Int, count: Int) {
175 buffer.log(TAG, INFO, {
176 str1 = entry.logKey
177 int1 = index
178 int2 = count
179 }, {
180 "CLEAR ALL DISMISSAL INTERCEPTED $str1 ($int1/$int2)"
181 })
182 }
183
logNotifInternalUpdatenull184 fun logNotifInternalUpdate(entry: NotificationEntry, name: String, reason: String) {
185 buffer.log(TAG, INFO, {
186 str1 = entry.logKey
187 str2 = name
188 str3 = reason
189 }, {
190 "UPDATED INTERNALLY $str1 BY $str2 BECAUSE $str3"
191 })
192 }
193
logNotifInternalUpdateFailednull194 fun logNotifInternalUpdateFailed(sbn: StatusBarNotification, name: String, reason: String) {
195 buffer.log(TAG, INFO, {
196 str1 = sbn.logKey
197 str2 = name
198 str3 = reason
199 }, {
200 "FAILED INTERNAL UPDATE $str1 BY $str2 BECAUSE $str3"
201 })
202 }
203
logNoNotificationToRemoveWithKeynull204 fun logNoNotificationToRemoveWithKey(
205 sbn: StatusBarNotification,
206 @CancellationReason reason: Int
207 ) {
208 buffer.log(TAG, ERROR, {
209 str1 = sbn.logKey
210 int1 = reason
211 }, {
212 "No notification to remove with key $str1 reason=${cancellationReasonDebugString(int1)}"
213 })
214 }
215
logMissingRankingsnull216 fun logMissingRankings(
217 newlyInconsistentEntries: List<NotificationEntry>,
218 totalInconsistent: Int,
219 rankingMap: RankingMap
220 ) {
221 buffer.log(TAG, WARNING, {
222 int1 = totalInconsistent
223 int2 = newlyInconsistentEntries.size
224 str1 = newlyInconsistentEntries.joinToString { it.logKey ?: "null" }
225 }, {
226 "Ranking update is missing ranking for $int1 entries ($int2 new): $str1"
227 })
228 buffer.log(TAG, DEBUG, {
229 str1 = rankingMap.orderedKeys.map { logKey(it) ?: "null" }.toString()
230 }, {
231 "Ranking map contents: $str1"
232 })
233 }
234
logRecoveredRankingsnull235 fun logRecoveredRankings(newlyConsistentKeys: List<String>, totalInconsistent: Int) {
236 buffer.log(TAG, INFO, {
237 int1 = totalInconsistent
238 int1 = newlyConsistentKeys.size
239 str1 = newlyConsistentKeys.joinToString { logKey(it) ?: "null" }
240 }, {
241 "Ranking update now contains rankings for $int1 previously inconsistent entries: $str1"
242 })
243 }
244
logMissingNotificationsnull245 fun logMissingNotifications(
246 newlyMissingKeys: List<String>,
247 totalMissing: Int,
248 ) {
249 buffer.log(TAG, WARNING, {
250 int1 = totalMissing
251 int2 = newlyMissingKeys.size
252 str1 = newlyMissingKeys.joinToString { logKey(it) ?: "null" }
253 }, {
254 "Collection missing $int1 entries in ranking update. Just lost $int2: $str1"
255 })
256 }
257
logFoundNotificationsnull258 fun logFoundNotifications(
259 newlyFoundKeys: List<String>,
260 totalMissing: Int,
261 ) {
262 buffer.log(TAG, INFO, {
263 int1 = totalMissing
264 int2 = newlyFoundKeys.size
265 str1 = newlyFoundKeys.joinToString { logKey(it) ?: "null" }
266 }, {
267 "Collection missing $int1 entries in ranking update. Just found $int2: $str1"
268 })
269 }
270
logRemoteExceptionOnNotificationClearnull271 fun logRemoteExceptionOnNotificationClear(
272 entry: NotificationEntry,
273 index: Int,
274 count: Int,
275 e: RemoteException
276 ) {
277 buffer.log(TAG, WTF, {
278 str1 = entry.logKey
279 int1 = index
280 int2 = count
281 str2 = e.toString()
282 }, {
283 "RemoteException while attempting to clear $str1 ($int1/$int2):\n$str2"
284 })
285 }
286
logRemoteExceptionOnClearAllNotificationsnull287 fun logRemoteExceptionOnClearAllNotifications(e: RemoteException) {
288 buffer.log(TAG, WTF, {
289 str1 = e.toString()
290 }, {
291 "RemoteException while attempting to clear all notifications:\n$str1"
292 })
293 }
294
logLifetimeExtendednull295 fun logLifetimeExtended(entry: NotificationEntry, extender: NotifLifetimeExtender) {
296 buffer.log(TAG, INFO, {
297 str1 = entry.logKey
298 str2 = extender.name
299 }, {
300 "LIFETIME EXTENDED: $str1 by $str2"
301 })
302 }
303
logLifetimeExtensionEndednull304 fun logLifetimeExtensionEnded(
305 entry: NotificationEntry,
306 extender: NotifLifetimeExtender,
307 totalExtenders: Int
308 ) {
309 buffer.log(TAG, INFO, {
310 str1 = entry.logKey
311 str2 = extender.name
312 int1 = totalExtenders
313 }, {
314 "LIFETIME EXTENSION ENDED for $str1 by '$str2'; $int1 remaining extensions"
315 })
316 }
317
logIgnoredErrornull318 fun logIgnoredError(message: String?) {
319 buffer.log(TAG, ERROR, {
320 str1 = message
321 }, {
322 "ERROR suppressed due to initialization forgiveness: $str1"
323 })
324 }
325
logEntryBeingExtendedNotInCollectionnull326 fun logEntryBeingExtendedNotInCollection(
327 entry: NotificationEntry,
328 extender: NotifLifetimeExtender,
329 collectionEntryIs: String
330 ) {
331 buffer.log(TAG, WARNING, {
332 str1 = entry.logKey
333 str2 = extender.name
334 str3 = collectionEntryIs
335 }, {
336 "While ending lifetime extension by $str2 of $str1, entry in collection is $str3"
337 })
338 }
339
logFutureDismissalReusednull340 fun logFutureDismissalReused(dismissal: FutureDismissal) {
341 buffer.log(TAG, INFO, {
342 str1 = dismissal.label
343 }, {
344 "Reusing existing registration: $str1"
345 })
346 }
347
logFutureDismissalRegisterednull348 fun logFutureDismissalRegistered(dismissal: FutureDismissal) {
349 buffer.log(TAG, DEBUG, {
350 str1 = dismissal.label
351 }, {
352 "Registered: $str1"
353 })
354 }
355
logFutureDismissalDoubleCancelledByServernull356 fun logFutureDismissalDoubleCancelledByServer(dismissal: FutureDismissal) {
357 buffer.log(TAG, WARNING, {
358 str1 = dismissal.label
359 }, {
360 "System server double cancelled: $str1"
361 })
362 }
363
logFutureDismissalDoubleRunnull364 fun logFutureDismissalDoubleRun(dismissal: FutureDismissal) {
365 buffer.log(TAG, WARNING, {
366 str1 = dismissal.label
367 }, {
368 "Double run: $str1"
369 })
370 }
371
logFutureDismissalAlreadyCancelledByServernull372 fun logFutureDismissalAlreadyCancelledByServer(dismissal: FutureDismissal) {
373 buffer.log(TAG, DEBUG, {
374 str1 = dismissal.label
375 }, {
376 "Ignoring: entry already cancelled by server: $str1"
377 })
378 }
379
logFutureDismissalGotSystemServerCancelnull380 fun logFutureDismissalGotSystemServerCancel(
381 dismissal: FutureDismissal,
382 @CancellationReason cancellationReason: Int
383 ) {
384 buffer.log(TAG, DEBUG, {
385 str1 = dismissal.label
386 int1 = cancellationReason
387 }, {
388 "SystemServer cancelled: $str1 reason=${cancellationReasonDebugString(int1)}"
389 })
390 }
391
logFutureDismissalDismissingnull392 fun logFutureDismissalDismissing(dismissal: FutureDismissal, type: String) {
393 buffer.log(TAG, DEBUG, {
394 str1 = dismissal.label
395 str2 = type
396 }, {
397 "Dismissing $str2 for: $str1"
398 })
399 }
400
logFutureDismissalMismatchedEntrynull401 fun logFutureDismissalMismatchedEntry(
402 dismissal: FutureDismissal,
403 type: String,
404 latestEntry: NotificationEntry?
405 ) {
406 buffer.log(TAG, WARNING, {
407 str1 = dismissal.label
408 str2 = type
409 str3 = latestEntry.logKey
410 }, {
411 "Mismatch: current $str2 is $str3 for: $str1"
412 })
413 }
414
logDismissAlreadyDismissedNotifnull415 fun logDismissAlreadyDismissedNotif(entry: NotificationEntry, index: Int, count: Int) {
416 buffer.log(TAG, DEBUG, {
417 str1 = entry.logKey
418 int1 = index
419 int2 = count
420 }, {
421 "DISMISS Already Dismissed $str1 ($int1/$int2)"
422 })
423 }
424
getParentLogKeynull425 private fun getParentLogKey(childEntry: NotificationEntry): String {
426 return if (childEntry.parent is GroupEntry) {
427 (childEntry.parent as? GroupEntry)?.summary?.logKey ?: "(null)"
428 } else {
429 "(null)"
430 }
431 }
432
logDismissAlreadyParentDismissedNotifnull433 fun logDismissAlreadyParentDismissedNotif(
434 childEntry: NotificationEntry,
435 childIndex: Int,
436 childCount: Int
437 ) {
438 buffer.log(TAG, DEBUG, {
439 str1 = childEntry.logKey
440 int1 = childIndex
441 int2 = childCount
442 str2 = getParentLogKey(childEntry)
443 }, {
444 "DISMISS Already Parent-Dismissed $str1 ($int1/$int2) with summary $str2"
445 })
446 }
447
logLocallyDismissNonExistentNotifnull448 fun logLocallyDismissNonExistentNotif(entry: NotificationEntry, index: Int, count: Int) {
449 buffer.log(TAG, INFO, {
450 str1 = entry.logKey
451 int1 = index
452 int2 = count
453 }, {
454 "LOCALLY DISMISS Non Existent $str1 ($int1/$int2)"
455 })
456 }
457
logLocallyDismissMismatchedEntrynull458 fun logLocallyDismissMismatchedEntry(
459 entry: NotificationEntry,
460 index: Int,
461 count: Int,
462 storedEntry: NotificationEntry
463 ) {
464 buffer.log(TAG, INFO, {
465 str1 = entry.logKey
466 int1 = index
467 int2 = count
468 str2 = Integer.toHexString(entry.hashCode())
469 str3 = Integer.toHexString(storedEntry.hashCode())
470 }, {
471 "LOCALLY DISMISS Mismatch $str1 ($int1/$int2): dismissing @$str2 but stored @$str3"
472 })
473 }
474
logLocallyDismissAlreadyDismissedNotifnull475 fun logLocallyDismissAlreadyDismissedNotif(
476 entry: NotificationEntry,
477 index: Int,
478 count: Int
479 ) {
480 buffer.log(TAG, INFO, {
481 str1 = entry.logKey
482 int1 = index
483 int2 = count
484 }, {
485 "LOCALLY DISMISS Already Dismissed $str1 ($int1/$int2)"
486 })
487 }
488
logLocallyDismissAlreadyParentDismissedNotifnull489 fun logLocallyDismissAlreadyParentDismissedNotif(
490 entry: NotificationEntry,
491 index: Int,
492 count: Int
493 ) {
494 buffer.log(TAG, INFO, {
495 str1 = entry.logKey
496 int1 = index
497 int2 = count
498 }, {
499 "LOCALLY DISMISS Already Dismissed $str1 ($int1/$int2)"
500 })
501 }
502
logLocallyDismissAlreadyDismissedChildnull503 fun logLocallyDismissAlreadyDismissedChild(
504 childEntry: NotificationEntry,
505 parentEntry: NotificationEntry,
506 parentIndex: Int,
507 parentCount: Int
508 ) {
509 buffer.log(TAG, INFO, {
510 str1 = childEntry.logKey
511 str2 = parentEntry.logKey
512 int1 = parentIndex
513 int2 = parentCount
514 }, {
515 "LOCALLY DISMISS Already Dismissed Child $str1 of parent $str2 ($int1/$int2)"
516 })
517 }
518
logLocallyDismissAlreadyParentDismissedChildnull519 fun logLocallyDismissAlreadyParentDismissedChild(
520 childEntry: NotificationEntry,
521 parentEntry: NotificationEntry,
522 parentIndex: Int,
523 parentCount: Int
524 ) {
525 buffer.log(TAG, INFO, {
526 str1 = childEntry.logKey
527 str2 = parentEntry.logKey
528 int1 = parentIndex
529 int2 = parentCount
530 }, {
531 "LOCALLY DISMISS Already Parent-Dismissed Child $str1 of parent $str2 ($int1/$int2)"
532 })
533 }
534
logCancelLocalDismissalNotDismissedNotifnull535 fun logCancelLocalDismissalNotDismissedNotif(entry: NotificationEntry) {
536 buffer.log(TAG, INFO, {
537 str1 = entry.logKey
538 }, {
539 "CANCEL LOCAL DISMISS Not Dismissed $str1"
540 })
541 }
542 }
543
544 private const val TAG = "NotifCollection"
545