chromium/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationUmaHelper.java

// Copyright 2017 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.download;

import static android.app.DownloadManager.ACTION_NOTIFICATION_CLICKED;

import static org.chromium.chrome.browser.download.DownloadNotificationService.ACTION_DOWNLOAD_CANCEL;
import static org.chromium.chrome.browser.download.DownloadNotificationService.ACTION_DOWNLOAD_OPEN;
import static org.chromium.chrome.browser.download.DownloadNotificationService.ACTION_DOWNLOAD_PAUSE;
import static org.chromium.chrome.browser.download.DownloadNotificationService.ACTION_DOWNLOAD_RESUME;

import androidx.annotation.IntDef;

import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.metrics.RecordHistogram;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;

/** Helper to track necessary stats in UMA related to downloads notifications. */
public final class DownloadNotificationUmaHelper {
    // NOTE: Keep these lists/classes in sync with DownloadNotification[...] in enums.xml.
    @IntDef({ForegroundLifecycle.START, ForegroundLifecycle.UPDATE, ForegroundLifecycle.STOP})
    @Retention(RetentionPolicy.SOURCE)
    public @interface ForegroundLifecycle {
        int START = 0; // Initial startForeground.
        int UPDATE = 1; // Switching pinned notification.
        int STOP = 2; // Calling stopForeground.
        int NUM_ENTRIES = 3;
    }

    private static List<String> sInteractions =
            Arrays.asList(
                    ACTION_NOTIFICATION_CLICKED, // Opening a download where
                    // LegacyHelpers.isLegacyDownload.
                    ACTION_DOWNLOAD_OPEN, // Opening a download that is not a legacy download.
                    ACTION_DOWNLOAD_CANCEL,
                    ACTION_DOWNLOAD_PAUSE,
                    ACTION_DOWNLOAD_RESUME);

    @IntDef({LaunchType.LAUNCH, LaunchType.RELAUNCH})
    @Retention(RetentionPolicy.SOURCE)
    public @interface LaunchType {
        int LAUNCH = 0; // "Denominator" for expected launched notifications.
        int RELAUNCH = 1;
        int NUM_ENTRIES = 2;
    }

    @IntDef({
        ServiceStopped.STOPPED,
        ServiceStopped.DESTROYED,
        ServiceStopped.TASK_REMOVED,
        ServiceStopped.LOW_MEMORY,
        ServiceStopped.START_STICKY
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ServiceStopped {
        int STOPPED = 0; // Expected, intentional stops, serves as a "denominator".
        int DESTROYED = 1;
        int TASK_REMOVED = 2;
        int LOW_MEMORY = 3;
        int START_STICKY = 4;
        int NUM_ENTRIES = 5;
    }

    /**
     * Records an instance where a user interacts with a notification (clicks on, pauses, etc).
     * @param action Notification interaction that was taken (ie. pause, resume).
     */
    static void recordNotificationInteractionHistogram(String action) {
        if (!LibraryLoader.getInstance().isInitialized()) return;
        int actionType = sInteractions.indexOf(action);
        if (actionType == -1) return;
        RecordHistogram.recordEnumeratedHistogram(
                "Android.DownloadManager.NotificationInteraction",
                actionType,
                sInteractions.size());
    }

    /**
     * Records an instance where the foreground stops, using expected stops as the denominator to
     * understand the frequency of unexpected stops (low memory, task removed, etc).
     * @param stopType Type of the foreground stop that is being recorded ({@link ServiceStopped}).
     */
    static void recordServiceStoppedHistogram(@ServiceStopped int stopType) {
        if (!LibraryLoader.getInstance().isInitialized()) return;
        RecordHistogram.recordEnumeratedHistogram(
                "Android.DownloadManager.ServiceStopped.DownloadForeground",
                stopType,
                ServiceStopped.NUM_ENTRIES);
    }

    /**
     * Records an instance where the foreground undergoes a lifecycle change (when the foreground
     * starts, changes pinned notification, or stops).
     * @param lifecycleStep The lifecycle step that is being recorded ({@link ForegroundLifecycle}).
     */
    static void recordForegroundServiceLifecycleHistogram(@ForegroundLifecycle int lifecycleStep) {
        if (!LibraryLoader.getInstance().isInitialized()) return;
        RecordHistogram.recordEnumeratedHistogram(
                "Android.DownloadManager.ForegroundServiceLifecycle",
                lifecycleStep,
                ForegroundLifecycle.NUM_ENTRIES);
    }
}