// Copyright 2019 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/policy/scheduled_task_handler/os_and_policies_update_checker.h"
#include <utility>
#include "chrome/browser/browser_process.h"
#include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "components/device_event_log/device_event_log.h"
#include "components/policy/core/common/policy_service.h"
#include "components/policy/core/common/policy_types.h"
namespace policy {
OsAndPoliciesUpdateChecker::OsAndPoliciesUpdateChecker(
ash::NetworkStateHandler* network_state_handler)
: network_state_handler_(network_state_handler),
update_check_task_executor_(
update_checker_internal::
kMaxOsAndPoliciesUpdateCheckerRetryIterations,
update_checker_internal::kOsAndPoliciesUpdateCheckerRetryTime),
update_engine_client_(ash::UpdateEngineClient::Get()) {}
OsAndPoliciesUpdateChecker::~OsAndPoliciesUpdateChecker() {
// Called to remove any observers.
ResetState();
}
void OsAndPoliciesUpdateChecker::Start(UpdateCheckCompletionCallback cb,
base::TimeDelta timeout) {
// Override any previous calls by resetting state.
ResetState();
is_running_ = true;
// Must be set before starting the task runner, as callbacks may be called
// synchronously.
update_check_completion_cb_ = std::move(cb);
timeout_timer_.Start(
FROM_HERE, timeout,
base::BindOnce(
&OsAndPoliciesUpdateChecker::RunCompletionCallbackAndResetState,
base::Unretained(this), false /* update_check_result */));
// If there is no network then wait for a network connection before starting
// an update check. If then network isn't found for a maximum time then report
// failure. It's safe to use |this| because |wait_for_network_timer_| is a
// member of this object.
if (!network_state_handler_->DefaultNetwork()) {
LOGIN_LOG(EVENT) << "Unable to start update check: no network";
wait_for_network_timer_.Start(
FROM_HERE, update_checker_internal::kWaitForNetworkTimeout,
base::BindOnce(&OsAndPoliciesUpdateChecker::OnNetworkWaitTimeout,
base::Unretained(this)));
network_state_handler_observer_.Observe(network_state_handler_.get());
return;
}
ScheduleUpdateCheck();
}
void OsAndPoliciesUpdateChecker::Stop() {
ResetState();
}
bool OsAndPoliciesUpdateChecker::IsRunning() const {
return is_running_;
}
void OsAndPoliciesUpdateChecker::DefaultNetworkChanged(
const ash::NetworkState* network) {
// If a network is found, it's okay to start an update check. Stop observing
// for more network changes, any network flakiness will now be handled by
// timeouts and retries.
// If no network is found, continue observing for network changes.
if (!network)
return;
wait_for_network_timer_.Stop();
network_state_handler_observer_.Reset();
ScheduleUpdateCheck();
}
void OsAndPoliciesUpdateChecker::ScheduleUpdateCheck() {
// If an update was downloaded but not applied then update engine won't do
// anything. Move straight to the policy state.
if (update_engine_client_->GetLastStatus().current_operation() ==
update_engine::Operation::UPDATED_NEED_REBOOT) {
RefreshPolicies(true /* update_check_result */);
return;
}
// Safe to use "this" as |update_check_task_executor_| is a member of this
// class.
update_check_task_executor_.Start(
base::BindOnce(&OsAndPoliciesUpdateChecker::StartUpdateCheck,
base::Unretained(this)),
base::BindOnce(&OsAndPoliciesUpdateChecker::OnUpdateCheckFailure,
base::Unretained(this)));
}
void OsAndPoliciesUpdateChecker::OnUpdateCheckFailure() {
// Refresh policies after the update check is finished successfully or
// unsuccessfully.
RefreshPolicies(false /* update_check_result */);
}
void OsAndPoliciesUpdateChecker::RunCompletionCallbackAndResetState(
bool update_check_result) {
// Flag must be set because |IsRunning| maybe queried when the callback is
// called below.
is_running_ = false;
if (update_check_completion_cb_)
std::move(update_check_completion_cb_).Run(update_check_result);
ResetState();
}
void OsAndPoliciesUpdateChecker::OnNetworkWaitTimeout() {
// No network has been detected, no point querying the server for an update
// check or polivy refresh. Report failure to the caller.
RunCompletionCallbackAndResetState(false /* update_check_result */);
}
void OsAndPoliciesUpdateChecker::StartUpdateCheck() {
// Only one update check can be pending at any time.
weak_factory_.InvalidateWeakPtrs();
// Register observer to keep track of different stages of the update check. An
// observer could be existing due to back to back calls to |StartUpdateCheck|.
if (!update_engine_client_->HasObserver(this))
update_engine_client_->AddObserver(this);
update_engine_client_->RequestUpdateCheck(
base::BindOnce(&OsAndPoliciesUpdateChecker::OnUpdateCheckStarted,
weak_factory_.GetWeakPtr()));
// Do nothing for the initial idle stage when the update check state machine
// has just started.
ignore_idle_status_ = true;
}
void OsAndPoliciesUpdateChecker::UpdateStatusChanged(
const update_engine::StatusResult& status) {
// Only ignore idle state if it is the first and only non-error state in the
// state machine.
if (ignore_idle_status_ &&
status.current_operation() > update_engine::Operation::IDLE) {
ignore_idle_status_ = false;
}
switch (status.current_operation()) {
case update_engine::Operation::IDLE:
if (!ignore_idle_status_) {
// No update to download or an error occured mid-way of an existing
// update download.
// TODO(abhishekbh): Differentiate between the two cases and call
// ScheduleRetry in case of error.
RefreshPolicies(true /* update_check_result */);
}
break;
case update_engine::Operation::UPDATED_NEED_REBOOT:
// Refresh policies after the update check is finished successfully or
// unsuccessfully.
RefreshPolicies(true /* update_check_result */);
break;
case update_engine::Operation::ERROR:
case update_engine::Operation::DISABLED:
case update_engine::Operation::NEED_PERMISSION_TO_UPDATE:
case update_engine::Operation::REPORTING_ERROR_EVENT:
update_check_task_executor_.ScheduleRetry(
base::BindOnce(&OsAndPoliciesUpdateChecker::StartUpdateCheck,
base::Unretained(this)));
break;
case update_engine::Operation::FINALIZING:
case update_engine::Operation::VERIFYING:
case update_engine::Operation::DOWNLOADING:
case update_engine::Operation::UPDATE_AVAILABLE:
case update_engine::Operation::CHECKING_FOR_UPDATE:
case update_engine::Operation::ATTEMPTING_ROLLBACK:
case update_engine::Operation::CLEANUP_PREVIOUS_UPDATE:
case update_engine::Operation::UPDATED_BUT_DEFERRED:
// Do nothing on intermediate states.
break;
default:
NOTREACHED_IN_MIGRATION();
}
}
void OsAndPoliciesUpdateChecker::OnUpdateCheckStarted(
ash::UpdateEngineClient::UpdateCheckResult result) {
switch (result) {
case ash::UpdateEngineClient::UPDATE_RESULT_SUCCESS:
// Nothing to do if the update check started successfully.
break;
case ash::UpdateEngineClient::UPDATE_RESULT_FAILED:
update_check_task_executor_.ScheduleRetry(
base::BindOnce(&OsAndPoliciesUpdateChecker::StartUpdateCheck,
base::Unretained(this)));
break;
case ash::UpdateEngineClient::UPDATE_RESULT_NOTIMPLEMENTED:
// No point retrying if the operation is not implemented. Refresh policies
// since the update check is done.
LOG(ERROR) << "Update check failed: Operation not implemented";
RefreshPolicies(false /* update_check_result */);
break;
}
}
void OsAndPoliciesUpdateChecker::RefreshPolicies(bool update_check_result) {
g_browser_process->policy_service()->RefreshPolicies(
base::BindOnce(&OsAndPoliciesUpdateChecker::OnRefreshPoliciesCompletion,
weak_factory_.GetWeakPtr(), update_check_result),
PolicyFetchReason::kScheduled);
}
void OsAndPoliciesUpdateChecker::OnRefreshPoliciesCompletion(
bool update_check_result) {
RunCompletionCallbackAndResetState(update_check_result);
}
void OsAndPoliciesUpdateChecker::ResetState() {
weak_factory_.InvalidateWeakPtrs();
update_engine_client_->RemoveObserver(this);
network_state_handler_observer_.Reset();
update_check_task_executor_.Stop();
ignore_idle_status_ = true;
is_running_ = false;
wait_for_network_timer_.Stop();
timeout_timer_.Stop();
}
} // namespace policy