// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_ASH_COMPONENTS_QUICK_START_QUICK_START_METRICS_H_
#define CHROMEOS_ASH_COMPONENTS_QUICK_START_QUICK_START_METRICS_H_
#include <optional>
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "chromeos/ash/components/quick_start/quick_start_response_type.h"
class GoogleServiceAuthError;
namespace ash::quick_start {
class QuickStartMetrics {
public:
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml as well as a
// CrOSEvents enum defined in
// //components/metrics/structured/structured_events.h, and should always
// reflect them (do not change one without changing the others). Entries
// should never be modified or deleted. Only additions possible.
enum class ScreenName {
kOther = 0, // We don't expect this value to ever be emitted.
kNone = 1, // There is no previous screen when automatically resuming after
// an update.
kWelcomeScreen = 2, // Quick Start entry point 1.
kNetworkScreen = 3, // Quick Start entry point 2, or in the middle of Quick
// Start when the host device is not connected to wifi.
kGaiaScreen = 4, // Quick Start entry point 4 (See kGaiaInfoScreen for
// entry point 3).
kQSSetUpWithAndroidPhone = 5, // Beginning of Quick Start flow.
kQSConnectingToWifi = 6, // Transferring wifi with Quick Start.
kCheckingForUpdateAndDeterminingDeviceConfiguration = 7, // Critical Update
kChooseChromebookSetup = 8,
kConsumerUpdate = 9,
kQSResumingConnectionAfterUpdate = 10,
kQSGettingGoogleAccountInfo = 11,
kQSComplete = 12,
kSetupDevicePIN = 13, // After Quick Start flow is complete.
kAddChild = 14, // Only for Unicorn accounts.
kReviewPrivacyAndTerms = 15, // Only for Unicorn accounts.
kUnifiedSetup = 16, // After Quick Start flow is complete, connect host
// phone to account.
kGaiaInfoScreen = 17, // Quick Start entry point 3
kQSWifiCredentialsReceived = 18, // Quick Start UI when wifi credentials
// transfer succeeds.
kQSSelectGoogleAccount = 19, // Quick Start UI informing user to confirm
// account on phone.
kQSCreatingAccount = 20, // Quick Start UI attempting to login with
// transferred account details.
kQSFallbackURL = 21, // Quick Start screen when when a signin challenge
// must be completed on the target device.
kMaxValue = kQSFallbackURL
};
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml, and should always
// reflect it (do not change one without changing the other). Entries should
// be never modified or deleted. Only additions possible.
enum ScreenClosedReason {
kAdvancedInFlow, // User moved to next screen as expected via flow.
kUserCancelled, // User clicked cancel.
kUserClickedBack, // User clicked back.
kSetupComplete, // User finished Quick Start.
kError, // An error occurred.
kMaxValue = kError
};
enum class ExitReason {
kAdvancedInFlow,
kUserCancelled,
};
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml, and should always
// reflect it (do not change one without changing the other). Entries should
// be never modified or deleted. Only additions possible.
enum class AuthenticationMethod {
kPin = 0,
kQRCode = 1,
kResumeAfterUpdate = 2,
kMaxValue = kResumeAfterUpdate,
};
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml and should always
// reflect it (do not change one without changing the other). Entries should
// be never modified or deleted. Only additions possible. The UMA enum cannot
// use |device::BluetoothAdvertisement::ErrorCode| directly, because it is
// missing the required |kMaxValue| field.
enum class FastPairAdvertisingErrorCode {
kUnsupportedPlatform = 0,
kAdvertisementAlreadyExists = 1,
kAdvertisementDoesNotExist = 2,
kAdvertisementInvalidLength = 3,
kStartingAdvertisement = 4,
kResetAdvertising = 5,
kAdapterPoweredOff = 6,
kInvalidAdvertisementInterval = 7,
kInvalidAdvertisementErrorCode = 8,
kMaxValue = kInvalidAdvertisementErrorCode,
};
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml and should always
// reflect it (do not change one without changing the other). Entries should
// be never modified or deleted. Only additions possible.
enum class NearbyConnectionsAdvertisingErrorCode {
kError = 0,
kOutOfOrderApiCall = 1,
kAlreadyHaveActiveStrategy = 2,
kAlreadyAdvertising = 3,
kBluetoothError = 4,
kBleError = 5,
kUnknown = 6,
kTimeout = 7,
kOther = 8,
kMaxValue = kOther,
};
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml, and should always
// reflect it (do not change one without changing the other). Entries should
// be never modified or deleted. Only additions possible.
enum class HandshakeErrorCode {
kFailedToReadResponse = 0,
kFailedToParse = 1,
kFailedToDecryptAuthPayload = 2,
kFailedToParseAuthPayload = 3,
kUnexpectedAuthPayloadRole = 4,
kUnexpectedAuthPayloadAuthToken = 5,
kInvalidHandshakeErrorCode = 6,
kMaxValue = kInvalidHandshakeErrorCode,
};
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml, and should always
// reflect it (do not change one without changing the other). Entries should
// be never modified or deleted. Only additions possible.
enum class MessageType {
kWifiCredentials = 0,
kBootstrapConfigurations = 1,
kHandshake = 2,
kNotifySourceOfUpdate = 3,
kGetInfo = 4,
kAssertion = 5,
kBootstrapStateCancel = 6,
kBootstrapStateComplete = 7,
kMaxValue = kBootstrapStateComplete,
};
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml, and should always
// reflect it (do not change one without changing the other). Entries should
// be never modified or deleted. Only additions possible.
enum class MessageReceivedErrorCode {
kTimeOut = 0,
kDeserializationFailure = 1,
kUnknownError = 2,
kMaxValue = kUnknownError,
};
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml, and should always
// reflect it (do not change one without changing the other). Entries should
// be never modified or deleted. Only additions possible.
enum class AttestationCertificateRequestErrorCode {
kUnknownError = 0,
kBadRequest = 1,
kAttestationNotSupportedOnDevice = 2,
kMaxValue = kAttestationNotSupportedOnDevice,
};
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml, and should always
// reflect it (do not change one without changing the other). Entries should
// be never modified or deleted. Only additions possible.
enum class GaiaAuthenticationResult {
kUnknownError = 0,
kSuccess = 1,
kResponseParsingError = 2,
kRejection = 3,
kAdditionalChallengesOnSource = 4,
kAdditionalChallengesOnTarget = 5,
kMaxValue = kAdditionalChallengesOnTarget,
};
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml, and should always
// reflect it (do not change one without changing the other). Entries should
// be never modified or deleted. Only additions possible.
enum class WifiTransferResultFailureReason {
kConnectionDroppedDuringAttempt = 0,
kEmptyResponseBytes = 1,
kUnableToReadAsJSON = 2,
kWifiNetworkInformationNotFound = 3,
kSsidNotFound = 4,
kEmptySsid = 5,
kSecurityTypeNotFound = 6,
kInvalidSecurityType = 7,
kPasswordFoundAndOpenNetwork = 8,
kPasswordNotFoundAndNotOpenNetwork = 9,
kWifiHideStatusNotFound = 10,
kMaxValue = kWifiHideStatusNotFound,
};
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml, and should always
// reflect it (do not change one without changing the other). Entries should
// be never modified or deleted. Only additions possible.
enum class GaiaTransferResultFailureReason {
kNoAccountOnPhone = 0,
kFailedFetchingChallengeBytesFromGaia = 1,
kConnectionLost = 2,
kGaiaAssertionNotReceived = 3,
kFailedFetchingAttestationCertificate = 4,
kFailedFetchingRefreshToken = 5,
kFallbackURLRequired = 6,
kErrorReceivingFIDOAssertion = 7,
kObfuscatedGaiaIdMissing = 8,
kMaxValue = kObfuscatedGaiaIdMissing,
};
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml as well as a
// CrOSEvents enum defined in
// //components/metrics/structured/structured_events.h, and should always
// reflect them (do not change one without changing the others). Entries
// should never be modified or deleted. Only additions possible.
enum class AbortFlowReason {
USER_CLICKED_BACK = 0,
USER_CLICKED_CANCEL = 1,
SIGNIN_SCHOOL = 2,
ENTERPRISE_ENROLLMENT = 3,
ERROR = 4,
// Child accounts are not yet supported.
ADD_CHILD = 5,
kMaxValue = ADD_CHILD,
};
// This enum is tied directly to a UMA enum defined in
// //tools/metrics/histograms/metadata/quickstart/enums.xml as well as a
// CrOSEvents enum defined in
// //components/metrics/structured/structured_events.h, and should always
// reflect them (do not change one without changing the others). Entries
// should never be modified or deleted. Only additions possible.
enum class EntryPoint {
WELCOME_SCREEN = 0,
NETWORK_SCREEN = 1,
GAIA_INFO_SCREEN = 2,
GAIA_SCREEN = 3,
kMaxValue = GAIA_SCREEN,
};
// Helper function that returns the MessageType equivalent of
// QuickStartResponseType.
static MessageType MapResponseToMessageType(
QuickStartResponseType response_type);
static ScreenClosedReason MapAbortFlowReasonToScreenClosedReason(
AbortFlowReason reason);
static void RecordWifiTransferResult(
bool succeeded,
std::optional<WifiTransferResultFailureReason> failure_reason);
static void RecordGaiaTransferStarted();
static void RecordCapturePortalEncountered(int32_t session_id);
static void RecordRedirectToEnterpriseEnrollment(int32_t session_id);
static void RecordForcedUpdateRequired(int32_t session_id);
static void RecordGaiaTransferResult(
bool succeeded,
std::optional<GaiaTransferResultFailureReason> failure_reason);
static void RecordEntryPoint(EntryPoint entry_point);
static void RecordAuthenticationMethod(AuthenticationMethod auth_method);
static void RecordAbortFlowReason(AbortFlowReason reason);
static void RecordUpdateStarted(bool is_forced);
static void RecordConsumerUpdateCancelled();
static void RecordEstablishConnection(bool success, bool is_automatic_resume);
QuickStartMetrics();
QuickStartMetrics(const QuickStartMetrics&) = delete;
const QuickStartMetrics& operator=(const QuickStartMetrics&) = delete;
virtual ~QuickStartMetrics();
void RecordScreenOpened(ScreenName screen);
void RecordScreenClosed(ScreenName screen, ScreenClosedReason reason);
// Records the start of an attempt to fetch challenge bytes from Gaia.
// Challenge bytes are later used to generate a Remote Attestation certificate
// and a FIDO assertion.
void RecordChallengeBytesRequested();
// Records the end of an attempt to fetch challenge bytes from Gaia.
// `status` is the overall status of the fetch. It is set to
// `GoogleServiceAuthError::State::NONE` if the request was successful.
void RecordChallengeBytesRequestEnded(const GoogleServiceAuthError& status);
void RecordAttestationCertificateRequested();
// Records the end of a Remote Attestation certificate request. `error_code`
// is empty if the request was successful - otherwise it contains the details
// of the error.
void RecordAttestationCertificateRequestEnded(
std::optional<AttestationCertificateRequestErrorCode> error_code);
void RecordGaiaAuthenticationStarted();
void RecordGaiaAuthenticationRequestEnded(
const GaiaAuthenticationResult& result);
void RecordFastPairAdvertisementStarted(
bool succeeded,
std::optional<FastPairAdvertisingErrorCode> error_code);
void RecordFastPairAdvertisementEnded(
bool succeeded,
std::optional<FastPairAdvertisingErrorCode> error_code);
void RecordNearbyConnectionsAdvertisementStarted(
bool succeeded,
std::optional<NearbyConnectionsAdvertisingErrorCode> error_code);
void RecordNearbyConnectionsAdvertisementEnded(
bool succeeded,
std::optional<NearbyConnectionsAdvertisingErrorCode> error_code);
void RecordHandshakeStarted();
void RecordHandshakeResult(bool succeeded,
std::optional<HandshakeErrorCode> error_code);
void RecordMessageSent(MessageType message_type);
void RecordMessageReceived(
MessageType desired_message_type,
bool succeeded,
std::optional<MessageReceivedErrorCode> error_code);
private:
ScreenName last_screen_opened_ = ScreenName::kNone;
// Timer to keep track of Fast Pair advertising duration. Should be
// constructed when advertising starts and destroyed when advertising
// finishes.
std::unique_ptr<base::ElapsedTimer> fast_pair_advertising_timer_;
// Timer to keep track of Nearby Connections advertising duration. Should be
// constructed when advertising starts and destroyed when advertising
// finishes.
std::unique_ptr<base::ElapsedTimer> nearby_connections_advertising_timer_;
// Timer to keep track of duration spent viewing a screen. Should be
// constructed when a screen is opened and destroyed when that screen is
// closed.
std::unique_ptr<base::ElapsedTimer> screen_opened_view_duration_timer_;
// Timer to keep track of handshake duration. Should be constructed when
// the handshake starts and destroyed when the handshake finishes.
std::unique_ptr<base::ElapsedTimer> handshake_elapsed_timer_;
// Timer to keep track of the duration of request/response pairs. Should be
// constructed when the request is sent and destroyed when the response is
// received.
std::unique_ptr<base::ElapsedTimer> message_elapsed_timer_;
// Timer to keep track of challenge bytes fetch requests. It should be set at
// the start of a challenge bytes fetch and destroyed when a response is
// received.
std::unique_ptr<base::ElapsedTimer> challenge_bytes_fetch_timer_;
// Timer to keep track of remote attestation certificate fetch requests. It
// should be set at the start of a certificate fetch and destroyed when a
// response is received.
std::unique_ptr<base::ElapsedTimer> attestation_certificate_timer_;
// Timer to keep track of Gaia authentication requests. It should be set at
// the start of a Gaia authentication request and destroyed when a response is
// received.
std::unique_ptr<base::ElapsedTimer> gaia_authentication_timer_;
};
std::ostream& operator<<(
std::ostream& stream,
const QuickStartMetrics::ScreenName& metrics_screen_name);
} // namespace ash::quick_start
#endif // CHROMEOS_ASH_COMPONENTS_QUICK_START_QUICK_START_METRICS_H_