Merge "[Media] Add logs for potential memory leaks in MediaCarouselController." into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
eccd61221b
@@ -230,6 +230,18 @@ public class LogModule {
|
|||||||
return factory.create("MediaBrowser", 100);
|
return factory.create("MediaBrowser", 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a buffer for updates to the media carousel.
|
||||||
|
*
|
||||||
|
* See {@link com.android.systemui.media.MediaCarouselController}.
|
||||||
|
*/
|
||||||
|
@Provides
|
||||||
|
@SysUISingleton
|
||||||
|
@MediaCarouselControllerLog
|
||||||
|
public static LogBuffer provideMediaCarouselControllerBuffer(LogBufferFactory factory) {
|
||||||
|
return factory.create("MediaCarouselCtlrLog", 20);
|
||||||
|
}
|
||||||
|
|
||||||
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
|
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
|
||||||
@Provides
|
@Provides
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.log.dagger;
|
||||||
|
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
import com.android.systemui.log.LogBuffer;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
|
||||||
|
import javax.inject.Qualifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link LogBuffer} for {@link com.android.systemui.media.MediaCarouselController}
|
||||||
|
*/
|
||||||
|
@Qualifier
|
||||||
|
@Documented
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
public @interface MediaCarouselControllerLog {
|
||||||
|
}
|
||||||
@@ -59,7 +59,8 @@ class MediaCarouselController @Inject constructor(
|
|||||||
falsingCollector: FalsingCollector,
|
falsingCollector: FalsingCollector,
|
||||||
falsingManager: FalsingManager,
|
falsingManager: FalsingManager,
|
||||||
dumpManager: DumpManager,
|
dumpManager: DumpManager,
|
||||||
private val logger: MediaUiEventLogger
|
private val logger: MediaUiEventLogger,
|
||||||
|
private val debugLogger: MediaCarouselControllerLogger
|
||||||
) : Dumpable {
|
) : Dumpable {
|
||||||
/**
|
/**
|
||||||
* The current width of the carousel
|
* The current width of the carousel
|
||||||
@@ -439,12 +440,16 @@ class MediaCarouselController @Inject constructor(
|
|||||||
newPlayer.mediaViewHolder?.player?.setLayoutParams(lp)
|
newPlayer.mediaViewHolder?.player?.setLayoutParams(lp)
|
||||||
newPlayer.bindPlayer(data, key)
|
newPlayer.bindPlayer(data, key)
|
||||||
newPlayer.setListening(currentlyExpanded)
|
newPlayer.setListening(currentlyExpanded)
|
||||||
MediaPlayerData.addMediaPlayer(key, data, newPlayer, systemClock, isSsReactivated)
|
MediaPlayerData.addMediaPlayer(
|
||||||
|
key, data, newPlayer, systemClock, isSsReactivated, debugLogger
|
||||||
|
)
|
||||||
updatePlayerToState(newPlayer, noAnimation = true)
|
updatePlayerToState(newPlayer, noAnimation = true)
|
||||||
reorderAllPlayers(curVisibleMediaKey)
|
reorderAllPlayers(curVisibleMediaKey)
|
||||||
} else {
|
} else {
|
||||||
existingPlayer.bindPlayer(data, key)
|
existingPlayer.bindPlayer(data, key)
|
||||||
MediaPlayerData.addMediaPlayer(key, data, existingPlayer, systemClock, isSsReactivated)
|
MediaPlayerData.addMediaPlayer(
|
||||||
|
key, data, existingPlayer, systemClock, isSsReactivated, debugLogger
|
||||||
|
)
|
||||||
if (isReorderingAllowed || shouldScrollToActivePlayer) {
|
if (isReorderingAllowed || shouldScrollToActivePlayer) {
|
||||||
reorderAllPlayers(curVisibleMediaKey)
|
reorderAllPlayers(curVisibleMediaKey)
|
||||||
} else {
|
} else {
|
||||||
@@ -475,7 +480,8 @@ class MediaCarouselController @Inject constructor(
|
|||||||
|
|
||||||
val existingSmartspaceMediaKey = MediaPlayerData.smartspaceMediaKey()
|
val existingSmartspaceMediaKey = MediaPlayerData.smartspaceMediaKey()
|
||||||
existingSmartspaceMediaKey?.let {
|
existingSmartspaceMediaKey?.let {
|
||||||
MediaPlayerData.removeMediaPlayer(existingSmartspaceMediaKey)
|
val removedPlayer = MediaPlayerData.removeMediaPlayer(existingSmartspaceMediaKey)
|
||||||
|
removedPlayer?.run { debugLogger.logPotentialMemoryLeak(existingSmartspaceMediaKey) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val newRecs = mediaControlPanelFactory.get()
|
val newRecs = mediaControlPanelFactory.get()
|
||||||
@@ -488,7 +494,9 @@ class MediaCarouselController @Inject constructor(
|
|||||||
newRecs.bindRecommendation(data)
|
newRecs.bindRecommendation(data)
|
||||||
val curVisibleMediaKey = MediaPlayerData.playerKeys()
|
val curVisibleMediaKey = MediaPlayerData.playerKeys()
|
||||||
.elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
|
.elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
|
||||||
MediaPlayerData.addMediaRecommendation(key, data, newRecs, shouldPrioritize, systemClock)
|
MediaPlayerData.addMediaRecommendation(
|
||||||
|
key, data, newRecs, shouldPrioritize, systemClock, debugLogger
|
||||||
|
)
|
||||||
updatePlayerToState(newRecs, noAnimation = true)
|
updatePlayerToState(newRecs, noAnimation = true)
|
||||||
reorderAllPlayers(curVisibleMediaKey)
|
reorderAllPlayers(curVisibleMediaKey)
|
||||||
updatePageIndicator()
|
updatePageIndicator()
|
||||||
@@ -883,7 +891,8 @@ class MediaCarouselController @Inject constructor(
|
|||||||
override fun dump(pw: PrintWriter, args: Array<out String>) {
|
override fun dump(pw: PrintWriter, args: Array<out String>) {
|
||||||
pw.apply {
|
pw.apply {
|
||||||
println("keysNeedRemoval: $keysNeedRemoval")
|
println("keysNeedRemoval: $keysNeedRemoval")
|
||||||
println("playerKeys: ${MediaPlayerData.playerKeys()}")
|
println("dataKeys: ${MediaPlayerData.dataKeys()}")
|
||||||
|
println("playerSortKeys: ${MediaPlayerData.playerKeys()}")
|
||||||
println("smartspaceMediaData: ${MediaPlayerData.smartspaceMediaData}")
|
println("smartspaceMediaData: ${MediaPlayerData.smartspaceMediaData}")
|
||||||
println("shouldPrioritizeSs: ${MediaPlayerData.shouldPrioritizeSs}")
|
println("shouldPrioritizeSs: ${MediaPlayerData.shouldPrioritizeSs}")
|
||||||
println("current size: $currentCarouselWidth x $currentCarouselHeight")
|
println("current size: $currentCarouselWidth x $currentCarouselHeight")
|
||||||
@@ -946,9 +955,13 @@ internal object MediaPlayerData {
|
|||||||
data: MediaData,
|
data: MediaData,
|
||||||
player: MediaControlPanel,
|
player: MediaControlPanel,
|
||||||
clock: SystemClock,
|
clock: SystemClock,
|
||||||
isSsReactivated: Boolean
|
isSsReactivated: Boolean,
|
||||||
|
debugLogger: MediaCarouselControllerLogger? = null
|
||||||
) {
|
) {
|
||||||
removeMediaPlayer(key)
|
val removedPlayer = removeMediaPlayer(key)
|
||||||
|
if (removedPlayer != null && removedPlayer != player) {
|
||||||
|
debugLogger?.logPotentialMemoryLeak(key)
|
||||||
|
}
|
||||||
val sortKey = MediaSortKey(isSsMediaRec = false,
|
val sortKey = MediaSortKey(isSsMediaRec = false,
|
||||||
data, clock.currentTimeMillis(), isSsReactivated = isSsReactivated)
|
data, clock.currentTimeMillis(), isSsReactivated = isSsReactivated)
|
||||||
mediaData.put(key, sortKey)
|
mediaData.put(key, sortKey)
|
||||||
@@ -960,10 +973,14 @@ internal object MediaPlayerData {
|
|||||||
data: SmartspaceMediaData,
|
data: SmartspaceMediaData,
|
||||||
player: MediaControlPanel,
|
player: MediaControlPanel,
|
||||||
shouldPrioritize: Boolean,
|
shouldPrioritize: Boolean,
|
||||||
clock: SystemClock
|
clock: SystemClock,
|
||||||
|
debugLogger: MediaCarouselControllerLogger? = null
|
||||||
) {
|
) {
|
||||||
shouldPrioritizeSs = shouldPrioritize
|
shouldPrioritizeSs = shouldPrioritize
|
||||||
removeMediaPlayer(key)
|
val removedPlayer = removeMediaPlayer(key)
|
||||||
|
if (removedPlayer != null && removedPlayer != player) {
|
||||||
|
debugLogger?.logPotentialMemoryLeak(key)
|
||||||
|
}
|
||||||
val sortKey = MediaSortKey(isSsMediaRec = true,
|
val sortKey = MediaSortKey(isSsMediaRec = true,
|
||||||
EMPTY.copy(isPlaying = false), clock.currentTimeMillis(), isSsReactivated = true)
|
EMPTY.copy(isPlaying = false), clock.currentTimeMillis(), isSsReactivated = true)
|
||||||
mediaData.put(key, sortKey)
|
mediaData.put(key, sortKey)
|
||||||
@@ -971,13 +988,18 @@ internal object MediaPlayerData {
|
|||||||
smartspaceMediaData = data
|
smartspaceMediaData = data
|
||||||
}
|
}
|
||||||
|
|
||||||
fun moveIfExists(oldKey: String?, newKey: String) {
|
fun moveIfExists(
|
||||||
|
oldKey: String?,
|
||||||
|
newKey: String,
|
||||||
|
debugLogger: MediaCarouselControllerLogger? = null
|
||||||
|
) {
|
||||||
if (oldKey == null || oldKey == newKey) {
|
if (oldKey == null || oldKey == newKey) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaData.remove(oldKey)?.let {
|
mediaData.remove(oldKey)?.let {
|
||||||
removeMediaPlayer(newKey)
|
val removedPlayer = removeMediaPlayer(newKey)
|
||||||
|
removedPlayer?.run { debugLogger?.logPotentialMemoryLeak(newKey) }
|
||||||
mediaData.put(newKey, it)
|
mediaData.put(newKey, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1005,6 +1027,8 @@ internal object MediaPlayerData {
|
|||||||
|
|
||||||
fun mediaData() = mediaData.entries.map { e -> Triple(e.key, e.value.data, e.value.isSsMediaRec) }
|
fun mediaData() = mediaData.entries.map { e -> Triple(e.key, e.value.data, e.value.isSsMediaRec) }
|
||||||
|
|
||||||
|
fun dataKeys() = mediaData.keys
|
||||||
|
|
||||||
fun players() = mediaPlayers.values
|
fun players() = mediaPlayers.values
|
||||||
|
|
||||||
fun playerKeys() = mediaPlayers.keys
|
fun playerKeys() = mediaPlayers.keys
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.media
|
||||||
|
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.log.LogBuffer
|
||||||
|
import com.android.systemui.log.LogLevel
|
||||||
|
import com.android.systemui.log.dagger.MediaCarouselControllerLog
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/** A debug logger for [MediaCarouselController]. */
|
||||||
|
@SysUISingleton
|
||||||
|
class MediaCarouselControllerLogger @Inject constructor(
|
||||||
|
@MediaCarouselControllerLog private val buffer: LogBuffer
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Log that there might be a potential memory leak for the [MediaControlPanel] and/or
|
||||||
|
* [MediaViewController] related to [key].
|
||||||
|
*/
|
||||||
|
fun logPotentialMemoryLeak(key: String) = buffer.log(
|
||||||
|
TAG,
|
||||||
|
LogLevel.DEBUG,
|
||||||
|
{ str1 = key },
|
||||||
|
{
|
||||||
|
"Potential memory leak: " +
|
||||||
|
"Removing control panel for $str1 from map without calling #onDestroy"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val TAG = "MediaCarouselCtlrLog"
|
||||||
@@ -63,6 +63,7 @@ class MediaCarouselControllerTest : SysuiTestCase() {
|
|||||||
@Mock lateinit var falsingManager: FalsingManager
|
@Mock lateinit var falsingManager: FalsingManager
|
||||||
@Mock lateinit var dumpManager: DumpManager
|
@Mock lateinit var dumpManager: DumpManager
|
||||||
@Mock lateinit var logger: MediaUiEventLogger
|
@Mock lateinit var logger: MediaUiEventLogger
|
||||||
|
@Mock lateinit var debugLogger: MediaCarouselControllerLogger
|
||||||
|
|
||||||
private val clock = FakeSystemClock()
|
private val clock = FakeSystemClock()
|
||||||
private lateinit var mediaCarouselController: MediaCarouselController
|
private lateinit var mediaCarouselController: MediaCarouselController
|
||||||
@@ -83,7 +84,8 @@ class MediaCarouselControllerTest : SysuiTestCase() {
|
|||||||
falsingCollector,
|
falsingCollector,
|
||||||
falsingManager,
|
falsingManager,
|
||||||
dumpManager,
|
dumpManager,
|
||||||
logger
|
logger,
|
||||||
|
debugLogger
|
||||||
)
|
)
|
||||||
|
|
||||||
MediaPlayerData.clear()
|
MediaPlayerData.clear()
|
||||||
|
|||||||
Reference in New Issue
Block a user