// // Copyright (C) 2012 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "update_engine/cros/update_attempter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "update_engine/certificate_checker.h" #include "update_engine/common/boot_control_interface.h" #include "update_engine/common/constants.h" #include "update_engine/common/dlcservice_interface.h" #include "update_engine/common/download_action.h" #include "update_engine/common/excluder_interface.h" #include "update_engine/common/hardware_interface.h" #include "update_engine/common/metrics_reporter_interface.h" #include "update_engine/common/platform_constants.h" #include "update_engine/common/prefs.h" #include "update_engine/common/prefs_interface.h" #include "update_engine/common/subprocess.h" #include "update_engine/common/system_state.h" #include "update_engine/common/utils.h" #include "update_engine/cros/download_action_chromeos.h" #include "update_engine/cros/omaha_request_action.h" #include "update_engine/cros/omaha_request_params.h" #include "update_engine/cros/omaha_response_handler_action.h" #include "update_engine/cros/omaha_utils.h" #include "update_engine/cros/p2p_manager.h" #include "update_engine/cros/payload_state_interface.h" #include "update_engine/cros/power_manager_interface.h" #include "update_engine/libcurl_http_fetcher.h" #include "update_engine/payload_consumer/filesystem_verifier_action.h" #include "update_engine/payload_consumer/postinstall_runner_action.h" #include "update_engine/update_boot_flags_action.h" #include "update_engine/update_manager/policy.h" #include "update_engine/update_manager/policy_utils.h" #include "update_engine/update_manager/update_manager.h" #include "update_engine/update_status_utils.h" using base::Bind; using base::Callback; using base::FilePath; using base::Time; using base::TimeDelta; using base::TimeTicks; using brillo::MessageLoop; using chromeos_update_manager::CalculateStagingCase; using chromeos_update_manager::EvalStatus; using chromeos_update_manager::Policy; using chromeos_update_manager::StagingCase; using chromeos_update_manager::UpdateCheckParams; using std::map; using std::string; using std::vector; using update_engine::UpdateAttemptFlags; using update_engine::UpdateEngineStatus; namespace chromeos_update_engine { const int UpdateAttempter::kMaxDeltaUpdateFailures = 3; namespace { const int kMaxConsecutiveObeyProxyRequests = 20; // Minimum threshold to broadcast an status update in progress and time. const double kBroadcastThresholdProgress = 0.01; // 1% const int kBroadcastThresholdSeconds = 10; // By default autest bypasses scattering. If we want to test scattering, // use kScheduledAUTestURLRequest. The URL used is same in both cases, but // different params are passed to CheckForUpdate(). const char kAUTestURLRequest[] = "autest"; const char kScheduledAUTestURLRequest[] = "autest-scheduled"; } // namespace ErrorCode GetErrorCodeForAction(AbstractAction* action, ErrorCode code) { if (code != ErrorCode::kError) return code; const string type = action->Type(); if (type == OmahaRequestAction::StaticType()) return ErrorCode::kOmahaRequestError; if (type == OmahaResponseHandlerAction::StaticType()) return ErrorCode::kOmahaResponseHandlerError; if (type == FilesystemVerifierAction::StaticType()) return ErrorCode::kFilesystemVerifierError; if (type == PostinstallRunnerAction::StaticType()) return ErrorCode::kPostinstallRunnerError; return code; } UpdateAttempter::UpdateAttempter(CertificateChecker* cert_checker) : processor_(new ActionProcessor()), cert_checker_(cert_checker), is_install_(false) {} UpdateAttempter::~UpdateAttempter() { // Prevent any DBus communication from UpdateAttempter when shutting down the // daemon. ClearObservers(); // CertificateChecker might not be initialized in unittests. if (cert_checker_) cert_checker_->SetObserver(nullptr); // Release ourselves as the ActionProcessor's delegate to prevent // re-scheduling the updates due to the processing stopped. processor_->set_delegate(nullptr); } void UpdateAttempter::Init() { // Pulling from the SystemState can only be done after construction, since // this is an aggregate of various objects (such as the UpdateAttempter), // which requires them all to be constructed prior to it being used. prefs_ = SystemState::Get()->prefs(); omaha_request_params_ = SystemState::Get()->request_params(); if (cert_checker_) cert_checker_->SetObserver(this); // In case of update_engine restart without a reboot we need to restore the // reboot needed state. if (GetBootTimeAtUpdate(nullptr)) { status_ = UpdateStatus::UPDATED_NEED_REBOOT; } else { status_ = UpdateStatus::IDLE; prefs_->Delete(kPrefsLastFp, {kDlcPrefsSubDir}); } } bool UpdateAttempter::ScheduleUpdates() { if (IsBusyOrUpdateScheduled()) return false; chromeos_update_manager::UpdateManager* const update_manager = SystemState::Get()->update_manager(); CHECK(update_manager); Callback callback = Bind(&UpdateAttempter::OnUpdateScheduled, base::Unretained(this)); // We limit the async policy request to a reasonably short time, to avoid a // starvation due to a transient bug. update_manager->AsyncPolicyRequestUpdateCheckAllowed( callback, &Policy::UpdateCheckAllowed); waiting_for_scheduled_check_ = true; return true; } bool UpdateAttempter::StartUpdater() { // Initiate update checks. ScheduleUpdates(); auto update_boot_flags_action = std::make_unique( SystemState::Get()->boot_control()); aux_processor_.EnqueueAction(std::move(update_boot_flags_action)); // Update boot flags after 45 seconds. MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&ActionProcessor::StartProcessing, base::Unretained(&aux_processor_)), base::TimeDelta::FromSeconds(45)); // Broadcast the update engine status on startup to ensure consistent system // state on crashes. MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&UpdateAttempter::BroadcastStatus, base::Unretained(this))); MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&UpdateAttempter::UpdateEngineStarted, base::Unretained(this))); return true; } void UpdateAttempter::CertificateChecked(ServerToCheck server_to_check, CertificateCheckResult result) { SystemState::Get()->metrics_reporter()->ReportCertificateCheckMetrics( server_to_check, result); } bool UpdateAttempter::CheckAndReportDailyMetrics() { int64_t stored_value; Time now = SystemState::Get()->clock()->GetWallclockTime(); if (SystemState::Get()->prefs()->Exists(kPrefsDailyMetricsLastReportedAt) && SystemState::Get()->prefs()->GetInt64(kPrefsDailyMetricsLastReportedAt, &stored_value)) { Time last_reported_at = Time::FromInternalValue(stored_value); TimeDelta time_reported_since = now - last_reported_at; if (time_reported_since.InSeconds() < 0) { LOG(WARNING) << "Last reported daily metrics " << utils::FormatTimeDelta(time_reported_since) << " ago " << "which is negative. Either the system clock is wrong or " << "the kPrefsDailyMetricsLastReportedAt state variable " << "is wrong."; // In this case, report daily metrics to reset. } else { if (time_reported_since.InSeconds() < 24 * 60 * 60) { LOG(INFO) << "Last reported daily metrics " << utils::FormatTimeDelta(time_reported_since) << " ago."; return false; } LOG(INFO) << "Last reported daily metrics " << utils::FormatTimeDelta(time_reported_since) << " ago, " << "which is more than 24 hours ago."; } } LOG(INFO) << "Reporting daily metrics."; SystemState::Get()->prefs()->SetInt64(kPrefsDailyMetricsLastReportedAt, now.ToInternalValue()); ReportOSAge(); return true; } void UpdateAttempter::ReportOSAge() { struct stat sb; if (stat("/etc/lsb-release", &sb) != 0) { PLOG(ERROR) << "Error getting file status for /etc/lsb-release " << "(Note: this may happen in some unit tests)"; return; } Time lsb_release_timestamp = Time::FromTimeSpec(sb.st_ctim); Time now = SystemState::Get()->clock()->GetWallclockTime(); TimeDelta age = now - lsb_release_timestamp; if (age.InSeconds() < 0) { LOG(ERROR) << "The OS age (" << utils::FormatTimeDelta(age) << ") is negative. Maybe the clock is wrong? " << "(Note: this may happen in some unit tests.)"; return; } SystemState::Get()->metrics_reporter()->ReportDailyMetrics(age); } void UpdateAttempter::Update(const UpdateCheckParams& params) { // This is normally called frequently enough so it's appropriate to use as a // hook for reporting daily metrics. // TODO(garnold) This should be hooked to a separate (reliable and consistent) // timeout event. CheckAndReportDailyMetrics(); fake_update_success_ = false; if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) { // Although we have applied an update, we still want to ping Omaha // to ensure the number of active statistics is accurate. // // Also convey to the UpdateEngine.Check.Result metric that we're // not performing an update check because of this. LOG(INFO) << "Not updating b/c we already updated and we're waiting for " << "reboot, we'll ping Omaha instead"; SystemState::Get()->metrics_reporter()->ReportUpdateCheckMetrics( metrics::CheckResult::kRebootPending, metrics::CheckReaction::kUnset, metrics::DownloadErrorCode::kUnset); PingOmaha(); return; } if (status_ != UpdateStatus::IDLE) { // Update in progress. Do nothing return; } if (!CalculateUpdateParams(params)) { return; } BuildUpdateActions(params.interactive); SetStatusAndNotify(UpdateStatus::CHECKING_FOR_UPDATE); // Update the last check time here; it may be re-updated when an Omaha // response is received, but this will prevent us from repeatedly scheduling // checks in the case where a response is not received. UpdateLastCheckedTime(); ScheduleProcessingStart(); } void UpdateAttempter::RefreshDevicePolicy() { // Lazy initialize the policy provider, or reload the latest policy data. if (!policy_provider_.get()) policy_provider_.reset(new policy::PolicyProvider()); policy_provider_->Reload(); const policy::DevicePolicy* device_policy = nullptr; if (policy_provider_->device_policy_is_loaded()) device_policy = &policy_provider_->GetDevicePolicy(); if (device_policy) LOG(INFO) << "Device policies/settings present"; else LOG(INFO) << "No device policies/settings present."; SystemState::Get()->set_device_policy(device_policy); SystemState::Get()->p2p_manager()->SetDevicePolicy(device_policy); } void UpdateAttempter::CalculateP2PParams(bool interactive) { bool use_p2p_for_downloading = false; bool use_p2p_for_sharing = false; // Never use p2p for downloading in interactive checks unless the developer // has opted in for it via a marker file. // // (Why would a developer want to opt in? If they are working on the // update_engine or p2p codebases so they can actually test their code.) if (!SystemState::Get()->p2p_manager()->IsP2PEnabled()) { LOG(INFO) << "p2p is not enabled - disallowing p2p for both" << " downloading and sharing."; } else { // Allow p2p for sharing, even in interactive checks. use_p2p_for_sharing = true; if (!interactive) { LOG(INFO) << "Non-interactive check - allowing p2p for downloading"; use_p2p_for_downloading = true; } else { LOG(INFO) << "Forcibly disabling use of p2p for downloading " << "since this update attempt is interactive."; } } PayloadStateInterface* const payload_state = SystemState::Get()->payload_state(); payload_state->SetUsingP2PForDownloading(use_p2p_for_downloading); payload_state->SetUsingP2PForSharing(use_p2p_for_sharing); } bool UpdateAttempter::CalculateUpdateParams(const UpdateCheckParams& params) { http_response_code_ = 0; PayloadStateInterface* const payload_state = SystemState::Get()->payload_state(); // Refresh the policy before computing all the update parameters. RefreshDevicePolicy(); // Check whether we need to clear the rollback-happened preference after // policy is available again. UpdateRollbackHappened(); CalculateStagingParams(params.interactive); // If staging_wait_time_ wasn't set, staging is off, use scattering instead. if (staging_wait_time_.InSeconds() == 0) { CalculateScatteringParams(params.interactive); } CalculateP2PParams(params.interactive); if (payload_state->GetUsingP2PForDownloading() || payload_state->GetUsingP2PForSharing()) { // OK, p2p is to be used - start it and perform housekeeping. if (!StartP2PAndPerformHousekeeping()) { // If this fails, disable p2p for this attempt LOG(INFO) << "Forcibly disabling use of p2p since starting p2p or " << "performing housekeeping failed."; payload_state->SetUsingP2PForDownloading(false); payload_state->SetUsingP2PForSharing(false); } } if (!omaha_request_params_->Init( forced_app_version_, forced_omaha_url_, params)) { LOG(ERROR) << "Unable to initialize Omaha request params."; return false; } // The function |CalculateDlcParams| makes use of the function |GetAppId| from // |OmahaRequestParams|, so to ensure that the return from |GetAppId| // doesn't change, no changes to the values |download_channel_|, // |image_props_.product_id| and |image_props_.canary_product_id| from // |omaha_request_params_| shall be made below this line. CalculateDlcParams(); LOG(INFO) << "target_version_prefix = " << omaha_request_params_->target_version_prefix() << ", rollback_allowed = " << omaha_request_params_->rollback_allowed() << ", scatter_factor_in_seconds = " << utils::FormatSecs(scatter_factor_.InSeconds()); LOG(INFO) << "Wall Clock Based Wait Enabled = " << omaha_request_params_->wall_clock_based_wait_enabled() << ", Update Check Count Wait Enabled = " << omaha_request_params_->update_check_count_wait_enabled() << ", Waiting Period = " << utils::FormatSecs( omaha_request_params_->waiting_period().InSeconds()); LOG(INFO) << "Use p2p For Downloading = " << payload_state->GetUsingP2PForDownloading() << ", Use p2p For Sharing = " << payload_state->GetUsingP2PForSharing(); obeying_proxies_ = true; if (proxy_manual_checks_ == 0) { LOG(INFO) << "forced to obey proxies"; // If forced to obey proxies, every 20th request will not use proxies proxy_manual_checks_++; LOG(INFO) << "proxy manual checks: " << proxy_manual_checks_; if (proxy_manual_checks_ >= kMaxConsecutiveObeyProxyRequests) { proxy_manual_checks_ = 0; obeying_proxies_ = false; } } else if (base::RandInt(0, 4) == 0) { obeying_proxies_ = false; } LOG_IF(INFO, !obeying_proxies_) << "To help ensure updates work, this update check we are ignoring the " << "proxy settings and using direct connections."; DisableDeltaUpdateIfNeeded(); return true; } void UpdateAttempter::CalculateScatteringParams(bool interactive) { // Take a copy of the old scatter value before we update it, as // we need to update the waiting period if this value changes. TimeDelta old_scatter_factor = scatter_factor_; const policy::DevicePolicy* device_policy = SystemState::Get()->device_policy(); if (device_policy) { int64_t new_scatter_factor_in_secs = 0; device_policy->GetScatterFactorInSeconds(&new_scatter_factor_in_secs); if (new_scatter_factor_in_secs < 0) // sanitize input, just in case. new_scatter_factor_in_secs = 0; scatter_factor_ = TimeDelta::FromSeconds(new_scatter_factor_in_secs); } bool is_scatter_enabled = false; if (scatter_factor_.InSeconds() == 0) { LOG(INFO) << "Scattering disabled since scatter factor is set to 0"; } else if (interactive) { LOG(INFO) << "Scattering disabled as this is an interactive update check"; } else if (SystemState::Get()->hardware()->IsOOBEEnabled() && !SystemState::Get()->hardware()->IsOOBEComplete(nullptr)) { LOG(INFO) << "Scattering disabled since OOBE is enabled but not complete " "yet"; } else { is_scatter_enabled = true; LOG(INFO) << "Scattering is enabled"; } if (is_scatter_enabled) { // This means the scattering policy is turned on. // Now check if we need to update the waiting period. The two cases // in which we'd need to update the waiting period are: // 1. First time in process or a scheduled check after a user-initiated one. // (omaha_request_params_->waiting_period will be zero in this case). // 2. Admin has changed the scattering policy value. // (new scattering value will be different from old one in this case). int64_t wait_period_in_secs = 0; if (omaha_request_params_->waiting_period().InSeconds() == 0) { // First case. Check if we have a suitable value to set for // the waiting period. if (prefs_->GetInt64(kPrefsWallClockScatteringWaitPeriod, &wait_period_in_secs) && wait_period_in_secs > 0 && wait_period_in_secs <= scatter_factor_.InSeconds()) { // This means: // 1. There's a persisted value for the waiting period available. // 2. And that persisted value is still valid. // So, in this case, we should reuse the persisted value instead of // generating a new random value to improve the chances of a good // distribution for scattering. omaha_request_params_->set_waiting_period( TimeDelta::FromSeconds(wait_period_in_secs)); LOG(INFO) << "Using persisted wall-clock waiting period: " << utils::FormatSecs( omaha_request_params_->waiting_period().InSeconds()); } else { // This means there's no persisted value for the waiting period // available or its value is invalid given the new scatter_factor value. // So, we should go ahead and regenerate a new value for the // waiting period. LOG(INFO) << "Persisted value not present or not valid (" << utils::FormatSecs(wait_period_in_secs) << ") for wall-clock waiting period."; GenerateNewWaitingPeriod(); } } else if (scatter_factor_ != old_scatter_factor) { // This means there's already a waiting period value, but we detected // a change in the scattering policy value. So, we should regenerate the // waiting period to make sure it's within the bounds of the new scatter // factor value. GenerateNewWaitingPeriod(); } else { // Neither the first time scattering is enabled nor the scattering value // changed. Nothing to do. LOG(INFO) << "Keeping current wall-clock waiting period: " << utils::FormatSecs( omaha_request_params_->waiting_period().InSeconds()); } // The invariant at this point is that omaha_request_params_->waiting_period // is non-zero no matter which path we took above. LOG_IF(ERROR, omaha_request_params_->waiting_period().InSeconds() == 0) << "Waiting Period should NOT be zero at this point!!!"; // Since scattering is enabled, wall clock based wait will always be // enabled. omaha_request_params_->set_wall_clock_based_wait_enabled(true); // If we don't have any issues in accessing the file system to update // the update check count value, we'll turn that on as well. bool decrement_succeeded = DecrementUpdateCheckCount(); omaha_request_params_->set_update_check_count_wait_enabled( decrement_succeeded); } else { // This means the scattering feature is turned off or disabled for // this particular update check. Make sure to disable // all the knobs and artifacts so that we don't invoke any scattering // related code. omaha_request_params_->set_wall_clock_based_wait_enabled(false); omaha_request_params_->set_update_check_count_wait_enabled(false); omaha_request_params_->set_waiting_period(TimeDelta::FromSeconds(0)); prefs_->Delete(kPrefsWallClockScatteringWaitPeriod); prefs_->Delete(kPrefsUpdateCheckCount); // Don't delete the UpdateFirstSeenAt file as we don't want manual checks // that result in no-updates (e.g. due to server side throttling) to // cause update starvation by having the client generate a new // UpdateFirstSeenAt for each scheduled check that follows a manual check. } } void UpdateAttempter::GenerateNewWaitingPeriod() { omaha_request_params_->set_waiting_period( TimeDelta::FromSeconds(base::RandInt(1, scatter_factor_.InSeconds()))); LOG(INFO) << "Generated new wall-clock waiting period: " << utils::FormatSecs( omaha_request_params_->waiting_period().InSeconds()); // Do a best-effort to persist this in all cases. Even if the persistence // fails, we'll still be able to scatter based on our in-memory value. // The persistence only helps in ensuring a good overall distribution // across multiple devices if they tend to reboot too often. SystemState::Get()->payload_state()->SetScatteringWaitPeriod( omaha_request_params_->waiting_period()); } void UpdateAttempter::CalculateStagingParams(bool interactive) { bool oobe_complete = SystemState::Get()->hardware()->IsOOBEEnabled() && SystemState::Get()->hardware()->IsOOBEComplete(nullptr); auto device_policy = SystemState::Get()->device_policy(); StagingCase staging_case = StagingCase::kOff; if (device_policy && !interactive && oobe_complete) { staging_wait_time_ = omaha_request_params_->waiting_period(); staging_case = CalculateStagingCase( device_policy, &staging_wait_time_, &staging_schedule_); } switch (staging_case) { case StagingCase::kOff: // Staging is off, get rid of persisted value. prefs_->Delete(kPrefsWallClockStagingWaitPeriod); // Set |staging_wait_time_| to its default value so scattering can still // be turned on staging_wait_time_ = TimeDelta(); break; // Let the cases fall through since they just add, and never remove, steps // to turning staging on. case StagingCase::kNoSavedValue: prefs_->SetInt64(kPrefsWallClockStagingWaitPeriod, staging_wait_time_.InDays()); FALLTHROUGH; case StagingCase::kSetStagingFromPref: omaha_request_params_->set_waiting_period(staging_wait_time_); FALLTHROUGH; case StagingCase::kNoAction: // Staging is on, enable wallclock based wait so that its values get used. omaha_request_params_->set_wall_clock_based_wait_enabled(true); // Use UpdateCheckCount if possible to prevent devices updating all at // once. omaha_request_params_->set_update_check_count_wait_enabled( DecrementUpdateCheckCount()); // Scattering should not be turned on if staging is on, delete the // existing scattering configuration. prefs_->Delete(kPrefsWallClockScatteringWaitPeriod); scatter_factor_ = TimeDelta(); } } bool UpdateAttempter::ResetDlcPrefs(const string& dlc_id) { vector failures; for (auto& sub_key : {kPrefsPingActive, kPrefsPingLastActive, kPrefsPingLastRollcall}) { auto key = prefs_->CreateSubKey({kDlcPrefsSubDir, dlc_id, sub_key}); if (!prefs_->Delete(key)) failures.emplace_back(sub_key); } if (failures.size() != 0) PLOG(ERROR) << "Failed to delete prefs (" << base::JoinString(failures, ",") << " for DLC (" << dlc_id << ")."; return failures.size() == 0; } void UpdateAttempter::SetPref(const string& pref_key, const string& pref_value, const string& payload_id) { string dlc_id; if (!omaha_request_params_->GetDlcId(payload_id, &dlc_id)) { // Not a DLC ID, set fingerprint in perf for platform ID. prefs_->SetString(pref_key, pref_value); } else { // Set fingerprint in pref for DLC ID. auto key = prefs_->CreateSubKey({kDlcPrefsSubDir, dlc_id, pref_key}); prefs_->SetString(key, pref_value); } } bool UpdateAttempter::SetDlcActiveValue(bool is_active, const string& dlc_id) { if (dlc_id.empty()) { LOG(ERROR) << "Empty DLC ID passed."; return false; } LOG(INFO) << "Set DLC (" << dlc_id << ") to " << (is_active ? "Active" : "Inactive"); if (is_active) { auto ping_active_key = prefs_->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsPingActive}); if (!prefs_->SetInt64(ping_active_key, kPingActiveValue)) { LOG(ERROR) << "Failed to set the value of ping metadata '" << kPrefsPingActive << "'."; return false; } } else { return ResetDlcPrefs(dlc_id); } return true; } int64_t UpdateAttempter::GetPingMetadata(const string& metadata_key) const { // The first time a ping is sent, the metadata files containing the values // sent back by the server still don't exist. A value of -1 is used to // indicate this. if (!SystemState::Get()->prefs()->Exists(metadata_key)) return kPingNeverPinged; int64_t value; if (SystemState::Get()->prefs()->GetInt64(metadata_key, &value)) return value; // Return -2 when the file exists and there is a problem reading from it, or // the value cannot be converted to an integer. return kPingUnknownValue; } void UpdateAttempter::CalculateDlcParams() { // Set the |dlc_ids_| only for an update. This is required to get the // currently installed DLC(s). if (!is_install_ && !SystemState::Get()->dlcservice()->GetDlcsToUpdate(&dlc_ids_)) { LOG(INFO) << "Failed to retrieve DLC module IDs from dlcservice. Check the " "state of dlcservice, will not update DLC modules."; } map dlc_apps_params; for (const auto& dlc_id : dlc_ids_) { OmahaRequestParams::AppParams dlc_params{ .active_counting_type = OmahaRequestParams::kDateBased, .name = dlc_id, .send_ping = false}; if (is_install_) { // In some cases, |SetDlcActiveValue| might fail to reset the DLC prefs // when a DLC is uninstalled. To avoid having stale values from that // scenario, we reset the metadata values on a new install request. // Ignore failure to delete stale prefs. ResetDlcPrefs(dlc_id); SetDlcActiveValue(true, dlc_id); } else { // Only send the ping when the request is to update DLCs. When installing // DLCs, we don't want to send the ping yet, since the DLCs might fail to // install or might not really be active yet. dlc_params.ping_active = kPingActiveValue; auto ping_active_key = prefs_->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsPingActive}); if (!prefs_->GetInt64(ping_active_key, &dlc_params.ping_active) || dlc_params.ping_active != kPingActiveValue) { dlc_params.ping_active = kPingInactiveValue; } auto ping_last_active_key = prefs_->CreateSubKey({kDlcPrefsSubDir, dlc_id, kPrefsPingLastActive}); dlc_params.ping_date_last_active = GetPingMetadata(ping_last_active_key); auto ping_last_rollcall_key = prefs_->CreateSubKey( {kDlcPrefsSubDir, dlc_id, kPrefsPingLastRollcall}); dlc_params.ping_date_last_rollcall = GetPingMetadata(ping_last_rollcall_key); dlc_params.send_ping = true; } dlc_apps_params[omaha_request_params_->GetDlcAppId(dlc_id)] = dlc_params; } omaha_request_params_->set_dlc_apps_params(dlc_apps_params); omaha_request_params_->set_is_install(is_install_); } void UpdateAttempter::BuildUpdateActions(bool interactive) { CHECK(!processor_->IsRunning()); processor_->set_delegate(this); // The session ID needs to be kept throughout the update flow. The value // of the session ID will reset/update only when it is a new update flow. session_id_ = base::GenerateGUID(); // Actions: auto update_check_fetcher = std::make_unique( GetProxyResolver(), SystemState::Get()->hardware()); update_check_fetcher->set_server_to_check(ServerToCheck::kUpdate); // Try harder to connect to the network, esp when not interactive. // See comment in libcurl_http_fetcher.cc. update_check_fetcher->set_no_network_max_retries(interactive ? 1 : 3); update_check_fetcher->set_is_update_check(true); auto update_check_action = std::make_unique( nullptr, std::move(update_check_fetcher), false, session_id_); auto response_handler_action = std::make_unique(); auto update_boot_flags_action = std::make_unique( SystemState::Get()->boot_control()); auto download_started_action = std::make_unique( new OmahaEvent(OmahaEvent::kTypeUpdateDownloadStarted), std::make_unique(GetProxyResolver(), SystemState::Get()->hardware()), false, session_id_); LibcurlHttpFetcher* download_fetcher = new LibcurlHttpFetcher( GetProxyResolver(), SystemState::Get()->hardware()); download_fetcher->set_server_to_check(ServerToCheck::kDownload); if (interactive) download_fetcher->set_max_retry_count(kDownloadMaxRetryCountInteractive); download_fetcher->SetHeader(kXGoogleUpdateSessionId, session_id_); auto download_action = std::make_unique( prefs_, SystemState::Get()->boot_control(), SystemState::Get()->hardware(), download_fetcher, // passes ownership interactive); download_action->set_delegate(this); auto download_finished_action = std::make_unique( new OmahaEvent(OmahaEvent::kTypeUpdateDownloadFinished), std::make_unique(GetProxyResolver(), SystemState::Get()->hardware()), false, session_id_); auto filesystem_verifier_action = std::make_unique( SystemState::Get()->boot_control()->GetDynamicPartitionControl()); auto update_complete_action = std::make_unique( new OmahaEvent(OmahaEvent::kTypeUpdateComplete), std::make_unique(GetProxyResolver(), SystemState::Get()->hardware()), false, session_id_); auto postinstall_runner_action = std::make_unique( SystemState::Get()->boot_control(), SystemState::Get()->hardware()); postinstall_runner_action->set_delegate(this); // Bond them together. We have to use the leaf-types when calling // BondActions(). BondActions(update_check_action.get(), response_handler_action.get()); BondActions(response_handler_action.get(), download_action.get()); BondActions(download_action.get(), filesystem_verifier_action.get()); BondActions(filesystem_verifier_action.get(), postinstall_runner_action.get()); processor_->EnqueueAction(std::move(update_check_action)); processor_->EnqueueAction(std::move(response_handler_action)); processor_->EnqueueAction(std::move(update_boot_flags_action)); processor_->EnqueueAction(std::move(download_started_action)); processor_->EnqueueAction(std::move(download_action)); processor_->EnqueueAction(std::move(download_finished_action)); processor_->EnqueueAction(std::move(filesystem_verifier_action)); processor_->EnqueueAction(std::move(postinstall_runner_action)); processor_->EnqueueAction(std::move(update_complete_action)); } bool UpdateAttempter::Rollback(bool powerwash) { is_install_ = false; if (!CanRollback()) { return false; } // Extra check for enterprise-enrolled devices since they don't support // powerwash. if (powerwash) { // Enterprise-enrolled devices have an empty owner in their device policy. string owner; RefreshDevicePolicy(); const policy::DevicePolicy* device_policy = SystemState::Get()->device_policy(); if (device_policy && (!device_policy->GetOwner(&owner) || owner.empty())) { LOG(ERROR) << "Enterprise device detected. " << "Cannot perform a powerwash for enterprise devices."; return false; } } processor_->set_delegate(this); // Initialize the default request params. if (!omaha_request_params_->Init("", "", {.interactive = true})) { LOG(ERROR) << "Unable to initialize Omaha request params."; return false; } LOG(INFO) << "Setting rollback options."; install_plan_.reset(new InstallPlan()); install_plan_->target_slot = GetRollbackSlot(); install_plan_->source_slot = SystemState::Get()->boot_control()->GetCurrentSlot(); TEST_AND_RETURN_FALSE(install_plan_->LoadPartitionsFromSlots( SystemState::Get()->boot_control())); install_plan_->powerwash_required = powerwash; install_plan_->Dump(); auto install_plan_action = std::make_unique(*install_plan_); auto postinstall_runner_action = std::make_unique( SystemState::Get()->boot_control(), SystemState::Get()->hardware()); postinstall_runner_action->set_delegate(this); BondActions(install_plan_action.get(), postinstall_runner_action.get()); processor_->EnqueueAction(std::move(install_plan_action)); processor_->EnqueueAction(std::move(postinstall_runner_action)); // Update the payload state for Rollback. SystemState::Get()->payload_state()->Rollback(); SetStatusAndNotify(UpdateStatus::ATTEMPTING_ROLLBACK); ScheduleProcessingStart(); return true; } bool UpdateAttempter::CanRollback() const { // We can only rollback if the update_engine isn't busy and we have a valid // rollback partition. return (status_ == UpdateStatus::IDLE && GetRollbackSlot() != BootControlInterface::kInvalidSlot); } BootControlInterface::Slot UpdateAttempter::GetRollbackSlot() const { LOG(INFO) << "UpdateAttempter::GetRollbackSlot"; const unsigned int num_slots = SystemState::Get()->boot_control()->GetNumSlots(); const BootControlInterface::Slot current_slot = SystemState::Get()->boot_control()->GetCurrentSlot(); LOG(INFO) << " Installed slots: " << num_slots; LOG(INFO) << " Booted from slot: " << BootControlInterface::SlotName(current_slot); if (current_slot == BootControlInterface::kInvalidSlot || num_slots < 2) { LOG(INFO) << "Device is not updateable."; return BootControlInterface::kInvalidSlot; } vector bootable_slots; for (BootControlInterface::Slot slot = 0; slot < num_slots; slot++) { if (slot != current_slot && SystemState::Get()->boot_control()->IsSlotBootable(slot)) { LOG(INFO) << "Found bootable slot " << BootControlInterface::SlotName(slot); return slot; } } LOG(INFO) << "No other bootable slot found."; return BootControlInterface::kInvalidSlot; } bool UpdateAttempter::CheckForUpdate(const string& app_version, const string& omaha_url, UpdateAttemptFlags flags) { if (status_ != UpdateStatus::IDLE) { LOG(INFO) << "Refusing to do an update as there is an " << (is_install_ ? "install" : "update") << " already in progress."; return false; } bool interactive = !(flags & UpdateAttemptFlags::kFlagNonInteractive); is_install_ = false; LOG(INFO) << "Forced update check requested."; forced_app_version_.clear(); forced_omaha_url_.clear(); // Certain conditions must be met to allow setting custom version and update // server URLs. However, kScheduledAUTestURLRequest and kAUTestURLRequest are // always allowed regardless of device state. if (IsAnyUpdateSourceAllowed()) { forced_app_version_ = app_version; forced_omaha_url_ = omaha_url; } if (omaha_url == kScheduledAUTestURLRequest) { forced_omaha_url_ = constants::kOmahaDefaultAUTestURL; // Pretend that it's not user-initiated even though it is, // so as to test scattering logic, etc. which get kicked off // only in scheduled update checks. interactive = false; } else if (omaha_url == kAUTestURLRequest) { forced_omaha_url_ = constants::kOmahaDefaultAUTestURL; } if (interactive) { // Use the passed-in update attempt flags for this update attempt instead // of the previously set ones. current_update_attempt_flags_ = flags; // Note: The caching for non-interactive update checks happens in // |OnUpdateScheduled()|. } // |forced_update_pending_callback_| should always be set, but even in the // case that it is not, we still return true indicating success because the // scheduled periodic check will pick up these changes. if (forced_update_pending_callback_.get()) { // Always call |ScheduleUpdates()| before forcing an update. This is because // we need an update to be scheduled for the // |forced_update_pending_callback_| to have an effect. Here we don't need // to care about the return value from |ScheduleUpdate()|. ScheduleUpdates(); forced_update_pending_callback_->Run(true, interactive); } return true; } bool UpdateAttempter::CheckForInstall(const vector& dlc_ids, const string& omaha_url) { if (status_ != UpdateStatus::IDLE) { LOG(INFO) << "Refusing to do an install as there is an " << (is_install_ ? "install" : "update") << " already in progress."; return false; } dlc_ids_ = dlc_ids; is_install_ = true; forced_omaha_url_.clear(); // Certain conditions must be met to allow setting custom version and update // server URLs. However, kScheduledAUTestURLRequest and kAUTestURLRequest are // always allowed regardless of device state. if (IsAnyUpdateSourceAllowed()) { forced_omaha_url_ = omaha_url; } if (omaha_url == kScheduledAUTestURLRequest || omaha_url == kAUTestURLRequest) { forced_omaha_url_ = constants::kOmahaDefaultAUTestURL; } // |forced_update_pending_callback_| should always be set, but even in the // case that it is not, we still return true indicating success because the // scheduled periodic check will pick up these changes. if (forced_update_pending_callback_.get()) { // Always call |ScheduleUpdates()| before forcing an update. This is because // we need an update to be scheduled for the // |forced_update_pending_callback_| to have an effect. Here we don't need // to care about the return value from |ScheduleUpdate()|. ScheduleUpdates(); forced_update_pending_callback_->Run(true, true); } return true; } bool UpdateAttempter::RebootIfNeeded() { if (SystemState::Get()->power_manager()->RequestReboot()) return true; return RebootDirectly(); } void UpdateAttempter::WriteUpdateCompletedMarker() { string boot_id; if (!utils::GetBootId(&boot_id)) return; prefs_->SetString(kPrefsUpdateCompletedOnBootId, boot_id); int64_t value = SystemState::Get()->clock()->GetBootTime().ToInternalValue(); prefs_->SetInt64(kPrefsUpdateCompletedBootTime, value); } bool UpdateAttempter::RebootDirectly() { vector command = {"/sbin/shutdown", "-r", "now"}; int rc = 0; Subprocess::SynchronousExec(command, &rc, nullptr, nullptr); return rc == 0; } void UpdateAttempter::OnUpdateScheduled(EvalStatus status, const UpdateCheckParams& params) { waiting_for_scheduled_check_ = false; if (status == EvalStatus::kSucceeded) { if (!params.updates_enabled) { LOG(WARNING) << "Updates permanently disabled."; // Signal disabled status, then switch right back to idle. This is // necessary for ensuring that observers waiting for a signal change will // actually notice one on subsequent calls. Note that we don't need to // re-schedule a check in this case as updates are permanently disabled; // further (forced) checks may still initiate a scheduling call. SetStatusAndNotify(UpdateStatus::DISABLED); SetStatusAndNotify(UpdateStatus::IDLE); return; } LOG(INFO) << "Running " << (params.interactive ? "interactive" : "periodic") << " update."; if (!params.interactive) { // Cache the update attempt flags that will be used by this update attempt // so that they can't be changed mid-way through. current_update_attempt_flags_ = update_attempt_flags_; } LOG(INFO) << "Update attempt flags in use = 0x" << std::hex << current_update_attempt_flags_; Update(params); // Always clear the forced app_version and omaha_url after an update attempt // so the next update uses the defaults. forced_app_version_.clear(); forced_omaha_url_.clear(); } else { LOG(WARNING) << "Update check scheduling failed (possibly timed out); retrying."; ScheduleUpdates(); } // This check ensures that future update checks will be or are already // scheduled. The check should never fail. A check failure means that there's // a bug that will most likely prevent further automatic update checks. It // seems better to crash in such cases and restart the update_engine daemon // into, hopefully, a known good state. CHECK(IsBusyOrUpdateScheduled()); } void UpdateAttempter::UpdateLastCheckedTime() { last_checked_time_ = SystemState::Get()->clock()->GetWallclockTime().ToTimeT(); } void UpdateAttempter::UpdateRollbackHappened() { DCHECK(SystemState::Get()->payload_state()); DCHECK(policy_provider_); if (SystemState::Get()->payload_state()->GetRollbackHappened() && (policy_provider_->device_policy_is_loaded() || policy_provider_->IsConsumerDevice())) { // Rollback happened, but we already went through OOBE and policy is // present or it's a consumer device. SystemState::Get()->payload_state()->SetRollbackHappened(false); } } void UpdateAttempter::ProcessingDoneInternal(const ActionProcessor* processor, ErrorCode code) { // Reset cpu shares back to normal. cpu_limiter_.StopLimiter(); ResetInteractivityFlags(); if (status_ == UpdateStatus::REPORTING_ERROR_EVENT) { LOG(INFO) << "Error event sent."; // Inform scheduler of new status. SetStatusAndNotify(UpdateStatus::IDLE); ScheduleUpdates(); if (!fake_update_success_) { return; } LOG(INFO) << "Booted from FW B and tried to install new firmware, " "so requesting reboot from user."; } attempt_error_code_ = utils::GetBaseErrorCode(code); if (code != ErrorCode::kSuccess) { if (ScheduleErrorEventAction()) { return; } LOG(INFO) << "No update."; SetStatusAndNotify(UpdateStatus::IDLE); ScheduleUpdates(); return; } ReportTimeToUpdateAppliedMetric(); prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0); prefs_->SetString(kPrefsPreviousVersion, omaha_request_params_->app_version()); DeltaPerformer::ResetUpdateProgress(prefs_, false); SystemState::Get()->payload_state()->UpdateSucceeded(); // Since we're done with scattering fully at this point, this is the // safest point delete the state files, as we're sure that the status is // set to reboot (which means no more updates will be applied until reboot) // This deletion is required for correctness as we want the next update // check to re-create a new random number for the update check count. // Similarly, we also delete the wall-clock-wait period that was persisted // so that we start with a new random value for the next update check // after reboot so that the same device is not favored or punished in any // way. prefs_->Delete(kPrefsUpdateCheckCount); SystemState::Get()->payload_state()->SetScatteringWaitPeriod(TimeDelta()); SystemState::Get()->payload_state()->SetStagingWaitPeriod(TimeDelta()); prefs_->Delete(kPrefsUpdateFirstSeenAt); // Note: below this comment should only be on |ErrorCode::kSuccess|. if (is_install_) { ProcessingDoneInstall(processor, code); } else { ProcessingDoneUpdate(processor, code); } } vector UpdateAttempter::GetSuccessfulDlcIds() { vector dlc_ids; for (const auto& pr : omaha_request_params_->dlc_apps_params()) if (pr.second.updated) dlc_ids.push_back(pr.second.name); return dlc_ids; } void UpdateAttempter::ProcessingDoneInstall(const ActionProcessor* processor, ErrorCode code) { if (!SystemState::Get()->dlcservice()->InstallCompleted( GetSuccessfulDlcIds())) LOG(WARNING) << "dlcservice didn't successfully handle install completion."; SetStatusAndNotify(UpdateStatus::IDLE); ScheduleUpdates(); LOG(INFO) << "DLC successfully installed, no reboot needed."; } void UpdateAttempter::ProcessingDoneUpdate(const ActionProcessor* processor, ErrorCode code) { WriteUpdateCompletedMarker(); if (!SystemState::Get()->dlcservice()->UpdateCompleted(GetSuccessfulDlcIds())) LOG(WARNING) << "dlcservice didn't successfully handle update completion."; SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT); ScheduleUpdates(); LOG(INFO) << "Update successfully applied, waiting to reboot."; // |install_plan_| is null during rollback operations, and the stats don't // make much sense then anyway. if (install_plan_) { // Generate an unique payload identifier. string target_version_uid; for (const auto& payload : install_plan_->payloads) { target_version_uid += brillo::data_encoding::Base64Encode(payload.hash) + ":" + payload.metadata_signature + ":"; // Set fingerprint value for updates only. if (!is_install_) SetPref(kPrefsLastFp, payload.fp, payload.app_id); } // If we just downloaded a rollback image, we should preserve this fact // over the following powerwash. if (install_plan_->is_rollback) { SystemState::Get()->payload_state()->SetRollbackHappened(true); SystemState::Get()->metrics_reporter()->ReportEnterpriseRollbackMetrics( /*success=*/true, install_plan_->version); } // Expect to reboot into the new version to send the proper metric during // next boot. SystemState::Get()->payload_state()->ExpectRebootInNewVersion( target_version_uid); } else { // If we just finished a rollback, then we expect to have no Omaha // response. Otherwise, it's an error. if (SystemState::Get()->payload_state()->GetRollbackVersion().empty()) { LOG(ERROR) << "Can't send metrics because there was no Omaha response"; } } } // Delegate methods: void UpdateAttempter::ProcessingDone(const ActionProcessor* processor, ErrorCode code) { LOG(INFO) << "Processing Done."; ProcessingDoneInternal(processor, code); // Note: do cleanups here for any variables that need to be reset after a // failure, error, update, or install. is_install_ = false; } void UpdateAttempter::ProcessingStopped(const ActionProcessor* processor) { // Reset cpu shares back to normal. cpu_limiter_.StopLimiter(); download_progress_ = 0.0; ResetInteractivityFlags(); SetStatusAndNotify(UpdateStatus::IDLE); ScheduleUpdates(); error_event_.reset(nullptr); } // Called whenever an action has finished processing, either successfully // or otherwise. void UpdateAttempter::ActionCompleted(ActionProcessor* processor, AbstractAction* action, ErrorCode code) { // Reset download progress regardless of whether or not the download // action succeeded. Also, get the response code from HTTP request // actions (update download as well as the initial update check // actions). const string type = action->Type(); if (type == DownloadActionChromeos::StaticType()) { download_progress_ = 0.0; DownloadActionChromeos* download_action = static_cast(action); http_response_code_ = download_action->GetHTTPResponseCode(); } else if (type == OmahaRequestAction::StaticType()) { OmahaRequestAction* omaha_request_action = static_cast(action); // If the request is not an event, then it's the update-check. if (!omaha_request_action->IsEvent()) { http_response_code_ = omaha_request_action->GetHTTPResponseCode(); // Record the number of consecutive failed update checks. if (http_response_code_ == kHttpResponseInternalServerError || http_response_code_ == kHttpResponseServiceUnavailable) { consecutive_failed_update_checks_++; } else { consecutive_failed_update_checks_ = 0; } const OmahaResponse& omaha_response = omaha_request_action->GetOutputObject(); // Store the server-dictated poll interval, if any. server_dictated_poll_interval_ = std::max(0, omaha_response.poll_interval); // This update is ignored by omaha request action because update over // cellular connection is not allowed. Needs to ask for user's permissions // to update. if (code == ErrorCode::kOmahaUpdateIgnoredOverCellular) { new_version_ = omaha_response.version; new_payload_size_ = 0; for (const auto& package : omaha_response.packages) { new_payload_size_ += package.size; } SetStatusAndNotify(UpdateStatus::NEED_PERMISSION_TO_UPDATE); } } } else if (type == OmahaResponseHandlerAction::StaticType()) { // Depending on the returned error code, note that an update is available. if (code == ErrorCode::kOmahaUpdateDeferredPerPolicy || code == ErrorCode::kSuccess) { // Note that the status will be updated to DOWNLOADING when some bytes // get actually downloaded from the server and the BytesReceived // callback is invoked. This avoids notifying the user that a download // has started in cases when the server and the client are unable to // initiate the download. auto omaha_response_handler_action = static_cast(action); install_plan_.reset( new InstallPlan(omaha_response_handler_action->install_plan())); UpdateLastCheckedTime(); new_version_ = install_plan_->version; new_payload_size_ = 0; for (const auto& payload : install_plan_->payloads) new_payload_size_ += payload.size; cpu_limiter_.StartLimiter(); SetStatusAndNotify(UpdateStatus::UPDATE_AVAILABLE); } } // General failure cases. if (code != ErrorCode::kSuccess) { // If the current state is at or past the download phase, count the failure // in case a switch to full update becomes necessary. Ignore network // transfer timeouts and failures. if (code != ErrorCode::kDownloadTransferError) { switch (status_) { case UpdateStatus::IDLE: case UpdateStatus::CHECKING_FOR_UPDATE: case UpdateStatus::UPDATE_AVAILABLE: case UpdateStatus::NEED_PERMISSION_TO_UPDATE: break; case UpdateStatus::DOWNLOADING: case UpdateStatus::VERIFYING: case UpdateStatus::FINALIZING: case UpdateStatus::UPDATED_NEED_REBOOT: case UpdateStatus::REPORTING_ERROR_EVENT: case UpdateStatus::ATTEMPTING_ROLLBACK: case UpdateStatus::DISABLED: case UpdateStatus::CLEANUP_PREVIOUS_UPDATE: MarkDeltaUpdateFailure(); break; } } if (code != ErrorCode::kNoUpdate) { // On failure, schedule an error event to be sent to Omaha. CreatePendingErrorEvent(action, code); } return; } // Find out which action completed (successfully). if (type == DownloadAction::StaticType()) { SetStatusAndNotify(UpdateStatus::FINALIZING); } else if (type == FilesystemVerifierAction::StaticType()) { // Log the system properties before the postinst and after the file system // is verified. It used to be done in the postinst itself. But postinst // cannot do this anymore. On the other hand, these logs are frequently // looked at and it is preferable not to scatter them in random location in // the log and rather log it right before the postinst. The reason not do // this in the |PostinstallRunnerAction| is to prevent dependency from // libpayload_consumer to libupdate_engine. LogImageProperties(); } } void UpdateAttempter::BytesReceived(uint64_t bytes_progressed, uint64_t bytes_received, uint64_t total) { // The PayloadState keeps track of how many bytes were actually downloaded // from a given URL for the URL skipping logic. SystemState::Get()->payload_state()->DownloadProgress(bytes_progressed); double progress = 0; if (total) progress = static_cast(bytes_received) / static_cast(total); if (status_ != UpdateStatus::DOWNLOADING || bytes_received == total) { download_progress_ = progress; SetStatusAndNotify(UpdateStatus::DOWNLOADING); } else { ProgressUpdate(progress); } } void UpdateAttempter::DownloadComplete() { SystemState::Get()->payload_state()->DownloadComplete(); } void UpdateAttempter::ProgressUpdate(double progress) { // Self throttle based on progress. Also send notifications if progress is // too slow. if (progress == 1.0 || progress - download_progress_ >= kBroadcastThresholdProgress || TimeTicks::Now() - last_notify_time_ >= TimeDelta::FromSeconds(kBroadcastThresholdSeconds)) { download_progress_ = progress; BroadcastStatus(); } } void UpdateAttempter::ResetInteractivityFlags() { // Reset the state that's only valid for a single update pass. current_update_attempt_flags_ = UpdateAttemptFlags::kNone; if (forced_update_pending_callback_.get()) // Clear prior interactive requests once the processor is done. forced_update_pending_callback_->Run(false, false); } bool UpdateAttempter::ResetStatus() { LOG(INFO) << "Attempting to reset state from " << UpdateStatusToString(status_) << " to UpdateStatus::IDLE"; switch (status_) { case UpdateStatus::IDLE: // no-op. return true; case UpdateStatus::UPDATED_NEED_REBOOT: { bool ret_value = true; status_ = UpdateStatus::IDLE; // Remove the reboot marker so that if the machine is rebooted // after resetting to idle state, it doesn't go back to // UpdateStatus::UPDATED_NEED_REBOOT state. ret_value = prefs_->Delete(kPrefsUpdateCompletedOnBootId) && ret_value; ret_value = prefs_->Delete(kPrefsUpdateCompletedBootTime) && ret_value; ret_value = prefs_->Delete(kPrefsLastFp, {kDlcPrefsSubDir}) && ret_value; // Update the boot flags so the current slot has higher priority. BootControlInterface* boot_control = SystemState::Get()->boot_control(); if (!boot_control->SetActiveBootSlot(boot_control->GetCurrentSlot())) ret_value = false; // Mark the current slot as successful again, since marking it as active // may reset the successful bit. We ignore the result of whether marking // the current slot as successful worked. if (!boot_control->MarkBootSuccessfulAsync(Bind([](bool successful) {}))) ret_value = false; // Notify the PayloadState that the successful payload was canceled. SystemState::Get()->payload_state()->ResetUpdateStatus(); // The previous version is used to report back to omaha after reboot that // we actually rebooted into the new version from this "prev-version". We // need to clear out this value now to prevent it being sent on the next // updatecheck request. ret_value = prefs_->SetString(kPrefsPreviousVersion, "") && ret_value; LOG(INFO) << "Reset status " << (ret_value ? "successful" : "failed"); return ret_value; } default: LOG(ERROR) << "Reset not allowed in this state."; return false; } } bool UpdateAttempter::GetStatus(UpdateEngineStatus* out_status) { out_status->last_checked_time = last_checked_time_; out_status->status = status_; out_status->current_version = omaha_request_params_->app_version(); out_status->progress = download_progress_; out_status->new_size_bytes = new_payload_size_; out_status->new_version = new_version_; out_status->is_enterprise_rollback = install_plan_ && install_plan_->is_rollback; out_status->is_install = is_install_; string str_eol_date; if (SystemState::Get()->prefs()->Exists(kPrefsOmahaEolDate) && !SystemState::Get()->prefs()->GetString(kPrefsOmahaEolDate, &str_eol_date)) LOG(ERROR) << "Failed to retrieve kPrefsOmahaEolDate pref."; out_status->eol_date = StringToEolDate(str_eol_date); // A powerwash will take place either if the install plan says it is required // or if an enterprise rollback is happening. out_status->will_powerwash_after_reboot = install_plan_ && (install_plan_->powerwash_required || install_plan_->is_rollback); return true; } void UpdateAttempter::BroadcastStatus() { UpdateEngineStatus broadcast_status; // Use common method for generating the current status. GetStatus(&broadcast_status); for (const auto& observer : service_observers_) { observer->SendStatusUpdate(broadcast_status); } last_notify_time_ = TimeTicks::Now(); } uint32_t UpdateAttempter::GetErrorCodeFlags() { uint32_t flags = 0; if (!SystemState::Get()->hardware()->IsNormalBootMode()) flags |= static_cast(ErrorCode::kDevModeFlag); if (install_plan_ && install_plan_->is_resume) flags |= static_cast(ErrorCode::kResumedFlag); if (!SystemState::Get()->hardware()->IsOfficialBuild()) flags |= static_cast(ErrorCode::kTestImageFlag); if (!omaha_request_params_->IsUpdateUrlOfficial()) { flags |= static_cast(ErrorCode::kTestOmahaUrlFlag); } return flags; } bool UpdateAttempter::ShouldCancel(ErrorCode* cancel_reason) { // Check if the channel we're attempting to update to is the same as the // target channel currently chosen by the user. OmahaRequestParams* params = SystemState::Get()->request_params(); if (params->download_channel() != params->target_channel()) { LOG(ERROR) << "Aborting download as target channel: " << params->target_channel() << " is different from the download channel: " << params->download_channel(); *cancel_reason = ErrorCode::kUpdateCanceledByChannelChange; return true; } return false; } void UpdateAttempter::SetStatusAndNotify(UpdateStatus status) { status_ = status; BroadcastStatus(); } void UpdateAttempter::CreatePendingErrorEvent(AbstractAction* action, ErrorCode code) { if (error_event_.get() || status_ == UpdateStatus::REPORTING_ERROR_EVENT) { // This shouldn't really happen. LOG(WARNING) << "There's already an existing pending error event."; return; } // Classify the code to generate the appropriate result so that // the Borgmon charts show up the results correctly. // Do this before calling GetErrorCodeForAction which could potentially // augment the bit representation of code and thus cause no matches for // the switch cases below. OmahaEvent::Result event_result; switch (code) { case ErrorCode::kOmahaUpdateIgnoredPerPolicy: case ErrorCode::kOmahaUpdateDeferredPerPolicy: case ErrorCode::kOmahaUpdateDeferredForBackoff: event_result = OmahaEvent::kResultUpdateDeferred; break; default: event_result = OmahaEvent::kResultError; break; } code = GetErrorCodeForAction(action, code); fake_update_success_ = code == ErrorCode::kPostinstallBootedFromFirmwareB; // Compute the final error code with all the bit flags to be sent to Omaha. code = static_cast(static_cast(code) | GetErrorCodeFlags()); error_event_.reset( new OmahaEvent(OmahaEvent::kTypeUpdateComplete, event_result, code)); } bool UpdateAttempter::ScheduleErrorEventAction() { if (error_event_.get() == nullptr) return false; LOG(ERROR) << "Update failed."; SystemState::Get()->payload_state()->UpdateFailed(error_event_->error_code); // Send metrics if it was a rollback. if (install_plan_ && install_plan_->is_rollback) { SystemState::Get()->metrics_reporter()->ReportEnterpriseRollbackMetrics( /*success=*/false, install_plan_->version); } // Send it to Omaha. LOG(INFO) << "Reporting the error event"; auto error_event_action = std::make_unique( error_event_.release(), // Pass ownership. std::make_unique(GetProxyResolver(), SystemState::Get()->hardware()), false, session_id_); processor_->EnqueueAction(std::move(error_event_action)); SetStatusAndNotify(UpdateStatus::REPORTING_ERROR_EVENT); processor_->StartProcessing(); return true; } void UpdateAttempter::ScheduleProcessingStart() { LOG(INFO) << "Scheduling an action processor start."; MessageLoop::current()->PostTask( FROM_HERE, Bind([](ActionProcessor* processor) { processor->StartProcessing(); }, base::Unretained(processor_.get()))); } void UpdateAttempter::DisableDeltaUpdateIfNeeded() { int64_t delta_failures; if (omaha_request_params_->delta_okay() && prefs_->GetInt64(kPrefsDeltaUpdateFailures, &delta_failures) && delta_failures >= kMaxDeltaUpdateFailures) { LOG(WARNING) << "Too many delta update failures, forcing full update."; omaha_request_params_->set_delta_okay(false); } } void UpdateAttempter::MarkDeltaUpdateFailure() { // Don't try to resume a failed delta update. DeltaPerformer::ResetUpdateProgress(prefs_, false); int64_t delta_failures; if (!prefs_->GetInt64(kPrefsDeltaUpdateFailures, &delta_failures) || delta_failures < 0) { delta_failures = 0; } prefs_->SetInt64(kPrefsDeltaUpdateFailures, ++delta_failures); } void UpdateAttempter::PingOmaha() { if (!processor_->IsRunning()) { ResetInteractivityFlags(); auto ping_action = std::make_unique( nullptr, std::make_unique(GetProxyResolver(), SystemState::Get()->hardware()), true, "" /* session_id */); processor_->set_delegate(nullptr); processor_->EnqueueAction(std::move(ping_action)); // Call StartProcessing() synchronously here to avoid any race conditions // caused by multiple outstanding ping Omaha requests. If we call // StartProcessing() asynchronously, the device can be suspended before we // get a chance to callback to StartProcessing(). When the device resumes // (assuming the device sleeps longer than the next update check period), // StartProcessing() is called back and at the same time, the next update // check is fired which eventually invokes StartProcessing(). A crash // can occur because StartProcessing() checks to make sure that the // processor is idle which it isn't due to the two concurrent ping Omaha // requests. processor_->StartProcessing(); } else { LOG(WARNING) << "Action processor running, Omaha ping suppressed."; } // Update the last check time here; it may be re-updated when an Omaha // response is received, but this will prevent us from repeatedly scheduling // checks in the case where a response is not received. UpdateLastCheckedTime(); // Update the status which will schedule the next update check SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT); ScheduleUpdates(); } bool UpdateAttempter::DecrementUpdateCheckCount() { int64_t update_check_count_value; if (!prefs_->Exists(kPrefsUpdateCheckCount)) { // This file does not exist. This means we haven't started our update // check count down yet, so nothing more to do. This file will be created // later when we first satisfy the wall-clock-based-wait period. LOG(INFO) << "No existing update check count. That's normal."; return true; } if (prefs_->GetInt64(kPrefsUpdateCheckCount, &update_check_count_value)) { // Only if we're able to read a proper integer value, then go ahead // and decrement and write back the result in the same file, if needed. LOG(INFO) << "Update check count = " << update_check_count_value; if (update_check_count_value == 0) { // It could be 0, if, for some reason, the file didn't get deleted // when we set our status to waiting for reboot. so we just leave it // as is so that we can prevent another update_check wait for this client. LOG(INFO) << "Not decrementing update check count as it's already 0."; return true; } if (update_check_count_value > 0) update_check_count_value--; else update_check_count_value = 0; // Write out the new value of update_check_count_value. if (prefs_->SetInt64(kPrefsUpdateCheckCount, update_check_count_value)) { // We successfully wrote out the new value, so enable the // update check based wait. LOG(INFO) << "New update check count = " << update_check_count_value; return true; } } LOG(INFO) << "Deleting update check count state due to read/write errors."; // We cannot read/write to the file, so disable the update check based wait // so that we don't get stuck in this OS version by any chance (which could // happen if there's some bug that causes to read/write incorrectly). // Also attempt to delete the file to do our best effort to cleanup. prefs_->Delete(kPrefsUpdateCheckCount); return false; } void UpdateAttempter::UpdateEngineStarted() { // If we just booted into a new update, keep the previous OS version // in case we rebooted because of a crash of the old version, so we // can do a proper crash report with correct information. // This must be done before calling // SystemState::Get()->payload_state()->UpdateEngineStarted() since it will // delete SystemUpdated marker file. if (SystemState::Get()->system_rebooted() && prefs_->Exists(kPrefsSystemUpdatedMarker)) { if (!prefs_->GetString(kPrefsPreviousVersion, &prev_version_)) { // If we fail to get the version string, make sure it stays empty. prev_version_.clear(); } } MoveToPrefs({kPrefsLastRollCallPingDay, kPrefsLastActivePingDay}); SystemState::Get()->payload_state()->UpdateEngineStarted(); StartP2PAtStartup(); excluder_ = CreateExcluder(); } void UpdateAttempter::MoveToPrefs(const vector& keys) { auto* powerwash_safe_prefs = SystemState::Get()->powerwash_safe_prefs(); for (const auto& key : keys) { // Do not overwrite existing pref key with powerwash prefs. if (!prefs_->Exists(key) && powerwash_safe_prefs->Exists(key)) { string value; if (!powerwash_safe_prefs->GetString(key, &value) || !prefs_->SetString(key, value)) { PLOG(ERROR) << "Unable to add powerwash safe key " << key << " to prefs. Powerwash safe key will be deleted."; } } // Delete keys regardless of operation success to preserve privacy. powerwash_safe_prefs->Delete(key); } } bool UpdateAttempter::StartP2PAtStartup() { if (!SystemState::Get()->p2p_manager()->IsP2PEnabled()) { LOG(INFO) << "Not starting p2p at startup since it's not enabled."; return false; } if (SystemState::Get()->p2p_manager()->CountSharedFiles() < 1) { LOG(INFO) << "Not starting p2p at startup since our application " << "is not sharing any files."; return false; } return StartP2PAndPerformHousekeeping(); } bool UpdateAttempter::StartP2PAndPerformHousekeeping() { if (!SystemState::Get()->p2p_manager()->IsP2PEnabled()) { LOG(INFO) << "Not starting p2p since it's not enabled."; return false; } LOG(INFO) << "Ensuring that p2p is running."; if (!SystemState::Get()->p2p_manager()->EnsureP2PRunning()) { LOG(ERROR) << "Error starting p2p."; return false; } LOG(INFO) << "Performing p2p housekeeping."; if (!SystemState::Get()->p2p_manager()->PerformHousekeeping()) { LOG(ERROR) << "Error performing housekeeping for p2p."; return false; } LOG(INFO) << "Done performing p2p housekeeping."; return true; } bool UpdateAttempter::GetBootTimeAtUpdate(Time* out_boot_time) { // In case of an update_engine restart without a reboot, we stored the boot_id // when the update was completed by setting a pref, so we can check whether // the last update was on this boot or a previous one. string boot_id; TEST_AND_RETURN_FALSE(utils::GetBootId(&boot_id)); string update_completed_on_boot_id; if (!prefs_->Exists(kPrefsUpdateCompletedOnBootId) || !prefs_->GetString(kPrefsUpdateCompletedOnBootId, &update_completed_on_boot_id) || update_completed_on_boot_id != boot_id) return false; // Short-circuit avoiding the read in case out_boot_time is nullptr. if (out_boot_time) { int64_t boot_time = 0; // Since the kPrefsUpdateCompletedOnBootId was correctly set, this pref // should not fail. TEST_AND_RETURN_FALSE( prefs_->GetInt64(kPrefsUpdateCompletedBootTime, &boot_time)); *out_boot_time = Time::FromInternalValue(boot_time); } return true; } bool UpdateAttempter::IsBusyOrUpdateScheduled() { return ((status_ != UpdateStatus::IDLE && status_ != UpdateStatus::UPDATED_NEED_REBOOT) || waiting_for_scheduled_check_); } bool UpdateAttempter::IsAnyUpdateSourceAllowed() const { // We allow updates from any source if either of these are true: // * The device is running an unofficial (dev/test) image. // * The debugd dev features are accessible (i.e. in devmode with no owner). // This protects users running a base image, while still allowing a specific // window (gated by the debug dev features) where `cros flash` is usable. if (!SystemState::Get()->hardware()->IsOfficialBuild()) { LOG(INFO) << "Non-official build; allowing any update source."; return true; } if (SystemState::Get()->hardware()->AreDevFeaturesEnabled()) { LOG(INFO) << "Developer features enabled; allowing custom update sources."; return true; } LOG(INFO) << "Developer features disabled; disallowing custom update sources."; return false; } void UpdateAttempter::ReportTimeToUpdateAppliedMetric() { const policy::DevicePolicy* device_policy = SystemState::Get()->device_policy(); if (device_policy && device_policy->IsEnterpriseEnrolled()) { vector parsed_intervals; bool has_time_restrictions = device_policy->GetDisallowedTimeIntervals(&parsed_intervals); int64_t update_first_seen_at_int; if (SystemState::Get()->prefs()->Exists(kPrefsUpdateFirstSeenAt)) { if (SystemState::Get()->prefs()->GetInt64(kPrefsUpdateFirstSeenAt, &update_first_seen_at_int)) { TimeDelta update_delay = SystemState::Get()->clock()->GetWallclockTime() - Time::FromInternalValue(update_first_seen_at_int); SystemState::Get() ->metrics_reporter() ->ReportEnterpriseUpdateSeenToDownloadDays(has_time_restrictions, update_delay.InDays()); } } } } } // namespace chromeos_update_engine