blob: 0c2fdc36d0349300dc43c45e9fab379ff822bd53 [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/upgrade_detector/installed_version_updater_chromeos.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "chrome/browser/upgrade_detector/build_state.h"
#include "chromeos/dbus/dbus_thread_manager.h"
namespace {
// The reason of the rollback used in the UpgradeDetector.RollbackReason
// histogram.
enum class RollbackReason {
kToMoreStableChannel = 0,
kEnterpriseRollback = 1,
kMaxValue = kEnterpriseRollback,
};
} // namespace
InstalledVersionUpdater::InstalledVersionUpdater(BuildState* build_state)
: build_state_(build_state) {
chromeos::DBusThreadManager::Get()->GetUpdateEngineClient()->AddObserver(
this);
}
InstalledVersionUpdater::~InstalledVersionUpdater() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
chromeos::DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(
this);
}
void InstalledVersionUpdater::UpdateStatusChanged(
const update_engine::StatusResult& status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (status.current_operation() !=
update_engine::Operation::UPDATED_NEED_REBOOT) {
return;
}
// Cancel a potential channel request from a previous status update.
weak_ptr_factory_.InvalidateWeakPtrs();
current_channel_.reset();
target_channel_.reset();
if (status.is_enterprise_rollback()) {
// Powerwash will be required. Determine what kind of notification to show
// based on the current and target channels.
// StatusResult::new_version is the new Chrome OS system version number.
new_version_ = status.new_version();
auto* client = chromeos::DBusThreadManager::Get()->GetUpdateEngineClient();
client->GetChannel(/*get_current_channel=*/false,
base::BindOnce(&InstalledVersionUpdater::OnChannel,
weak_ptr_factory_.GetWeakPtr(), false));
client->GetChannel(/*get_current_channel=*/true,
base::BindOnce(&InstalledVersionUpdater::OnChannel,
weak_ptr_factory_.GetWeakPtr(), true));
} else {
// Not going to an earlier version; no powerwash or rollback message is
// required.
new_version_.reset();
build_state_->SetUpdate(BuildState::UpdateType::kNormalUpdate,
base::Version(status.new_version()), base::nullopt);
}
}
void InstalledVersionUpdater::OnChannel(bool is_current_channel,
const std::string& channel_name) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Preserve this channel name.
(is_current_channel ? current_channel_ : target_channel_) = channel_name;
// Nothing else to do if still waiting on the other value.
if (!current_channel_.has_value() || !target_channel_.has_value())
return;
// Set the UpdateType based on whether or not the device is moving to a more
// stable channel.
// TODO(crbug.com/864672): Use is_rollback from the update engine so that
// admin-driven channel changes appear as enterprise rollbacks rather than
// channel switches.
BuildState::UpdateType update_type =
chromeos::UpdateEngineClient::IsTargetChannelMoreStable(
current_channel_.value(), target_channel_.value())
? BuildState::UpdateType::kChannelSwitchRollback
: BuildState::UpdateType::kEnterpriseRollback;
base::UmaHistogramEnumeration(
"UpgradeDetector.RollbackReason",
update_type == BuildState::UpdateType::kChannelSwitchRollback
? RollbackReason::kToMoreStableChannel
: RollbackReason::kEnterpriseRollback);
LOG(WARNING) << "Device is rolling back, will require powerwash. Reason: "
<< (update_type ==
BuildState::UpdateType::kChannelSwitchRollback)
<< ", current_channel: " << current_channel_.value()
<< ", target_channel: " << target_channel_.value();
build_state_->SetUpdate(update_type, base::Version(new_version_.value()),
base::nullopt);
// All done; clear state.
new_version_.reset();
current_channel_.reset();
target_channel_.reset();
}