// Copyright 2021 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.components.messages;
import androidx.annotation.IntDef;
import org.chromium.base.Log;
import org.chromium.base.TimeUtils;
import org.chromium.base.metrics.RecordHistogram;
/**
* Static utility methods for recording messages related metrics. TODO(crbug.com/40877562): remove
* logs.
*/
public class MessagesMetrics {
private static final String TAG = "MessagesMetrics";
private static final String ENQUEUED_HISTOGRAM_NAME = "Android.Messages.Enqueued";
private static final String ENQUEUED_SUSPEND_HISTOGRAM_NAME =
"Android.Messages.Enqueued.Suspended";
private static final String ENQUEUED_RESUME_HISTOGRAM_NAME =
"Android.Messages.Enqueued.Resumed";
private static final String ENQUEUED_SCOPE_ACTIVE_HISTOGRAM_NAME =
"Android.Messages.Enqueued.ScopeActive";
private static final String ENQUEUED_SCOPE_INACTIVE_HISTOGRAM_NAME =
"Android.Messages.Enqueued.ScopeInactive";
private static final String ENQUEUED_VISIBLE_HISTOGRAM_NAME =
"Android.Messages.Enqueued.Visible";
private static final String ENQUEUED_HIDDEN_HISTOGRAM_NAME = "Android.Messages.Enqueued.Hidden";
private static final String ENQUEUED_HIDING_HISTOGRAM_NAME = "Android.Messages.Enqueued.Hiding";
private static final String FULLY_VISIBLE_NAME = "Android.Messages.FullyVisible";
private static final String ERROR_FULLY_VISIBLE_NOT_INFORMED_NAME =
"Android.Messages.Error.FullyVisibleNotInformed";
private static final String DISMISSED_WITHOUT_FULLY_VISIBLE =
"Android.Messages.DismissedWithoutFullyVisible";
private static final String DISMISSED_HISTOGRAM_PREFIX = "Android.Messages.Dismissed.";
private static final String TIME_TO_ACTION_HISTOGRAM_PREFIX = "Android.Messages.TimeToAction.";
private static final String TIME_TO_ACTION_DISMISS_HISTOGRAM_PREFIX =
"Android.Messages.TimeToAction.Dismiss.";
static final String STACKING_HISTOGRAM_NAME = "Android.Messages.Stacking";
static final String STACKING_HIDDEN_NAME = "Android.Messages.Stacking.Hidden";
static final String STACKING_HIDING_NAME = "Android.Messages.Stacking.Hiding";
static final String STACKING_REQUEST_TO_SHOW_NAME =
"Android.Messages.Stacking.RequestToFullyShow";
static final String STACKING_BLOCKED_BY_BROWSER_CONTROL_NAME =
"Android.Messages.Stacking.BlockedByBrowserControl";
static final String STACKING_BLOCKED_BY_CONTAINER_INITING_NAME =
"Android.Messages.Stacking.BlockedByContainerInitializing";
static final String STACKING_BLOCKED_BY_CONTAINER_NOT_INITED_NAME =
"Android.Messages.Stacking.BlockedByContainerNotInitialized";
static final String STACKING_TIME_TO_FULLY_SHOW_PREFIX = "Android.Messages.TimeToFullyShow.";
static final String STACKING_ACTION_HISTOGRAM_PREFIX = "Android.Messages.Stacking.";
static final String THREE_STACKED_HISTOGRAM_NAME = "Android.Messages.Stacking.ThreeStacked";
@IntDef({
StackingAnimationType.SHOW_ALL,
StackingAnimationType.SHOW_FRONT_ONLY,
StackingAnimationType.REMOVE_FRONT_AND_SHOW_BACK,
StackingAnimationType.REMOVE_ALL,
StackingAnimationType.REMOVE_FRONT_ONLY,
StackingAnimationType.REMOVE_BACK_ONLY,
StackingAnimationType.SHOW_BACK_ONLY,
StackingAnimationType.INSERT_AT_FRONT,
StackingAnimationType.MAX_VALUE
})
public @interface StackingAnimationType {
int SHOW_ALL = 0;
int SHOW_FRONT_ONLY = 1;
int REMOVE_FRONT_AND_SHOW_BACK = 2;
int REMOVE_ALL = 3;
int REMOVE_FRONT_ONLY = 4;
int REMOVE_BACK_ONLY = 5;
int SHOW_BACK_ONLY = 6;
int INSERT_AT_FRONT = 7;
int MAX_VALUE = 8;
}
@IntDef({
StackingAnimationAction.INSERT_AT_FRONT,
StackingAnimationAction.INSERT_AT_BACK,
StackingAnimationAction.PUSH_TO_FRONT,
StackingAnimationAction.PUSH_TO_BACK,
StackingAnimationAction.REMOVE_FRONT,
StackingAnimationAction.REMOVE_BACK,
StackingAnimationAction.MAX_VALUE
})
public @interface StackingAnimationAction {
int INSERT_AT_FRONT = 0;
int INSERT_AT_BACK = 1;
int PUSH_TO_FRONT = 2;
int PUSH_TO_BACK = 3;
int REMOVE_FRONT = 4;
int REMOVE_BACK = 5;
int MAX_VALUE = 6;
}
@IntDef({
ThreeStackedScenario.HIGH_PRIORITY,
ThreeStackedScenario.IN_SEQUENCE,
ThreeStackedScenario.MAX_VALUE
})
public @interface ThreeStackedScenario {
int HIGH_PRIORITY = 0;
int IN_SEQUENCE = 1;
int MAX_VALUE = 2;
}
/** Records metrics when a message is being enqueued. */
static void recordMessageEnqueued(@MessageIdentifier int messageIdentifier) {
RecordHistogram.recordEnumeratedHistogram(
ENQUEUED_HISTOGRAM_NAME, messageIdentifier, MessageIdentifier.COUNT);
}
static void recordMessageEnqueuedScopeActive(
@MessageIdentifier int messageIdentifier, boolean active) {
String histogram =
active
? ENQUEUED_SCOPE_ACTIVE_HISTOGRAM_NAME
: ENQUEUED_SCOPE_INACTIVE_HISTOGRAM_NAME;
RecordHistogram.recordEnumeratedHistogram(
histogram, messageIdentifier, MessageIdentifier.COUNT);
}
static void recordMessageEnqueuedQueueSuspended(
@MessageIdentifier int messageIdentifier, boolean suspended) {
String histogram =
suspended ? ENQUEUED_SUSPEND_HISTOGRAM_NAME : ENQUEUED_RESUME_HISTOGRAM_NAME;
RecordHistogram.recordEnumeratedHistogram(
histogram, messageIdentifier, MessageIdentifier.COUNT);
}
/** Records metrics when a message is visible after being enqueued. */
static void recordMessageEnqueuedVisible(@MessageIdentifier int messageIdentifier) {
RecordHistogram.recordEnumeratedHistogram(
ENQUEUED_VISIBLE_HISTOGRAM_NAME, messageIdentifier, MessageIdentifier.COUNT);
}
/** Records metrics when a message is hidden after being enqueued.*/
static void recordMessageEnqueuedHidden(
@MessageIdentifier int enqueuedMessage,
@MessageIdentifier int currentDisplayedMessage) {
RecordHistogram.recordEnumeratedHistogram(
ENQUEUED_HIDDEN_HISTOGRAM_NAME, enqueuedMessage, MessageIdentifier.COUNT);
RecordHistogram.recordEnumeratedHistogram(
ENQUEUED_HIDING_HISTOGRAM_NAME, currentDisplayedMessage, MessageIdentifier.COUNT);
}
/** Records metrics when a message is dismissed. */
static void recordDismissReason(
@MessageIdentifier int messageIdentifier, @DismissReason int dismissReason) {
String histogramName =
DISMISSED_HISTOGRAM_PREFIX + messageIdentifierToHistogramSuffix(messageIdentifier);
RecordHistogram.recordEnumeratedHistogram(
histogramName, dismissReason, DismissReason.COUNT);
}
/**
* Records metrics with duration of time a message was visible before it was dismissed by a user
* action.
*/
static void recordTimeToAction(
@MessageIdentifier int messageIdentifier,
boolean messageDismissedByGesture,
long durationMs) {
String histogramSuffix = messageIdentifierToHistogramSuffix(messageIdentifier);
RecordHistogram.recordMediumTimesHistogram(
TIME_TO_ACTION_HISTOGRAM_PREFIX + histogramSuffix, durationMs);
if (messageDismissedByGesture) {
RecordHistogram.recordMediumTimesHistogram(
TIME_TO_ACTION_DISMISS_HISTOGRAM_PREFIX + histogramSuffix, durationMs);
}
}
/**
* Record the id of candidate which will be displayed in the foreground.
*
* @param messageIdentifier The id of the next front message.
*/
static void recordRequestToFullyShow(@MessageIdentifier int messageIdentifier) {
RecordHistogram.recordEnumeratedHistogram(
STACKING_REQUEST_TO_SHOW_NAME, messageIdentifier, MessageIdentifier.COUNT);
}
/**
* Record the id of candidate which will be displayed in the foreground but now is waiting for
* browser control to be ready.
*
* @param messageIdentifier The id of the next front message.
*/
static void recordBlockedByBrowserControl(@MessageIdentifier int messageIdentifier) {
RecordHistogram.recordEnumeratedHistogram(
STACKING_BLOCKED_BY_BROWSER_CONTROL_NAME,
messageIdentifier,
MessageIdentifier.COUNT);
}
/**
* Record the id of candidate which will be displayed in the foreground but now is waiting for
* message container to finishing initialization.
*
* @param messageIdentifier The id of the next front message.
*/
static void recordBlockedByContainerInitializing(@MessageIdentifier int messageIdentifier) {
RecordHistogram.recordEnumeratedHistogram(
STACKING_BLOCKED_BY_CONTAINER_INITING_NAME,
messageIdentifier,
MessageIdentifier.COUNT);
}
/**
* Record the id of candidate which will be displayed in the foreground but container has not
* been initialized.
*
* @param messageIdentifier The id of the next front message.
*/
static void recordBlockedByContainerNotInitialized(@MessageIdentifier int messageIdentifier) {
RecordHistogram.recordEnumeratedHistogram(
STACKING_BLOCKED_BY_CONTAINER_NOT_INITED_NAME,
messageIdentifier,
MessageIdentifier.COUNT);
}
static void recordTimeToFullyShow(@MessageIdentifier int messageIdentifier, long durationMs) {
String histogramSuffix = messageIdentifierToHistogramSuffix(messageIdentifier);
RecordHistogram.recordMediumTimesHistogram(
STACKING_TIME_TO_FULLY_SHOW_PREFIX + histogramSuffix, durationMs);
}
/**
* Record the id of background message when it is stacked.
* @param messageIdentifier The id of the background message.
*/
static void recordStackingHidden(@MessageIdentifier int messageIdentifier) {
RecordHistogram.recordEnumeratedHistogram(
STACKING_HIDDEN_NAME, messageIdentifier, MessageIdentifier.COUNT);
}
/**
* Record the id of the front message when there is a background message.
* @param messageIdentifier The id of the foreground message.
*/
static void recordStackingHiding(@MessageIdentifier int messageIdentifier) {
RecordHistogram.recordEnumeratedHistogram(
STACKING_HIDING_NAME, messageIdentifier, MessageIdentifier.COUNT);
}
static void recordStackingAnimationType(@StackingAnimationType int type) {
Log.i(TAG, "Triggered message stacking animation type %s.", type);
RecordHistogram.recordEnumeratedHistogram(
STACKING_HISTOGRAM_NAME, type, StackingAnimationType.MAX_VALUE);
}
static void recordStackingAnimationAction(
@StackingAnimationAction int action, @MessageIdentifier int messageIdentifier) {
String suffix = stackingAnimationActionToHistogramSuffix(action);
RecordHistogram.recordEnumeratedHistogram(
STACKING_ACTION_HISTOGRAM_PREFIX + suffix,
messageIdentifier,
MessageIdentifier.COUNT);
}
static void recordThreeStackedScenario(@ThreeStackedScenario int scenario) {
RecordHistogram.recordEnumeratedHistogram(
THREE_STACKED_HISTOGRAM_NAME, scenario, ThreeStackedScenario.MAX_VALUE);
}
/** Record the message has been fully visible. */
static void recordFullyVisible(@MessageIdentifier int messageIdentifier) {
RecordHistogram.recordEnumeratedHistogram(
FULLY_VISIBLE_NAME, messageIdentifier, MessageIdentifier.COUNT);
}
/**
* Record the fully visible callback is not triggered when it is supposed to be. E.g. when the
* message is dismissed by gesture, primary action, secondary action, timer.
*/
static void recordErrorFullyVisibleNotInformed(@MessageIdentifier int messageIdentifier) {
RecordHistogram.recordEnumeratedHistogram(
ERROR_FULLY_VISIBLE_NOT_INFORMED_NAME, messageIdentifier, MessageIdentifier.COUNT);
}
/** Record when the message is dismissed without being fully visible before. */
static void recordDismissedWithoutFullyVisible(@MessageIdentifier int messageIdentifier) {
RecordHistogram.recordEnumeratedHistogram(
DISMISSED_WITHOUT_FULLY_VISIBLE, messageIdentifier, MessageIdentifier.COUNT);
}
/**
* Returns current timestamp in milliseconds to be used when recording message's visible
* duration.
*/
static long now() {
return TimeUtils.uptimeMillis();
}
private static String stackingAnimationActionToHistogramSuffix(
@StackingAnimationAction int action) {
if (action == StackingAnimationAction.INSERT_AT_FRONT) {
return "InsertAtFront";
} else if (action == StackingAnimationAction.INSERT_AT_BACK) {
return "InsertAtBack";
} else if (action == StackingAnimationAction.PUSH_TO_FRONT) {
return "PushToFront";
} else if (action == StackingAnimationAction.PUSH_TO_BACK) {
return "PushToBack";
} else if (action == StackingAnimationAction.REMOVE_FRONT) {
return "RemoveFront";
} else {
return "RemoveBack";
}
}
/**
* Returns a histogram suffix string that corresponds to message identifier of the current
* message.
* Update this function when adding a new message identifier.
*/
public static String messageIdentifierToHistogramSuffix(
@MessageIdentifier int messageIdentifier) {
switch (messageIdentifier) {
case MessageIdentifier.TEST_MESSAGE:
return "TestMessage";
case MessageIdentifier.SAVE_PASSWORD:
return "SavePassword";
case MessageIdentifier.UPDATE_PASSWORD:
return "UpdatePassword";
case MessageIdentifier.GENERATED_PASSWORD_SAVED:
return "GeneratedPasswordSaved";
case MessageIdentifier.POPUP_BLOCKED:
return "PopupBlocked";
case MessageIdentifier.SAFETY_TIP:
return "SafetyTip";
case MessageIdentifier.SAVE_ADDRESS_PROFILE:
return "SaveAddressProfile";
case MessageIdentifier.MERCHANT_TRUST:
return "MerchantTrust";
case MessageIdentifier.SEND_TAB_TO_SELF:
return "SendTabToSelf";
case MessageIdentifier.READER_MODE:
return "ReaderMode";
case MessageIdentifier.SAVE_CARD:
return "SaveCard";
case MessageIdentifier.CHROME_SURVEY:
return "ChromeSurvey";
case MessageIdentifier.NOTIFICATION_BLOCKED:
return "NotificationBlocked";
case MessageIdentifier.PERMISSION_UPDATE:
return "PermissionUpdate";
case MessageIdentifier.ADS_BLOCKED:
return "AdsBlocked";
case MessageIdentifier.DOWNLOAD_PROGRESS:
return "DownloadProgress";
case MessageIdentifier.SYNC_ERROR:
return "SyncError";
case MessageIdentifier.SHARED_HIGHLIGHTING:
return "SharedHighlighting";
case MessageIdentifier.NEAR_OOM_REDUCTION:
return "NearOomReduction";
case MessageIdentifier.INSTALLABLE_AMBIENT_BADGE:
return "InstallableAmbientBadge";
case MessageIdentifier.AUTO_DARK_WEB_CONTENTS:
return "AutoDarkWebContents";
case MessageIdentifier.TAILORED_SECURITY_ENABLED:
return "TailoredSecurityEnabled";
case MessageIdentifier.TAILORED_SECURITY_DISABLED:
return "TailoredSecurityDisabled";
case MessageIdentifier.VR_SERVICES_UPGRADE:
return "VrServicesUpgrade";
case MessageIdentifier.AR_CORE_UPGRADE:
return "ArCoreUpgrade";
case MessageIdentifier.ABOUT_THIS_SITE:
return "AboutThisSite";
case MessageIdentifier.TRANSLATE:
return "Translate";
case MessageIdentifier.OFFER_NOTIFICATION:
return "OfferNotification";
case MessageIdentifier.EXTERNAL_NAVIGATION:
return "ExternalNavigation";
case MessageIdentifier.FRAMEBUST_BLOCKED:
return "FramebustBlocked";
case MessageIdentifier.INVALID_MESSAGE:
return "InvalidMessage";
case MessageIdentifier.DESKTOP_SITE_GLOBAL_DEFAULT_OPT_OUT:
return "DesktopSiteGlobalDefaultOptOut";
case MessageIdentifier.DESKTOP_SITE_GLOBAL_OPT_IN:
return "DesktopSiteGlobalOptIn";
case MessageIdentifier.DOWNLOAD_INCOGNITO_WARNING:
return "DownloadIncognitoWarning";
case MessageIdentifier.CVC_SAVE:
return "CvcSave";
case MessageIdentifier.DESKTOP_SITE_WINDOW_SETTING:
return "DesktopSiteWindowSetting";
case MessageIdentifier.PROMPT_HATS_LOCATION_CUSTOM_INVITATION:
return "PromptHatsLocationCustomInvitation";
case MessageIdentifier.PROMPT_HATS_LOCATION_GENERIC_INVITATION:
return "PromptHatsLocationGenericInvitation";
case MessageIdentifier.PROMPT_HATS_CAMERA_CUSTOM_INVITATION:
return "PromptHatsCameraCustomInvitation";
case MessageIdentifier.PROMPT_HATS_CAMERA_GENERIC_INVITATION:
return "PromptHatsCameraGenericInvitation";
case MessageIdentifier.PROMPT_HATS_MICROPHONE_CUSTOM_INVITATION:
return "PromptHatsMicrophoneCustomInvitation";
case MessageIdentifier.PROMPT_HATS_MICROPHONE_GENERIC_INVITATION:
return "PromptHatsMicrophoneGenericInvitation";
case MessageIdentifier.PERMISSION_BLOCKED:
return "PermissionBlocked";
case MessageIdentifier.SAVE_CARD_FAILURE:
return "SaveCardFailure";
case MessageIdentifier.VIRTUAL_CARD_ENROLL_FAILURE:
return "VirtualCardEnrollFailure";
case MessageIdentifier.PROMPT_HATS_QUICK_DELETE:
return "PromptHatsQuickDelete";
case MessageIdentifier.PROMPT_HATS_SAFETY_HUB:
return "PromptHatsSafetyHub";
default:
return "Unknown";
}
}
static String getEnqueuedHistogramNameForTesting() {
return ENQUEUED_HISTOGRAM_NAME;
}
static String getDismissHistogramNameForTesting(@MessageIdentifier int messageIdentifier) {
return DISMISSED_HISTOGRAM_PREFIX + messageIdentifierToHistogramSuffix(messageIdentifier);
}
}