chromium/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreviewMetrics.java

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.paint_preview;

import android.os.SystemClock;

import androidx.annotation.IntDef;

import org.chromium.base.ObserverList;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.base.supplier.Supplier;
import org.chromium.components.paintpreview.player.CompositorStatus;

import java.util.HashMap;
import java.util.Map;

/** Helper class for recording metrics related to TabbedPaintPreview. */
public class StartupPaintPreviewMetrics {
    /** Used for recording the cause for exiting the Paint Preview player. */
    @IntDef({
        ExitCause.PULL_TO_REFRESH,
        ExitCause.SNACK_BAR_ACTION,
        ExitCause.COMPOSITOR_FAILURE,
        ExitCause.TAB_FINISHED_LOADING,
        ExitCause.LINK_CLICKED,
        ExitCause.NAVIGATION_STARTED,
        ExitCause.TAB_DESTROYED,
        ExitCause.TAB_HIDDEN,
        ExitCause.OFFLINE_AVAILABLE,
        ExitCause.ACCESSIBILITY_NOT_SUPPORTED
    })
    @interface ExitCause {
        int PULL_TO_REFRESH = 0;
        int SNACK_BAR_ACTION = 1;
        int COMPOSITOR_FAILURE = 2;
        int TAB_FINISHED_LOADING = 3;
        int LINK_CLICKED = 4;
        int NAVIGATION_STARTED = 5;
        int TAB_DESTROYED = 6;
        int TAB_HIDDEN = 7;
        int OFFLINE_AVAILABLE = 8;
        int ACCESSIBILITY_NOT_SUPPORTED = 9;
        int COUNT = 10;
    }

    /** An interface to get notified of various paint preview metric events */
    public interface PaintPreviewMetricsObserver {
        /**
         * Called on the first paint of a paint preview
         * @param durationMs duration from activity creation to first paint. Reported in millis.
         */
        void onFirstPaint(long durationMs);

        /**
         * Called on the first paint of a paint preview in the case where the first paint was not
         * recorded. Added to aid in investigating crbug.com/1273097.
         */
        void onUnrecordedFirstPaint();
    }

    private static final Map<Integer, String> UPTIME_HISTOGRAM_MAP = new HashMap<>();

    static {
        UPTIME_HISTOGRAM_MAP.put(
                ExitCause.PULL_TO_REFRESH,
                "Browser.PaintPreview.TabbedPlayer.UpTime.RemovedByPullToRefresh");
        UPTIME_HISTOGRAM_MAP.put(
                ExitCause.SNACK_BAR_ACTION,
                "Browser.PaintPreview.TabbedPlayer.UpTime.RemovedBySnackBar");
        UPTIME_HISTOGRAM_MAP.put(
                ExitCause.COMPOSITOR_FAILURE,
                "Browser.PaintPreview.TabbedPlayer.UpTime.RemovedByCompositorFailure");
        UPTIME_HISTOGRAM_MAP.put(
                ExitCause.TAB_FINISHED_LOADING,
                "Browser.PaintPreview.TabbedPlayer.UpTime.RemovedOnLoad");
        UPTIME_HISTOGRAM_MAP.put(
                ExitCause.LINK_CLICKED,
                "Browser.PaintPreview.TabbedPlayer.UpTime.RemovedByLinkClick");
        UPTIME_HISTOGRAM_MAP.put(
                ExitCause.NAVIGATION_STARTED,
                "Browser.PaintPreview.TabbedPlayer.UpTime.RemovedByNavigation");
        UPTIME_HISTOGRAM_MAP.put(
                ExitCause.TAB_DESTROYED,
                "Browser.PaintPreview.TabbedPlayer.UpTime.RemovedOnTabDestroy");
        UPTIME_HISTOGRAM_MAP.put(
                ExitCause.TAB_HIDDEN,
                "Browser.PaintPreview.TabbedPlayer.UpTime.RemovedOnTabHidden");
        UPTIME_HISTOGRAM_MAP.put(
                ExitCause.OFFLINE_AVAILABLE,
                "Browser.PaintPreview.TabbedPlayer.UpTime.RemovedOnOfflineAvailable");
        UPTIME_HISTOGRAM_MAP.put(
                ExitCause.ACCESSIBILITY_NOT_SUPPORTED,
                "Browser.PaintPreview.TabbedPlayer.UpTime.RemovedOnAccessibilityNotSupported");
    }

    private long mShownTime;
    private boolean mFirstPaintHappened;
    private final ObserverList<PaintPreviewMetricsObserver> mObservers = new ObserverList<>();

    void onShown() {
        mShownTime = System.currentTimeMillis();
    }

    void onFirstPaint(long activityOnCreateTimestamp, Supplier<Boolean> shouldRecordFirstPaint) {
        mFirstPaintHappened = true;
        if (shouldRecordFirstPaint != null && shouldRecordFirstPaint.get()) {
            long durationMs = SystemClock.elapsedRealtime() - activityOnCreateTimestamp;
            RecordHistogram.recordLongTimesHistogram(
                    "Browser.PaintPreview.TabbedPlayer.TimeToFirstBitmap", durationMs);
            for (PaintPreviewMetricsObserver observer : mObservers) {
                observer.onFirstPaint(durationMs);
            }
        } else if (shouldRecordFirstPaint != null) {
            for (PaintPreviewMetricsObserver observer : mObservers) {
                observer.onUnrecordedFirstPaint();
            }
        }
    }

    void onTabLoadFinished() {
        RecordHistogram.recordBooleanHistogram(
                "Browser.PaintPreview.TabbedPlayer.FirstPaintBeforeTabLoad", mFirstPaintHappened);
    }

    void onCompositorFailure(@CompositorStatus int status) {
        RecordHistogram.recordEnumeratedHistogram(
                "Browser.PaintPreview.TabbedPlayer.CompositorFailureReason",
                status,
                CompositorStatus.COUNT);
    }

    void recordHadCapture(boolean hadCapture) {
        RecordHistogram.recordBooleanHistogram(
                "Browser.PaintPreview.TabbedPlayer.HadCapture", hadCapture);
    }

    void recordExitMetrics(int exitCause, int snackbarShownCount) {
        if (exitCause == ExitCause.SNACK_BAR_ACTION) {
            RecordUserAction.record("PaintPreview.TabbedPlayer.Actionbar.Action");
        }

        RecordUserAction.record("PaintPreview.TabbedPlayer.Removed");
        RecordHistogram.recordCount1MHistogram(
                "Browser.PaintPreview.TabbedPlayer.SnackbarCount", snackbarShownCount);
        RecordHistogram.recordEnumeratedHistogram(
                "Browser.PaintPreview.TabbedPlayer.ExitCause", exitCause, ExitCause.COUNT);
        if (mShownTime == 0 || !UPTIME_HISTOGRAM_MAP.containsKey(exitCause)) return;

        long upTime = System.currentTimeMillis() - mShownTime;
        RecordHistogram.recordLongTimesHistogram(UPTIME_HISTOGRAM_MAP.get(exitCause), upTime);
    }

    void addMetricsObserver(PaintPreviewMetricsObserver observer) {
        mObservers.addObserver(observer);
    }
}