chromium/ios/chrome/browser/first_run/model/first_run.mm

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/first_run/model/first_run.h"

#import <string_view>

#import "base/files/file.h"
#import "base/files/file_path.h"
#import "base/files/file_util.h"
#import "base/no_destructor.h"
#import "base/path_service.h"
#import "components/pref_registry/pref_registry_syncable.h"
#import "components/startup_metric_utils/browser/startup_metric_utils.h"
#import "ios/chrome/browser/shared/model/paths/paths.h"

namespace {

// The absence of kSentinelFile file will tell us it is a first run.
const base::FilePath::CharType kSentinelFile[] = FILE_PATH_LITERAL("First Run");

// RLZ ping delay pref name.
const char kPingDelayPrefName[] = "distribution.ping_delay";

// The info from first run sentinel file; if the file doesn't exist, the value
// would be `std::nullopt`. Only accessed through LoadSentinelInfo() and
// GetSentinelInfo();
std::optional<base::File::Info>& GetSentinelInfoGlobal() {
  static base::NoDestructor<std::optional<base::File::Info>> kInstance(
      std::nullopt);
  return *kInstance;
}

}  // namespace

FirstRun::FirstRunState FirstRun::first_run_ = FIRST_RUN_UNKNOWN;

// static
bool FirstRun::GetFirstRunSentinelFilePath(base::FilePath* path) {
  base::FilePath first_run_sentinel;
  if (!base::PathService::Get(ios::DIR_USER_DATA, &first_run_sentinel)) {
    NOTREACHED_IN_MIGRATION();
    return false;
  }
  *path = first_run_sentinel.Append(kSentinelFile);
  return true;
}

// static
bool FirstRun::IsChromeFirstRun() {
  if (first_run_ != FIRST_RUN_UNKNOWN)
    return first_run_ == FIRST_RUN_TRUE;

  base::FilePath first_run_sentinel;
  if (!GetFirstRunSentinelFilePath(&first_run_sentinel) ||
      base::PathExists(first_run_sentinel)) {
    LoadSentinelInfo();
    first_run_ = FIRST_RUN_FALSE;
    return false;
  }
  first_run_ = FIRST_RUN_TRUE;
  return true;
}

// static
std::optional<base::File::Info> FirstRun::GetSentinelInfo() {
  return GetSentinelInfoGlobal();
}

// static
bool FirstRun::RemoveSentinel() {
  base::FilePath first_run_sentinel;
  if (!GetFirstRunSentinelFilePath(&first_run_sentinel))
    return false;
  return base::DeleteFile(first_run_sentinel);
}

// static
startup_metric_utils::FirstRunSentinelCreationResult FirstRun::CreateSentinel(
    base::File::Error* error) {
  base::FilePath first_run_sentinel;
  if (!GetFirstRunSentinelFilePath(&first_run_sentinel))
    return startup_metric_utils::FirstRunSentinelCreationResult::
        kFailedToGetPath;
  if (base::PathExists(first_run_sentinel))
    return startup_metric_utils::FirstRunSentinelCreationResult::
        kFilePathExists;
  GetSentinelInfoGlobal() = std::nullopt;
  bool success = base::WriteFile(first_run_sentinel, std::string_view());
  if (error)
    *error = base::File::GetLastFileError();

  if (success) {
    LoadSentinelInfo();
  }
  return success
             ? startup_metric_utils::FirstRunSentinelCreationResult::kSuccess
             : startup_metric_utils::FirstRunSentinelCreationResult::
                   kFileSystemError;
}

// static
void FirstRun::LoadSentinelInfo() {
  std::optional<base::File::Info>& global_sentinel_info =
      GetSentinelInfoGlobal();
  if (global_sentinel_info.has_value()) {
    return;
  }
  base::FilePath first_run_sentinel;
  base::File::Info sentinel_info;
  if (GetFirstRunSentinelFilePath(&first_run_sentinel) &&
      base::PathExists(first_run_sentinel) &&
      base::GetFileInfo(first_run_sentinel, &sentinel_info)) {
    global_sentinel_info = sentinel_info;
  }
}

// static
const char* FirstRun::GetPingDelayPrefName() {
  return kPingDelayPrefName;
}

// static
void FirstRun::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* registry) {
  registry->RegisterIntegerPref(GetPingDelayPrefName(), 0);
}

// static
void FirstRun::ClearStateForTesting() {
  GetSentinelInfoGlobal() = std::nullopt;
  first_run_ = FIRST_RUN_UNKNOWN;
}