1 /* 2 * 3 * 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.app.Notification 21 import android.app.PendingIntent 22 import android.app.Person 23 import android.content.Intent 24 import android.graphics.Bitmap 25 import android.graphics.drawable.Icon 26 import android.stats.sysui.NotificationEnums 27 import android.widget.RemoteViews 28 import androidx.test.ext.junit.runners.AndroidJUnit4 29 import androidx.test.filters.SmallTest 30 import com.android.systemui.SysuiTestCase 31 import com.android.systemui.statusbar.notification.NotificationUtils 32 import com.android.systemui.statusbar.notification.collection.NotificationEntry 33 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder 34 import com.google.common.truth.Truth.assertThat 35 import org.junit.Test 36 import org.junit.runner.RunWith 37 38 @SmallTest 39 @RunWith(AndroidJUnit4::class) 40 class NotificationMemoryMeterTest : SysuiTestCase() { 41 42 @Test currentNotificationMemoryUse_plainNotificationnull43 fun currentNotificationMemoryUse_plainNotification() { 44 val notification = createBasicNotification().build() 45 val memoryUse = 46 NotificationMemoryMeter.notificationMemoryUse(createNotificationEntry(notification)) 47 assertNotificationObjectSizes( 48 memoryUse, 49 smallIcon = notification.smallIcon.bitmap.allocationByteCount, 50 largeIcon = notification.getLargeIcon().bitmap.allocationByteCount, 51 extras = 3316, 52 bigPicture = 0, 53 extender = 0, 54 style = NotificationEnums.STYLE_NONE, 55 styleIcon = 0, 56 hasCustomView = false, 57 ) 58 } 59 60 @Test currentNotificationMemoryUse_rankerGroupNotificationnull61 fun currentNotificationMemoryUse_rankerGroupNotification() { 62 val notification = createBasicNotification().build() 63 val memoryUse = 64 NotificationMemoryMeter.notificationMemoryUse( 65 createNotificationEntry(createBasicNotification().setGroup("ranker_group").build()) 66 ) 67 assertNotificationObjectSizes( 68 memoryUse, 69 smallIcon = notification.smallIcon.bitmap.allocationByteCount, 70 largeIcon = notification.getLargeIcon().bitmap.allocationByteCount, 71 extras = 3316, 72 bigPicture = 0, 73 extender = 0, 74 style = NotificationEnums.STYLE_RANKER_GROUP, 75 styleIcon = 0, 76 hasCustomView = false, 77 ) 78 } 79 80 @Test currentNotificationMemoryUse_plainNotification_dontDoubleCountSameBitmapnull81 fun currentNotificationMemoryUse_plainNotification_dontDoubleCountSameBitmap() { 82 val icon = Icon.createWithBitmap(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)) 83 val notification = createBasicNotification().setLargeIcon(icon).setSmallIcon(icon).build() 84 val memoryUse = 85 NotificationMemoryMeter.notificationMemoryUse(createNotificationEntry(notification)) 86 assertNotificationObjectSizes( 87 memoryUse = memoryUse, 88 smallIcon = notification.smallIcon.bitmap.allocationByteCount, 89 largeIcon = 0, 90 extras = 3316, 91 bigPicture = 0, 92 extender = 0, 93 style = NotificationEnums.STYLE_NONE, 94 styleIcon = 0, 95 hasCustomView = false, 96 ) 97 } 98 99 @Test currentNotificationMemoryUse_customViewNotification_marksTruenull100 fun currentNotificationMemoryUse_customViewNotification_marksTrue() { 101 val notification = 102 createBasicNotification() 103 .setCustomContentView( 104 RemoteViews(context.packageName, android.R.layout.list_content) 105 ) 106 .build() 107 val memoryUse = 108 NotificationMemoryMeter.notificationMemoryUse(createNotificationEntry(notification)) 109 assertNotificationObjectSizes( 110 memoryUse = memoryUse, 111 smallIcon = notification.smallIcon.bitmap.allocationByteCount, 112 largeIcon = notification.getLargeIcon().bitmap.allocationByteCount, 113 extras = 3384, 114 bigPicture = 0, 115 extender = 0, 116 style = NotificationEnums.STYLE_NONE, 117 styleIcon = 0, 118 hasCustomView = true, 119 ) 120 } 121 122 @Test currentNotificationMemoryUse_notificationWithDataIcon_calculatesCorrectlynull123 fun currentNotificationMemoryUse_notificationWithDataIcon_calculatesCorrectly() { 124 val dataIcon = Icon.createWithData(ByteArray(444444), 0, 444444) 125 val notification = 126 createBasicNotification().setLargeIcon(dataIcon).setSmallIcon(dataIcon).build() 127 val memoryUse = 128 NotificationMemoryMeter.notificationMemoryUse(createNotificationEntry(notification)) 129 assertNotificationObjectSizes( 130 memoryUse = memoryUse, 131 smallIcon = 444444, 132 largeIcon = 0, 133 extras = 3212, 134 bigPicture = 0, 135 extender = 0, 136 style = NotificationEnums.STYLE_NONE, 137 styleIcon = 0, 138 hasCustomView = false, 139 ) 140 } 141 142 @Test currentNotificationMemoryUse_bigPictureStylenull143 fun currentNotificationMemoryUse_bigPictureStyle() { 144 val bigPicture = 145 Icon.createWithBitmap(Bitmap.createBitmap(600, 400, Bitmap.Config.ARGB_8888)) 146 val bigPictureIcon = 147 Icon.createWithAdaptiveBitmap(Bitmap.createBitmap(386, 432, Bitmap.Config.ARGB_8888)) 148 val notification = 149 createBasicNotification() 150 .setStyle( 151 Notification.BigPictureStyle() 152 .bigPicture(bigPicture) 153 .bigLargeIcon(bigPictureIcon) 154 ) 155 .build() 156 val memoryUse = 157 NotificationMemoryMeter.notificationMemoryUse(createNotificationEntry(notification)) 158 assertNotificationObjectSizes( 159 memoryUse = memoryUse, 160 smallIcon = notification.smallIcon.bitmap.allocationByteCount, 161 largeIcon = notification.getLargeIcon().bitmap.allocationByteCount, 162 extras = 4092, 163 bigPicture = bigPicture.bitmap.allocationByteCount, 164 extender = 0, 165 style = NotificationEnums.STYLE_BIG_PICTURE, 166 styleIcon = bigPictureIcon.bitmap.allocationByteCount, 167 hasCustomView = false, 168 ) 169 } 170 171 @Test currentNotificationMemoryUse_callingStylenull172 fun currentNotificationMemoryUse_callingStyle() { 173 val personIcon = 174 Icon.createWithBitmap(Bitmap.createBitmap(386, 432, Bitmap.Config.ARGB_8888)) 175 val person = Person.Builder().setIcon(personIcon).setName("Person").build() 176 val fakeIntent = 177 PendingIntent.getActivity(context, 0, Intent(), PendingIntent.FLAG_IMMUTABLE) 178 val notification = 179 createBasicNotification() 180 .setStyle(Notification.CallStyle.forIncomingCall(person, fakeIntent, fakeIntent)) 181 .build() 182 val memoryUse = 183 NotificationMemoryMeter.notificationMemoryUse(createNotificationEntry(notification)) 184 assertNotificationObjectSizes( 185 memoryUse = memoryUse, 186 smallIcon = notification.smallIcon.bitmap.allocationByteCount, 187 largeIcon = notification.getLargeIcon().bitmap.allocationByteCount, 188 extras = 4084, 189 bigPicture = 0, 190 extender = 0, 191 style = NotificationEnums.STYLE_CALL, 192 styleIcon = personIcon.bitmap.allocationByteCount, 193 hasCustomView = false, 194 ) 195 } 196 197 @Test currentNotificationMemoryUse_messagingStylenull198 fun currentNotificationMemoryUse_messagingStyle() { 199 val personIcon = 200 Icon.createWithBitmap(Bitmap.createBitmap(386, 432, Bitmap.Config.ARGB_8888)) 201 val person = Person.Builder().setIcon(personIcon).setName("Person").build() 202 val message = Notification.MessagingStyle.Message("Message!", 4323, person) 203 val historicPersonIcon = 204 Icon.createWithBitmap(Bitmap.createBitmap(348, 382, Bitmap.Config.ARGB_8888)) 205 val historicPerson = 206 Person.Builder().setIcon(historicPersonIcon).setName("Historic person").build() 207 val historicMessage = 208 Notification.MessagingStyle.Message("Historic message!", 5848, historicPerson) 209 210 val notification = 211 createBasicNotification() 212 .setStyle( 213 Notification.MessagingStyle(person) 214 .addMessage(message) 215 .addHistoricMessage(historicMessage) 216 ) 217 .build() 218 val memoryUse = 219 NotificationMemoryMeter.notificationMemoryUse(createNotificationEntry(notification)) 220 assertNotificationObjectSizes( 221 memoryUse = memoryUse, 222 smallIcon = notification.smallIcon.bitmap.allocationByteCount, 223 largeIcon = notification.getLargeIcon().bitmap.allocationByteCount, 224 extras = 5024, 225 bigPicture = 0, 226 extender = 0, 227 style = NotificationEnums.STYLE_MESSAGING, 228 styleIcon = 229 personIcon.bitmap.allocationByteCount + 230 historicPersonIcon.bitmap.allocationByteCount, 231 hasCustomView = false, 232 ) 233 } 234 235 @Test currentNotificationMemoryUse_carExtendernull236 fun currentNotificationMemoryUse_carExtender() { 237 val carIcon = Bitmap.createBitmap(432, 322, Bitmap.Config.ARGB_8888) 238 val extender = Notification.CarExtender().setLargeIcon(carIcon) 239 val notification = createBasicNotification().extend(extender).build() 240 val memoryUse = 241 NotificationMemoryMeter.notificationMemoryUse(createNotificationEntry(notification)) 242 assertNotificationObjectSizes( 243 memoryUse = memoryUse, 244 smallIcon = notification.smallIcon.bitmap.allocationByteCount, 245 largeIcon = notification.getLargeIcon().bitmap.allocationByteCount, 246 extras = 3612, 247 bigPicture = 0, 248 extender = 556656, 249 style = NotificationEnums.STYLE_NONE, 250 styleIcon = 0, 251 hasCustomView = false, 252 ) 253 } 254 255 @Test currentNotificationMemoryUse_tvWearExtendernull256 fun currentNotificationMemoryUse_tvWearExtender() { 257 val tvExtender = Notification.TvExtender().setChannel("channel2") 258 val wearBackground = Bitmap.createBitmap(443, 433, Bitmap.Config.ARGB_8888) 259 val wearExtender = Notification.WearableExtender().setBackground(wearBackground) 260 val notification = createBasicNotification().extend(tvExtender).extend(wearExtender).build() 261 val memoryUse = 262 NotificationMemoryMeter.notificationMemoryUse(createNotificationEntry(notification)) 263 assertNotificationObjectSizes( 264 memoryUse = memoryUse, 265 smallIcon = notification.smallIcon.bitmap.allocationByteCount, 266 largeIcon = notification.getLargeIcon().bitmap.allocationByteCount, 267 extras = 3820, 268 bigPicture = 0, 269 extender = 388 + wearBackground.allocationByteCount, 270 style = NotificationEnums.STYLE_NONE, 271 styleIcon = 0, 272 hasCustomView = false, 273 ) 274 } 275 createBasicNotificationnull276 private fun createBasicNotification(): Notification.Builder { 277 val smallIcon = 278 Icon.createWithBitmap(Bitmap.createBitmap(250, 250, Bitmap.Config.ARGB_8888)) 279 val largeIcon = 280 Icon.createWithBitmap(Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888)) 281 return Notification.Builder(context) 282 .setSmallIcon(smallIcon) 283 .setLargeIcon(largeIcon) 284 .setContentTitle("This is a title") 285 .setContentText("This is content text.") 286 } 287 288 /** This will generate a nicer error message than comparing objects */ assertNotificationObjectSizesnull289 private fun assertNotificationObjectSizes( 290 memoryUse: NotificationMemoryUsage, 291 smallIcon: Int, 292 largeIcon: Int, 293 extras: Int, 294 bigPicture: Int, 295 extender: Int, 296 style: Int, 297 styleIcon: Int, 298 hasCustomView: Boolean, 299 ) { 300 assertThat(memoryUse.packageName).isEqualTo("test_pkg") 301 assertThat(memoryUse.notificationKey) 302 .isEqualTo(NotificationUtils.logKey("0|test_pkg|0|test|0")) 303 assertThat(memoryUse.objectUsage.smallIcon).isEqualTo(smallIcon) 304 assertThat(memoryUse.objectUsage.largeIcon).isEqualTo(largeIcon) 305 assertThat(memoryUse.objectUsage.bigPicture).isEqualTo(bigPicture) 306 assertThat(memoryUse.objectUsage.style).isEqualTo(style) 307 assertThat(memoryUse.objectUsage.styleIcon).isEqualTo(styleIcon) 308 assertThat(memoryUse.objectUsage.hasCustomView).isEqualTo(hasCustomView) 309 } 310 getUseObjectnull311 private fun getUseObject( 312 singleItemUseList: List<NotificationMemoryUsage>, 313 ): NotificationMemoryUsage { 314 assertThat(singleItemUseList).hasSize(1) 315 return singleItemUseList[0] 316 } 317 createNotificationEntrynull318 private fun createNotificationEntry( 319 notification: Notification, 320 ): NotificationEntry = 321 NotificationEntryBuilder().setTag("test").setNotification(notification).build() 322 } 323