910e62b5创建于 1月15日历史提交
// 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