chromium/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc

// Copyright 2013 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/app_mode/kiosk_app_launch_error.h"

#include <optional>
#include <string>

#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/values.h"
#include "chrome/browser/ash/app_mode/kiosk_chrome_app_manager.h"
#include "chrome/browser/browser_process.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ash/components/login/auth/public/auth_failure.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "ui/base/l10n/l10n_util.h"

namespace ash {

namespace {

// Key under "kiosk" dictionary to store the last launch error.
constexpr char kKeyLaunchError[] = "launch_error";

// Key under "kiosk" dictionary to store the last cryptohome error.
constexpr char kKeyCryptohomeFailure[] = "cryptohome_failure";

// Error from the last kiosk launch.
std::optional<KioskAppLaunchError::Error> s_last_error = std::nullopt;

}  // namespace

// static
std::string KioskAppLaunchError::GetErrorMessage(Error error) {
  switch (error) {
    case Error::kNone:
      return std::string();

    case Error::kHasPendingLaunch:
    case Error::kNotKioskEnabled:
    case Error::kUnableToRetrieveHash:
    case Error::kPolicyLoadFailed:
    case Error::kUserNotAllowlisted:
    case Error::kLacrosDataMigrationStarted:
    case Error::kLacrosBackwardDataMigrationStarted:
      return l10n_util::GetStringUTF8(IDS_KIOSK_APP_FAILED_TO_LAUNCH);

    case Error::kCryptohomedNotRunning:
    case Error::kAlreadyMounted:
    case Error::kUnableToMount:
    case Error::kUnableToRemove:
      return l10n_util::GetStringUTF8(IDS_KIOSK_APP_ERROR_UNABLE_TO_MOUNT);

    case Error::kUnableToInstall:
      return l10n_util::GetStringUTF8(IDS_KIOSK_APP_ERROR_UNABLE_TO_INSTALL);

    case Error::kUserCancel:
      return l10n_util::GetStringUTF8(IDS_KIOSK_APP_ERROR_USER_CANCEL);

    case Error::kUnableToDownload:
      return l10n_util::GetStringUTF8(IDS_KIOSK_APP_ERROR_UNABLE_TO_DOWNLOAD);

    case Error::kUnableToLaunch:
      return l10n_util::GetStringUTF8(IDS_KIOSK_APP_ERROR_UNABLE_TO_LAUNCH);

    case Error::kExtensionsLoadTimeout:
      return l10n_util::GetStringUTF8(
          IDS_KIOSK_APP_ERROR_EXTENSIONS_LOAD_TIMEOUT);

    case Error::kExtensionsPolicyInvalid:
      return l10n_util::GetStringUTF8(
          IDS_KIOSK_APP_ERROR_EXTENSIONS_POLICY_INVALID);
  }

  NOTREACHED_IN_MIGRATION()
      << "Unknown kiosk app launch error, error=" << static_cast<int>(error);
  return l10n_util::GetStringUTF8(IDS_KIOSK_APP_FAILED_TO_LAUNCH);
}

// static
void KioskAppLaunchError::Save(KioskAppLaunchError::Error error) {
  s_last_error = error;

  PrefService* local_state = g_browser_process->local_state();
  {
    ScopedDictPrefUpdate dict_update(
        local_state, KioskChromeAppManager::kKioskDictionaryName);
    dict_update->SetByDottedPath(kKeyLaunchError, static_cast<int>(error));
  }

  // Make sure that the kiosk launch error gets written to disk before the
  // browser is killed.
  local_state->CommitPendingWrite();
}

// static
void KioskAppLaunchError::SaveCryptohomeFailure(
    const AuthFailure& auth_failure) {
  PrefService* local_state = g_browser_process->local_state();
  ScopedDictPrefUpdate dict_update(local_state,
                                   KioskChromeAppManager::kKioskDictionaryName);
  dict_update->SetByDottedPath(kKeyCryptohomeFailure, auth_failure.reason());
}

// static
KioskAppLaunchError::Error KioskAppLaunchError::Get() {
  if (s_last_error) {
    return *s_last_error;
  }
  s_last_error = Error::kNone;
  PrefService* local_state = g_browser_process->local_state();
  const base::Value::Dict& dict =
      local_state->GetDict(KioskChromeAppManager::kKioskDictionaryName);

  std::optional<int> error = dict.FindInt(kKeyLaunchError);
  if (error.has_value()) {
    s_last_error = static_cast<KioskAppLaunchError::Error>(error.value());
    return *s_last_error;
  }

  return Error::kNone;
}

// static
void KioskAppLaunchError::RecordMetricAndClear() {
  PrefService* local_state = g_browser_process->local_state();
  ScopedDictPrefUpdate dict_update(local_state,
                                   KioskChromeAppManager::kKioskDictionaryName);

  std::optional<int> error = dict_update->FindInt(kKeyLaunchError);
  if (error) {
    base::UmaHistogramEnumeration("Kiosk.Launch.Error",
                                  static_cast<Error>(*error));
  }

  dict_update->Remove(kKeyLaunchError);
  s_last_error = std::nullopt;

  std::optional<int> cryptohome_failure =
      dict_update->FindInt(kKeyCryptohomeFailure);
  if (cryptohome_failure) {
    UMA_HISTOGRAM_ENUMERATION(
        "Kiosk.Launch.CryptohomeFailure",
        static_cast<AuthFailure::FailureReason>(*cryptohome_failure),
        AuthFailure::NUM_FAILURE_REASONS);
  }
  dict_update->Remove(kKeyCryptohomeFailure);
}

}  // namespace ash