blob: 82dda9f0b0a8fb596f6fc0bfde6709883a18d817 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/disks/suspend_unmount_manager.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/time/time.h"
#include "chromeos/ash/components/disks/disk.h"
#include "chromeos/ash/components/disks/disk_mount_manager.h"
namespace ash::disks {
namespace {
using base::TimeDelta;
using chromeos::PowerManagerClient;
} // namespace
SuspendUnmountManager::SuspendUnmountManager(
DiskMountManager* disk_mount_manager)
: disk_mount_manager_(disk_mount_manager) {
PowerManagerClient::Get()->AddObserver(this);
}
SuspendUnmountManager::~SuspendUnmountManager() {
PowerManagerClient::Get()->RemoveObserver(this);
if (block_suspend_token_) {
PowerManagerClient::Get()->UnblockSuspend(block_suspend_token_);
VLOG(1) << "Unblocked the suspension";
}
}
void SuspendUnmountManager::SuspendImminent(
const power_manager::SuspendImminent::Reason reason) {
VLOG(1) << "SuspendImminent("
<< power_manager::SuspendImminent::Reason_Name(reason) << ")";
// Start unmounting the removable devices.
for (const std::unique_ptr<Disk>& disk : disk_mount_manager_->disks()) {
const DeviceType t = disk->device_type();
if (t != DeviceType::kUSB && t != DeviceType::kSD) {
continue;
}
const std::string& path = disk->mount_path();
if (path.empty()) {
continue;
}
if (unmounting_paths_.insert(path).second) {
VLOG(1) << "Unmounting '" << path << "'";
disk_mount_manager_->UnmountPath(
path, base::BindOnce(&SuspendUnmountManager::OnUnmountComplete,
weak_ptr_factory_.GetWeakPtr(), path));
} else {
VLOG(1) << "Already unmounting '" << path << "'";
}
}
if (unmounting_paths_.empty()) {
VLOG(1) << "No removable device to unmount before going to sleep";
return;
}
VLOG(1) << "Unmounting " << unmounting_paths_.size()
<< " removable drives before going to sleep";
if (block_suspend_token_.is_empty()) {
block_suspend_token_ = base::UnguessableToken::Create();
block_suspend_time_ = base::TimeTicks::Now();
PowerManagerClient::Get()->BlockSuspend(block_suspend_token_,
"SuspendUnmountManager");
VLOG(1) << "Delaying the suspension";
}
}
void SuspendUnmountManager::SuspendDone(const TimeDelta sleep_duration) {
VLOG(1) << "SuspendDone(" << sleep_duration << ")";
// SuspendDone can be called before OnUnmountComplete when suspend is
// cancelled, or it takes a long time to unmount volumes.
if (block_suspend_token_) {
PowerManagerClient::Get()->UnblockSuspend(block_suspend_token_);
block_suspend_token_ = {};
VLOG(1) << "Unblocked the suspension";
}
if (unmounting_paths_.empty()) {
VLOG(1) << "Remounting all the removable drives";
disk_mount_manager_->EnsureMountInfoRefreshed(base::DoNothing(),
true /* force */);
} else {
LOG(WARNING) << "There are still " << unmounting_paths_.size()
<< " removable drives waiting to be unmounted when the system"
" is waking up after sleeping for "
<< sleep_duration;
base::UmaHistogramSparse("CrosDisks.StillUnmountingWhenWakingUp",
unmounting_paths_.size());
}
}
void SuspendUnmountManager::OnUnmountComplete(const std::string& mount_path,
const MountError error_code) {
VLOG(1) << "Unmounted '" << mount_path << "': " << error_code;
const bool tracked = unmounting_paths_.erase(mount_path);
DCHECK(tracked) << " Mount point '" << mount_path << "' is not tracked";
if (!unmounting_paths_.empty()) {
VLOG(1) << "Still waiting for " << unmounting_paths_.size()
<< " removable drives to be unmounted";
return;
}
const TimeDelta block_time = base::TimeTicks::Now() - block_suspend_time_;
VLOG(1) << "Unmounted all the removable drives in " << block_time;
if (block_suspend_token_) {
PowerManagerClient::Get()->UnblockSuspend(block_suspend_token_);
block_suspend_token_ = {};
VLOG(1) << "Unblocked the suspension after " << block_time;
base::UmaHistogramMediumTimes("CrosDisks.Time.BlockSuspend", block_time);
} else {
VLOG(1) << "Remounting all the removable drives";
disk_mount_manager_->EnsureMountInfoRefreshed(base::DoNothing(),
true /* force */);
}
}
} // namespace ash::disks