chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_open_metrics.cc

// 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.

#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_open_metrics.h"

#include <concepts>
#include <string>

#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/types/cxx23_to_underlying.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"

namespace ash::cloud_upload {

template <typename T>
  requires(std::is_enum_v<T>)
std::ostream& operator<<(std::ostream& os, T metric) {
  if constexpr (std::same_as<T, MetricState>) {
    switch (metric) {
      case MetricState::kCorrectlyNotLogged:
        return os << "NL";
      case MetricState::kCorrectlyLogged:
        return os << "L";
      case MetricState::kIncorrectlyNotLogged:
        return os << "INL";
      case MetricState::kIncorrectlyLogged:
        return os << "IL";
      case MetricState::kIncorrectlyLoggedMultipleTimes:
        return os << "ILM";
      case MetricState::kWrongValueLogged:
        return os << "WVL";
    }
  }
  return os << base::to_underlying(metric);
}

// Print debug information about this metric.
template <typename MetricType>
std::ostream& operator<<(std::ostream& os, const Metric<MetricType>& metric) {
  os << metric.metric_name;
  os << ": ";
  os << metric.state;
  if (metric.state != MetricState::kCorrectlyNotLogged &&
      metric.state != MetricState::kIncorrectlyNotLogged) {
    os << ", value: ";
    os << metric.value;
    if (metric.state == MetricState::kIncorrectlyLoggedMultipleTimes) {
      os << ", old value: ";
      os << metric.old_value;
    }
  }
  return os;
}

// Returns true when `task_result` represents the cloud open/upload flow ending
// before calling CloudOpenTask::OpenOrMoveFiles().
bool DidEndBeforeCallingOpenOrMoveFiles(OfficeTaskResult task_result) {
  switch (task_result) {
    case OfficeTaskResult::kFallbackQuickOffice:
    case OfficeTaskResult::kFallbackOther:
    case OfficeTaskResult::kCancelledAtFallback:
    case OfficeTaskResult::kCannotGetFallbackChoice:
    case OfficeTaskResult::kLocalFileTask:
    case OfficeTaskResult::kCancelledAtSetup:
    case OfficeTaskResult::kCannotShowSetupDialog:
    case OfficeTaskResult::kNoFilesToOpen:
    case OfficeTaskResult::kOkAtFallback:
    case OfficeTaskResult::kFileAlreadyBeingOpened:
      return true;
    case OfficeTaskResult::kOpened:
    case OfficeTaskResult::kMoved:
    case OfficeTaskResult::kCancelledAtConfirmation:
    case OfficeTaskResult::kFailedToUpload:
    case OfficeTaskResult::kFailedToOpen:
    case OfficeTaskResult::kCopied:
    case OfficeTaskResult::kFileAlreadyBeingUploaded:
    case OfficeTaskResult::kCannotShowMoveConfirmation:
    case OfficeTaskResult::kOkAtFallbackAfterOpen:
    case OfficeTaskResult::kFallbackQuickOfficeAfterOpen:
    case OfficeTaskResult::kCancelledAtFallbackAfterOpen:
    case OfficeTaskResult::kCannotGetFallbackChoiceAfterOpen:
      return false;
  }
}

// Returns true when `task_result` represents the cloud open/upload flow ending
// at the office fallback stage.
bool DidEndAtFallback(OfficeTaskResult task_result) {
  switch (task_result) {
    case OfficeTaskResult::kFallbackQuickOffice:
    case OfficeTaskResult::kFallbackOther:
    case OfficeTaskResult::kCancelledAtFallback:
    case OfficeTaskResult::kCannotGetFallbackChoice:
    case OfficeTaskResult::kOkAtFallback:
    case OfficeTaskResult::kOkAtFallbackAfterOpen:
    case OfficeTaskResult::kFallbackQuickOfficeAfterOpen:
    case OfficeTaskResult::kCancelledAtFallbackAfterOpen:
    case OfficeTaskResult::kCannotGetFallbackChoiceAfterOpen:
      return true;
    case OfficeTaskResult::kOpened:
    case OfficeTaskResult::kMoved:
    case OfficeTaskResult::kCancelledAtConfirmation:
    case OfficeTaskResult::kFailedToUpload:
    case OfficeTaskResult::kFailedToOpen:
    case OfficeTaskResult::kCopied:
    case OfficeTaskResult::kCancelledAtSetup:
    case OfficeTaskResult::kLocalFileTask:
    case OfficeTaskResult::kFileAlreadyBeingUploaded:
    case OfficeTaskResult::kCannotShowSetupDialog:
    case OfficeTaskResult::kCannotShowMoveConfirmation:
    case OfficeTaskResult::kNoFilesToOpen:
    case OfficeTaskResult::kFileAlreadyBeingOpened:
      return false;
  }
}

// Returns true when `task_result` represents the cloud open/upload flow ending
// at the move confirmation stage.
bool DidEndAtMoveConfirmation(OfficeTaskResult task_result) {
  switch (task_result) {
    case OfficeTaskResult::kCancelledAtConfirmation:
    case OfficeTaskResult::kCannotShowMoveConfirmation:
      return true;
    case OfficeTaskResult::kFallbackQuickOffice:
    case OfficeTaskResult::kFallbackOther:
    case OfficeTaskResult::kCancelledAtFallback:
    case OfficeTaskResult::kCannotGetFallbackChoice:
    case OfficeTaskResult::kOpened:
    case OfficeTaskResult::kMoved:
    case OfficeTaskResult::kFailedToUpload:
    case OfficeTaskResult::kFailedToOpen:
    case OfficeTaskResult::kCopied:
    case OfficeTaskResult::kCancelledAtSetup:
    case OfficeTaskResult::kLocalFileTask:
    case OfficeTaskResult::kFileAlreadyBeingUploaded:
    case OfficeTaskResult::kCannotShowSetupDialog:
    case OfficeTaskResult::kNoFilesToOpen:
    case OfficeTaskResult::kOkAtFallback:
    case OfficeTaskResult::kOkAtFallbackAfterOpen:
    case OfficeTaskResult::kFallbackQuickOfficeAfterOpen:
    case OfficeTaskResult::kCancelledAtFallbackAfterOpen:
    case OfficeTaskResult::kCannotGetFallbackChoiceAfterOpen:
    case OfficeTaskResult::kFileAlreadyBeingOpened:
      return false;
  }
}

CloudOpenMetrics::CloudOpenMetrics(CloudProvider cloud_provider,
                                   size_t file_count)
    : multiple_files_(file_count > 1),
      cloud_provider_(cloud_provider),
      drive_copy_error_(kGoogleDriveCopyErrorMetricName,
                        kGoogleDriveCopyErrorMetricStateMetricName),
      one_drive_copy_error_(kOneDriveCopyErrorMetricName,
                            kOneDriveCopyErrorMetricStateMetricName),
      drive_move_error_(kGoogleDriveMoveErrorMetricName,
                        kGoogleDriveMoveErrorMetricStateMetricName),
      one_drive_move_error_(kOneDriveMoveErrorMetricName,
                            kOneDriveMoveErrorMetricStateMetricName),
      drive_open_error_(kDriveErrorMetricName,
                        kDriveErrorMetricStateMetricName),
      one_drive_open_error_(kOneDriveErrorMetricName,
                            kOneDriveErrorMetricStateMetricName),
      drive_source_volume_(kDriveOpenSourceVolumeMetric,
                           kDriveOpenSourceVolumeMetricStateMetric),
      one_drive_source_volume_(kOneDriveOpenSourceVolumeMetric,
                               kOneDriveOpenSourceVolumeMetricStateMetric),
      drive_task_result_(kGoogleDriveTaskResultMetricName,
                         kGoogleDriveTaskResultMetricStateMetricName),
      one_drive_task_result_(kOneDriveTaskResultMetricName,
                             kOneDriveTaskResultMetricStateMetricName),
      drive_transfer_required_(kDriveTransferRequiredMetric,
                               kDriveTransferRequiredMetricStateMetric),
      one_drive_transfer_required_(kOneDriveTransferRequiredMetric,
                                   kOneDriveTransferRequiredMetricStateMetric),
      drive_upload_result_(kGoogleDriveUploadResultMetricName,
                           kGoogleDriveUploadResultMetricStateMetricName),
      one_drive_upload_result_(kOneDriveUploadResultMetricName,
                               kOneDriveUploadResultMetricStateMetricName) {}

void CloudOpenMetrics::CheckForInconsistencies(
    Metric<base::File::Error>& copy_error,
    Metric<base::File::Error>& move_error,
    Metric<OfficeDriveOpenErrors>& drive_open_error,
    Metric<OfficeOneDriveOpenErrors>& one_drive_open_error,
    Metric<OfficeFilesSourceVolume>& source_volume,
    Metric<OfficeTaskResult>& task_result,
    Metric<OfficeFilesTransferRequired>& transfer_required,
    Metric<OfficeFilesUploadResult>& upload_result) {
  bool google_drive = cloud_provider_ == CloudProvider::kGoogleDrive;
  // Task result should always be logged.
  ExpectLogged(task_result);
  if (task_result.logged()) {
    if (DidEndBeforeCallingOpenOrMoveFiles(task_result.value)) {
      // The cloud open/upload flow was exited before calling
      // CloudOpenTask::OpenOrMoveFiles().
      ExpectNotLogged(transfer_required);
      ExpectNotLogged(source_volume);
      ExpectNotLogged(upload_result);
      if (DidEndAtFallback(task_result.value)) {
        // The cloud open/upload flow was exited at the Fallback Dialog.
        // OpenErrors should give a fallback reason.
        if (google_drive) {
          ExpectLogged(drive_open_error);
          if (drive_open_error.logged()) {
            switch (drive_open_error.value) {
              case OfficeDriveOpenErrors::kOffline:
              case OfficeDriveOpenErrors::kDriveFsInterface:
              case OfficeDriveOpenErrors::kDriveDisabled:
              case OfficeDriveOpenErrors::kNoDriveService:
              case OfficeDriveOpenErrors::kDriveAuthenticationNotReady:
              case OfficeDriveOpenErrors::kMeteredConnection:
              case OfficeDriveOpenErrors::kDisableDrivePreferenceSet:
              case OfficeDriveOpenErrors::kDriveDisabledForAccountType:
                break;
              case OfficeDriveOpenErrors::kTimeout:
              case OfficeDriveOpenErrors::kNoMetadata:
              case OfficeDriveOpenErrors::kInvalidAlternateUrl:
              case OfficeDriveOpenErrors::kDriveAlternateUrl:
              case OfficeDriveOpenErrors::kUnexpectedAlternateUrl:
              case OfficeDriveOpenErrors::kEmptyAlternateUrl:
              case OfficeDriveOpenErrors::kWaitingForUpload:
              case OfficeDriveOpenErrors::kCannotGetRelativePath:
              case OfficeDriveOpenErrors::kSuccess:
                SetWrongValueLogged(drive_open_error);
                break;
            }
          }
        } else {
          ExpectLogged(one_drive_open_error);
          if (one_drive_open_error.logged()) {
            switch (one_drive_open_error.value) {
              case OfficeOneDriveOpenErrors::kOffline:
                break;
              case OfficeOneDriveOpenErrors::kSuccess:
              case OfficeOneDriveOpenErrors::kNoProfile:
              case OfficeOneDriveOpenErrors::kNoFileSystemURL:
              case OfficeOneDriveOpenErrors::kInvalidFileSystemURL:
              case OfficeOneDriveOpenErrors::kGetActionsGenericError:
              case OfficeOneDriveOpenErrors::kGetActionsReauthRequired:
              case OfficeOneDriveOpenErrors::kGetActionsInvalidUrl:
              case OfficeOneDriveOpenErrors::kGetActionsNoUrl:
              case OfficeOneDriveOpenErrors::kGetActionsAccessDenied:
              case OfficeOneDriveOpenErrors::kGetActionsNoEmail:
              case OfficeOneDriveOpenErrors::kConversionToODFSUrlError:
              case OfficeOneDriveOpenErrors::kAndroidOneDriveInvalidUrl:
              case OfficeOneDriveOpenErrors::kEmailsDoNotMatch:
              case OfficeOneDriveOpenErrors::
                  kAndroidOneDriveUnsupportedLocation:
                SetWrongValueLogged(one_drive_open_error);
                break;
            }
          }
        }
      }
    } else {
      // CloudOpenTask::OpenOrMoveFiles() was called.
      ExpectLogged(source_volume);
      ExpectLogged(transfer_required);
      if (DidEndAtFallback(task_result.value)) {
        // The cloud open/upload flow was exited at the Fallback Dialog after
        // an open was attempted. OpenErrors should give a fallback reason.
        if (google_drive) {
          SetWrongValueLogged(task_result);
        } else {
          ExpectLogged(one_drive_open_error);
          if (one_drive_open_error.logged()) {
            switch (one_drive_open_error.value) {
              case OfficeOneDriveOpenErrors::
                  kAndroidOneDriveUnsupportedLocation:
                break;
              case OfficeOneDriveOpenErrors::kSuccess:
              case OfficeOneDriveOpenErrors::kOffline:
              case OfficeOneDriveOpenErrors::kNoProfile:
              case OfficeOneDriveOpenErrors::kNoFileSystemURL:
              case OfficeOneDriveOpenErrors::kInvalidFileSystemURL:
              case OfficeOneDriveOpenErrors::kGetActionsGenericError:
              case OfficeOneDriveOpenErrors::kGetActionsReauthRequired:
              case OfficeOneDriveOpenErrors::kGetActionsInvalidUrl:
              case OfficeOneDriveOpenErrors::kGetActionsNoUrl:
              case OfficeOneDriveOpenErrors::kGetActionsAccessDenied:
              case OfficeOneDriveOpenErrors::kGetActionsNoEmail:
              case OfficeOneDriveOpenErrors::kConversionToODFSUrlError:
              case OfficeOneDriveOpenErrors::kAndroidOneDriveInvalidUrl:
              case OfficeOneDriveOpenErrors::kEmailsDoNotMatch:
                SetWrongValueLogged(one_drive_open_error);
                break;
            }
          }
        }
      } else if (DidEndAtMoveConfirmation(task_result.value)) {
        ExpectNotLogged(upload_result);
        ExpectNotLogged(drive_open_error);
        ExpectNotLogged(one_drive_open_error);
        // TransferRequired should be kMove or kCopy.
        ExpectLogged(transfer_required);
        if (transfer_required.logged()) {
          switch (transfer_required.value) {
            case OfficeFilesTransferRequired::kMove:
            case OfficeFilesTransferRequired::kCopy:
              break;
            case OfficeFilesTransferRequired::kNotRequired:
              SetWrongValueLogged(transfer_required);
              break;
          }
        }
      } else if (task_result.value == OfficeTaskResult::kFailedToUpload) {
        ExpectNotLogged(drive_open_error);
        ExpectNotLogged(one_drive_open_error);
        ExpectLogged(upload_result);
        switch (upload_result.value) {
          case OfficeFilesUploadResult::kOtherError:
          case OfficeFilesUploadResult::kFileSystemNotFound:
          case OfficeFilesUploadResult::kMoveOperationCancelled:
          case OfficeFilesUploadResult::kMoveOperationError:
          case OfficeFilesUploadResult::kMoveOperationNeedPassword:
          case OfficeFilesUploadResult::kCopyOperationCancelled:
          case OfficeFilesUploadResult::kCopyOperationError:
          case OfficeFilesUploadResult::kCopyOperationNeedPassword:
          case OfficeFilesUploadResult::kPinningFailedDiskFull:
          case OfficeFilesUploadResult::kCloudAccessDenied:
          case OfficeFilesUploadResult::kCloudMetadataError:
          case OfficeFilesUploadResult::kCloudQuotaFull:
          case OfficeFilesUploadResult::kCloudError:
          case OfficeFilesUploadResult::kNoConnection:
          case OfficeFilesUploadResult::kDestinationUrlError:
          case OfficeFilesUploadResult::kInvalidURL:
          case OfficeFilesUploadResult::kCloudReauthRequired:
          case OfficeFilesUploadResult::kInvalidAlternateUrl:
          case OfficeFilesUploadResult::kUnexpectedAlternateUrlHost:
          case OfficeFilesUploadResult::kSyncError:
          case OfficeFilesUploadResult::kSyncCancelledAndDeleted:
          case OfficeFilesUploadResult::kSyncCancelledAndTrashed:
          case OfficeFilesUploadResult::
              kUploadNotStartedReauthenticationRequired:
          case OfficeFilesUploadResult::kFileNotAnOfficeFile:
            break;
          case OfficeFilesUploadResult::kSuccess:
          case OfficeFilesUploadResult::kSuccessAfterReauth:
            SetWrongValueLogged(upload_result);
            break;
        }
      } else if (task_result.value == OfficeTaskResult::kFailedToOpen) {
        // The cloud open flow failed.
        // OpenErrors should be an error.
        if (google_drive) {
          ExpectLogged(drive_open_error);
          if (drive_open_error.logged()) {
            switch (drive_open_error.value) {
              case OfficeDriveOpenErrors::kOffline:
              case OfficeDriveOpenErrors::kDriveFsInterface:
              case OfficeDriveOpenErrors::kTimeout:
              case OfficeDriveOpenErrors::kNoMetadata:
              case OfficeDriveOpenErrors::kInvalidAlternateUrl:
              case OfficeDriveOpenErrors::kDriveAlternateUrl:
              case OfficeDriveOpenErrors::kUnexpectedAlternateUrl:
              case OfficeDriveOpenErrors::kDriveDisabled:
              case OfficeDriveOpenErrors::kNoDriveService:
              case OfficeDriveOpenErrors::kDriveAuthenticationNotReady:
              case OfficeDriveOpenErrors::kMeteredConnection:
              case OfficeDriveOpenErrors::kEmptyAlternateUrl:
              case OfficeDriveOpenErrors::kWaitingForUpload:
              case OfficeDriveOpenErrors::kDisableDrivePreferenceSet:
              case OfficeDriveOpenErrors::kDriveDisabledForAccountType:
              case OfficeDriveOpenErrors::kCannotGetRelativePath:
                break;
              case OfficeDriveOpenErrors::kSuccess:
                SetWrongValueLogged(drive_open_error);
                break;
            }
          }
        } else {
          ExpectLogged(one_drive_open_error);
          if (one_drive_open_error.logged()) {
            switch (one_drive_open_error.value) {
              case OfficeOneDriveOpenErrors::kOffline:
              case OfficeOneDriveOpenErrors::kNoProfile:
              case OfficeOneDriveOpenErrors::kNoFileSystemURL:
              case OfficeOneDriveOpenErrors::kInvalidFileSystemURL:
              case OfficeOneDriveOpenErrors::kGetActionsGenericError:
              case OfficeOneDriveOpenErrors::kGetActionsReauthRequired:
              case OfficeOneDriveOpenErrors::kGetActionsInvalidUrl:
              case OfficeOneDriveOpenErrors::kGetActionsNoUrl:
              case OfficeOneDriveOpenErrors::kGetActionsAccessDenied:
              case OfficeOneDriveOpenErrors::kGetActionsNoEmail:
              case OfficeOneDriveOpenErrors::kConversionToODFSUrlError:
              case OfficeOneDriveOpenErrors::kAndroidOneDriveInvalidUrl:
              case OfficeOneDriveOpenErrors::kEmailsDoNotMatch:
                break;
              case OfficeOneDriveOpenErrors::kSuccess:
              case OfficeOneDriveOpenErrors::
                  kAndroidOneDriveUnsupportedLocation:
                SetWrongValueLogged(one_drive_open_error);
                break;
            }
          }
        }
      } else if (task_result.value == OfficeTaskResult::kOpened ||
                 task_result.value == OfficeTaskResult::kCopied ||
                 task_result.value == OfficeTaskResult::kMoved) {
        // The cloud open/upload flow was successful.
        // The OpenErrors should be success.
        if (google_drive) {
          ExpectLogged(drive_open_error);
          if (drive_open_error.logged()) {
            switch (drive_open_error.value) {
              case OfficeDriveOpenErrors::kSuccess:
                break;
              case OfficeDriveOpenErrors::kOffline:
              case OfficeDriveOpenErrors::kDriveFsInterface:
              case OfficeDriveOpenErrors::kTimeout:
              case OfficeDriveOpenErrors::kNoMetadata:
              case OfficeDriveOpenErrors::kInvalidAlternateUrl:
              case OfficeDriveOpenErrors::kDriveAlternateUrl:
              case OfficeDriveOpenErrors::kUnexpectedAlternateUrl:
              case OfficeDriveOpenErrors::kDriveDisabled:
              case OfficeDriveOpenErrors::kNoDriveService:
              case OfficeDriveOpenErrors::kDriveAuthenticationNotReady:
              case OfficeDriveOpenErrors::kMeteredConnection:
              case OfficeDriveOpenErrors::kEmptyAlternateUrl:
              case OfficeDriveOpenErrors::kWaitingForUpload:
              case OfficeDriveOpenErrors::kDisableDrivePreferenceSet:
              case OfficeDriveOpenErrors::kDriveDisabledForAccountType:
              case OfficeDriveOpenErrors::kCannotGetRelativePath:
                SetWrongValueLogged(drive_open_error);
                break;
            }
          }
        } else {
          ExpectLogged(one_drive_open_error);
          if (one_drive_open_error.logged()) {
            switch (one_drive_open_error.value) {
              case OfficeOneDriveOpenErrors::kSuccess:
                break;
              case OfficeOneDriveOpenErrors::kOffline:
              case OfficeOneDriveOpenErrors::kNoProfile:
              case OfficeOneDriveOpenErrors::kNoFileSystemURL:
              case OfficeOneDriveOpenErrors::kInvalidFileSystemURL:
              case OfficeOneDriveOpenErrors::kGetActionsGenericError:
              case OfficeOneDriveOpenErrors::kGetActionsReauthRequired:
              case OfficeOneDriveOpenErrors::kGetActionsInvalidUrl:
              case OfficeOneDriveOpenErrors::kGetActionsNoUrl:
              case OfficeOneDriveOpenErrors::kGetActionsAccessDenied:
              case OfficeOneDriveOpenErrors::kGetActionsNoEmail:
              case OfficeOneDriveOpenErrors::kConversionToODFSUrlError:
              case OfficeOneDriveOpenErrors::kAndroidOneDriveInvalidUrl:
              case OfficeOneDriveOpenErrors::kEmailsDoNotMatch:
              case OfficeOneDriveOpenErrors::
                  kAndroidOneDriveUnsupportedLocation:
                SetWrongValueLogged(one_drive_open_error);
                break;
            }
          }
        }
        ExpectLogged(transfer_required);
        if (task_result.value == OfficeTaskResult::kOpened) {
          // The cloud open flow was successful.
          ExpectNotLogged(upload_result);
          // TransferRequired should be kNotRequired.
          if (transfer_required.logged()) {
            switch (transfer_required.value) {
              case OfficeFilesTransferRequired::kNotRequired:
                break;
              case OfficeFilesTransferRequired::kMove:
              case OfficeFilesTransferRequired::kCopy:
                SetWrongValueLogged(transfer_required);
                break;
            }
          }
        } else {
          // The cloud upload flow was successful.
          // The UploadResult should be success.
          ExpectLogged(upload_result);
          if (upload_result.logged()) {
            switch (upload_result.value) {
              case OfficeFilesUploadResult::kSuccess:
              case OfficeFilesUploadResult::kSuccessAfterReauth:
                break;
              case OfficeFilesUploadResult::kOtherError:
              case OfficeFilesUploadResult::kFileSystemNotFound:
              case OfficeFilesUploadResult::kMoveOperationCancelled:
              case OfficeFilesUploadResult::kMoveOperationError:
              case OfficeFilesUploadResult::kMoveOperationNeedPassword:
              case OfficeFilesUploadResult::kCopyOperationCancelled:
              case OfficeFilesUploadResult::kCopyOperationError:
              case OfficeFilesUploadResult::kCopyOperationNeedPassword:
              case OfficeFilesUploadResult::kPinningFailedDiskFull:
              case OfficeFilesUploadResult::kCloudAccessDenied:
              case OfficeFilesUploadResult::kCloudMetadataError:
              case OfficeFilesUploadResult::kCloudQuotaFull:
              case OfficeFilesUploadResult::kCloudError:
              case OfficeFilesUploadResult::kNoConnection:
              case OfficeFilesUploadResult::kDestinationUrlError:
              case OfficeFilesUploadResult::kInvalidURL:
              case OfficeFilesUploadResult::kCloudReauthRequired:
              case OfficeFilesUploadResult::kInvalidAlternateUrl:
              case OfficeFilesUploadResult::kUnexpectedAlternateUrlHost:
              case OfficeFilesUploadResult::kSyncError:
              case OfficeFilesUploadResult::kSyncCancelledAndDeleted:
              case OfficeFilesUploadResult::kSyncCancelledAndTrashed:
              case OfficeFilesUploadResult::
                  kUploadNotStartedReauthenticationRequired:
              case OfficeFilesUploadResult::kFileNotAnOfficeFile:
                SetWrongValueLogged(upload_result);
                break;
            }
          }
          if (task_result.value == OfficeTaskResult::kCopied) {
            // The cloud upload (copy) flow was successful.
            // TransferRequired should be kCopy.
            if (transfer_required.logged()) {
              switch (transfer_required.value) {
                case OfficeFilesTransferRequired::kCopy:
                  break;
                case OfficeFilesTransferRequired::kNotRequired:
                case OfficeFilesTransferRequired::kMove:
                  SetWrongValueLogged(transfer_required);
                  break;
              }
            }
          } else {
            // The cloud upload (move) flow was successful.
            // TransferRequired should be kMove.
            if (transfer_required.logged()) {
              switch (transfer_required.value) {
                case OfficeFilesTransferRequired::kMove:
                  break;
                case OfficeFilesTransferRequired::kNotRequired:
                case OfficeFilesTransferRequired::kCopy:
                  SetWrongValueLogged(transfer_required);
                  break;
              }
            }
          }
        }
      }
    }
  }

  if (transfer_required.logged()) {
    ExpectLogged(source_volume);
    if (transfer_required.value == OfficeFilesTransferRequired::kNotRequired) {
      ExpectNotLogged(upload_result);
      // SourceVolume should match the CloudProvider.
      if (google_drive) {
        ExpectLogged(drive_open_error);
        if (source_volume.logged()) {
          switch (source_volume.value) {
            case OfficeFilesSourceVolume::kGoogleDrive:
              break;
            case OfficeFilesSourceVolume::kDownloadsDirectory:
            case OfficeFilesSourceVolume::kRemovableDiskPartition:
            case OfficeFilesSourceVolume::kMountedArchiveFile:
            case OfficeFilesSourceVolume::kProvided:
            case OfficeFilesSourceVolume::kMtp:
            case OfficeFilesSourceVolume::kMediaView:
            case OfficeFilesSourceVolume::kCrostini:
            case OfficeFilesSourceVolume::kAndriodFiles:
            case OfficeFilesSourceVolume::kDocumentsProvider:
            case OfficeFilesSourceVolume::kSmb:
            case OfficeFilesSourceVolume::kSystemInternal:
            case OfficeFilesSourceVolume::kGuestOS:
            case OfficeFilesSourceVolume::kUnknown:
            case OfficeFilesSourceVolume::kMicrosoftOneDrive:
            case OfficeFilesSourceVolume::kAndroidOneDriveDocumentsProvider:
              SetWrongValueLogged(source_volume);
              break;
          }
        }
      } else {
        ExpectLogged(one_drive_open_error);
        if (source_volume.logged()) {
          switch (source_volume.value) {
            case OfficeFilesSourceVolume::kMicrosoftOneDrive:
            case OfficeFilesSourceVolume::kAndroidOneDriveDocumentsProvider:
              break;
            case OfficeFilesSourceVolume::kDownloadsDirectory:
            case OfficeFilesSourceVolume::kRemovableDiskPartition:
            case OfficeFilesSourceVolume::kMountedArchiveFile:
            case OfficeFilesSourceVolume::kProvided:
            case OfficeFilesSourceVolume::kMtp:
            case OfficeFilesSourceVolume::kMediaView:
            case OfficeFilesSourceVolume::kCrostini:
            case OfficeFilesSourceVolume::kAndriodFiles:
            case OfficeFilesSourceVolume::kDocumentsProvider:
            case OfficeFilesSourceVolume::kSmb:
            case OfficeFilesSourceVolume::kSystemInternal:
            case OfficeFilesSourceVolume::kGuestOS:
            case OfficeFilesSourceVolume::kUnknown:
            case OfficeFilesSourceVolume::kGoogleDrive:
              SetWrongValueLogged(source_volume);
              break;
          }
        }
      }
    } else {
      // TransferRequired was kCopy or kMove.
      if (task_result.logged() &&
          (DidEndAtMoveConfirmation(task_result.value))) {
        // The cloud upload flow was exited at the Move Confirmation Dialog.
        ExpectNotLogged(upload_result);
      } else {
        // The upload should have succeeded or failed.
        ExpectLogged(upload_result);
      }
      // SourceVolume should not match the CloudProvider.
      if (google_drive) {
        if (source_volume.logged()) {
          switch (source_volume.value) {
            case OfficeFilesSourceVolume::kDownloadsDirectory:
            case OfficeFilesSourceVolume::kRemovableDiskPartition:
            case OfficeFilesSourceVolume::kMountedArchiveFile:
            case OfficeFilesSourceVolume::kProvided:
            case OfficeFilesSourceVolume::kMtp:
            case OfficeFilesSourceVolume::kMediaView:
            case OfficeFilesSourceVolume::kCrostini:
            case OfficeFilesSourceVolume::kAndriodFiles:
            case OfficeFilesSourceVolume::kDocumentsProvider:
            case OfficeFilesSourceVolume::kSmb:
            case OfficeFilesSourceVolume::kSystemInternal:
            case OfficeFilesSourceVolume::kGuestOS:
            case OfficeFilesSourceVolume::kUnknown:
            case OfficeFilesSourceVolume::kMicrosoftOneDrive:
            case OfficeFilesSourceVolume::kAndroidOneDriveDocumentsProvider:
              break;
            case OfficeFilesSourceVolume::kGoogleDrive:
              SetWrongValueLogged(source_volume);
              break;
          }
        }
      } else {
        if (source_volume.logged()) {
          switch (source_volume.value) {
            case OfficeFilesSourceVolume::kDownloadsDirectory:
            case OfficeFilesSourceVolume::kRemovableDiskPartition:
            case OfficeFilesSourceVolume::kMountedArchiveFile:
            case OfficeFilesSourceVolume::kProvided:
            case OfficeFilesSourceVolume::kMtp:
            case OfficeFilesSourceVolume::kMediaView:
            case OfficeFilesSourceVolume::kCrostini:
            case OfficeFilesSourceVolume::kAndriodFiles:
            case OfficeFilesSourceVolume::kDocumentsProvider:
            case OfficeFilesSourceVolume::kSmb:
            case OfficeFilesSourceVolume::kSystemInternal:
            case OfficeFilesSourceVolume::kGuestOS:
            case OfficeFilesSourceVolume::kUnknown:
            case OfficeFilesSourceVolume::kGoogleDrive:
              break;
            case OfficeFilesSourceVolume::kMicrosoftOneDrive:
            case OfficeFilesSourceVolume::kAndroidOneDriveDocumentsProvider:
              SetWrongValueLogged(source_volume);
              break;
          }
        }
      }
    }
  }

  if (upload_result.logged()) {
    if (upload_result.value == OfficeFilesUploadResult::kCopyOperationError) {
      ExpectLogged(copy_error);
    } else if (upload_result.value ==
               OfficeFilesUploadResult::kMoveOperationError) {
      ExpectLogged(move_error);
    } else if (upload_result.value ==
               OfficeFilesUploadResult::
                   kUploadNotStartedReauthenticationRequired) {
      ExpectNotLogged(copy_error);
      ExpectNotLogged(move_error);
      // TaskResult should be kFailedToUpload.
      ExpectLogged(task_result);
      if (task_result.logged()) {
        switch (task_result.value) {
          case OfficeTaskResult::kFailedToUpload:
            break;
          case OfficeTaskResult::kFallbackQuickOffice:
          case OfficeTaskResult::kFallbackOther:
          case OfficeTaskResult::kOpened:
          case OfficeTaskResult::kMoved:
          case OfficeTaskResult::kCancelledAtConfirmation:
          case OfficeTaskResult::kFailedToOpen:
          case OfficeTaskResult::kCopied:
          case OfficeTaskResult::kCancelledAtFallback:
          case OfficeTaskResult::kCancelledAtSetup:
          case OfficeTaskResult::kLocalFileTask:
          case OfficeTaskResult::kFileAlreadyBeingUploaded:
          case OfficeTaskResult::kCannotGetFallbackChoice:
          case OfficeTaskResult::kCannotShowSetupDialog:
          case OfficeTaskResult::kCannotShowMoveConfirmation:
          case OfficeTaskResult::kNoFilesToOpen:
          case OfficeTaskResult::kOkAtFallback:
          case OfficeTaskResult::kOkAtFallbackAfterOpen:
          case OfficeTaskResult::kFallbackQuickOfficeAfterOpen:
          case OfficeTaskResult::kCancelledAtFallbackAfterOpen:
          case OfficeTaskResult::kCannotGetFallbackChoiceAfterOpen:
          case OfficeTaskResult::kFileAlreadyBeingOpened:
            SetWrongValueLogged(task_result);
            break;
        }
      }
    }
  }

  if (copy_error.logged() || move_error.logged()) {
    ExpectLogged(upload_result);
  }
}

// Check metric consistency and update metric states as required. Log the
// companion metrics with the final metric states. Dump without crashing if an
// inconsistency was found.
CloudOpenMetrics::~CloudOpenMetrics() {
  if (multiple_files_) {
    // TODO(b/242685536): Define CloudOpenMetrics for multiple files.
    return;
  }

  if (cloud_provider_ == CloudProvider::kGoogleDrive) {
    CheckForInconsistencies(drive_copy_error_, drive_move_error_,
                            drive_open_error_, one_drive_open_error_,
                            drive_source_volume_, drive_task_result_,
                            drive_transfer_required_, drive_upload_result_);
    ExpectNotLogged(one_drive_copy_error_);
    ExpectNotLogged(one_drive_move_error_);
    ExpectNotLogged(one_drive_open_error_);
    ExpectNotLogged(one_drive_source_volume_);
    ExpectNotLogged(one_drive_task_result_);
    ExpectNotLogged(one_drive_transfer_required_);
    ExpectNotLogged(one_drive_upload_result_);
  } else {
    CheckForInconsistencies(
        one_drive_copy_error_, one_drive_move_error_, drive_open_error_,
        one_drive_open_error_, one_drive_source_volume_, one_drive_task_result_,
        one_drive_transfer_required_, one_drive_upload_result_);
    ExpectNotLogged(drive_copy_error_);
    ExpectNotLogged(drive_move_error_);
    ExpectNotLogged(drive_open_error_);
    ExpectNotLogged(drive_source_volume_);
    ExpectNotLogged(drive_task_result_);
    ExpectNotLogged(drive_transfer_required_);
    ExpectNotLogged(drive_upload_result_);
  }

  drive_copy_error_.LogCompanionMetric();
  one_drive_copy_error_.LogCompanionMetric();
  drive_move_error_.LogCompanionMetric();
  one_drive_move_error_.LogCompanionMetric();
  drive_open_error_.LogCompanionMetric();
  one_drive_open_error_.LogCompanionMetric();
  drive_source_volume_.LogCompanionMetric();
  one_drive_source_volume_.LogCompanionMetric();
  drive_task_result_.LogCompanionMetric();
  one_drive_task_result_.LogCompanionMetric();
  drive_transfer_required_.LogCompanionMetric();
  one_drive_transfer_required_.LogCompanionMetric();
  drive_upload_result_.LogCompanionMetric();
  one_drive_upload_result_.LogCompanionMetric();

  if (delayed_dump_) {
    DumpState();
  }
}

void CloudOpenMetrics::LogCopyError(base::File::Error value) {
  LogAndCheckForInconsistency(drive_copy_error_, one_drive_copy_error_, value);
}

void CloudOpenMetrics::LogMoveError(base::File::Error value) {
  LogAndCheckForInconsistency(drive_move_error_, one_drive_move_error_, value);
}

void CloudOpenMetrics::LogGoogleDriveOpenError(OfficeDriveOpenErrors value) {
  if (!drive_open_error_.Log(value)) {
    OnInconsistencyFound(drive_open_error_);
  }
}

void CloudOpenMetrics::LogOneDriveOpenError(OfficeOneDriveOpenErrors value) {
  if (!one_drive_open_error_.Log(value)) {
    OnInconsistencyFound(one_drive_open_error_);
  }
}

void CloudOpenMetrics::LogSourceVolume(OfficeFilesSourceVolume value) {
  LogAndCheckForInconsistency(drive_source_volume_, one_drive_source_volume_,
                              value);
}

void CloudOpenMetrics::LogTaskResult(OfficeTaskResult value) {
  LogAndCheckForInconsistency(drive_task_result_, one_drive_task_result_,
                              value);
}

void CloudOpenMetrics::LogTransferRequired(OfficeFilesTransferRequired value) {
  LogAndCheckForInconsistency(drive_transfer_required_,
                              one_drive_transfer_required_, value);
}

void CloudOpenMetrics::LogUploadResult(OfficeFilesUploadResult value) {
  LogAndCheckForInconsistency(drive_upload_result_, one_drive_upload_result_,
                              value);
}

void CloudOpenMetrics::set_cloud_provider(CloudProvider cloud_provider) {
  cloud_provider_ = cloud_provider;
}

base::SafeRef<CloudOpenMetrics> CloudOpenMetrics::GetSafeRef() const {
  return weak_ptr_factory_.GetSafeRef();
}

// For testing.
base::WeakPtr<CloudOpenMetrics> CloudOpenMetrics::GetWeakPtr() {
  return weak_ptr_factory_.GetWeakPtr();
}

void CloudOpenMetrics::DumpState() {
  switch (inconsistent_state_) {
    case MetricState::kCorrectlyNotLogged: {
      SCOPED_CRASH_KEY_STRING64("CloudOpenMetrics", "NL",
                                inconsistent_metric_name_);
      base::debug::DumpWithoutCrashing();
      break;
    }
    case MetricState::kCorrectlyLogged: {
      SCOPED_CRASH_KEY_STRING64("CloudOpenMetrics", "L",
                                inconsistent_metric_name_);
      base::debug::DumpWithoutCrashing();
      break;
    }
    case MetricState::kIncorrectlyNotLogged: {
      SCOPED_CRASH_KEY_STRING64("CloudOpenMetrics", "INL",
                                inconsistent_metric_name_);
      base::debug::DumpWithoutCrashing();
      break;
    }
    case MetricState::kIncorrectlyLogged: {
      SCOPED_CRASH_KEY_STRING64("CloudOpenMetrics", "IL",
                                inconsistent_metric_name_);
      base::debug::DumpWithoutCrashing();
      break;
    }
    case MetricState::kIncorrectlyLoggedMultipleTimes: {
      SCOPED_CRASH_KEY_STRING64("CloudOpenMetrics", "ILM",
                                inconsistent_metric_name_);
      base::debug::DumpWithoutCrashing();
      break;
    }
    case MetricState::kWrongValueLogged: {
      SCOPED_CRASH_KEY_STRING64("CloudOpenMetrics", "WVL",
                                inconsistent_metric_name_);
      base::debug::DumpWithoutCrashing();
      break;
    }
  }
}

template <typename MetricType>
void CloudOpenMetrics::OnInconsistencyFound(Metric<MetricType>& metric,
                                            bool immediately_dump) {
  if (multiple_files_) {
    // TODO(b/242685536): Define CloudOpenMetrics for multiple files.
    return;
  }
  LOG(ERROR) << "Inconsistent metric found: " << metric
             << ". ----- Cloud provider: " << cloud_provider_
             << ". ----- Drive metrics: " << drive_copy_error_ << ". "
             << drive_move_error_ << ". " << drive_open_error_ << ". "
             << drive_source_volume_ << ". " << drive_task_result_ << ". "
             << drive_transfer_required_ << ". " << drive_upload_result_
             << ". -----  OneDrive metrics: " << one_drive_copy_error_ << ". "
             << one_drive_move_error_ << ". " << one_drive_open_error_ << ". "
             << one_drive_source_volume_ << ". " << one_drive_task_result_
             << ". " << one_drive_transfer_required_ << ". "
             << one_drive_upload_result_ << ".";

  // Set dump key-value pair.
  inconsistent_metric_name_ = metric.metric_name;
  inconsistent_state_ = metric.state;

  if (immediately_dump) {
    DumpState();
  } else {
    delayed_dump_ = true;
  }
}

template <typename MetricType>
void CloudOpenMetrics::ExpectNotLogged(Metric<MetricType>& metric) {
  if (!metric.IsNotLogged()) {
    OnInconsistencyFound(metric, /*immediately_dump=*/false);
  }
}

template <typename MetricType>
void CloudOpenMetrics::ExpectLogged(Metric<MetricType>& metric) {
  if (!metric.IsLogged()) {
    OnInconsistencyFound(metric, /*immediately_dump=*/false);
  }
}

template <typename MetricType>
void CloudOpenMetrics::SetWrongValueLogged(Metric<MetricType>& metric) {
  metric.set_state(MetricState::kWrongValueLogged);
  OnInconsistencyFound(metric, /*immediately_dump=*/false);
}

template <typename MetricType>
void CloudOpenMetrics::LogAndCheckForInconsistency(
    Metric<MetricType>& drive_metric,
    Metric<MetricType>& one_drive_metric,
    MetricType value) {
  if (cloud_provider_ == CloudProvider::kGoogleDrive) {
    if (!drive_metric.Log(value)) {
      OnInconsistencyFound(drive_metric);
    }
  } else if (!one_drive_metric.Log(value)) {
    OnInconsistencyFound(one_drive_metric);
  }
}

}  // namespace ash::cloud_upload