• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
<lambda>null3  * Copyright (C) 2022 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.systemui.statusbar.notification.logging
19 
20 import android.stats.sysui.NotificationEnums
21 import android.util.Log
22 import com.android.systemui.Dumpable
23 import com.android.systemui.dagger.SysUISingleton
24 import com.android.systemui.dump.DumpManager
25 import com.android.systemui.dump.DumpsysTableLogger
26 import com.android.systemui.dump.Row
27 import com.android.systemui.statusbar.notification.collection.NotifPipeline
28 import java.io.PrintWriter
29 import javax.inject.Inject
30 
31 /** Dumps current notification memory use to bug reports for easier debugging. */
32 @SysUISingleton
33 class NotificationMemoryDumper
34 @Inject
35 constructor(val dumpManager: DumpManager, val notificationPipeline: NotifPipeline) : Dumpable {
36 
37     fun init() {
38         dumpManager.registerNormalDumpable(javaClass.simpleName, this)
39         Log.i("NotificationMemory", "Registered dumpable.")
40     }
41 
42     override fun dump(pw: PrintWriter, args: Array<out String>) {
43         val memoryUse =
44             NotificationMemoryMeter.notificationMemoryUse(notificationPipeline.allNotifs)
45                 .sortedWith(compareBy({ it.packageName }, { it.notificationKey }))
46         dumpNotificationObjects(pw, memoryUse)
47         dumpNotificationViewUsage(pw, memoryUse)
48     }
49 
50     /** Renders a table of notification object usage into passed [PrintWriter]. */
51     private fun dumpNotificationObjects(pw: PrintWriter, memoryUse: List<NotificationMemoryUsage>) {
52         val columns =
53             listOf(
54                 "Package",
55                 "Small Icon",
56                 "Large Icon",
57                 "Style",
58                 "Style Icon",
59                 "Big Picture",
60                 "Extender",
61                 "Extras",
62                 "Custom View",
63                 "Key"
64             )
65         val rows: List<Row> =
66             memoryUse.map {
67                 listOf(
68                     it.packageName,
69                     toKb(it.objectUsage.smallIcon),
70                     toKb(it.objectUsage.largeIcon),
71                     styleEnumToString(it.objectUsage.style),
72                     toKb(it.objectUsage.styleIcon),
73                     toKb(it.objectUsage.bigPicture),
74                     toKb(it.objectUsage.extender),
75                     toKb(it.objectUsage.extras),
76                     it.objectUsage.hasCustomView.toString(),
77                     // | is a  field delimiter in the output format so we need to replace
78                     // it to avoid breakage.
79                     it.notificationKey.replace('|', '│')
80                 )
81             }
82 
83         // Calculate totals for easily glanceable summary.
84         data class Totals(
85             var smallIcon: Int = 0,
86             var largeIcon: Int = 0,
87             var styleIcon: Int = 0,
88             var bigPicture: Int = 0,
89             var extender: Int = 0,
90             var extras: Int = 0,
91         )
92 
93         val totals =
94             memoryUse.fold(Totals()) { t, usage ->
95                 t.smallIcon += usage.objectUsage.smallIcon
96                 t.largeIcon += usage.objectUsage.largeIcon
97                 t.styleIcon += usage.objectUsage.styleIcon
98                 t.bigPicture += usage.objectUsage.bigPicture
99                 t.extender += usage.objectUsage.extender
100                 t.extras += usage.objectUsage.extras
101                 t
102             }
103 
104         val totalsRow: List<Row> =
105             listOf(
106                 listOf(
107                     "TOTALS",
108                     toKb(totals.smallIcon),
109                     toKb(totals.largeIcon),
110                     "",
111                     toKb(totals.styleIcon),
112                     toKb(totals.bigPicture),
113                     toKb(totals.extender),
114                     toKb(totals.extras),
115                     "",
116                     ""
117                 )
118             )
119         val tableLogger = DumpsysTableLogger("Notification Object Usage", columns, rows + totalsRow)
120         tableLogger.printTableData(pw)
121     }
122 
123     /** Renders a table of notification view usage into passed [PrintWriter] */
124     private fun dumpNotificationViewUsage(
125         pw: PrintWriter,
126         memoryUse: List<NotificationMemoryUsage>,
127     ) {
128 
129         data class Totals(
130             var smallIcon: Int = 0,
131             var largeIcon: Int = 0,
132             var style: Int = 0,
133             var customViews: Int = 0,
134             var softwareBitmapsPenalty: Int = 0,
135         )
136 
137         val columns =
138             listOf(
139                 "Package",
140                 "View Type",
141                 "Small Icon",
142                 "Large Icon",
143                 "Style Use",
144                 "Custom View",
145                 "Software Bitmaps",
146                 "Key"
147             )
148         val rows =
149             memoryUse
150                 .filter { it.viewUsage.isNotEmpty() }
151                 .flatMap { use ->
152                     use.viewUsage.map { view ->
153                         listOf(
154                             use.packageName,
155                             view.viewType.toString(),
156                             toKb(view.smallIcon),
157                             toKb(view.largeIcon),
158                             toKb(view.style),
159                             toKb(view.customViews),
160                             toKb(view.softwareBitmapsPenalty),
161                             // | is a  field delimiter in the output format so we need to replace
162                             // it to avoid breakage.
163                             use.notificationKey.replace('|', '│')
164                         )
165                     }
166                 }
167 
168         val totals = Totals()
169         memoryUse
170             .filter { it.viewUsage.isNotEmpty() }
171             .map { it.viewUsage.firstOrNull { view -> view.viewType == ViewType.TOTAL } }
172             .filterNotNull()
173             .forEach { view ->
174                 totals.smallIcon += view.smallIcon
175                 totals.largeIcon += view.largeIcon
176                 totals.style += view.style
177                 totals.customViews += view.customViews
178                 totals.softwareBitmapsPenalty += view.softwareBitmapsPenalty
179             }
180 
181         val totalsRow: List<Row> =
182             listOf(
183                 listOf(
184                     "TOTALS",
185                     "",
186                     toKb(totals.smallIcon),
187                     toKb(totals.largeIcon),
188                     toKb(totals.style),
189                     toKb(totals.customViews),
190                     toKb(totals.softwareBitmapsPenalty),
191                     ""
192                 )
193             )
194         val tableLogger = DumpsysTableLogger("Notification View Usage", columns, rows + totalsRow)
195         tableLogger.printTableData(pw)
196     }
197 
198     private fun styleEnumToString(styleEnum: Int): String =
199         when (styleEnum) {
200             NotificationEnums.STYLE_UNSPECIFIED -> "Unspecified"
201             NotificationEnums.STYLE_NONE -> "None"
202             NotificationEnums.STYLE_BIG_PICTURE -> "BigPicture"
203             NotificationEnums.STYLE_BIG_TEXT -> "BigText"
204             NotificationEnums.STYLE_CALL -> "Call"
205             NotificationEnums.STYLE_DECORATED_CUSTOM_VIEW -> "DCustomView"
206             NotificationEnums.STYLE_INBOX -> "Inbox"
207             NotificationEnums.STYLE_MEDIA -> "Media"
208             NotificationEnums.STYLE_MESSAGING -> "Messaging"
209             NotificationEnums.STYLE_RANKER_GROUP -> "RankerGroup"
210             else -> "Unknown"
211         }
212 
213     private fun toKb(bytes: Int): String {
214         if (bytes == 0) {
215             return "--"
216         }
217 
218         return "%.2f KB".format(bytes / 1024f)
219     }
220 }
221