chromium/chrome/browser/ash/sparky/storage/simple_size_calculator.cc

// Copyright 2024 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/sparky/storage/simple_size_calculator.h"

#include <cstdint>
#include <numeric>

#include "base/memory/scoped_refptr.h"
#include "base/system/sys_info.h"
#include "base/task/thread_pool.h"
#include "base/values.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/ash/components/dbus/spaced/spaced_client.h"
#include "components/user_manager/user_manager.h"

namespace ash {

namespace {

void GetTotalDiskSpaceBlocking(const base::FilePath& mount_path,
                               int64_t* total_bytes) {
  int64_t size = base::SysInfo::AmountOfTotalDiskSpace(mount_path);
  if (size >= 0) {
    *total_bytes = size;
  }
}

void GetFreeDiskSpaceBlocking(const base::FilePath& mount_path,
                              int64_t* available_bytes) {
  int64_t size = base::SysInfo::AmountOfFreeDiskSpace(mount_path);
  if (size >= 0) {
    *available_bytes = size;
  }
}

}  // namespace

SimpleSizeCalculator::SimpleSizeCalculator(
    const CalculationType& calculation_type)
    : calculation_type_(calculation_type) {}

SimpleSizeCalculator::~SimpleSizeCalculator() {}

void SimpleSizeCalculator::StartCalculation() {
  if (calculating_) {
    return;
  }
  calculating_ = true;
  PerformCalculation();
}

void SimpleSizeCalculator::AddObserver(
    SimpleSizeCalculator::Observer* observer) {
  observers_.AddObserver(observer);
}

void SimpleSizeCalculator::RemoveObserver(
    SimpleSizeCalculator::Observer* observer) {
  observers_.RemoveObserver(observer);
}

void SimpleSizeCalculator::NotifySizeCalculated(int64_t total_bytes) {
  calculating_ = false;
  for (SimpleSizeCalculator::Observer& observer : observers_) {
    observer.OnSizeCalculated(calculation_type_, total_bytes);
  }
}

TotalDiskSpaceCalculator::TotalDiskSpaceCalculator(Profile* profile)
    : SimpleSizeCalculator(CalculationType::kTotal), profile_(profile) {}

TotalDiskSpaceCalculator::~TotalDiskSpaceCalculator() = default;

void TotalDiskSpaceCalculator::PerformCalculation() {
  if (user_manager::UserManager::Get()
          ->IsCurrentUserCryptohomeDataEphemeral()) {
    GetTotalDiskSpace();
    return;
  }
  GetRootDeviceSize();
}

void TotalDiskSpaceCalculator::GetRootDeviceSize() {
  SpacedClient::Get()->GetRootDeviceSize(
      base::BindOnce(&TotalDiskSpaceCalculator::OnGetRootDeviceSize,
                     weak_ptr_factory_.GetWeakPtr()));
}

void TotalDiskSpaceCalculator::OnGetRootDeviceSize(
    std::optional<int64_t> reply) {
  if (reply.has_value()) {
    if (reply.value() < 0) {
      LOG(DFATAL) << "Negative root device size (" << reply.value() << ")";
    }
    NotifySizeCalculated(reply.value());
    return;
  }

  // FakeSpacedClient does not have a proper implementation of
  // GetRootDeviceSize. If SpacedClient::GetRootDeviceSize does not return a
  // value, use GetTotalDiskSpace as a fallback.
  VLOG(1) << "SpacedClient::OnGetRootDeviceSize: Empty reply. Using "
             "GetTotalDiskSpace as fallback.";
  GetTotalDiskSpace();
}

void TotalDiskSpaceCalculator::GetTotalDiskSpace() {
  const base::FilePath my_files_path =
      file_manager::util::GetMyFilesFolderForProfile(profile_);

  int64_t* total_bytes = new int64_t(-1);
  base::ThreadPool::PostTaskAndReply(
      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
      base::BindOnce(&GetTotalDiskSpaceBlocking, my_files_path, total_bytes),
      base::BindOnce(&TotalDiskSpaceCalculator::OnGetTotalDiskSpace,
                     weak_ptr_factory_.GetWeakPtr(), base::Owned(total_bytes)));
}

void TotalDiskSpaceCalculator::OnGetTotalDiskSpace(int64_t* total_bytes) {
  if (*total_bytes < 0) {
    LOG(DFATAL) << "Negative total disk space (" << *total_bytes << ")";
  }
  NotifySizeCalculated(*total_bytes);
}

FreeDiskSpaceCalculator::FreeDiskSpaceCalculator(Profile* profile)
    : SimpleSizeCalculator(CalculationType::kAvailable), profile_(profile) {}

FreeDiskSpaceCalculator::~FreeDiskSpaceCalculator() = default;

void FreeDiskSpaceCalculator::PerformCalculation() {
  if (user_manager::UserManager::Get()
          ->IsCurrentUserCryptohomeDataEphemeral()) {
    GetFreeDiskSpace();
    return;
  }
  GetUserFreeDiskSpace();
}

void FreeDiskSpaceCalculator::GetUserFreeDiskSpace() {
  const base::FilePath my_files_path =
      file_manager::util::GetMyFilesFolderForProfile(profile_);
  SpacedClient::Get()->GetFreeDiskSpace(
      my_files_path.value(),
      base::BindOnce(&FreeDiskSpaceCalculator::OnGetUserFreeDiskSpace,
                     weak_ptr_factory_.GetWeakPtr()));
}

void FreeDiskSpaceCalculator::OnGetUserFreeDiskSpace(
    std::optional<int64_t> reply) {
  if (reply.has_value()) {
    if (reply.value() < 0) {
      LOG(DFATAL) << "Negative user free disk space (" << reply.value() << ")";
    }
    NotifySizeCalculated(reply.value());
    return;
  }

  // FakeSpacedClient does not have a proper implementation of
  // GetFreeDiskSpace. If SpacedClient::GetFreeDiskSpace does not return a
  // value, use GetFreeDiskSpaceBlocking as a fallback.
  VLOG(1) << "SpacedClient::GetFreeDiskSpace: Empty reply. Using "
             "GetFreeDiskSpaceBlocking as fallback.";
  GetFreeDiskSpace();
}

void FreeDiskSpaceCalculator::GetFreeDiskSpace() {
  const base::FilePath my_files_path =
      file_manager::util::GetMyFilesFolderForProfile(profile_);

  int64_t* available_bytes = new int64_t(-1);
  base::ThreadPool::PostTaskAndReply(
      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
      base::BindOnce(&GetFreeDiskSpaceBlocking, my_files_path, available_bytes),
      base::BindOnce(&FreeDiskSpaceCalculator::OnGetFreeDiskSpace,
                     weak_ptr_factory_.GetWeakPtr(),
                     base::Owned(available_bytes)));
}

void FreeDiskSpaceCalculator::OnGetFreeDiskSpace(int64_t* available_bytes) {
  if (*available_bytes < 0) {
    LOG(DFATAL) << "Negative free disk space (" << *available_bytes << ")";
  }
  NotifySizeCalculated(*available_bytes);
}

}  // namespace ash