Merge "Fix SysUI crash when memory collection times out" into tm-qpr-dev am: 884e40d7db

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21150128

Change-Id: I3163adc4c818f677945e8740403fa5439535ea2f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Jernej Virag
2023-02-02 20:55:38 +00:00
committed by Automerger Merge Worker
4 changed files with 80 additions and 44 deletions

View File

@@ -63,7 +63,7 @@ import javax.inject.Inject;
* are not. * are not.
*/ */
public class NotificationLogger implements StateListener { public class NotificationLogger implements StateListener {
private static final String TAG = "NotificationLogger"; static final String TAG = "NotificationLogger";
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG); private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
/** The minimum delay in ms between reports of notification visibility. */ /** The minimum delay in ms between reports of notification visibility. */

View File

@@ -18,6 +18,7 @@
package com.android.systemui.statusbar.notification.logging package com.android.systemui.statusbar.notification.logging
import android.app.StatsManager import android.app.StatsManager
import android.util.Log
import android.util.StatsEvent import android.util.StatsEvent
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Background
@@ -25,6 +26,7 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.shared.system.SysUiStatsLog import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.util.traceSection import com.android.systemui.util.traceSection
import java.lang.Exception
import java.util.concurrent.Executor import java.util.concurrent.Executor
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.roundToInt import kotlin.math.roundToInt
@@ -82,43 +84,56 @@ constructor(
return StatsManager.PULL_SKIP return StatsManager.PULL_SKIP
} }
// Notifications can only be retrieved on the main thread, so switch to that thread. try {
val notifications = getAllNotificationsOnMainThread() // Notifications can only be retrieved on the main thread, so switch to that thread.
val notificationMemoryUse = val notifications = getAllNotificationsOnMainThread()
NotificationMemoryMeter.notificationMemoryUse(notifications) val notificationMemoryUse =
.sortedWith( NotificationMemoryMeter.notificationMemoryUse(notifications)
compareBy( .sortedWith(
{ it.packageName }, compareBy(
{ it.objectUsage.style }, { it.packageName },
{ it.notificationKey } { it.objectUsage.style },
{ it.notificationKey }
)
)
val usageData = aggregateMemoryUsageData(notificationMemoryUse)
usageData.forEach { (_, use) ->
data.add(
SysUiStatsLog.buildStatsEvent(
SysUiStatsLog.NOTIFICATION_MEMORY_USE,
use.uid,
use.style,
use.count,
use.countWithInflatedViews,
toKb(use.smallIconObject),
use.smallIconBitmapCount,
toKb(use.largeIconObject),
use.largeIconBitmapCount,
toKb(use.bigPictureObject),
use.bigPictureBitmapCount,
toKb(use.extras),
toKb(use.extenders),
toKb(use.smallIconViews),
toKb(use.largeIconViews),
toKb(use.systemIconViews),
toKb(use.styleViews),
toKb(use.customViews),
toKb(use.softwareBitmaps),
use.seenCount
) )
) )
val usageData = aggregateMemoryUsageData(notificationMemoryUse) }
usageData.forEach { (_, use) -> } catch (e: InterruptedException) {
data.add( // This can happen if the device is sleeping or view walking takes too long.
SysUiStatsLog.buildStatsEvent( // The statsd collector will interrupt the thread and we need to handle it
SysUiStatsLog.NOTIFICATION_MEMORY_USE, // gracefully.
use.uid, Log.w(NotificationLogger.TAG, "Timed out when measuring notification memory.", e)
use.style, return@traceSection StatsManager.PULL_SKIP
use.count, } catch (e: Exception) {
use.countWithInflatedViews, // Error while collecting data, this should not crash prod SysUI. Just
toKb(use.smallIconObject), // log WTF and move on.
use.smallIconBitmapCount, Log.wtf(NotificationLogger.TAG, "Failed to measure notification memory.", e)
toKb(use.largeIconObject), return@traceSection StatsManager.PULL_SKIP
use.largeIconBitmapCount,
toKb(use.bigPictureObject),
use.bigPictureBitmapCount,
toKb(use.extras),
toKb(use.extenders),
toKb(use.smallIconViews),
toKb(use.largeIconViews),
toKb(use.systemIconViews),
toKb(use.styleViews),
toKb(use.customViews),
toKb(use.softwareBitmaps),
use.seenCount
)
)
} }
return StatsManager.PULL_SUCCESS return StatsManager.PULL_SUCCESS

View File

@@ -184,19 +184,21 @@ internal object NotificationMemoryViewWalker {
private fun computeDrawableUse(drawable: Drawable, seenObjects: HashSet<Int>): Int = private fun computeDrawableUse(drawable: Drawable, seenObjects: HashSet<Int>): Int =
when (drawable) { when (drawable) {
is BitmapDrawable -> { is BitmapDrawable -> {
val ref = System.identityHashCode(drawable.bitmap) drawable.bitmap?.let {
if (seenObjects.contains(ref)) { val ref = System.identityHashCode(it)
0 if (seenObjects.contains(ref)) {
} else { 0
seenObjects.add(ref) } else {
drawable.bitmap.allocationByteCount seenObjects.add(ref)
} it.allocationByteCount
}
} ?: 0
} }
else -> 0 else -> 0
} }
private fun isDrawableSoftwareBitmap(drawable: Drawable) = private fun isDrawableSoftwareBitmap(drawable: Drawable) =
drawable is BitmapDrawable && drawable.bitmap.config != Bitmap.Config.HARDWARE drawable is BitmapDrawable && drawable.bitmap?.config != Bitmap.Config.HARDWARE
private fun identifierForView(view: View) = private fun identifierForView(view: View) =
if (view.id == View.NO_ID) { if (view.id == View.NO_ID) {

View File

@@ -32,6 +32,7 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import java.lang.RuntimeException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@@ -113,6 +114,24 @@ class NotificationMemoryLoggerTest : SysuiTestCase() {
assertThat(data).hasSize(2) assertThat(data).hasSize(2)
} }
@Test
fun onPullAtom_throwsInterruptedException_failsGracefully() {
val pipeline: NotifPipeline = mock()
whenever(pipeline.allNotifs).thenAnswer { throw InterruptedException("Timeout") }
val logger = NotificationMemoryLogger(pipeline, statsManager, immediate, bgExecutor)
assertThat(logger.onPullAtom(SysUiStatsLog.NOTIFICATION_MEMORY_USE, mutableListOf()))
.isEqualTo(StatsManager.PULL_SKIP)
}
@Test
fun onPullAtom_throwsRuntimeException_failsGracefully() {
val pipeline: NotifPipeline = mock()
whenever(pipeline.allNotifs).thenThrow(RuntimeException("Something broke!"))
val logger = NotificationMemoryLogger(pipeline, statsManager, immediate, bgExecutor)
assertThat(logger.onPullAtom(SysUiStatsLog.NOTIFICATION_MEMORY_USE, mutableListOf()))
.isEqualTo(StatsManager.PULL_SKIP)
}
private fun createLoggerWithNotifications( private fun createLoggerWithNotifications(
notifications: List<Notification> notifications: List<Notification>
): NotificationMemoryLogger { ): NotificationMemoryLogger {