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

Commit 21169ec3 authored by Beth Thibodeau's avatar Beth Thibodeau
Browse files

Add logging for media controls

Adds logging for interactions and events with the media controls and
carousel, with instance IDs for events relating to individual controls

Bug: 216488338
Test: atest
Change-Id: I9bb312c572ce7c1a0817595444279afc8d5cfb6b
parent 90f2f63c
Loading
Loading
Loading
Loading
+20 −12
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.annotation.VisibleForTesting
import com.android.internal.logging.InstanceId
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.classifier.FalsingCollector
@@ -59,7 +60,7 @@ class MediaCarouselController @Inject constructor(
    falsingCollector: FalsingCollector,
    falsingManager: FalsingManager,
    dumpManager: DumpManager,
    private val mediaFlags: MediaFlags
    private val logger: MediaUiEventLogger
) : Dumpable {
    /**
     * The current width of the carousel
@@ -119,7 +120,9 @@ class MediaCarouselController @Inject constructor(
    private val mediaCarousel: MediaScrollView
    val mediaCarouselScrollHandler: MediaCarouselScrollHandler
    val mediaFrame: ViewGroup
    private lateinit var settingsButton: View
    @VisibleForTesting
    lateinit var settingsButton: View
        private set
    private val mediaContent: ViewGroup
    private val pageIndicator: PageIndicator
    private val visualStabilityCallback: OnReorderingAllowedListener
@@ -183,7 +186,8 @@ class MediaCarouselController @Inject constructor(
        pageIndicator = mediaFrame.requireViewById(R.id.media_page_indicator)
        mediaCarouselScrollHandler = MediaCarouselScrollHandler(mediaCarousel, pageIndicator,
                executor, this::onSwipeToDismiss, this::updatePageIndicatorLocation,
                this::closeGuts, falsingCollector, falsingManager, this::logSmartspaceImpression)
                this::closeGuts, falsingCollector, falsingManager, this::logSmartspaceImpression,
                logger)
        isRtl = context.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
        inflateSettingsButton()
        mediaContent = mediaCarousel.requireViewById(R.id.media_carousel)
@@ -220,7 +224,7 @@ class MediaCarouselController @Inject constructor(
                    MediaPlayerData.getMediaPlayer(key)?.let {
                        /* ktlint-disable max-line-length */
                        logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
                                it.mInstanceId,
                                it.mSmartspaceId,
                                it.mUid,
                                /* isRecommendationCard */ false,
                                intArrayOf(
@@ -239,12 +243,12 @@ class MediaCarouselController @Inject constructor(
                    // resume card is ranked first
                    MediaPlayerData.players().forEachIndexed { index, it ->
                        if (it.recommendationViewHolder == null) {
                            it.mInstanceId = SmallHash.hash(it.mUid +
                            it.mSmartspaceId = SmallHash.hash(it.mUid +
                                    systemClock.currentTimeMillis().toInt())
                            it.mIsImpressed = false
                            /* ktlint-disable max-line-length */
                            logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
                                    it.mInstanceId,
                                    it.mSmartspaceId,
                                    it.mUid,
                                    /* isRecommendationCard */ false,
                                    intArrayOf(
@@ -291,12 +295,12 @@ class MediaCarouselController @Inject constructor(
                        // recommendation card is valid and ranked first
                        MediaPlayerData.players().forEachIndexed { index, it ->
                            if (it.recommendationViewHolder == null) {
                                it.mInstanceId = SmallHash.hash(it.mUid +
                                it.mSmartspaceId = SmallHash.hash(it.mUid +
                                        systemClock.currentTimeMillis().toInt())
                                it.mIsImpressed = false
                                /* ktlint-disable max-line-length */
                                logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
                                        it.mInstanceId,
                                        it.mSmartspaceId,
                                        it.mUid,
                                        /* isRecommendationCard */ false,
                                        intArrayOf(
@@ -312,7 +316,7 @@ class MediaCarouselController @Inject constructor(
                    MediaPlayerData.getMediaPlayer(key)?.let {
                        /* ktlint-disable max-line-length */
                        logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
                                it.mInstanceId,
                                it.mSmartspaceId,
                                it.mUid,
                                /* isRecommendationCard */ true,
                                intArrayOf(
@@ -369,6 +373,7 @@ class MediaCarouselController @Inject constructor(
        mediaFrame.addView(settingsButton)
        mediaCarouselScrollHandler.onSettingsButtonUpdated(settings)
        settingsButton.setOnClickListener {
            logger.logCarouselSettings()
            activityStarter.startActivity(settingsIntent, true /* dismissShade */)
        }
    }
@@ -752,7 +757,7 @@ class MediaCarouselController @Inject constructor(
                return
            }
            logSmartspaceCardReported(800, // SMARTSPACE_CARD_SEEN
                    mediaControlPanel.mInstanceId,
                    mediaControlPanel.mSmartspaceId,
                    mediaControlPanel.mUid,
                    isRecommendationCard,
                    intArrayOf(mediaControlPanel.surfaceForSmartspaceLogging))
@@ -836,7 +841,7 @@ class MediaCarouselController @Inject constructor(
            index, it ->
            if (it.mIsImpressed) {
                logSmartspaceCardReported(SMARTSPACE_CARD_DISMISS_EVENT,
                        it.mInstanceId,
                        it.mSmartspaceId,
                        it.mUid,
                        it.recommendationViewHolder != null,
                        intArrayOf(it.surfaceForSmartspaceLogging),
@@ -846,6 +851,7 @@ class MediaCarouselController @Inject constructor(
                it.mIsImpressed = false
            }
        }
        logger.logSwipeDismiss()
        mediaManager.onSwipeToDismiss()
    }

@@ -881,7 +887,9 @@ internal object MediaPlayerData {
            clickIntent = null,
            device = null,
            active = true,
            resumeAction = null)
            resumeAction = null,
            instanceId = InstanceId.fakeInstanceId(-1),
            appUid = -1)
    // Whether should prioritize Smartspace card.
    internal var shouldPrioritizeSs: Boolean = false
        private set
+4 −2
Original line number Diff line number Diff line
@@ -57,12 +57,13 @@ class MediaCarouselScrollHandler(
    private val scrollView: MediaScrollView,
    private val pageIndicator: PageIndicator,
    private val mainExecutor: DelayableExecutor,
    private val dismissCallback: () -> Unit,
    val dismissCallback: () -> Unit,
    private var translationChangedListener: () -> Unit,
    private val closeGuts: (immediate: Boolean) -> Unit,
    private val falsingCollector: FalsingCollector,
    private val falsingManager: FalsingManager,
    private val logSmartspaceImpression: (Boolean) -> Unit
    private val logSmartspaceImpression: (Boolean) -> Unit,
    private val logger: MediaUiEventLogger
) {
    /**
     * Is the view in RTL
@@ -476,6 +477,7 @@ class MediaCarouselScrollHandler(
            visibleMediaIndex = newIndex
            if (oldIndex != visibleMediaIndex && visibleToUser) {
                logSmartspaceImpression(qsExpanded)
                logger.logMediaCarouselPage(newIndex)
            }
            closeGuts(false)
            updatePlayerVisibilities()
+25 −16
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ import androidx.annotation.UiThread;
import androidx.constraintlayout.widget.ConstraintSet;

import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -131,8 +132,6 @@ public class MediaControlPanel {
    private MediaController mController;
    private Lazy<MediaDataManager> mMediaDataManagerLazy;
    private int mBackgroundColor;
    // Instance id for logging purpose.
    protected int mInstanceId = -1;
    // Uid for the media app.
    protected int mUid = Process.INVALID_UID;
    private int mSmartspaceMediaItemsCount;
@@ -140,9 +139,13 @@ public class MediaControlPanel {
    private final MediaOutputDialogFactory mMediaOutputDialogFactory;
    private final FalsingManager mFalsingManager;

    // Used for swipe-to-dismiss logging.
    // Used for logging.
    protected boolean mIsImpressed = false;
    private SystemClock mSystemClock;
    private MediaUiEventLogger mLogger;
    private InstanceId mInstanceId;
    protected int mSmartspaceId = -1;
    private String mPackageName;

    /**
     * Initialize a new control panel
@@ -157,7 +160,7 @@ public class MediaControlPanel {
            Lazy<MediaDataManager> lazyMediaDataManager,
            MediaOutputDialogFactory mediaOutputDialogFactory,
            MediaCarouselController mediaCarouselController,
            FalsingManager falsingManager, SystemClock systemClock) {
            FalsingManager falsingManager, SystemClock systemClock, MediaUiEventLogger logger) {
        mContext = context;
        mBackgroundExecutor = backgroundExecutor;
        mActivityStarter = activityStarter;
@@ -169,8 +172,12 @@ public class MediaControlPanel {
        mMediaCarouselController = mediaCarouselController;
        mFalsingManager = falsingManager;
        mSystemClock = systemClock;
        mLogger = logger;

        mSeekBarViewModel.setLogSmartspaceClick(() -> {
        mSeekBarViewModel.setLogSeek(() -> {
            if (mPackageName != null && mInstanceId != null) {
                mLogger.logSeek(mUid, mPackageName, mInstanceId);
            }
            logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT,
                    /* isRecommendationCard */ false);
            return Unit.INSTANCE;
@@ -261,6 +268,7 @@ public class MediaControlPanel {
        });
        vh.getSettings().setOnClickListener(v -> {
            if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
                mLogger.logLongPressSettings(mUid, mPackageName, mInstanceId);
                mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
            }
        });
@@ -301,16 +309,13 @@ public class MediaControlPanel {
        }
        mKey = key;
        MediaSession.Token token = data.getToken();
        PackageManager packageManager = mContext.getPackageManager();
        try {
            mUid = packageManager.getApplicationInfo(data.getPackageName(), 0 /* flags */).uid;
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Unable to look up package name", e);
        }
        mPackageName = data.getPackageName();
        mUid = data.getAppUid();
        // Only assigns instance id if it's unassigned.
        if (mInstanceId == -1) {
            mInstanceId = SmallHash.hash(mUid + (int) mSystemClock.currentTimeMillis());
        if (mSmartspaceId == -1) {
            mSmartspaceId = SmallHash.hash(mUid + (int) mSystemClock.currentTimeMillis());
        }
        mInstanceId = data.getInstanceId();

        mBackgroundColor = data.getBackgroundColor();
        if (mToken == null || !mToken.equals(token)) {
@@ -401,6 +406,7 @@ public class MediaControlPanel {
                    if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
                        return;
                    }
                    mLogger.logOpenOutputSwitcher(mUid, mPackageName, mInstanceId);
                    if (device.getIntent() != null) {
                        if (device.getIntent().isActivity()) {
                            mActivityStarter.startActivity(
@@ -413,7 +419,7 @@ public class MediaControlPanel {
                            }
                        }
                    } else {
                        mMediaOutputDialogFactory.create(data.getPackageName(), true,
                        mMediaOutputDialogFactory.create(mPackageName, true,
                                mMediaViewHolder.getSeamlessButton());
                    }
                });
@@ -437,6 +443,7 @@ public class MediaControlPanel {

            logSmartspaceCardReported(SMARTSPACE_CARD_DISMISS_EVENT,
                    /* isRecommendationCard */ false);
            mLogger.logLongPressDismiss(mUid, mPackageName, mInstanceId);

            if (mKey != null) {
                closeGuts();
@@ -675,6 +682,7 @@ public class MediaControlPanel {
                button.setEnabled(true);
                button.setOnClickListener(v -> {
                    if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
                        mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId);
                        logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT,
                                /* isRecommendationCard */ false);
                        action.run();
@@ -794,7 +802,7 @@ public class MediaControlPanel {
            return;
        }

        mInstanceId = SmallHash.hash(data.getTargetId());
        mSmartspaceId = SmallHash.hash(data.getTargetId());
        mBackgroundColor = data.getBackgroundColor();
        TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations();
        recommendationCard.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor));
@@ -976,6 +984,7 @@ public class MediaControlPanel {
            mRecommendationViewHolder.marquee(true, mMediaViewController.GUTS_ANIMATION_DURATION);
        }
        mMediaViewController.openGuts();
        mLogger.logLongPressOpen(mUid, mPackageName, mInstanceId);
    }

    /**
@@ -1138,7 +1147,7 @@ public class MediaControlPanel {
    private void logSmartspaceCardReported(int eventId, boolean isRecommendationCard,
            int interactedSubcardRank, int interactedSubcardCardinality) {
        mMediaCarouselController.logSmartspaceCardReported(eventId,
                mInstanceId,
                mSmartspaceId,
                mUid,
                isRecommendationCard,
                new int[]{getSurfaceForSmartspaceLogging()},
+12 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.app.PendingIntent
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.media.session.MediaSession
import com.android.internal.logging.InstanceId
import com.android.systemui.R

/** State of a media view. */
@@ -115,7 +116,17 @@ data class MediaData(
    /**
     * Timestamp when this player was last active.
     */
    var lastActive: Long = 0L
    var lastActive: Long = 0L,

    /**
     * Instance ID for logging purposes
     */
    val instanceId: InstanceId,

    /**
     * The UID of the app, used for logging
     */
    val appUid: Int
) {
    companion object {
        /** Media is playing on the local device */
+75 −15
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.ImageDecoder
import android.graphics.drawable.Icon
@@ -37,6 +38,7 @@ import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.net.Uri
import android.os.Parcelable
import android.os.Process
import android.os.UserHandle
import android.provider.Settings
import android.service.notification.StatusBarNotification
@@ -44,6 +46,7 @@ import android.text.TextUtils
import android.util.Log
import androidx.media.utils.MediaConstants
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.logging.InstanceId
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -94,7 +97,9 @@ private val LOADING = MediaData(
        clickIntent = null,
        device = null,
        active = true,
        resumeAction = null)
        resumeAction = null,
        instanceId = InstanceId.fakeInstanceId(-1),
        appUid = Process.INVALID_UID)

@VisibleForTesting
internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData("INVALID", false, false,
@@ -138,7 +143,8 @@ class MediaDataManager(
    private val useQsMediaPlayer: Boolean,
    private val systemClock: SystemClock,
    private val tunerService: TunerService,
    private val mediaFlags: MediaFlags
    private val mediaFlags: MediaFlags,
    private val logger: MediaUiEventLogger
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {

    companion object {
@@ -202,12 +208,13 @@ class MediaDataManager(
        smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
        clock: SystemClock,
        tunerService: TunerService,
        mediaFlags: MediaFlags
        mediaFlags: MediaFlags,
        logger: MediaUiEventLogger
    ) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory,
            broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener,
            mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter,
            activityStarter, smartspaceMediaDataProvider, Utils.useMediaResumption(context),
            Utils.useQsMediaPlayer(context), clock, tunerService, mediaFlags)
            Utils.useQsMediaPlayer(context), clock, tunerService, mediaFlags, logger)

    private val appChangeReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
@@ -298,17 +305,24 @@ class MediaDataManager(

    fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
        if (useQsMediaPlayer && isMediaNotification(sbn)) {
            var logEvent = false
            Assert.isMainThread()
            val oldKey = findExistingEntry(key, sbn.packageName)
            if (oldKey == null) {
                val temp = LOADING.copy(packageName = sbn.packageName)
                val instanceId = logger.getNewInstanceId()
                val temp = LOADING.copy(
                    packageName = sbn.packageName,
                    instanceId = instanceId
                )
                mediaEntries.put(key, temp)
                logEvent = true
            } else if (oldKey != key) {
                // Move to new key
                // Resume -> active conversion; move to new key
                val oldData = mediaEntries.remove(oldKey)!!
                logEvent = true
                mediaEntries.put(key, oldData)
            }
            loadMediaData(key, sbn, oldKey)
            loadMediaData(key, sbn, oldKey, logEvent)
        } else {
            onNotificationRemoved(key)
        }
@@ -340,9 +354,23 @@ class MediaDataManager(
    ) {
        // Resume controls don't have a notification key, so store by package name instead
        if (!mediaEntries.containsKey(packageName)) {
            val resumeData = LOADING.copy(packageName = packageName, resumeAction = action,
                hasCheckedForResume = true)
            val instanceId = logger.getNewInstanceId()
            val appUid = try {
                context.packageManager.getApplicationInfo(packageName, 0)?.uid!!
            } catch (e: PackageManager.NameNotFoundException) {
                Log.w(TAG, "Could not get app UID for $packageName", e)
                Process.INVALID_UID
            }

            val resumeData = LOADING.copy(
                packageName = packageName,
                resumeAction = action,
                hasCheckedForResume = true,
                instanceId = instanceId,
                appUid = appUid
            )
            mediaEntries.put(packageName, resumeData)
            logger.logResumeMediaAdded(appUid, packageName, instanceId)
        }
        backgroundExecutor.execute {
            loadMediaDataInBgForResumption(userId, desc, action, token, appName, appIntent,
@@ -368,10 +396,11 @@ class MediaDataManager(
    private fun loadMediaData(
        key: String,
        sbn: StatusBarNotification,
        oldKey: String?
        oldKey: String?,
        logEvent: Boolean = false
    ) {
        backgroundExecutor.execute {
            loadMediaDataInBg(key, sbn, oldKey)
            loadMediaDataInBg(key, sbn, oldKey, logEvent)
        }
    }

@@ -449,6 +478,10 @@ class MediaDataManager(
     */
    internal fun setTimedOut(key: String, timedOut: Boolean, forceUpdate: Boolean = false) {
        mediaEntries[key]?.let {
            if (timedOut && !forceUpdate) {
                // Only log this event when media expires on its own
                logger.logMediaTimeout(it.appUid, it.packageName, it.instanceId)
            }
            if (it.active == !timedOut && !forceUpdate) {
                if (it.resumption) {
                    if (DEBUG) Log.d(TAG, "timing out resume player $key")
@@ -463,7 +496,9 @@ class MediaDataManager(
    }

    private fun removeEntry(key: String) {
        mediaEntries.remove(key)
        mediaEntries.remove(key)?.let {
            logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
        }
        notifyMediaDataRemoved(key)
    }

@@ -537,6 +572,10 @@ class MediaDataManager(
            null
        }

        val currentEntry = mediaEntries.get(packageName)
        val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
        val appUid = currentEntry?.appUid ?: Process.INVALID_UID

        val mediaAction = getResumeMediaAction(resumeAction)
        val lastActive = systemClock.elapsedRealtime()
        foregroundExecutor.execute {
@@ -545,14 +584,16 @@ class MediaDataManager(
                    MediaButton(playOrPause = mediaAction), packageName, token, appIntent,
                    device = null, active = false,
                    resumeAction = resumeAction, resumption = true, notificationKey = packageName,
                    hasCheckedForResume = true, lastActive = lastActive))
                    hasCheckedForResume = true, lastActive = lastActive, instanceId = instanceId,
                    appUid = appUid))
        }
    }

    private fun loadMediaDataInBg(
        key: String,
        sbn: StatusBarNotification,
        oldKey: String?
        oldKey: String?,
        logEvent: Boolean = false
    ) {
        val token = sbn.notification.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION)
                as MediaSession.Token?
@@ -636,6 +677,22 @@ class MediaDataManager(
                        MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) MediaData.PLAYBACK_LOCAL
                else MediaData.PLAYBACK_CAST_LOCAL
        val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null

        val currentEntry = mediaEntries.get(key)
        val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
        val appUid = try {
            context.packageManager.getApplicationInfo(sbn.packageName, 0)?.uid!!
        } catch (e: PackageManager.NameNotFoundException) {
            Log.w(TAG, "Could not get app UID for ${sbn.packageName}", e)
            Process.INVALID_UID
        }

        if (logEvent) {
            logger.logActiveMediaAdded(appUid, sbn.packageName, instanceId, playbackLocation)
        } else if (playbackLocation != currentEntry?.playbackLocation) {
            logger.logPlaybackLocationChange(appUid, sbn.packageName, instanceId, playbackLocation)
        }

        val lastActive = systemClock.elapsedRealtime()
        foregroundExecutor.execute {
            val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
@@ -647,7 +704,7 @@ class MediaDataManager(
                    active, resumeAction = resumeAction, playbackLocation = playbackLocation,
                    notificationKey = key, hasCheckedForResume = hasCheckedForResume,
                    isPlaying = isPlaying, isClearable = sbn.isClearable(),
                    lastActive = lastActive))
                    lastActive = lastActive, instanceId = instanceId, appUid = appUid))
        }
    }

@@ -989,10 +1046,12 @@ class MediaDataManager(
                notifyMediaDataRemoved(key)
                notifyMediaDataLoaded(pkg, pkg, updated)
            }
            logger.logActiveConvertedToResume(updated.appUid, pkg, updated.instanceId)
            return
        }
        if (removed != null) {
            notifyMediaDataRemoved(key)
            logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId)
        }
    }

@@ -1009,6 +1068,7 @@ class MediaDataManager(
            filtered.forEach {
                mediaEntries.remove(it.key)
                notifyMediaDataRemoved(it.key)
                logger.logMediaRemoved(it.value.appUid, it.value.packageName, it.value.instanceId)
            }
        }
    }
Loading