chromium/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubMetricUtils.java

// Copyright 2024 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.safety_hub;

import androidx.annotation.IntDef;
import androidx.annotation.StringDef;
import androidx.annotation.VisibleForTesting;

import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.safety_hub.SafetyHubModuleProperties.ModuleOption;
import org.chromium.chrome.browser.safety_hub.SafetyHubModuleProperties.ModuleState;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.StringJoiner;

/** Helper utils to log UMA histograms for Safety Hub. */
public class SafetyHubMetricUtils {
    @VisibleForTesting
    public static final String EXTERNAL_INTERACTIONS_HISTOGRAM_NAME =
            "Settings.SafetyHub.ExternalInteractions";

    @VisibleForTesting
    public static final String PERMISSIONS_INTERACTIONS_HISTOGRAM_NAME =
            "Settings.SafetyCheck.UnusedSitePermissionsModule.Interactions";

    @VisibleForTesting
    public static final String NOTIFICATIONS_INTERACTIONS_HISTOGRAM_NAME =
            "Settings.SafetyHub.NotificationPermissionsModule.Interactions";

    @VisibleForTesting
    public static final String DASHBOARD_INTERACTIONS_HISTOGRAM_NAME =
            "Settings.SafetyHub.Dashboard.Interactions";

    @VisibleForTesting
    public static final String MODULE_STATE_HISTOGRAM_NAME =
            "Settings.SafetyHub.Dashboard.Interactions";

    /**
     * Interactions on surfaces outside of the Safety Hub settings pages. These can be in the Magic
     * Stack or in other settings surfaces. Must be kept in sync with SafetyHubExternalInteractions
     * in settings/enums.xml.
     */
    @IntDef({
        ExternalInteractions.OPEN_FROM_SETTINGS_PAGE,
        ExternalInteractions.OPEN_FROM_MAGIC_STACK,
        ExternalInteractions.OPEN_SAFE_BROWSING_FROM_MAGIC_STACK,
        ExternalInteractions.OPEN_GPM_FROM_MAGIC_STACK,
        ExternalInteractions.MAX_VALUE
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ExternalInteractions {
        int OPEN_FROM_SETTINGS_PAGE = 0;
        int OPEN_FROM_MAGIC_STACK = 1;
        int OPEN_SAFE_BROWSING_FROM_MAGIC_STACK = 2;
        int OPEN_GPM_FROM_MAGIC_STACK = 3;
        int MAX_VALUE = OPEN_GPM_FROM_MAGIC_STACK;
    }

    /**
     * All module types that can appear on the Safety Hub dashboard, including the browser state
     * module. Must be kept in sync with SafetyHubDashboardModuleType in settings/histograms.xml.
     */
    @StringDef({
        DashboardModuleType.UPDATE_CHECK,
        DashboardModuleType.PASSWORDS,
        DashboardModuleType.SAFE_BROWSING,
        DashboardModuleType.REVOKED_PERMISSIONS,
        DashboardModuleType.NOTIFICATION_REVIEW,
        DashboardModuleType.BROWSER_STATE
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface DashboardModuleType {
        String UPDATE_CHECK = "UpdateCheck";
        String PASSWORDS = "Passwords";
        String SAFE_BROWSING = "SafeBrowsing";
        String REVOKED_PERMISSIONS = "RevokedPermissions";
        String NOTIFICATION_REVIEW = "NotificationReview";
        String BROWSER_STATE = "BrowserState";
    }

    /**
     * The moment when the dashboard state metrics are logged. Currently we log the state of each
     * module only page impression and exit. Must be kept in sync with SafetyHubLifecycleEvent in
     * settings/histograms.xml.
     */
    @StringDef({LifecycleEvent.ON_IMPRESSION, LifecycleEvent.ON_EXIT})
    @Retention(RetentionPolicy.SOURCE)
    @interface LifecycleEvent {
        String ON_IMPRESSION = "OnImpression";
        String ON_EXIT = "OnExit";
    }

    /**
     * All user interactions that can happen in the permissions review subpage or the permissions
     * review dashboard module. Must be kept in sync with
     * SafetyCheckUnusedSitePermissionsModuleInteractions in settings/enums.xml.
     */
    @IntDef({
        PermissionsModuleInteractions.OPEN_REVIEW_UI,
        PermissionsModuleInteractions.ALLOW_AGAIN,
        PermissionsModuleInteractions.ACKNOWLEDGE_ALL,
        PermissionsModuleInteractions.UNDO_ALLOW_AGAIN,
        PermissionsModuleInteractions.UNDO_ACKNOWLEDGE_ALL,
        PermissionsModuleInteractions.MINIMIZE_REVIEW_UI,
        PermissionsModuleInteractions.GO_TO_SETTINGS,
        PermissionsModuleInteractions.MAX_VALUE
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface PermissionsModuleInteractions {
        int OPEN_REVIEW_UI = 0;
        int ALLOW_AGAIN = 1;
        int ACKNOWLEDGE_ALL = 2;
        int UNDO_ALLOW_AGAIN = 3;
        int UNDO_ACKNOWLEDGE_ALL = 4;
        int MINIMIZE_REVIEW_UI = 5;
        int GO_TO_SETTINGS = 6;
        int MAX_VALUE = GO_TO_SETTINGS;
    }

    /**
     * All user interactions that can happen in the notifications review subpage or the
     * notifications review dashboard module. Must be kipe in sync with
     * SafetyCheckNotificationsModuleInteractions in settings/enums.xml.
     */
    @IntDef({
        NotificationsModuleInteractions.BLOCK,
        NotificationsModuleInteractions.BLOCK_ALL,
        NotificationsModuleInteractions.IGNORE,
        NotificationsModuleInteractions.MINIMIZE,
        NotificationsModuleInteractions.RESET,
        NotificationsModuleInteractions.UNDO_BLOCK,
        NotificationsModuleInteractions.UNDO_IGNORE,
        NotificationsModuleInteractions.UNDO_RESET,
        NotificationsModuleInteractions.OPEN_UI_REVIEW,
        NotificationsModuleInteractions.UNDO_BLOCK_ALL,
        NotificationsModuleInteractions.GO_TO_SETTINGS,
        NotificationsModuleInteractions.MAX_VALUE
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface NotificationsModuleInteractions {
        int BLOCK = 0;
        int BLOCK_ALL = 1;
        int IGNORE = 2;
        int MINIMIZE = 3;
        int RESET = 4;
        int UNDO_BLOCK = 5;
        int UNDO_IGNORE = 6;
        int UNDO_RESET = 7;
        int OPEN_UI_REVIEW = 8;
        int UNDO_BLOCK_ALL = 9;
        int GO_TO_SETTINGS = 10;
        int MAX_VALUE = GO_TO_SETTINGS;
    }

    /**
     * All user interactions that can happen in the Safety Hub dashboard, excluding the permissions
     * and notifications review modules. Must be kept in sync with SafetyHubDashboardInteractions in
     * settings/enums.xml.
     */
    @IntDef({
        DashboardInteractions.OPEN_PLAY_STORE,
        DashboardInteractions.GO_TO_SAFE_BROWSING_SETTINGS,
        DashboardInteractions.OPEN_SAFETY_TOOLS_INFO,
        DashboardInteractions.OPEN_INCOGNITO_INFO,
        DashboardInteractions.OPEN_SAFE_BROWSING_INFO,
        DashboardInteractions.OPEN_HELP_CENTER,
        DashboardInteractions.OPEN_PASSWORD_MANAGER,
        DashboardInteractions.SHOW_SIGN_IN_PROMO,
        DashboardInteractions.MAX_VALUE
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface DashboardInteractions {
        int OPEN_PLAY_STORE = 0;
        int GO_TO_SAFE_BROWSING_SETTINGS = 1;
        int OPEN_SAFETY_TOOLS_INFO = 2;
        int OPEN_INCOGNITO_INFO = 3;
        int OPEN_SAFE_BROWSING_INFO = 4;
        int OPEN_HELP_CENTER = 5;
        int OPEN_PASSWORD_MANAGER = 6;
        int SHOW_SIGN_IN_PROMO = 7;
        int MAX_VALUE = SHOW_SIGN_IN_PROMO;
    }

    public static String getDashboardModuleTypeForModuleOption(@ModuleOption int option) {
        switch (option) {
            case ModuleOption.UPDATE_CHECK:
                return DashboardModuleType.UPDATE_CHECK;
            case ModuleOption.ACCOUNT_PASSWORDS:
                return DashboardModuleType.PASSWORDS;
            case ModuleOption.SAFE_BROWSING:
                return DashboardModuleType.SAFE_BROWSING;
            case ModuleOption.UNUSED_PERMISSIONS:
                return DashboardModuleType.REVOKED_PERMISSIONS;
            case ModuleOption.NOTIFICATION_REVIEW:
                return DashboardModuleType.NOTIFICATION_REVIEW;
            default:
                throw new IllegalArgumentException();
        }
    }

    public static void recordExternalInteractions(@ExternalInteractions int value) {
        RecordHistogram.recordEnumeratedHistogram(
                EXTERNAL_INTERACTIONS_HISTOGRAM_NAME, value, ExternalInteractions.MAX_VALUE);
    }

    static void recordModuleState(
            @ModuleState int state,
            @DashboardModuleType String moduleType,
            @LifecycleEvent String lifecycleEvent) {
        StringJoiner joiner = new StringJoiner(".");
        joiner.add(MODULE_STATE_HISTOGRAM_NAME);
        joiner.add(moduleType);
        joiner.add(lifecycleEvent);
        String histogramName = joiner.toString();

        RecordHistogram.recordEnumeratedHistogram(histogramName, state, ModuleState.MAX_VALUE);
    }

    static void recordRevokedPermissionsInteraction(@PermissionsModuleInteractions int value) {
        RecordHistogram.recordEnumeratedHistogram(
                PERMISSIONS_INTERACTIONS_HISTOGRAM_NAME,
                value,
                PermissionsModuleInteractions.MAX_VALUE);
    }

    static void recordNotificationsInteraction(@NotificationsModuleInteractions int value) {
        RecordHistogram.recordEnumeratedHistogram(
                NOTIFICATIONS_INTERACTIONS_HISTOGRAM_NAME,
                value,
                NotificationsModuleInteractions.MAX_VALUE);
    }

    static void recordDashboardInteractions(@DashboardInteractions int value) {
        RecordHistogram.recordEnumeratedHistogram(
                DASHBOARD_INTERACTIONS_HISTOGRAM_NAME, value, DashboardInteractions.MAX_VALUE);
    }
}