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

This commit is contained in:
Automerger Merge Worker
2023-02-02 21:38:16 +00:00
committed by Android (Google) Code Review
4 changed files with 80 additions and 44 deletions

View File

@@ -63,7 +63,7 @@ import javax.inject.Inject;
* are not.
*/
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);
/** The minimum delay in ms between reports of notification visibility. */

View File

@@ -18,6 +18,7 @@
package com.android.systemui.statusbar.notification.logging
import android.app.StatsManager
import android.util.Log
import android.util.StatsEvent
import com.android.systemui.dagger.SysUISingleton
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.statusbar.notification.collection.NotifPipeline
import com.android.systemui.util.traceSection
import java.lang.Exception
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlin.math.roundToInt
@@ -82,43 +84,56 @@ constructor(
return StatsManager.PULL_SKIP
}
// Notifications can only be retrieved on the main thread, so switch to that thread.
val notifications = getAllNotificationsOnMainThread()
val notificationMemoryUse =
NotificationMemoryMeter.notificationMemoryUse(notifications)
.sortedWith(
compareBy(
{ it.packageName },
{ it.objectUsage.style },
{ it.notificationKey }
try {
// Notifications can only be retrieved on the main thread, so switch to that thread.
val notifications = getAllNotificationsOnMainThread()
val notificationMemoryUse =
NotificationMemoryMeter.notificationMemoryUse(notifications)
.sortedWith(
compareBy(
{ it.packageName },
{ 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) ->
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
)
)
}
} catch (e: InterruptedException) {
// This can happen if the device is sleeping or view walking takes too long.
// The statsd collector will interrupt the thread and we need to handle it
// gracefully.
Log.w(NotificationLogger.TAG, "Timed out when measuring notification memory.", e)
return@traceSection StatsManager.PULL_SKIP
} catch (e: Exception) {
// Error while collecting data, this should not crash prod SysUI. Just
// log WTF and move on.
Log.wtf(NotificationLogger.TAG, "Failed to measure notification memory.", e)
return@traceSection StatsManager.PULL_SKIP
}
return StatsManager.PULL_SUCCESS

View File

@@ -184,19 +184,21 @@ internal object NotificationMemoryViewWalker {
private fun computeDrawableUse(drawable: Drawable, seenObjects: HashSet<Int>): Int =
when (drawable) {
is BitmapDrawable -> {
val ref = System.identityHashCode(drawable.bitmap)
if (seenObjects.contains(ref)) {
0
} else {
seenObjects.add(ref)
drawable.bitmap.allocationByteCount
}
drawable.bitmap?.let {
val ref = System.identityHashCode(it)
if (seenObjects.contains(ref)) {
0
} else {
seenObjects.add(ref)
it.allocationByteCount
}
} ?: 0
}
else -> 0
}
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) =
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.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.lang.RuntimeException
import kotlinx.coroutines.Dispatchers
import org.junit.Before
import org.junit.Test
@@ -113,6 +114,24 @@ class NotificationMemoryLoggerTest : SysuiTestCase() {
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(
notifications: List<Notification>
): NotificationMemoryLogger {