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:
@@ -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. */
|
||||||
|
|||||||
@@ -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,6 +84,7 @@ constructor(
|
|||||||
return StatsManager.PULL_SKIP
|
return StatsManager.PULL_SKIP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
// Notifications can only be retrieved on the main thread, so switch to that thread.
|
// Notifications can only be retrieved on the main thread, so switch to that thread.
|
||||||
val notifications = getAllNotificationsOnMainThread()
|
val notifications = getAllNotificationsOnMainThread()
|
||||||
val notificationMemoryUse =
|
val notificationMemoryUse =
|
||||||
@@ -120,6 +123,18 @@ constructor(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} 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
|
return StatsManager.PULL_SUCCESS
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
val ref = System.identityHashCode(it)
|
||||||
if (seenObjects.contains(ref)) {
|
if (seenObjects.contains(ref)) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
seenObjects.add(ref)
|
seenObjects.add(ref)
|
||||||
drawable.bitmap.allocationByteCount
|
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) {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user