blob: 1015aa142aa76fdadf887576df4e27285c10d61b [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 "components/arc/enterprise/arc_data_snapshotd_manager.h"
#include <utility>
#include "ash/constants/ash_switches.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/i18n/time_formatting.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
#include "base/time/time.h"
#include "base/util/values/values_util.h"
#include "base/values.h"
#include "chromeos/cryptohome/cryptohome_parameters.h"
#include "chromeos/dbus/constants/dbus_switches.h"
#include "chromeos/dbus/upstart/upstart_client.h"
#include "components/arc/arc_prefs.h"
#include "components/arc/enterprise/arc_data_remove_requested_pref_handler.h"
#include "components/arc/enterprise/arc_data_snapshotd_bridge.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "ui/gl/gl_switches.h"
#include "ui/ozone/public/ozone_switches.h"
namespace arc {
namespace data_snapshotd {
namespace {
// SnapshotInfo related keys.
constexpr char kOsVersion[] = "os_version";
constexpr char kCreationDate[] = "creation_date";
constexpr char kVerified[] = "verified";
constexpr char kUpdated[] = "updated";
// Snapshot related keys.
constexpr char kPrevious[] = "previous";
constexpr char kLast[] = "last";
constexpr char kBlockedUiReboot[] = "blocked_ui_reboot";
constexpr char kStarted[] = "started";
// Snapshot muss automatically expire in 30 days if not updated.
constexpr base::TimeDelta kSnapshotMaxLifetime = base::TimeDelta::FromDays(30);
// Returns true if the Chrome session is restored after crash.
bool IsRestoredSession() {
auto* command_line = base::CommandLine::ForCurrentProcess();
return command_line->HasSwitch(chromeos::switches::kLoginUser) &&
!command_line->HasSwitch(chromeos::switches::kLoginManager);
}
// Returns true if it is the first Chrome start up after reboot.
bool IsFirstExecAfterBoot() {
return user_manager::UserManager::Get() &&
user_manager::UserManager::Get()->IsFirstExecAfterBoot();
}
// Returns true if in ozone platform headless UI mode.
bool IsInHeadlessMode() {
auto* command_line = base::CommandLine::ForCurrentProcess();
return command_line->GetSwitchValueASCII(switches::kOzonePlatform) ==
kHeadless;
}
// Enables ozone platform headless via command line.
void EnableHeadlessMode() {
auto* command_line = base::CommandLine::ForCurrentProcess();
command_line->AppendSwitchASCII(switches::kOzonePlatform, kHeadless);
command_line->AppendSwitchASCII(switches::kUseGL,
gl::kGLImplementationSwiftShaderName);
}
// Disables D-Bus clients:
// * BIOD
// * CrosDisks
// Should be called while in BlockedUi state.
void DisableDBusClients() {
auto* command_line = base::CommandLine::ForCurrentProcess();
// Disable BIOD input.
command_line->AppendSwitch(chromeos::switches::kBiodFake);
// Disable USB input.
command_line->AppendSwitch(chromeos::switches::kCrosDisksFake);
}
// Returns non-empty account ID string if a MGS is active.
// Otherwise returns an empty string.
std::string GetMgsCryptohomeAccountId() {
// Take snapshots only for MGSs.
if (user_manager::UserManager::Get() &&
user_manager::UserManager::Get()->IsLoggedInAsPublicAccount() &&
user_manager::UserManager::Get()->GetActiveUser()) {
return cryptohome::Identification(user_manager::UserManager::Get()
->GetActiveUser()
->GetAccountId())
.id();
}
return std::string();
}
} // namespace
const char kHeadless[] = "headless";
const char kRestartFreconEnv[] = "RESTART_FRECON=1";
bool ArcDataSnapshotdManager::is_snapshot_enabled_for_testing_ = false;
// This class is owned by ChromeBrowserMainPartsChromeos.
static ArcDataSnapshotdManager* g_arc_data_snapshotd_manager = nullptr;
ArcDataSnapshotdManager::SnapshotInfo::SnapshotInfo(bool last)
: is_last_(last) {
os_version_ = base::SysInfo::OperatingSystemVersion();
UpdateCreationDate(base::Time::Now());
}
ArcDataSnapshotdManager::SnapshotInfo::SnapshotInfo(const base::Value* value,
bool last)
: is_last_(last) {
const base::DictionaryValue* dict;
if (!value || !value->GetAsDictionary(&dict) || !dict)
return;
{
auto* found = dict->FindStringPath(kOsVersion);
if (found)
os_version_ = *found;
}
{
auto* found = dict->FindPath(kCreationDate);
if (found && util::ValueToTime(found).has_value()) {
auto parsed_time = util::ValueToTime(found).value();
UpdateCreationDate(parsed_time);
}
}
{
auto found = dict->FindBoolPath(kVerified);
if (found.has_value())
verified_ = found.value();
}
{
auto found = dict->FindBoolPath(kUpdated);
if (found.has_value())
updated_ = found.value();
}
}
ArcDataSnapshotdManager::SnapshotInfo::~SnapshotInfo() = default;
// static
std::unique_ptr<ArcDataSnapshotdManager::SnapshotInfo>
ArcDataSnapshotdManager::SnapshotInfo::CreateForTesting(
const std::string& os_version,
const base::Time& creation_date,
bool verified,
bool updated,
bool last) {
return base::WrapUnique(new ArcDataSnapshotdManager::SnapshotInfo(
os_version, creation_date, verified, updated, last));
}
void ArcDataSnapshotdManager::SnapshotInfo::Sync(base::Value* dict) {
if (!dict)
return;
base::DictionaryValue value;
value.SetStringKey(kOsVersion, os_version_);
value.SetKey(kCreationDate, util::TimeToValue(creation_date_));
value.SetBoolKey(kVerified, verified_);
value.SetBoolKey(kUpdated, updated_);
dict->SetKey(GetDictPath(), std::move(value));
}
bool ArcDataSnapshotdManager::SnapshotInfo::IsExpired() const {
if (creation_date_ + kSnapshotMaxLifetime <= base::Time::Now()) {
VLOG(1) << GetDictPath() << " snapshot is expired. creation_date="
<< base::UTF16ToUTF8(
base::TimeFormatShortDateAndTime(creation_date_));
return true;
}
return false;
}
bool ArcDataSnapshotdManager::SnapshotInfo::IsOsVersionUpdated() const {
return os_version_ != base::SysInfo::OperatingSystemVersion();
}
ArcDataSnapshotdManager::SnapshotInfo::SnapshotInfo(
const std::string& os_version,
const base::Time& creation_date,
bool verified,
bool updated,
bool last)
: is_last_(last),
os_version_(os_version),
verified_(verified),
updated_(updated) {
UpdateCreationDate(creation_date);
}
std::string ArcDataSnapshotdManager::SnapshotInfo::GetDictPath() const {
return is_last_ ? kLast : kPrevious;
}
void ArcDataSnapshotdManager::SnapshotInfo::UpdateCreationDate(
const base::Time& creation_date) {
creation_date_ = creation_date;
if (lifetime_timer_.IsRunning()) {
LOG(ERROR) << "Updating a snapshot lifetime timer.";
lifetime_timer_.Stop();
}
// If the snapshot is expired on initialization, it is expected to be cleared
// soon in the flow.
if (IsExpired())
return;
base::TimeDelta delay =
creation_date_ + kSnapshotMaxLifetime - base::Time::Now();
lifetime_timer_.Start(
FROM_HERE, delay,
base::BindOnce(&ArcDataSnapshotdManager::SnapshotInfo::OnSnapshotExpired,
weak_ptr_factory_.GetWeakPtr()));
}
void ArcDataSnapshotdManager::SnapshotInfo::OnSnapshotExpired() {
DCHECK(ArcDataSnapshotdManager::Get());
ArcDataSnapshotdManager::Get()->OnSnapshotExpired();
}
ArcDataSnapshotdManager::Snapshot::Snapshot(PrefService* local_state)
: local_state_(local_state) {
DCHECK(local_state_);
}
ArcDataSnapshotdManager::Snapshot::~Snapshot() = default;
// static
std::unique_ptr<ArcDataSnapshotdManager::Snapshot>
ArcDataSnapshotdManager::Snapshot::CreateForTesting(
PrefService* local_state,
bool blocked_ui_mode,
bool started,
std::unique_ptr<SnapshotInfo> last,
std::unique_ptr<SnapshotInfo> previous) {
return base::WrapUnique(new ArcDataSnapshotdManager::Snapshot(
local_state, blocked_ui_mode, started, std::move(last),
std::move(previous)));
}
void ArcDataSnapshotdManager::Snapshot::Parse() {
const base::DictionaryValue* dict =
local_state_->GetDictionary(arc::prefs::kArcSnapshotInfo);
if (!dict)
return;
{
const auto* found = dict->FindDictPath(kPrevious);
if (found)
previous_ = std::make_unique<SnapshotInfo>(found, false);
}
{
const auto* found = dict->FindDictPath(kLast);
if (found)
last_ = std::make_unique<SnapshotInfo>(found, true);
}
{
auto found = dict->FindBoolPath(kBlockedUiReboot);
if (found.has_value())
blocked_ui_mode_ = found.value();
}
{
auto found = dict->FindBoolPath(kStarted);
if (found.has_value())
started_ = found.value();
}
}
void ArcDataSnapshotdManager::Snapshot::Sync() {
base::DictionaryValue dict;
if (previous_)
previous_->Sync(&dict);
if (last_)
last_->Sync(&dict);
dict.SetBoolKey(kBlockedUiReboot, blocked_ui_mode_);
dict.SetBoolKey(kStarted, started_);
local_state_->Set(arc::prefs::kArcSnapshotInfo, std::move(dict));
}
void ArcDataSnapshotdManager::Snapshot::ClearSnapshot(bool last) {
std::unique_ptr<SnapshotInfo>* snapshot = (last ? &last_ : &previous_);
snapshot->reset();
Sync();
}
void ArcDataSnapshotdManager::Snapshot::StartNewSnapshot() {
previous_ = std::move(last_);
last_ = nullptr;
started_ = true;
Sync();
}
void ArcDataSnapshotdManager::Snapshot::OnSnapshotTaken() {
if (last_) {
LOG(WARNING) << "Last snapshot exists";
last_.reset();
}
last_ = std::make_unique<SnapshotInfo>(true /* last */);
// Clear snapshot started pref to highlight that the snapshot creation process
// is over.
started_ = false;
}
ArcDataSnapshotdManager::SnapshotInfo*
ArcDataSnapshotdManager::Snapshot::GetCurrentSnapshot() {
if (last_)
return last_.get();
DCHECK(previous_);
return previous_.get();
}
ArcDataSnapshotdManager::Snapshot::Snapshot(
PrefService* local_state,
bool blocked_ui_mode,
bool started,
std::unique_ptr<SnapshotInfo> last,
std::unique_ptr<SnapshotInfo> previous)
: local_state_(local_state),
blocked_ui_mode_(blocked_ui_mode),
started_(started),
last_(std::move(last)),
previous_(std::move(previous)) {
DCHECK(local_state_);
}
ArcDataSnapshotdManager::ArcDataSnapshotdManager(
PrefService* local_state,
std::unique_ptr<Delegate> delegate,
base::OnceClosure attempt_user_exit_callback)
: policy_service_{local_state},
snapshot_{local_state},
delegate_(std::move(delegate)),
attempt_user_exit_callback_(std::move(attempt_user_exit_callback)) {
DCHECK(!g_arc_data_snapshotd_manager);
DCHECK(local_state);
DCHECK(delegate_);
g_arc_data_snapshotd_manager = this;
snapshot_.Parse();
policy_service_.AddObserver(this);
if (IsRestoredSession()) {
state_ = State::kRestored;
DoClearSnapshots();
return;
}
if (local_state->GetAllPrefStoresInitializationStatus() !=
PrefService::INITIALIZATION_STATUS_SUCCESS) {
local_state->AddPrefInitObserver(
base::BindOnce(&ArcDataSnapshotdManager::OnLocalStateInitialized,
weak_ptr_factory_.GetWeakPtr()));
} else {
// Ensure the snapshot's info is up-to-date.
OnLocalStateInitialized(true /* initialized */);
}
}
ArcDataSnapshotdManager::~ArcDataSnapshotdManager() {
DCHECK(g_arc_data_snapshotd_manager);
g_arc_data_snapshotd_manager = nullptr;
if (session_controller_)
session_controller_->RemoveObserver(this);
policy_service_.RemoveObserver(this);
snapshot_.Sync();
EnsureDaemonStopped(base::DoNothing());
}
// static
ArcDataSnapshotdManager* ArcDataSnapshotdManager::Get() {
return g_arc_data_snapshotd_manager;
}
// static
base::TimeDelta ArcDataSnapshotdManager::snapshot_max_lifetime_for_testing() {
return kSnapshotMaxLifetime;
}
void ArcDataSnapshotdManager::EnsureDaemonStarted(base::OnceClosure callback) {
if (bridge_) {
std::move(callback).Run();
return;
}
VLOG(1) << "Starting arc-data-snapshotd";
daemon_weak_ptr_factory_.InvalidateWeakPtrs();
chromeos::UpstartClient::Get()->StartArcDataSnapshotd(
GetStartEnvVars(),
base::BindOnce(&ArcDataSnapshotdManager::OnDaemonStarted,
daemon_weak_ptr_factory_.GetWeakPtr(),
std::move(callback)));
}
void ArcDataSnapshotdManager::EnsureDaemonStopped(base::OnceClosure callback) {
if (!bridge_) {
std::move(callback).Run();
return;
}
StopDaemon(std::move(callback));
}
void ArcDataSnapshotdManager::StartLoadingSnapshot(base::OnceClosure callback) {
// Do not load a snapshot if it's not a normal MGS setup.
if (state_ != State::kNone) {
std::move(callback).Run();
return;
}
std::string account_id = GetMgsCryptohomeAccountId();
if (!account_id.empty() && IsSnapshotEnabled() &&
(snapshot_.last() || snapshot_.previous())) {
state_ = State::kLoading;
EnsureDaemonStarted(base::BindOnce(
&ArcDataSnapshotdManager::LoadSnapshot, weak_ptr_factory_.GetWeakPtr(),
std::move(account_id), std::move(callback)));
return;
}
std::move(callback).Run();
}
bool ArcDataSnapshotdManager::IsAutoLoginConfigured() {
switch (state_) {
case State::kBlockedUi:
case State::kMgsToLaunch:
case State::kMgsLaunched:
return true;
case State::kLoading:
case State::kNone:
case State::kRestored:
case State::kRunning:
case State::kStopping:
return false;
}
}
bool ArcDataSnapshotdManager::IsAutoLoginAllowed() {
switch (state_) {
case State::kBlockedUi:
case State::kLoading:
case State::kStopping:
return false;
case State::kNone:
case State::kRestored:
case State::kRunning:
case State::kMgsLaunched:
case State::kMgsToLaunch:
return true;
}
}
bool ArcDataSnapshotdManager::IsBlockedUiScreenShown() {
return IsAutoLoginConfigured() && IsInHeadlessMode();
}
bool ArcDataSnapshotdManager::IsSnapshotInProgress() {
return state_ == State::kMgsLaunched;
}
void ArcDataSnapshotdManager::OnSnapshotSessionStarted() {
if (state_ != State::kMgsToLaunch)
return;
state_ = State::kMgsLaunched;
}
void ArcDataSnapshotdManager::OnSnapshotSessionStopped() {
if (state_ != State::kRunning)
NOTREACHED();
state_ = State::kNone;
snapshot_.GetCurrentSnapshot()->set_verified(true);
snapshot_.Sync();
session_controller_->RemoveObserver(this);
session_controller_.reset();
}
void ArcDataSnapshotdManager::OnSnapshotSessionFailed() {
session_controller_->RemoveObserver(this);
session_controller_.reset();
switch (state_) {
case State::kMgsLaunched:
state_ = State::kNone;
OnSnapshotTaken(false /* success */);
break;
case State::kRunning:
state_ = State::kNone;
snapshot_.ClearSnapshot(snapshot_.GetCurrentSnapshot()->is_last());
snapshot_.Sync();
DCHECK(!attempt_user_exit_callback_.is_null());
EnsureDaemonStopped(std::move(attempt_user_exit_callback_));
break;
case State::kBlockedUi:
case State::kLoading:
case State::kMgsToLaunch:
case State::kNone:
case State::kRestored:
case State::kStopping:
NOTREACHED();
}
}
void ArcDataSnapshotdManager::OnSnapshotAppInstalled(int percent) {
if (state_ != State::kMgsLaunched)
return;
Update(percent);
}
void ArcDataSnapshotdManager::OnSnapshotSessionPolicyCompliant() {
if (state_ != State::kMgsLaunched)
return;
// Stop tracking apps, since ARC is compliant with policy.
// That means that 100% of required apps got installed and ARC is fully
// prepared to be snapshotted.
// If the policy changes or an app gets uninstalled, the compliance with the
// required apps list will be fixed automatically on the next session
// startup.
session_controller_->RemoveObserver(this);
session_controller_.reset();
delegate_->RequestStopArcInstance(
base::BindOnce(&ArcDataSnapshotdManager::OnArcInstanceStopped,
weak_ptr_factory_.GetWeakPtr()));
}
void ArcDataSnapshotdManager::OnSnapshotsDisabled() {
// Stop all ongoing flows.
daemon_weak_ptr_factory_.InvalidateWeakPtrs();
weak_ptr_factory_.InvalidateWeakPtrs();
switch (state_) {
// If in process of taking or loading a snapshot, stop and restart browser.
case State::kBlockedUi:
case State::kLoading:
case State::kMgsLaunched:
case State::kMgsToLaunch:
state_ = State::kStopping;
snapshot_.set_blocked_ui_mode(false);
if (session_controller_)
session_controller_->RemoveObserver(this);
session_controller_.reset();
reboot_controller_.reset();
break;
// Otherwise, stop all flows, clear snapshots and do not restart browser.
case State::kNone:
case State::kRestored:
case State::kStopping:
case State::kRunning:
break;
}
DoClearSnapshots();
}
void ArcDataSnapshotdManager::OnSnapshotUpdateEndTimeChanged() {
if (policy_service_.snapshot_update_end_time().is_null()) {
// Process the end of the snapshot update interval.
if (reboot_controller_) {
// Stop the reboot process if already requested.
snapshot_.set_blocked_ui_mode(false);
snapshot_.Sync();
}
reboot_controller_.reset();
return;
}
if (!IsSnapshotEnabled())
return;
// Snapshot can be updated if necessary. Inside the snapshot update interval.
// Do not request the reboot of device if already requested.
if (reboot_controller_)
return;
// Do not reboot if last and previous snapshots exist and should not be
// updated.
if (snapshot_.last() && !snapshot_.last()->updated() &&
snapshot_.previous() && !snapshot_.previous()->updated()) {
return;
}
switch (state_) {
case State::kNone:
case State::kLoading:
case State::kRestored:
case State::kRunning:
snapshot_.set_blocked_ui_mode(true);
snapshot_.Sync();
// Request device to be reboot in a blocked UI mode.
reboot_controller_ = std::make_unique<SnapshotRebootController>(
delegate_->CreateRebootNotification());
return;
case State::kBlockedUi:
case State::kMgsToLaunch:
case State::kMgsLaunched:
case State::kStopping:
// Do not reboot the device if in blocked UI mode or in process of
// disabling the feature.
return;
}
}
bool ArcDataSnapshotdManager::IsSnapshotEnabled() {
if (ArcDataSnapshotdManager::is_snapshot_enabled_for_testing())
return true;
return policy_service_.is_snapshot_enabled();
}
void ArcDataSnapshotdManager::OnLocalStateInitialized(bool initialized) {
if (!initialized)
LOG(ERROR) << "Local State intiialization failed.";
if (snapshot_.is_blocked_ui_mode() && IsFirstExecAfterBoot() &&
IsSnapshotEnabled()) {
if (!IsInHeadlessMode()) {
EnableHeadlessMode();
DisableDBusClients();
delegate_->RestartChrome(*base::CommandLine::ForCurrentProcess());
return;
}
state_ = State::kBlockedUi;
}
DoClearSnapshots();
}
void ArcDataSnapshotdManager::StopDaemon(base::OnceClosure callback) {
VLOG(1) << "Stopping arc-data-snapshotd";
daemon_weak_ptr_factory_.InvalidateWeakPtrs();
chromeos::UpstartClient::Get()->StopArcDataSnapshotd(base::BindOnce(
&ArcDataSnapshotdManager::OnDaemonStopped,
daemon_weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ArcDataSnapshotdManager::DoClearSnapshots() {
DoClearSnapshot(
snapshot_.previous(),
base::BindOnce(
&ArcDataSnapshotdManager::DoClearSnapshot,
weak_ptr_factory_.GetWeakPtr(), snapshot_.last(),
base::BindOnce(&ArcDataSnapshotdManager::OnSnapshotsCleared,
weak_ptr_factory_.GetWeakPtr())),
true /* success */);
}
void ArcDataSnapshotdManager::DoClearSnapshot(
SnapshotInfo* snapshot,
base::OnceCallback<void(bool)> callback,
bool success) {
if (!success)
LOG(ERROR) << "Failed to clear snapshot";
if (snapshot && (!IsSnapshotEnabled() || snapshot->IsExpired() ||
snapshot->IsOsVersionUpdated())) {
EnsureDaemonStarted(base::BindOnce(
&ArcDataSnapshotdManager::ClearSnapshot, weak_ptr_factory_.GetWeakPtr(),
snapshot->is_last(), std::move(callback)));
snapshot_.ClearSnapshot(snapshot->is_last());
} else {
std::move(callback).Run(success);
}
}
void ArcDataSnapshotdManager::GenerateKeyPair() {
if (!bridge_) {
OnKeyPairGenerated(false /* success */);
return;
}
bridge_->GenerateKeyPair(
base::BindOnce(&ArcDataSnapshotdManager::OnKeyPairGenerated,
weak_ptr_factory_.GetWeakPtr()));
}
void ArcDataSnapshotdManager::ClearSnapshot(
bool last,
base::OnceCallback<void(bool)> callback) {
if (!bridge_) {
std::move(callback).Run(false /* success */);
return;
}
bridge_->ClearSnapshot(last, std::move(callback));
}
void ArcDataSnapshotdManager::TakeSnapshot(const std::string& account_id) {
if (!bridge_) {
OnSnapshotTaken(false /* success */);
return;
}
bridge_->TakeSnapshot(
account_id, base::BindOnce(&ArcDataSnapshotdManager::OnSnapshotTaken,
weak_ptr_factory_.GetWeakPtr()));
}
void ArcDataSnapshotdManager::LoadSnapshot(const std::string& account_id,
base::OnceClosure callback) {
if (!bridge_) {
OnSnapshotLoaded(std::move(callback), false /* success */,
false /* last */);
return;
}
bridge_->LoadSnapshot(
account_id,
base::BindOnce(&ArcDataSnapshotdManager::OnSnapshotLoaded,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ArcDataSnapshotdManager::UpdateUi(int percent) {
if (!bridge_) {
OnUiUpdated(false /* success */);
return;
}
bridge_->Update(percent, base::BindOnce(&ArcDataSnapshotdManager::OnUiUpdated,
weak_ptr_factory_.GetWeakPtr()));
}
void ArcDataSnapshotdManager::OnSnapshotExpired() {
switch (state_) {
case State::kBlockedUi:
case State::kMgsToLaunch:
case State::kMgsLaunched:
case State::kStopping:
LOG(WARNING) << "Expired snapshots are cleared in scope of this process.";
return;
case State::kLoading:
// The expired snapshot may be in the process of being loaded to the
// running MGS. Postpone the removal until the chrome session restart.
LOG(WARNING)
<< "The snapshot is expired while might be in use. Postpone exire.";
return;
case State::kNone:
case State::kRestored:
case State::kRunning:
DoClearSnapshots();
return;
}
}
void ArcDataSnapshotdManager::OnSnapshotsCleared(bool success) {
switch (state_) {
case State::kBlockedUi:
EnsureDaemonStarted(
base::BindOnce(&ArcDataSnapshotdManager::GenerateKeyPair,
weak_ptr_factory_.GetWeakPtr()));
return;
case State::kNone:
case State::kRestored:
case State::kRunning:
StopDaemon(base::DoNothing());
return;
case State::kStopping:
StopDaemon(std::move(attempt_user_exit_callback_));
return;
case State::kLoading:
case State::kMgsToLaunch:
case State::kMgsLaunched:
LOG(WARNING) << "Snapshots are cleared while in incorrect state";
NOTREACHED();
return;
}
}
void ArcDataSnapshotdManager::OnKeyPairGenerated(bool success) {
if (success) {
VLOG(1) << "Managed Guest Session is ready to be started with blocked UI.";
state_ = State::kMgsToLaunch;
session_controller_ =
SnapshotSessionController::Create(delegate_->CreateAppsTracker());
session_controller_->AddObserver(this);
bridge_->ConnectToUiCancelledSignal(base::BindRepeating(
&ArcDataSnapshotdManager::OnUiClosed, weak_ptr_factory_.GetWeakPtr()));
// Move last to previous snapshot:
snapshot_.StartNewSnapshot();
snapshot_.Sync();
if (!reset_autologin_callback_.is_null())
std::move(reset_autologin_callback_).Run();
} else {
LOG(ERROR) << "Key pair generation failed. Abort snapshot creation.";
snapshot_.set_blocked_ui_mode(false);
snapshot_.Sync();
DCHECK(!attempt_user_exit_callback_.is_null());
EnsureDaemonStopped(std::move(attempt_user_exit_callback_));
}
}
void ArcDataSnapshotdManager::OnDaemonStarted(base::OnceClosure callback,
bool success) {
if (!success) {
DLOG(ERROR) << "Failed to start arc-data-snapshotd, it might be already "
<< "running";
} else {
VLOG(1) << "arc-data-snapshotd started";
}
// The bridge has to be created regardless of a |success| value. When
// arc-data-snapshotd is already running, it responds with an error on
// attempt to start it.
if (!bridge_) {
bridge_ = std::make_unique<ArcDataSnapshotdBridge>(std::move(callback));
DCHECK(bridge_);
} else {
std::move(callback).Run();
}
}
void ArcDataSnapshotdManager::OnDaemonStopped(base::OnceClosure callback,
bool success) {
if (!success) {
DLOG(ERROR) << "Failed to stop arc-data-snapshotd, it might be already "
<< "stopped";
} else {
VLOG(1) << "arc-data-snapshotd stopped";
}
bridge_.reset();
std::move(callback).Run();
}
void ArcDataSnapshotdManager::Update(int percent) {
DCHECK_EQ(state_, State::kMgsLaunched);
EnsureDaemonStarted(base::BindOnce(&ArcDataSnapshotdManager::UpdateUi,
weak_ptr_factory_.GetWeakPtr(), percent));
}
void ArcDataSnapshotdManager::OnArcInstanceStopped(bool success) {
if (!success) {
LOG(ERROR) << "Failed to stop ARC instance.";
OnSnapshotTaken(false /* success */);
return;
}
std::string account_id = GetMgsCryptohomeAccountId();
if (account_id.empty() || state_ != State::kMgsLaunched) {
LOG(ERROR) << "Cryptohome account ID is empty.";
OnSnapshotTaken(false /* success */);
return;
}
data_remove_requested_handler_ = ArcDataRemoveRequestedPrefHandler::Create(
delegate_->GetProfilePrefService(),
base::BindOnce(
&ArcDataSnapshotdManager::OnUnexpectedArcDataRemoveRequested,
weak_ptr_factory_.GetWeakPtr()));
// OnUnexpectedArcDataRemoveRequested is called already if ARC data removal
// is in the process (data_remove_requested_handler_ is nullptr).
if (!data_remove_requested_handler_)
return;
// Take a snapshot only if ARC data removal is not requested yet.
EnsureDaemonStarted(base::BindOnce(&ArcDataSnapshotdManager::TakeSnapshot,
weak_ptr_factory_.GetWeakPtr(),
std::move(account_id)));
}
void ArcDataSnapshotdManager::OnSnapshotTaken(bool success) {
// Stop handling ARC data remove requests.
data_remove_requested_handler_.reset();
if (success)
snapshot_.OnSnapshotTaken();
else
LOG(ERROR) << "Failed to take ARC data directory snapshot.";
snapshot_.set_blocked_ui_mode(false);
snapshot_.Sync();
DCHECK(!attempt_user_exit_callback_.is_null());
EnsureDaemonStopped(std::move(attempt_user_exit_callback_));
}
void ArcDataSnapshotdManager::OnSnapshotLoaded(base::OnceClosure callback,
bool success,
bool last) {
if (!success) {
LOG(ERROR) << "Failed to load ARC data directory snapshot.";
state_ = State::kNone;
std::move(callback).Run();
return;
}
VLOG(1) << "Successfully loaded " << (last ? "last" : "previous")
<< " snapshot";
state_ = State::kRunning;
// Clear last snapshot if the previous one was loaded.
if (!last && snapshot_.last()) {
snapshot_.ClearSnapshot(true /* last */);
snapshot_.Sync();
}
EnsureDaemonStopped(base::DoNothing());
session_controller_ =
SnapshotSessionController::Create(delegate_->CreateAppsTracker());
session_controller_->AddObserver(this);
std::move(callback).Run();
}
void ArcDataSnapshotdManager::OnUnexpectedArcDataRemoveRequested() {
// Stop TakeSnapshot flow.
weak_ptr_factory_.InvalidateWeakPtrs();
OnSnapshotTaken(false /* success */);
}
void ArcDataSnapshotdManager::OnUiUpdated(bool success) {
if (!success)
LOG(ERROR) << "Failed to update UI progress bar.";
}
void ArcDataSnapshotdManager::OnUiClosed() {
// Stop all ongoing flows.
daemon_weak_ptr_factory_.InvalidateWeakPtrs();
weak_ptr_factory_.InvalidateWeakPtrs();
switch (state_) {
// If in process of taking a snapshot, stop and restart browser.
case State::kBlockedUi:
case State::kMgsLaunched:
case State::kMgsToLaunch:
state_ = State::kStopping;
snapshot_.set_blocked_ui_mode(false);
if (session_controller_)
session_controller_->RemoveObserver(this);
session_controller_.reset();
reboot_controller_.reset();
break;
case State::kNone:
case State::kLoading:
case State::kRestored:
case State::kStopping:
case State::kRunning:
LOG(ERROR) << "Received a signal from UI when not in blocked UI mode.";
break;
}
StopDaemon(std::move(attempt_user_exit_callback_));
}
std::vector<std::string> ArcDataSnapshotdManager::GetStartEnvVars() {
if (ArcDataSnapshotdManager::IsBlockedUiScreenShown())
return {kRestartFreconEnv};
else
return {};
}
} // namespace data_snapshotd
} // namespace arc