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.plugins.log.LogBuffer
25 import com.android.systemui.plugins.log.LogLevel.DEBUG
26 import com.android.systemui.plugins.log.LogLevel.ERROR
27 import com.android.systemui.plugins.log.LogLevel.INFO
28 import com.android.systemui.plugins.log.LogLevel.WARNING
29 import com.android.systemui.plugins.log.LogLevel.WTF
30 import com.android.systemui.statusbar.notification.collection.NotifCollection
31 import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason
32 import com.android.systemui.statusbar.notification.collection.NotifCollection.FutureDismissal
33 import com.android.systemui.statusbar.notification.collection.NotificationEntry
34 import com.android.systemui.statusbar.notification.logKey
35 import javax.inject.Inject
36
cancellationReasonDebugStringnull37 fun cancellationReasonDebugString(@CancellationReason reason: Int) =
38 "$reason:" + when (reason) {
39 -1 -> "REASON_NOT_CANCELED" // NotifCollection.REASON_NOT_CANCELED
40 NotifCollection.REASON_UNKNOWN -> "REASON_UNKNOWN"
41 NotificationListenerService.REASON_CLICK -> "REASON_CLICK"
42 NotificationListenerService.REASON_CANCEL -> "REASON_CANCEL"
43 NotificationListenerService.REASON_CANCEL_ALL -> "REASON_CANCEL_ALL"
44 NotificationListenerService.REASON_ERROR -> "REASON_ERROR"
45 NotificationListenerService.REASON_PACKAGE_CHANGED -> "REASON_PACKAGE_CHANGED"
46 NotificationListenerService.REASON_USER_STOPPED -> "REASON_USER_STOPPED"
47 NotificationListenerService.REASON_PACKAGE_BANNED -> "REASON_PACKAGE_BANNED"
48 NotificationListenerService.REASON_APP_CANCEL -> "REASON_APP_CANCEL"
49 NotificationListenerService.REASON_APP_CANCEL_ALL -> "REASON_APP_CANCEL_ALL"
50 NotificationListenerService.REASON_LISTENER_CANCEL -> "REASON_LISTENER_CANCEL"
51 NotificationListenerService.REASON_LISTENER_CANCEL_ALL -> "REASON_LISTENER_CANCEL_ALL"
52 NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED -> "REASON_GROUP_SUMMARY_CANCELED"
53 NotificationListenerService.REASON_GROUP_OPTIMIZATION -> "REASON_GROUP_OPTIMIZATION"
54 NotificationListenerService.REASON_PACKAGE_SUSPENDED -> "REASON_PACKAGE_SUSPENDED"
55 NotificationListenerService.REASON_PROFILE_TURNED_OFF -> "REASON_PROFILE_TURNED_OFF"
56 NotificationListenerService.REASON_UNAUTOBUNDLED -> "REASON_UNAUTOBUNDLED"
57 NotificationListenerService.REASON_CHANNEL_BANNED -> "REASON_CHANNEL_BANNED"
58 NotificationListenerService.REASON_SNOOZED -> "REASON_SNOOZED"
59 NotificationListenerService.REASON_TIMEOUT -> "REASON_TIMEOUT"
60 NotificationListenerService.REASON_CHANNEL_REMOVED -> "REASON_CHANNEL_REMOVED"
61 NotificationListenerService.REASON_CLEAR_DATA -> "REASON_CLEAR_DATA"
62 NotificationListenerService.REASON_ASSISTANT_CANCEL -> "REASON_ASSISTANT_CANCEL"
63 else -> "unknown"
64 }
65
66 class NotifCollectionLogger @Inject constructor(
67 @NotificationLog private val buffer: LogBuffer
68 ) {
logNotifPostednull69 fun logNotifPosted(entry: NotificationEntry) {
70 buffer.log(TAG, INFO, {
71 str1 = entry.logKey
72 }, {
73 "POSTED $str1"
74 })
75 }
76
logNotifGroupPostednull77 fun logNotifGroupPosted(groupKey: String, batchSize: Int) {
78 buffer.log(TAG, INFO, {
79 str1 = logKey(groupKey)
80 int1 = batchSize
81 }, {
82 "POSTED GROUP $str1 ($int1 events)"
83 })
84 }
85
logNotifUpdatednull86 fun logNotifUpdated(entry: NotificationEntry) {
87 buffer.log(TAG, INFO, {
88 str1 = entry.logKey
89 }, {
90 "UPDATED $str1"
91 })
92 }
93
logNotifRemovednull94 fun logNotifRemoved(sbn: StatusBarNotification, @CancellationReason reason: Int) {
95 buffer.log(TAG, INFO, {
96 str1 = sbn.logKey
97 int1 = reason
98 }, {
99 "REMOVED $str1 reason=${cancellationReasonDebugString(int1)}"
100 })
101 }
102
logNotifReleasednull103 fun logNotifReleased(entry: NotificationEntry) {
104 buffer.log(TAG, INFO, {
105 str1 = entry.logKey
106 }, {
107 "RELEASED $str1"
108 })
109 }
110
logNotifDismissednull111 fun logNotifDismissed(entry: NotificationEntry) {
112 buffer.log(TAG, INFO, {
113 str1 = entry.logKey
114 }, {
115 "DISMISSED $str1"
116 })
117 }
118
logNonExistentNotifDismissednull119 fun logNonExistentNotifDismissed(entry: NotificationEntry) {
120 buffer.log(TAG, INFO, {
121 str1 = entry.logKey
122 }, {
123 "DISMISSED Non Existent $str1"
124 })
125 }
126
logChildDismissednull127 fun logChildDismissed(entry: NotificationEntry) {
128 buffer.log(TAG, DEBUG, {
129 str1 = entry.logKey
130 }, {
131 "CHILD DISMISSED (inferred): $str1"
132 })
133 }
134
logDismissAllnull135 fun logDismissAll(userId: Int) {
136 buffer.log(TAG, INFO, {
137 int1 = userId
138 }, {
139 "DISMISS ALL notifications for user $int1"
140 })
141 }
142
logDismissOnAlreadyCanceledEntrynull143 fun logDismissOnAlreadyCanceledEntry(entry: NotificationEntry) {
144 buffer.log(TAG, DEBUG, {
145 str1 = entry.logKey
146 }, {
147 "Dismiss on $str1, which was already canceled. Trying to remove..."
148 })
149 }
150
logNotifDismissedInterceptednull151 fun logNotifDismissedIntercepted(entry: NotificationEntry) {
152 buffer.log(TAG, INFO, {
153 str1 = entry.logKey
154 }, {
155 "DISMISS INTERCEPTED $str1"
156 })
157 }
158
logNotifClearAllDismissalInterceptednull159 fun logNotifClearAllDismissalIntercepted(entry: NotificationEntry) {
160 buffer.log(TAG, INFO, {
161 str1 = entry.logKey
162 }, {
163 "CLEAR ALL DISMISSAL INTERCEPTED $str1"
164 })
165 }
166
logNotifInternalUpdatenull167 fun logNotifInternalUpdate(entry: NotificationEntry, name: String, reason: String) {
168 buffer.log(TAG, INFO, {
169 str1 = entry.logKey
170 str2 = name
171 str3 = reason
172 }, {
173 "UPDATED INTERNALLY $str1 BY $str2 BECAUSE $str3"
174 })
175 }
176
logNotifInternalUpdateFailednull177 fun logNotifInternalUpdateFailed(sbn: StatusBarNotification, name: String, reason: String) {
178 buffer.log(TAG, INFO, {
179 str1 = sbn.logKey
180 str2 = name
181 str3 = reason
182 }, {
183 "FAILED INTERNAL UPDATE $str1 BY $str2 BECAUSE $str3"
184 })
185 }
186
logNoNotificationToRemoveWithKeynull187 fun logNoNotificationToRemoveWithKey(
188 sbn: StatusBarNotification,
189 @CancellationReason reason: Int
190 ) {
191 buffer.log(TAG, ERROR, {
192 str1 = sbn.logKey
193 int1 = reason
194 }, {
195 "No notification to remove with key $str1 reason=${cancellationReasonDebugString(int1)}"
196 })
197 }
198
logMissingRankingsnull199 fun logMissingRankings(
200 newlyInconsistentEntries: List<NotificationEntry>,
201 totalInconsistent: Int,
202 rankingMap: RankingMap
203 ) {
204 buffer.log(TAG, WARNING, {
205 int1 = totalInconsistent
206 int2 = newlyInconsistentEntries.size
207 str1 = newlyInconsistentEntries.joinToString { it.logKey ?: "null" }
208 }, {
209 "Ranking update is missing ranking for $int1 entries ($int2 new): $str1"
210 })
211 buffer.log(TAG, DEBUG, {
212 str1 = rankingMap.orderedKeys.map { logKey(it) ?: "null" }.toString()
213 }, {
214 "Ranking map contents: $str1"
215 })
216 }
217
logRecoveredRankingsnull218 fun logRecoveredRankings(newlyConsistentKeys: List<String>, totalInconsistent: Int) {
219 buffer.log(TAG, INFO, {
220 int1 = totalInconsistent
221 int1 = newlyConsistentKeys.size
222 str1 = newlyConsistentKeys.joinToString { logKey(it) ?: "null" }
223 }, {
224 "Ranking update now contains rankings for $int1 previously inconsistent entries: $str1"
225 })
226 }
227
logMissingNotificationsnull228 fun logMissingNotifications(
229 newlyMissingKeys: List<String>,
230 totalMissing: Int,
231 ) {
232 buffer.log(TAG, WARNING, {
233 int1 = totalMissing
234 int2 = newlyMissingKeys.size
235 str1 = newlyMissingKeys.joinToString { logKey(it) ?: "null" }
236 }, {
237 "Collection missing $int1 entries in ranking update. Just lost $int2: $str1"
238 })
239 }
240
logFoundNotificationsnull241 fun logFoundNotifications(
242 newlyFoundKeys: List<String>,
243 totalMissing: Int,
244 ) {
245 buffer.log(TAG, INFO, {
246 int1 = totalMissing
247 int2 = newlyFoundKeys.size
248 str1 = newlyFoundKeys.joinToString { logKey(it) ?: "null" }
249 }, {
250 "Collection missing $int1 entries in ranking update. Just found $int2: $str1"
251 })
252 }
253
logRemoteExceptionOnNotificationClearnull254 fun logRemoteExceptionOnNotificationClear(entry: NotificationEntry, e: RemoteException) {
255 buffer.log(TAG, WTF, {
256 str1 = entry.logKey
257 str2 = e.toString()
258 }, {
259 "RemoteException while attempting to clear $str1:\n$str2"
260 })
261 }
262
logRemoteExceptionOnClearAllNotificationsnull263 fun logRemoteExceptionOnClearAllNotifications(e: RemoteException) {
264 buffer.log(TAG, WTF, {
265 str1 = e.toString()
266 }, {
267 "RemoteException while attempting to clear all notifications:\n$str1"
268 })
269 }
270
logLifetimeExtendednull271 fun logLifetimeExtended(entry: NotificationEntry, extender: NotifLifetimeExtender) {
272 buffer.log(TAG, INFO, {
273 str1 = entry.logKey
274 str2 = extender.name
275 }, {
276 "LIFETIME EXTENDED: $str1 by $str2"
277 })
278 }
279
logLifetimeExtensionEndednull280 fun logLifetimeExtensionEnded(
281 entry: NotificationEntry,
282 extender: NotifLifetimeExtender,
283 totalExtenders: Int
284 ) {
285 buffer.log(TAG, INFO, {
286 str1 = entry.logKey
287 str2 = extender.name
288 int1 = totalExtenders
289 }, {
290 "LIFETIME EXTENSION ENDED for $str1 by '$str2'; $int1 remaining extensions"
291 })
292 }
293
logIgnoredErrornull294 fun logIgnoredError(message: String?) {
295 buffer.log(TAG, ERROR, {
296 str1 = message
297 }, {
298 "ERROR suppressed due to initialization forgiveness: $str1"
299 })
300 }
301
logEntryBeingExtendedNotInCollectionnull302 fun logEntryBeingExtendedNotInCollection(
303 entry: NotificationEntry,
304 extender: NotifLifetimeExtender,
305 collectionEntryIs: String
306 ) {
307 buffer.log(TAG, WARNING, {
308 str1 = entry.logKey
309 str2 = extender.name
310 str3 = collectionEntryIs
311 }, {
312 "While ending lifetime extension by $str2 of $str1, entry in collection is $str3"
313 })
314 }
315
logFutureDismissalReusednull316 fun logFutureDismissalReused(dismissal: FutureDismissal) {
317 buffer.log(TAG, INFO, {
318 str1 = dismissal.label
319 }, {
320 "Reusing existing registration: $str1"
321 })
322 }
323
logFutureDismissalRegisterednull324 fun logFutureDismissalRegistered(dismissal: FutureDismissal) {
325 buffer.log(TAG, DEBUG, {
326 str1 = dismissal.label
327 }, {
328 "Registered: $str1"
329 })
330 }
331
logFutureDismissalDoubleCancelledByServernull332 fun logFutureDismissalDoubleCancelledByServer(dismissal: FutureDismissal) {
333 buffer.log(TAG, WARNING, {
334 str1 = dismissal.label
335 }, {
336 "System server double cancelled: $str1"
337 })
338 }
339
logFutureDismissalDoubleRunnull340 fun logFutureDismissalDoubleRun(dismissal: FutureDismissal) {
341 buffer.log(TAG, WARNING, {
342 str1 = dismissal.label
343 }, {
344 "Double run: $str1"
345 })
346 }
347
logFutureDismissalAlreadyCancelledByServernull348 fun logFutureDismissalAlreadyCancelledByServer(dismissal: FutureDismissal) {
349 buffer.log(TAG, DEBUG, {
350 str1 = dismissal.label
351 }, {
352 "Ignoring: entry already cancelled by server: $str1"
353 })
354 }
355
logFutureDismissalGotSystemServerCancelnull356 fun logFutureDismissalGotSystemServerCancel(
357 dismissal: FutureDismissal,
358 @CancellationReason cancellationReason: Int
359 ) {
360 buffer.log(TAG, DEBUG, {
361 str1 = dismissal.label
362 int1 = cancellationReason
363 }, {
364 "SystemServer cancelled: $str1 reason=${cancellationReasonDebugString(int1)}"
365 })
366 }
367
logFutureDismissalDismissingnull368 fun logFutureDismissalDismissing(dismissal: FutureDismissal, type: String) {
369 buffer.log(TAG, DEBUG, {
370 str1 = dismissal.label
371 str2 = type
372 }, {
373 "Dismissing $str2 for: $str1"
374 })
375 }
376
logFutureDismissalMismatchedEntrynull377 fun logFutureDismissalMismatchedEntry(
378 dismissal: FutureDismissal,
379 type: String,
380 latestEntry: NotificationEntry?
381 ) {
382 buffer.log(TAG, WARNING, {
383 str1 = dismissal.label
384 str2 = type
385 str3 = latestEntry.logKey
386 }, {
387 "Mismatch: current $str2 is $str3 for: $str1"
388 })
389 }
390 }
391
392 private const val TAG = "NotifCollection"
393