blob: 7c8d870bb91bc32385455e717daca43b4a8418e4 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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/settings/chromeos/calculator/size_calculator.h"
#include <numeric>
#include "base/system/sys_info.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/values.h"
#include "chrome/browser/browsing_data/browsing_data_file_system_util.h"
#include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h"
#include "chrome/browser/chromeos/crostini/crostini_features.h"
#include "chrome/browser/chromeos/file_manager/path_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/cryptohome/cryptohome_util.h"
#include "chromeos/dbus/cryptohome/cryptohome_client.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/session/arc_bridge_service.h"
#include "components/arc/storage_manager/arc_storage_manager.h"
#include "components/browsing_data/content/appcache_helper.h"
#include "components/browsing_data/content/cache_storage_helper.h"
#include "components/browsing_data/content/conditional_cache_counting_helper.h"
#include "components/browsing_data/content/cookie_helper.h"
#include "components/browsing_data/content/database_helper.h"
#include "components/browsing_data/content/file_system_helper.h"
#include "components/browsing_data/content/indexed_db_helper.h"
#include "components/browsing_data/content/local_storage_helper.h"
#include "components/browsing_data/content/service_worker_helper.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/storage_partition.h"
namespace chromeos {
namespace settings {
namespace calculator {
namespace {
void GetSizeStatBlocking(const base::FilePath& mount_path,
int64_t* total_size,
int64_t* available_size) {
int64_t size = base::SysInfo::AmountOfTotalDiskSpace(mount_path);
if (size >= 0)
*total_size = size;
size = base::SysInfo::AmountOfFreeDiskSpace(mount_path);
if (size >= 0)
*available_size = size;
}
} // namespace
SizeCalculator::SizeCalculator(const CalculationType& calculation_type)
: calculation_type_(calculation_type) {}
SizeCalculator::~SizeCalculator() {}
void SizeCalculator::StartCalculation() {
if (calculating_)
return;
calculating_ = true;
PerformCalculation();
}
void SizeCalculator::AddObserver(SizeCalculator::Observer* observer) {
observers_.AddObserver(observer);
}
void SizeCalculator::RemoveObserver(SizeCalculator::Observer* observer) {
observers_.RemoveObserver(observer);
}
void SizeCalculator::NotifySizeCalculated(
int64_t total_bytes,
const base::Optional<int64_t>& available_bytes) {
calculating_ = false;
for (SizeCalculator::Observer& observer : observers_) {
observer.OnSizeCalculated(calculation_type_, total_bytes, available_bytes);
}
}
SizeStatCalculator::SizeStatCalculator(Profile* profile)
: SizeCalculator(CalculationType::kInUse), profile_(profile) {}
SizeStatCalculator::~SizeStatCalculator() = default;
void SizeStatCalculator::PerformCalculation() {
const base::FilePath my_files_path =
file_manager::util::GetMyFilesFolderForProfile(profile_);
int64_t* total_size = new int64_t(0);
int64_t* available_size = new int64_t(0);
base::ThreadPool::PostTaskAndReply(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::BindOnce(&GetSizeStatBlocking, my_files_path, total_size,
available_size),
base::BindOnce(&SizeStatCalculator::OnGetSizeStat,
weak_ptr_factory_.GetWeakPtr(), base::Owned(total_size),
base::Owned(available_size)));
}
void SizeStatCalculator::OnGetSizeStat(int64_t* total_bytes,
int64_t* available_bytes) {
NotifySizeCalculated(*total_bytes, *available_bytes);
}
MyFilesSizeCalculator::MyFilesSizeCalculator(Profile* profile)
: SizeCalculator(CalculationType::kMyFiles), profile_(profile) {}
MyFilesSizeCalculator::~MyFilesSizeCalculator() = default;
void MyFilesSizeCalculator::PerformCalculation() {
const base::FilePath my_files_path =
file_manager::util::GetMyFilesFolderForProfile(profile_);
const base::FilePath android_files_path =
base::FilePath(file_manager::util::GetAndroidFilesPath());
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&MyFilesSizeCalculator::ComputeLocalFilesSize,
base::Unretained(this), my_files_path, android_files_path),
base::BindOnce(&MyFilesSizeCalculator::OnGetMyFilesSize,
weak_ptr_factory_.GetWeakPtr()));
}
int64_t MyFilesSizeCalculator::ComputeLocalFilesSize(
const base::FilePath& my_files_path,
const base::FilePath& android_files_path) {
int64_t size = 0;
// Compute directory size of My Files.
size += base::ComputeDirectorySize(my_files_path);
// Compute directory size of Play Files.
size += base::ComputeDirectorySize(android_files_path);
// Remove size of Download. If Android is enabled, the size of the Download
// folder is counted in both My Files and Play files. If Android is disabled,
// the Download folder doesn't exist and the returned size is 0.
const base::FilePath download_files_path =
android_files_path.AppendASCII("Download");
size -= base::ComputeDirectorySize(download_files_path);
return size;
}
void MyFilesSizeCalculator::OnGetMyFilesSize(int64_t total_bytes) {
NotifySizeCalculated(total_bytes);
}
BrowsingDataSizeCalculator::BrowsingDataSizeCalculator(Profile* profile)
: SizeCalculator(CalculationType::kBrowsingData), profile_(profile) {}
BrowsingDataSizeCalculator::~BrowsingDataSizeCalculator() = default;
void BrowsingDataSizeCalculator::PerformCalculation() {
has_browser_cache_size_ = false;
has_browser_site_data_size_ = false;
// Fetch the size of http cache in browsing data.
browsing_data::ConditionalCacheCountingHelper::Count(
content::BrowserContext::GetDefaultStoragePartition(profile_),
base::Time(), base::Time::Max(),
base::BindOnce(&BrowsingDataSizeCalculator::OnGetCacheSize,
weak_ptr_factory_.GetWeakPtr()));
// Fetch the size of site data in browsing data.
if (!site_data_size_collector_.get()) {
content::StoragePartition* storage_partition =
content::BrowserContext::GetDefaultStoragePartition(profile_);
site_data_size_collector_ = std::make_unique<SiteDataSizeCollector>(
storage_partition->GetPath(),
new browsing_data::CookieHelper(storage_partition,
base::NullCallback()),
new browsing_data::DatabaseHelper(profile_),
new browsing_data::LocalStorageHelper(profile_),
new browsing_data::AppCacheHelper(
storage_partition->GetAppCacheService()),
new browsing_data::IndexedDBHelper(storage_partition),
browsing_data::FileSystemHelper::Create(
storage_partition->GetFileSystemContext(),
browsing_data_file_system_util::GetAdditionalFileSystemTypes()),
new browsing_data::ServiceWorkerHelper(
storage_partition->GetServiceWorkerContext()),
new browsing_data::CacheStorageHelper(
storage_partition->GetCacheStorageContext()),
BrowsingDataFlashLSOHelper::Create(profile_));
}
site_data_size_collector_->Fetch(
base::BindOnce(&BrowsingDataSizeCalculator::OnGetBrowsingDataSize,
weak_ptr_factory_.GetWeakPtr(), /*is_site_data=*/true));
}
void BrowsingDataSizeCalculator::OnGetCacheSize(bool is_upper_limit,
int64_t size) {
DCHECK(!is_upper_limit);
OnGetBrowsingDataSize(/*is_site_data=*/false, size);
}
void BrowsingDataSizeCalculator::OnGetBrowsingDataSize(bool is_site_data,
int64_t size) {
if (is_site_data) {
has_browser_site_data_size_ = true;
browser_site_data_size_ = size;
} else {
has_browser_cache_size_ = true;
browser_cache_size_ = size;
}
if (has_browser_cache_size_ && has_browser_site_data_size_) {
int64_t browsing_data_size;
if (browser_cache_size_ >= 0 && browser_site_data_size_ >= 0) {
browsing_data_size = browser_site_data_size_ + browser_cache_size_;
} else {
browsing_data_size = -1;
}
calculating_ = false;
NotifySizeCalculated(browsing_data_size);
}
}
AppsSizeCalculator::AppsSizeCalculator(Profile* profile)
: SizeCalculator(CalculationType::kAppsExtensions), profile_(profile) {}
AppsSizeCalculator::~AppsSizeCalculator() {
arc::ArcServiceManager::Get()
->arc_bridge_service()
->storage_manager()
->RemoveObserver(this);
}
void AppsSizeCalculator::OnConnectionReady() {
is_android_running_ = true;
StartCalculation();
}
void AppsSizeCalculator::OnConnectionClosed() {
is_android_running_ = false;
}
void AppsSizeCalculator::AddObserver(SizeCalculator::Observer* observer) {
// Start observing arc mojo connection when the first observer is added, to
// allow the calculation of android apps.
if (!observers_.might_have_observers()) {
arc::ArcServiceManager::Get()
->arc_bridge_service()
->storage_manager()
->AddObserver(this);
}
observers_.AddObserver(observer);
}
void AppsSizeCalculator::RemoveObserver(SizeCalculator::Observer* observer) {
observers_.RemoveObserver(observer);
// Stop observing arc connection if all observers have been removed.
if (!observers_.might_have_observers()) {
arc::ArcServiceManager::Get()
->arc_bridge_service()
->storage_manager()
->RemoveObserver(this);
}
}
void AppsSizeCalculator::PerformCalculation() {
apps_extensions_size_ = 0;
has_apps_extensions_size_ = false;
android_apps_size_ = 0;
has_android_apps_size_ = false;
UpdateAppsSize();
UpdateAndroidAppsSize();
}
void AppsSizeCalculator::UpdateAppsSize() {
// Apps and extensions installed from the web store located in
// [user-hash]/Extensions.
const base::FilePath extensions_path =
profile_->GetPath().AppendASCII("Extensions");
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&base::ComputeDirectorySize, extensions_path),
base::BindOnce(&AppsSizeCalculator::OnGetAppsSize,
weak_ptr_factory_.GetWeakPtr()));
}
void AppsSizeCalculator::OnGetAppsSize(int64_t total_bytes) {
apps_extensions_size_ = total_bytes;
has_apps_extensions_size_ = true;
UpdateAppsAndExtensionsSize();
}
void AppsSizeCalculator::UpdateAndroidAppsSize() {
if (!is_android_running_) {
has_android_apps_size_ = true;
UpdateAppsAndExtensionsSize();
return;
}
bool success = false;
auto* arc_storage_manager =
arc::ArcStorageManager::GetForBrowserContext(profile_);
if (arc_storage_manager) {
success = arc_storage_manager->GetApplicationsSize(
base::BindOnce(&AppsSizeCalculator::OnGetAndroidAppsSize,
weak_ptr_factory_.GetWeakPtr()));
}
if (!success) {
has_android_apps_size_ = true;
UpdateAppsAndExtensionsSize();
}
}
void AppsSizeCalculator::OnGetAndroidAppsSize(
bool succeeded,
arc::mojom::ApplicationsSizePtr size) {
has_android_apps_size_ = true;
if (succeeded) {
android_apps_size_ = size->total_code_bytes + size->total_data_bytes +
size->total_cache_bytes;
}
UpdateAppsAndExtensionsSize();
}
void AppsSizeCalculator::UpdateAppsAndExtensionsSize() {
if (has_apps_extensions_size_ && has_android_apps_size_) {
calculating_ = false;
NotifySizeCalculated(apps_extensions_size_ + android_apps_size_);
}
}
CrostiniSizeCalculator::CrostiniSizeCalculator(Profile* profile)
: SizeCalculator(CalculationType::kCrostini), profile_(profile) {}
CrostiniSizeCalculator::~CrostiniSizeCalculator() = default;
void CrostiniSizeCalculator::PerformCalculation() {
if (!crostini::CrostiniFeatures::Get()->IsEnabled(profile_)) {
NotifySizeCalculated(0);
return;
}
crostini::CrostiniManager::GetForProfile(profile_)->ListVmDisks(
base::BindOnce(&CrostiniSizeCalculator::OnGetCrostiniSize,
weak_ptr_factory_.GetWeakPtr()));
}
void CrostiniSizeCalculator::OnGetCrostiniSize(crostini::CrostiniResult result,
int64_t total_bytes) {
calculating_ = false;
NotifySizeCalculated(total_bytes);
}
OtherUsersSizeCalculator::OtherUsersSizeCalculator()
: SizeCalculator(CalculationType::kOtherUsers) {}
OtherUsersSizeCalculator::~OtherUsersSizeCalculator() = default;
void OtherUsersSizeCalculator::PerformCalculation() {
other_users_.clear();
user_sizes_.clear();
const user_manager::UserList& users =
user_manager::UserManager::Get()->GetUsers();
for (auto* user : users) {
if (user->is_active())
continue;
other_users_.push_back(user);
CryptohomeClient::Get()->GetAccountDiskUsage(
cryptohome::CreateAccountIdentifierFromAccountId(user->GetAccountId()),
base::BindOnce(&OtherUsersSizeCalculator::OnGetOtherUserSize,
weak_ptr_factory_.GetWeakPtr()));
}
// We should show "0 B" if there is no other user.
if (other_users_.empty()) {
NotifySizeCalculated(0);
}
}
void OtherUsersSizeCalculator::OnGetOtherUserSize(
base::Optional<cryptohome::BaseReply> reply) {
user_sizes_.push_back(cryptohome::AccountDiskUsageReplyToUsageSize(reply));
if (user_sizes_.size() != other_users_.size())
return;
int64_t other_users_total_bytes;
// If all the requests succeed, shows the total bytes in the UI.
if (std::count(user_sizes_.begin(), user_sizes_.end(), -1) == 0) {
other_users_total_bytes =
std::accumulate(user_sizes_.begin(), user_sizes_.end(), 0LL);
} else {
other_users_total_bytes = -1;
}
NotifySizeCalculated(other_users_total_bytes);
}
} // namespace calculator
} // namespace settings
} // namespace chromeos