chromium/chrome/browser/android/metrics/java/src/org/chromium/chrome/browser/metrics/MainIntentBehaviorMetrics.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.metrics;

import android.text.format.DateUtils;

import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.ResettersForTesting;
import org.chromium.base.ThreadUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.base.shared_preferences.SharedPreferencesManager;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoUtils;

/** Records the behavior metrics after an ACTION_MAIN intent is received. */
public class MainIntentBehaviorMetrics {
    static final long TIMEOUT_DURATION_MS = 10000;

    private static long sTimeoutDurationMs = TIMEOUT_DURATION_MS;
    private static boolean sLoggedLaunchBehavior;
    private static boolean sHasRegisteredApplicationStateListener;

    private final Runnable mLogLaunchRunnable;

    /** Constructs a metrics handler for ACTION_MAIN intents received for an activity. */
    public MainIntentBehaviorMetrics() {
        mLogLaunchRunnable = () -> logLaunchBehaviorInternal();
    }

    private void ensureApplicationStateListenerRegistered() {
        if (sHasRegisteredApplicationStateListener) return;
        sHasRegisteredApplicationStateListener = true;
        ApplicationStatus.registerApplicationStateListener(
                newState -> {
                    if (newState == ApplicationState.HAS_STOPPED_ACTIVITIES) {
                        sLoggedLaunchBehavior = false;
                    }
                });
    }

    /**
     * Signal that an intent with ACTION_MAIN was received.
     *
     * This must only be called after the native libraries have been initialized.
     */
    public void onMainIntentWithNative(long backgroundDurationMs) {
        RecordUserAction.record("MobileStartup.MainIntentReceived");

        if (backgroundDurationMs >= DateUtils.HOUR_IN_MILLIS * 24) {
            RecordUserAction.record("MobileStartup.MainIntentReceived.After24Hours");
        } else if (backgroundDurationMs >= DateUtils.HOUR_IN_MILLIS * 12) {
            RecordUserAction.record("MobileStartup.MainIntentReceived.After12Hours");
        } else if (backgroundDurationMs >= DateUtils.HOUR_IN_MILLIS * 6) {
            RecordUserAction.record("MobileStartup.MainIntentReceived.After6Hours");
        } else if (backgroundDurationMs >= DateUtils.HOUR_IN_MILLIS) {
            RecordUserAction.record("MobileStartup.MainIntentReceived.After1Hour");
        }

        logLaunchBehaviorInternal();
    }

    /** Allows test to override the timeout duration. */
    public static void setTimeoutDurationMsForTesting(long duration) {
        var oldValue = sTimeoutDurationMs;
        sTimeoutDurationMs = duration;
        ResettersForTesting.register(() -> sTimeoutDurationMs = oldValue);
    }

    /**
     * Log how many times user intentionally (from launcher or recents) launch Chrome per day,
     * and the type of each launch.
     */
    public void logLaunchBehavior() {
        ensureApplicationStateListenerRegistered();
        if (sLoggedLaunchBehavior) return;
        ThreadUtils.getUiThreadHandler().postDelayed(mLogLaunchRunnable, sTimeoutDurationMs);
    }

    private void logLaunchBehaviorInternal() {
        ensureApplicationStateListenerRegistered();
        if (sLoggedLaunchBehavior) return;
        sLoggedLaunchBehavior = true;

        SharedPreferencesManager prefs = ChromeSharedPreferences.getInstance();
        long current = System.currentTimeMillis();
        long timestamp =
                prefs.readLong(ChromePreferenceKeys.METRICS_MAIN_INTENT_LAUNCH_TIMESTAMP, 0);
        int count = prefs.readInt(ChromePreferenceKeys.METRICS_MAIN_INTENT_LAUNCH_COUNT, 0);

        if (current - timestamp > DateUtils.DAY_IN_MILLIS) {
            // Log count if it's not first launch of Chrome.
            if (timestamp != 0) {
                RecordHistogram.recordCount1MHistogram("MobileStartup.DailyLaunchCount", count);
            }
            count = 0;
            prefs.writeLong(ChromePreferenceKeys.METRICS_MAIN_INTENT_LAUNCH_TIMESTAMP, current);
        }

        count++;
        prefs.writeInt(ChromePreferenceKeys.METRICS_MAIN_INTENT_LAUNCH_COUNT, count);

        DefaultBrowserPromoUtils.incrementSessionCount();

        ThreadUtils.getUiThreadHandler().removeCallbacks(mLogLaunchRunnable);
    }
}