Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3e76eb37 authored by Jieru Shi's avatar Jieru Shi Committed by Automerger Merge Worker
Browse files

Merge "Fix Smartspace media logging bugs" into sc-v2-dev am: 60b5873b

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

Change-Id: I59315a18991c6c94e61d8490a3e41f5c2d036e28
parents c1448554 60b5873b
Loading
Loading
Loading
Loading
+116 −71
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ import javax.inject.Provider

private const val TAG = "MediaCarouselController"
private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS)
private const val DEBUG = false
private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)

/**
 * Class that is responsible for keeping the view carousel up to date.
@@ -209,41 +209,56 @@ class MediaCarouselController @Inject constructor(
                oldKey: String?,
                data: MediaData,
                immediately: Boolean,
                isSsReactivated: Boolean
                receivedSmartspaceCardLatency: Int
            ) {
                if (addOrUpdatePlayer(key, oldKey, data)) {
                    // Log card received if a new resumable media card is added
                    MediaPlayerData.getMediaPlayer(key)?.let {
                        /* ktlint-disable max-line-length */
                        logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
                                it.mInstanceId,
                                it.mUid,
                                /* isRecommendationCard */ false,
                                it.surfaceForSmartspaceLogging,
                                intArrayOf(
                                        SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
                                        SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN),
                                rank = MediaPlayerData.getMediaPlayerIndex(key))
                        /* ktlint-disable max-line-length */
                    }
                    if (mediaCarouselScrollHandler.visibleToUser &&
                            mediaCarouselScrollHandler.visibleMediaIndex
                            == MediaPlayerData.getMediaPlayerIndex(key)) {
                        logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
                    }
                if (isSsReactivated) {
                    // If resumable media is reactivated by headphone connection, update instance
                    // id for each card and log a receive event.
                } else if (receivedSmartspaceCardLatency != 0) {
                    // Log resume card received if resumable media card is reactivated and
                    // resume card is ranked first
                    MediaPlayerData.players().forEachIndexed { index, it ->
                        if (it.recommendationViewHolder == null) {
                            it.mInstanceId = SmallHash.hash(it.mUid +
                                    systemClock.currentTimeMillis().toInt())
                            it.mIsImpressed = false
                            /* ktlint-disable max-line-length */
                            logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
                                    it.mInstanceId,
                                    it.mUid,
                                    /* isRecommendationCard */ false,
                                    it.surfaceForSmartspaceLogging,
                                    rank = index)
                        }
                                    intArrayOf(
                                            SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
                                            SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN),
                                    rank = index,
                                    receivedLatencyMillis = receivedSmartspaceCardLatency)
                            /* ktlint-disable max-line-length */
                        }
                    }
                    // If media container area already visible to the user, log impression for
                    // reactivated card.
                    if (mediaCarouselScrollHandler.visibleToUser &&
                        isSsReactivated && !mediaCarouselScrollHandler.qsExpanded) {
                    // It could happen that reactived media player isn't visible to user because
                    // of it is a resumption card.
                            !mediaCarouselScrollHandler.qsExpanded) {
                        logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
                    }
                }

                val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
                if (canRemove && !Utils.useMediaResumption(context)) {
                    // This view isn't playing, let's remove this! This happens e.g when
@@ -262,28 +277,51 @@ class MediaCarouselController @Inject constructor(
            override fun onSmartspaceMediaDataLoaded(
                key: String,
                data: SmartspaceMediaData,
                shouldPrioritize: Boolean
                shouldPrioritize: Boolean,
                isSsReactivated: Boolean
            ) {
                if (DEBUG) Log.d(TAG, "Loading Smartspace media update")
                if (data.isActive) {
                    addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
                    MediaPlayerData.getMediaPlayer(key)?.let {
                    if (isSsReactivated && shouldPrioritize) {
                        // Log resume card received if resumable media card is reactivated and
                        // recommendation card is valid and ranked first
                        MediaPlayerData.players().forEachIndexed { index, it ->
                            if (it.recommendationViewHolder == null) {
                                it.mInstanceId = SmallHash.hash(it.mUid +
                                        systemClock.currentTimeMillis().toInt())
                                it.mIsImpressed = false
                                /* ktlint-disable max-line-length */
                                logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
                                        it.mInstanceId,
                                        it.mUid,
                                /* isRecommendationCard */ true,
                                it.surfaceForSmartspaceLogging,
                                rank = MediaPlayerData.getMediaPlayerIndex(key))

                        if (mediaCarouselScrollHandler.visibleToUser &&
                                mediaCarouselScrollHandler.visibleMediaIndex ==
                                MediaPlayerData.getMediaPlayerIndex(key)) {
                            logSmartspaceCardReported(800, // SMARTSPACE_CARD_SEEN
                                        /* isRecommendationCard */ false,
                                        intArrayOf(
                                                SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
                                                SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN),
                                        rank = index,
                                        receivedLatencyMillis = (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis).toInt())
                                /* ktlint-disable max-line-length */
                            }
                        }
                    }
                    addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
                    MediaPlayerData.getMediaPlayer(key)?.let {
                        /* ktlint-disable max-line-length */
                        logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
                                it.mInstanceId,
                                it.mUid,
                                /* isRecommendationCard */ true,
                                    it.surfaceForSmartspaceLogging)
                                intArrayOf(
                                        SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
                                        SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN),
                                rank = MediaPlayerData.getMediaPlayerIndex(key),
                                receivedLatencyMillis = (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis).toInt())
                        /* ktlint-disable max-line-length */
                    }
                    if (mediaCarouselScrollHandler.visibleToUser &&
                            mediaCarouselScrollHandler.visibleMediaIndex
                            == MediaPlayerData.getMediaPlayerIndex(key)) {
                        logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
                    }
                } else {
                    onSmartspaceMediaDataRemoved(data.targetId, immediately = true)
@@ -718,7 +756,8 @@ class MediaCarouselController @Inject constructor(
                    mediaControlPanel.mInstanceId,
                    mediaControlPanel.mUid,
                    isRecommendationCard,
                    mediaControlPanel.surfaceForSmartspaceLogging)
                    intArrayOf(mediaControlPanel.surfaceForSmartspaceLogging))
            mediaControlPanel.mIsImpressed = true
        }
    }

@@ -731,12 +770,15 @@ class MediaCarouselController @Inject constructor(
     * instanceId
     * @param uid uid for the application that media comes from
     * @param isRecommendationCard whether the card is media recommendation
     * @param surface which display surface the media card is on (e.g. lockscreen, shade)
     * @param surfaces list of display surfaces the media card is on (e.g. lockscreen, shade) when
     * the event happened
     * @param interactedSubcardRank the rank for interacted media item for recommendation card, -1
     * for tapping on card but not on any media item, 0 for first media item, 1 for second, etc.
     * @param interactedSubcardCardinality how many media items were shown to the user when there
     * is user interaction
     * @param rank the rank for media card in the media carousel, starting from 0
     * @param receivedLatencyMillis latency in milliseconds for card received events. E.g. latency
     * between headphone connection to sysUI displays media recommendation card
     *
     */
    fun logSmartspaceCardReported(
@@ -744,10 +786,11 @@ class MediaCarouselController @Inject constructor(
        instanceId: Int,
        uid: Int,
        isRecommendationCard: Boolean,
        surface: Int,
        surfaces: IntArray,
        interactedSubcardRank: Int = 0,
        interactedSubcardCardinality: Int = 0,
        rank: Int = mediaCarouselScrollHandler.visibleMediaIndex
        rank: Int = mediaCarouselScrollHandler.visibleMediaIndex,
        receivedLatencyMillis: Int = 0
    ) {
        // Only log media resume card when Smartspace data is available
        if (!isRecommendationCard &&
@@ -756,6 +799,8 @@ class MediaCarouselController @Inject constructor(
            return
        }

        val cardinality = mediaContent.getChildCount()
        surfaces.forEach { surface ->
            /* ktlint-disable max-line-length */
            SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
                    eventId,
@@ -765,7 +810,7 @@ class MediaCarouselController @Inject constructor(
                    SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD,
                    surface,
                    rank,
                mediaContent.getChildCount(),
                    cardinality,
                    if (isRecommendationCard)
                        15 // MEDIA_RECOMMENDATION
                    else
@@ -773,33 +818,33 @@ class MediaCarouselController @Inject constructor(
                    uid,
                    interactedSubcardRank,
                    interactedSubcardCardinality,
                0 // received_latency_millis
                    receivedLatencyMillis
            )
            /* ktlint-disable max-line-length */
            if (DEBUG) {
                Log.d(TAG, "Log Smartspace card event id: $eventId instance id: $instanceId" +
                        " surface: $surface rank: $rank cardinality: $cardinality " +
                        "isRecommendationCard: $isRecommendationCard uid: $uid " +
                        "interactedSubcardRank: $interactedSubcardRank " +
                        "interactedSubcardCardinality: $interactedSubcardCardinality " +
                        "received_latency_millis: $receivedLatencyMillis")
            }
        }
    }

    private fun onSwipeToDismiss() {
        val recommendation = MediaPlayerData.players().filter {
            it.recommendationViewHolder != null
        }
        // Use -1 as rank value to indicate user swipe to dismiss the card
        if (!recommendation.isEmpty()) {
            logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
                    recommendation.get(0).mInstanceId,
                    recommendation.get(0).mUid,
                    true,
                    recommendation.get(0).surfaceForSmartspaceLogging,
                    rank = -1)
        } else {
            val visibleMediaIndex = mediaCarouselScrollHandler.visibleMediaIndex
            if (MediaPlayerData.players().size > visibleMediaIndex) {
                val player = MediaPlayerData.players().elementAt(visibleMediaIndex)
        MediaPlayerData.players().forEachIndexed {
            index, it ->
            if (it.mIsImpressed) {
                logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
                        player.mInstanceId,
                        player.mUid,
                        false,
                        player.surfaceForSmartspaceLogging,
                        it.mInstanceId,
                        it.mUid,
                        it.recommendationViewHolder != null,
                        intArrayOf(it.surfaceForSmartspaceLogging),
                        // Use -1 as rank value to indicate user swipe to dismiss the card
                        rank = -1)
                // Reset card impressed state when swipe to dismissed
                it.mIsImpressed = false
            }
        }
        mediaManager.onSwipeToDismiss()
+12 −3
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.util.animation.TransitionLayout;
import com.android.systemui.util.time.SystemClock;

import java.net.URISyntaxException;
import java.util.List;
@@ -121,6 +122,9 @@ public class MediaControlPanel {
    private MediaCarouselController mMediaCarouselController;
    private final MediaOutputDialogFactory mMediaOutputDialogFactory;
    private final FalsingManager mFalsingManager;
    // Used for swipe-to-dismiss logging.
    protected boolean mIsImpressed = false;
    private SystemClock mSystemClock;

    /**
     * Initialize a new control panel
@@ -134,7 +138,7 @@ public class MediaControlPanel {
            SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager,
            KeyguardDismissUtil keyguardDismissUtil, MediaOutputDialogFactory
            mediaOutputDialogFactory, MediaCarouselController mediaCarouselController,
            FalsingManager falsingManager) {
            FalsingManager falsingManager, SystemClock systemClock) {
        mContext = context;
        mBackgroundExecutor = backgroundExecutor;
        mActivityStarter = activityStarter;
@@ -145,6 +149,8 @@ public class MediaControlPanel {
        mMediaOutputDialogFactory = mediaOutputDialogFactory;
        mMediaCarouselController = mediaCarouselController;
        mFalsingManager = falsingManager;
        mSystemClock = systemClock;

        loadDimens();

        mSeekBarViewModel.setLogSmartspaceClick(() -> {
@@ -291,7 +297,10 @@ public class MediaControlPanel {
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Unable to look up package name", e);
        }
        mInstanceId = SmallHash.hash(mUid);
        // Only assigns instance id if it's unassigned.
        if (mInstanceId == -1) {
            mInstanceId = SmallHash.hash(mUid + (int) mSystemClock.currentTimeMillis());
        }

        mBackgroundColor = data.getBackgroundColor();
        if (mToken == null || !mToken.equals(token)) {
@@ -885,7 +894,7 @@ public class MediaControlPanel {
                mInstanceId,
                mUid,
                isRecommendationCard,
                getSurfaceForSmartspaceLogging(),
                new int[]{getSurfaceForSmartspaceLogging()},
                interactedSubcardRank,
                interactedSubcardCardinality);
    }
+3 −2
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ class MediaDataCombineLatest @Inject constructor() : MediaDataManager.Listener,
        oldKey: String?,
        data: MediaData,
        immediately: Boolean,
        isSsReactivated: Boolean
        receivedSmartspaceCardLatency: Int
    ) {
        if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
            entries[key] = data to entries.remove(oldKey)?.second
@@ -46,7 +46,8 @@ class MediaDataCombineLatest @Inject constructor() : MediaDataManager.Listener,
    override fun onSmartspaceMediaDataLoaded(
        key: String,
        data: SmartspaceMediaData,
        shouldPrioritize: Boolean
        shouldPrioritize: Boolean,
        isSsReactivated: Boolean
    ) {
        listeners.toSet().forEach { it.onSmartspaceMediaDataLoaded(key, data) }
    }
+24 −18
Original line number Diff line number Diff line
@@ -56,7 +56,6 @@ internal val SMARTSPACE_MAX_AGE = SystemProperties
class MediaDataFilter @Inject constructor(
    private val context: Context,
    private val broadcastDispatcher: BroadcastDispatcher,
    private val mediaResumeListener: MediaResumeListener,
    private val lockscreenUserManager: NotificationLockscreenUserManager,
    @Main private val executor: Executor,
    private val systemClock: SystemClock
@@ -88,7 +87,7 @@ class MediaDataFilter @Inject constructor(
        oldKey: String?,
        data: MediaData,
        immediately: Boolean,
        isSsReactivated: Boolean
        receivedSmartspaceCardLatency: Int
    ) {
        if (oldKey != null && oldKey != key) {
            allEntries.remove(oldKey)
@@ -106,14 +105,15 @@ class MediaDataFilter @Inject constructor(

        // Notify listeners
        listeners.forEach {
            it.onMediaDataLoaded(key, oldKey, data, isSsReactivated = isSsReactivated)
            it.onMediaDataLoaded(key, oldKey, data)
        }
    }

    override fun onSmartspaceMediaDataLoaded(
        key: String,
        data: SmartspaceMediaData,
        shouldPrioritize: Boolean
        shouldPrioritize: Boolean,
        isSsReactivated: Boolean
    ) {
        if (!data.isActive) {
            Log.d(TAG, "Inactive recommendation data. Skip triggering.")
@@ -123,8 +123,6 @@ class MediaDataFilter @Inject constructor(
        // Override the pass-in value here, as the order of Smartspace card is only determined here.
        var shouldPrioritizeMutable = false
        smartspaceMediaData = data
        // Override the pass-in value here, as the Smartspace reactivation could only happen here.
        var isSsReactivated = false

        // Before forwarding the smartspace target, first check if we have recently inactive media
        val sorted = userEntries.toSortedMap(compareBy {
@@ -139,18 +137,25 @@ class MediaDataFilter @Inject constructor(
                smartspaceMaxAgeMillis = TimeUnit.SECONDS.toMillis(smartspaceMaxAgeSeconds)
            }
        }

        val activeMedia = userEntries.filter { (key, value) -> value.active }
        var isSsReactivatedMutable = activeMedia.isEmpty() && userEntries.isNotEmpty()

        if (timeSinceActive < smartspaceMaxAgeMillis) {
            // It could happen there are existing active media resume cards, then we don't need to
            // reactivate.
            if (isSsReactivatedMutable) {
                val lastActiveKey = sorted.lastKey() // most recently active
                // Notify listeners to consider this media active
                Log.d(TAG, "reactivating $lastActiveKey instead of smartspace")
                reactivatedKey = lastActiveKey
            if (MediaPlayerData.firstActiveMediaIndex() == -1) {
                isSsReactivated = true
            }
                val mediaData = sorted.get(lastActiveKey)!!.copy(active = true)
                listeners.forEach {
                    it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData,
                        isSsReactivated = isSsReactivated)
                            receivedSmartspaceCardLatency =
                            (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis)
                                    .toInt())
                }
            }
        } else {
            // Mark to prioritize Smartspace card if no recent media.
@@ -161,7 +166,8 @@ class MediaDataFilter @Inject constructor(
            Log.d(TAG, "Invalid recommendation data. Skip showing the rec card")
            return
        }
        listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
        listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable,
                isSsReactivatedMutable) }
    }

    override fun onMediaDataRemoved(key: String) {
+14 −8
Original line number Diff line number Diff line
@@ -84,7 +84,7 @@ private val LOADING = MediaData(-1, false, 0, null, null, null, null, null,
        emptyList(), emptyList(), "INVALID", null, null, null, true, null)
@VisibleForTesting
internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData("INVALID", false, false,
    "INVALID", null, emptyList(), null, 0)
    "INVALID", null, emptyList(), null, 0, 0)

fun isMediaNotification(sbn: StatusBarNotification): Boolean {
    return sbn.notification.isMediaNotification()
@@ -852,15 +852,16 @@ class MediaDataManager(
         * until the next refresh-round before UI becomes visible. True by default to take in place
         * immediately.
         *
         * @param isSsReactivated indicates transition from a state with no active media players to
         * a state with active media players upon receiving Smartspace media data.
         * @param receivedSmartspaceCardLatency is the latency between headphone connects and sysUI
         * displays Smartspace media targets. Will be 0 if the data is not activated by Smartspace
         * signal.
         */
        fun onMediaDataLoaded(
            key: String,
            oldKey: String?,
            data: MediaData,
            immediately: Boolean = true,
            isSsReactivated: Boolean = false
            receivedSmartspaceCardLatency: Int = 0
        ) {}

        /**
@@ -869,11 +870,15 @@ class MediaDataManager(
         * @param shouldPrioritize indicates the sorting priority of the Smartspace card. If true,
         * it will be prioritized as the first card. Otherwise, it will show up as the last card as
         * default.
         *
         * @param isSsReactivated indicates resume media card is reactivated by Smartspace
         * recommendation signal
         */
        fun onSmartspaceMediaDataLoaded(
            key: String,
            data: SmartspaceMediaData,
            shouldPrioritize: Boolean = false
            shouldPrioritize: Boolean = false,
            isSsReactivated: Boolean = false
        ) {}

        /** Called whenever a previously existing Media notification was removed. */
@@ -909,12 +914,13 @@ class MediaDataManager(
        packageName(target)?.let {
            return SmartspaceMediaData(target.smartspaceTargetId, isActive, true, it,
                target.baseAction, target.iconGrid,
                dismissIntent, 0)
                dismissIntent, 0, target.creationTimeMillis)
        }
        return EMPTY_SMARTSPACE_MEDIA_DATA
            .copy(targetId = target.smartspaceTargetId,
                    isActive = isActive,
                dismissIntent = dismissIntent)
                    dismissIntent = dismissIntent,
                    headphoneConnectionTimeMillis = target.creationTimeMillis)
    }

    private fun packageName(target: SmartspaceTarget): String? {
Loading