blob: 04ab2cd334e19a37841ab0b69d6e41e9c5bb9c2e [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/arc/arc_optin_uma.h"
#include <string>
#include "ash/shell.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/histogram_macros_local.h"
#include "chrome/browser/ash/arc/arc_util.h"
#include "chrome/browser/ash/arc/policy/arc_policy_util.h"
#include "chrome/browser/ash/arc/session/arc_provisioning_result.h"
#include "chrome/browser/ash/login/demo_mode/demo_session.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
#include "chromeos/ash/experiences/arc/arc_util.h"
#include "chromeos/ash/experiences/arc/metrics/stability_metrics_manager.h"
#include "chromeos/ash/experiences/arc/mojom/app.mojom.h"
#include "chromeos/ash/experiences/arc/mojom/auth.mojom.h"
#include "components/user_manager/user_manager.h"
// Enable VLOG level 1.
#undef ENABLED_VLOG_LEVEL
#define ENABLED_VLOG_LEVEL 1
namespace arc {
namespace {
// Logs UMA enum values to facilitate finding feedback reports in Xamine.
template <typename T>
void LogStabilityUmaEnum(const std::string& name, T sample) {
base::UmaHistogramEnumeration(name, sample);
VLOG(1) << name << ": " << static_cast<std::underlying_type_t<T>>(sample);
}
ArcEnabledState ComputeEnabledState(bool enabled, const Profile* profile) {
if (!IsArcAllowedForProfile(profile)) {
return enabled ? ArcEnabledState::ENABLED_NOT_ALLOWED
: ArcEnabledState::DISABLED_NOT_ALLOWED;
}
if (!IsArcPlayStoreEnabledPreferenceManagedForProfile(profile)) {
return enabled ? ArcEnabledState::ENABLED_NOT_MANAGED
: ArcEnabledState::DISABLED_NOT_MANAGED;
}
if (IsArcPlayStoreEnabledForProfile(profile)) {
return enabled ? ArcEnabledState::ENABLED_MANAGED_ON
: ArcEnabledState::DISABLED_MANAGED_ON;
}
DCHECK(!enabled);
return ArcEnabledState::DISABLED_MANAGED_OFF;
}
} // namespace
void UpdateEnabledStateByUserTypeUMA() {
auto* primary_user = user_manager::UserManager::Get()->GetPrimaryUser();
// Don't record UMA if there is no primary user.
if (!primary_user) {
return;
}
const Profile* profile = Profile::FromBrowserContext(
ash::BrowserContextHelper::Get()->GetBrowserContextByUser(primary_user));
// Don't record UMA if the primary user profile is not loaded.
if (!profile) {
return;
}
// Don't record UMA if we're currently in guest session.
if (profile->IsGuestSession()) {
return;
}
std::optional<bool> enabled_state;
if (auto* stability_metrics_manager = StabilityMetricsManager::Get())
enabled_state = stability_metrics_manager->GetArcEnabledState();
base::UmaHistogramEnumeration(
GetHistogramNameByUserType("Arc.StateByUserType", profile),
ComputeEnabledState(enabled_state.value_or(false), profile));
}
void UpdateOptInActionUMA(OptInActionType type) {
base::UmaHistogramEnumeration("Arc.OptInAction", type);
}
void UpdateOptInCancelUMA(OptInCancelReason reason) {
base::UmaHistogramEnumeration("Arc.OptInCancel", reason);
}
void UpdateOptInFlowResultUMA(OptInFlowResult result) {
LogStabilityUmaEnum("Arc.OptInResult", result);
}
void UpdateOptinTosLoadResultUMA(bool success) {
base::UmaHistogramBoolean("Arc.OptinTosLoadResult", success);
}
void UpdateProvisioningStatusUMA(ProvisioningStatus status,
const Profile* profile) {
DCHECK_NE(status, ProvisioningStatus::CHROME_SERVER_COMMUNICATION_ERROR);
LogStabilityUmaEnum(
GetHistogramNameByUserType("Arc.Provisioning.Status", profile), status);
}
void UpdateProvisioningDpcResultUMA(ArcProvisioningDpcResult result,
const Profile* profile) {
LogStabilityUmaEnum(
GetHistogramNameByUserType("Arc.Provisioning.DpcResult", profile),
result);
}
void UpdateProvisioningSigninResultUMA(ArcProvisioningSigninResult result,
const Profile* profile) {
LogStabilityUmaEnum(
GetHistogramNameByUserType("Arc.Provisioning.SigninResult", profile),
result);
}
void UpdateProvisioningCheckinResultUMA(ArcProvisioningCheckinResult result,
const Profile* profile) {
LogStabilityUmaEnum(
GetHistogramNameByUserType("Arc.Provisioning.CheckinResult", profile),
result);
}
void UpdateSecondarySigninResultUMA(ProvisioningStatus status) {
LogStabilityUmaEnum("Arc.Secondary.Signin.Result", status);
}
void UpdateProvisioningTiming(const base::TimeDelta& elapsed_time,
bool success,
const Profile* profile) {
std::string histogram_name = "Arc.Provisioning.TimeDelta";
histogram_name += success ? ".Success" : ".Failure";
// The macro UMA_HISTOGRAM_CUSTOM_TIMES expects a constant string, but since
// this measurement happens very infrequently, we do not need to use a macro
// here.
base::UmaHistogramCustomTimes(
GetHistogramNameByUserType(histogram_name, profile), elapsed_time,
base::Seconds(1), base::Minutes(6), 50);
}
void UpdateReauthorizationResultUMA(ProvisioningStatus status,
const Profile* profile) {
LogStabilityUmaEnum(
GetHistogramNameByUserType("Arc.Reauthorization.Result", profile),
status);
}
void UpdatePlayAutoInstallRequestState(mojom::PaiFlowState state,
const Profile* profile) {
base::UmaHistogramEnumeration(
GetHistogramNameByUserType("Arc.PlayAutoInstallRequest.State", profile),
state);
}
void UpdatePlayAutoInstallRequestTime(const base::TimeDelta& elapsed_time,
const Profile* profile) {
base::UmaHistogramCustomTimes(
GetHistogramNameByUserType("Arc.PlayAutoInstallRequest.TimeDelta",
profile),
elapsed_time, base::Seconds(1), base::Minutes(10), 50);
}
void UpdateArcUiAvailableTime(const base::TimeDelta& elapsed_time,
const std::string& mode,
const Profile* profile) {
if (ash::Shell::HasInstance()) {
ash::Shell::Get()
->login_unlock_throughput_recorder()
->ArcUiAvailableAfterLogin();
}
base::UmaHistogramCustomTimes(
GetHistogramNameByUserType("Arc.UiAvailable." + mode + ".TimeDelta",
profile),
elapsed_time, base::Seconds(1), base::Minutes(5), 50);
// This is local test-only histogram.
LOCAL_HISTOGRAM_CUSTOM_TIMES("Arc.Tast.UiAvailable.TimeDelta", elapsed_time,
base::Seconds(1), base::Minutes(5), 50);
}
void UpdatePlayStoreLaunchTime(const base::TimeDelta& elapsed_time) {
base::UmaHistogramCustomTimes("Arc.PlayStoreLaunch.TimeDelta", elapsed_time,
base::Milliseconds(10), base::Seconds(20), 50);
}
void UpdateDeferredLaunchTime(const base::TimeDelta& elapsed_time) {
base::UmaHistogramCustomTimes(
"Arc.FirstAppLaunchDelay.TimeDeltaUntilAppLaunch", elapsed_time,
base::Milliseconds(10), base::Seconds(60), 50);
}
void UpdateAuthTiming(const char* histogram_name,
base::TimeDelta elapsed_time,
const Profile* profile) {
base::UmaHistogramCustomTimes(
GetHistogramNameByUserType(histogram_name, profile), elapsed_time,
base::Seconds(1) /* minimum */, base::Minutes(3) /* maximum */,
50 /* bucket_count */);
}
void UpdateAuthCheckinAttempts(int32_t num_attempts, const Profile* profile) {
base::UmaHistogramExactLinear(
GetHistogramNameByUserType("Arc.Auth.Checkin.Attempts", profile),
num_attempts, 11 /* exclusive_max */);
}
void UpdateAuthAccountCheckStatus(mojom::AccountCheckStatus status,
const Profile* profile) {
DCHECK_LE(status, mojom::AccountCheckStatus::CHECK_FAILED);
LogStabilityUmaEnum(
GetHistogramNameByUserType("Arc.Auth.AccountCheck.Status", profile),
status);
}
void UpdateAccountReauthReason(mojom::ReauthReason reason,
const Profile* profile) {
LogStabilityUmaEnum(
GetHistogramNameByUserType("Arc.Auth.Reauth.Reason", profile), reason);
}
void UpdateMainAccountResolutionStatus(
const Profile* profile,
mojom::MainAccountResolutionStatus status) {
DCHECK(mojom::IsKnownEnumValue(status));
LogStabilityUmaEnum(GetHistogramNameByUserType(
"Arc.Auth.MainAccountResolution.Status", profile),
status);
}
void UpdateSilentAuthCodeUMA(OptInSilentAuthCode state) {
LogStabilityUmaEnum("Arc.OptInSilentAuthCode", state);
}
// TODO(tantoshchuk): rename UMA histogram to "Arc.Management.Transition.Result"
void UpdateSupervisionTransitionResultUMA(
mojom::ManagementChangeStatus result) {
LogStabilityUmaEnum("Arc.Supervision.Transition.Result", result);
}
void UpdateReauthorizationSilentAuthCodeUMA(OptInSilentAuthCode state) {
LogStabilityUmaEnum("Arc.OptInSilentAuthCode.Reauthorization", state);
}
void UpdateSecondaryAccountSilentAuthCodeUMA(OptInSilentAuthCode state) {
LogStabilityUmaEnum("Arc.OptInSilentAuthCode.SecondaryAccount", state);
}
ArcProvisioningDpcResult GetDpcErrorResult(
mojom::CloudProvisionFlowError error) {
switch (error) {
case mojom::CloudProvisionFlowError::ERROR_ENROLLMENT_TOKEN_INVALID:
return ArcProvisioningDpcResult::kInvalidToken;
case mojom::CloudProvisionFlowError::ERROR_ADD_ACCOUNT_FAILED:
return ArcProvisioningDpcResult::kAccountAddFail;
case mojom::CloudProvisionFlowError::ERROR_TIMEOUT:
return ArcProvisioningDpcResult::kTimeout;
case mojom::CloudProvisionFlowError::ERROR_NETWORK_UNAVAILABLE:
return ArcProvisioningDpcResult::kNetworkError;
case mojom::CloudProvisionFlowError::
ERROR_OAUTH_TOKEN_AUTHENTICATOR_EXCEPTION:
return ArcProvisioningDpcResult::kOAuthAuthException;
case mojom::CloudProvisionFlowError::ERROR_OAUTH_TOKEN_IO_EXCEPTION:
return ArcProvisioningDpcResult::kOAuthIOException;
case mojom::CloudProvisionFlowError::ERROR_OTHER:
case mojom::CloudProvisionFlowError::ERROR_ENTERPRISE_INVALID:
case mojom::CloudProvisionFlowError::ERROR_USER_CANCEL:
case mojom::CloudProvisionFlowError::ERROR_NO_ACCOUNT_IN_WORK_PROFILE:
case mojom::CloudProvisionFlowError::ERROR_INVALID_POLICY_STATE:
case mojom::CloudProvisionFlowError::ERROR_DPC_SUPPORT:
case mojom::CloudProvisionFlowError::ERROR_ACCOUNT_NOT_READY:
case mojom::CloudProvisionFlowError::ERROR_CHECKIN_FAILED:
case mojom::CloudProvisionFlowError::ERROR_ACCOUNT_NOT_ALLOWLISTED:
case mojom::CloudProvisionFlowError::ERROR_JSON:
case mojom::CloudProvisionFlowError::ERROR_MANAGED_PROVISIONING_FAILED:
case mojom::CloudProvisionFlowError::ERROR_INVALID_SETUP_ACTION:
case mojom::CloudProvisionFlowError::ERROR_SERVER:
case mojom::CloudProvisionFlowError::ERROR_REMOVE_ACCOUNT_FAILED:
case mojom::CloudProvisionFlowError::ERROR_OAUTH_TOKEN:
case mojom::CloudProvisionFlowError::ERROR_ACCOUNT_OTHER:
case mojom::CloudProvisionFlowError::ERROR_QUARANTINE:
case mojom::CloudProvisionFlowError::ERROR_DEVICE_QUOTA_EXCEEDED:
case mojom::CloudProvisionFlowError::ERROR_SERVER_TRANSIENT_ERROR:
case mojom::CloudProvisionFlowError::
ERROR_OAUTH_TOKEN_OPERATION_CANCELED_EXCEPTION:
case mojom::CloudProvisionFlowError::ERROR_REQUEST_ANDROID_ID_FAILED:
VLOG(1) << "ArcProvisioningDpcResult[kUnknownError]="
<< static_cast<
std::underlying_type_t<mojom::CloudProvisionFlowError>>(
error);
return ArcProvisioningDpcResult::kUnknownError;
}
}
ArcProvisioningSigninResult GetSigninErrorResult(mojom::GMSSignInError error) {
switch (error) {
case mojom::GMSSignInError::GMS_SIGN_IN_NETWORK_ERROR:
return ArcProvisioningSigninResult::kNetworkError;
case mojom::GMSSignInError::GMS_SIGN_IN_SERVICE_UNAVAILABLE:
return ArcProvisioningSigninResult::kServiceUnavailable;
case mojom::GMSSignInError::GMS_SIGN_IN_BAD_AUTHENTICATION:
return ArcProvisioningSigninResult::kAuthFailure;
case mojom::GMSSignInError::GMS_SIGN_IN_TIMEOUT:
return ArcProvisioningSigninResult::kTimeout;
case mojom::GMSSignInError::GMS_SIGN_IN_FAILED:
case mojom::GMSSignInError::GMS_SIGN_IN_INTERNAL_ERROR:
VLOG(1) << "ArcProvisioningSigninResult[kUnknownError]="
<< static_cast<std::underlying_type_t<mojom::GMSSignInError>>(
error);
return ArcProvisioningSigninResult::kUnknownError;
}
}
ArcProvisioningCheckinResult GetCheckinErrorResult(
mojom::GMSCheckInError error) {
switch (error) {
case mojom::GMSCheckInError::GMS_CHECK_IN_TIMEOUT:
return ArcProvisioningCheckinResult::kTimeout;
case mojom::GMSCheckInError::GMS_CHECK_IN_FAILED:
case mojom::GMSCheckInError::GMS_CHECK_IN_INTERNAL_ERROR:
VLOG(1) << "ArcProvisioningCheckinResult[kUnknownError]="
<< static_cast<std::underlying_type_t<mojom::GMSCheckInError>>(
error);
return ArcProvisioningCheckinResult::kUnknownError;
}
}
ProvisioningStatus GetProvisioningStatus(
const ArcProvisioningResult& provisioning_result) {
if (provisioning_result.stop_reason())
return ProvisioningStatus::ARC_STOPPED;
if (provisioning_result.is_timedout())
return ProvisioningStatus::CHROME_PROVISIONING_TIMEOUT;
if (provisioning_result.is_success())
return ProvisioningStatus::SUCCESS;
if (provisioning_result.cloud_provision_flow_error())
return ProvisioningStatus::CLOUD_PROVISION_FLOW_ERROR;
if (provisioning_result.gms_check_in_error())
return ProvisioningStatus::GMS_CHECK_IN_ERROR;
if (provisioning_result.gms_sign_in_error())
return ProvisioningStatus::GMS_SIGN_IN_ERROR;
if (provisioning_result.general_error()) {
#define MAP_GENERAL_ERROR(name) \
case mojom::GeneralSignInError::name: \
return ProvisioningStatus::name
switch (provisioning_result.general_error().value()) {
MAP_GENERAL_ERROR(UNKNOWN_ERROR);
MAP_GENERAL_ERROR(MOJO_VERSION_MISMATCH);
MAP_GENERAL_ERROR(GENERIC_PROVISIONING_TIMEOUT);
MAP_GENERAL_ERROR(NO_NETWORK_CONNECTION);
MAP_GENERAL_ERROR(CHROME_SERVER_COMMUNICATION_ERROR);
MAP_GENERAL_ERROR(ARC_DISABLED);
MAP_GENERAL_ERROR(UNSUPPORTED_ACCOUNT_TYPE);
MAP_GENERAL_ERROR(CHROME_ACCOUNT_NOT_FOUND);
}
#undef MAP_GENERAL_ERROR
}
NOTREACHED() << "unexpected provisioning result";
}
std::ostream& operator<<(std::ostream& os, const ProvisioningStatus& status) {
#define MAP_PROVISIONING_RESULT(name) \
case ProvisioningStatus::name: \
return os << #name
switch (status) {
MAP_PROVISIONING_RESULT(SUCCESS);
MAP_PROVISIONING_RESULT(UNKNOWN_ERROR);
MAP_PROVISIONING_RESULT(GMS_SIGN_IN_ERROR);
MAP_PROVISIONING_RESULT(GMS_CHECK_IN_ERROR);
MAP_PROVISIONING_RESULT(CLOUD_PROVISION_FLOW_ERROR);
MAP_PROVISIONING_RESULT(MOJO_VERSION_MISMATCH);
MAP_PROVISIONING_RESULT(GENERIC_PROVISIONING_TIMEOUT);
MAP_PROVISIONING_RESULT(CHROME_PROVISIONING_TIMEOUT);
MAP_PROVISIONING_RESULT(ARC_STOPPED);
MAP_PROVISIONING_RESULT(ARC_DISABLED);
MAP_PROVISIONING_RESULT(CHROME_SERVER_COMMUNICATION_ERROR);
MAP_PROVISIONING_RESULT(NO_NETWORK_CONNECTION);
MAP_PROVISIONING_RESULT(UNSUPPORTED_ACCOUNT_TYPE);
MAP_PROVISIONING_RESULT(CHROME_ACCOUNT_NOT_FOUND);
}
#undef MAP_PROVISIONING_RESULT
NOTREACHED() << "Invalid value " << static_cast<int>(status);
}
} // namespace arc