// // 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/update_attempter.h" #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/clock_interface.h" #include "update_engine/common/constants.h" #include "update_engine/common/dlcservice_interface.h" #include "update_engine/common/hardware_interface.h" #include "update_engine/common/platform_constants.h" #include "update_engine/common/prefs_interface.h" #include "update_engine/common/subprocess.h" #include "update_engine/common/utils.h" #include "update_engine/libcurl_http_fetcher.h" #include "update_engine/metrics_reporter_interface.h" #include "update_engine/omaha_request_action.h" #include "update_engine/omaha_request_params.h" #include "update_engine/omaha_response_handler_action.h" #include "update_engine/p2p_manager.h" #include "update_engine/payload_consumer/download_action.h" #include "update_engine/payload_consumer/filesystem_verifier_action.h" #include "update_engine/payload_consumer/postinstall_runner_action.h" #include "update_engine/payload_state_interface.h" #include "update_engine/power_manager_interface.h" #include "update_engine/system_state.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::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::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(SystemState* system_state, CertificateChecker* cert_checker) : processor_(new ActionProcessor()), system_state_(system_state), cert_checker_(cert_checker), is_install_(false) {} UpdateAttempter::~UpdateAttempter() { // 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_ = system_state_->prefs(); omaha_request_params_ = system_state_->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; } bool UpdateAttempter::ScheduleUpdates() { if (IsUpdateRunningOrScheduled()) return false; chromeos_update_manager::UpdateManager* const update_manager = system_state_->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->AsyncPolicyRequest(callback, &Policy::UpdateCheckAllowed); waiting_for_scheduled_check_ = true; return true; } void UpdateAttempter::CertificateChecked(ServerToCheck server_to_check, CertificateCheckResult result) { system_state_->metrics_reporter()->ReportCertificateCheckMetrics( server_to_check, result); } bool UpdateAttempter::CheckAndReportDailyMetrics() { int64_t stored_value; Time now = system_state_->clock()->GetWallclockTime(); if (system_state_->prefs()->Exists(kPrefsDailyMetricsLastReportedAt) && system_state_->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."; system_state_->prefs()->SetInt64(kPrefsDailyMetricsLastReportedAt, now.ToInternalValue()); ReportOSAge(); return true; } void UpdateAttempter::ReportOSAge() { struct stat sb; if (system_state_ == nullptr) return; 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 = system_state_->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; } system_state_->metrics_reporter()->ReportDailyMetrics(age); } void UpdateAttempter::Update(const string& app_version, const string& omaha_url, const string& target_channel, const string& target_version_prefix, bool rollback_allowed, bool obey_proxies, bool interactive) { // 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"; system_state_->metrics_reporter()->ReportUpdateCheckMetrics( system_state_, metrics::CheckResult::kRebootPending, metrics::CheckReaction::kUnset, metrics::DownloadErrorCode::kUnset); PingOmaha(); return; } if (status_ != UpdateStatus::IDLE) { // Update in progress. Do nothing return; } if (!CalculateUpdateParams(app_version, omaha_url, target_channel, target_version_prefix, rollback_allowed, obey_proxies, interactive)) { return; } BuildUpdateActions(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."; system_state_->set_device_policy(device_policy); system_state_->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 (system_state_ != nullptr) { if (!system_state_->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 = system_state_->payload_state(); payload_state->SetUsingP2PForDownloading(use_p2p_for_downloading); payload_state->SetUsingP2PForSharing(use_p2p_for_sharing); } bool UpdateAttempter::CalculateUpdateParams(const string& app_version, const string& omaha_url, const string& target_channel, const string& target_version_prefix, bool rollback_allowed, bool obey_proxies, bool interactive) { http_response_code_ = 0; PayloadStateInterface* const payload_state = system_state_->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(); // Update the target version prefix. omaha_request_params_->set_target_version_prefix(target_version_prefix); // Set whether rollback is allowed. omaha_request_params_->set_rollback_allowed(rollback_allowed); CalculateStagingParams(interactive); // If staging_wait_time_ wasn't set, staging is off, use scattering instead. if (staging_wait_time_.InSeconds() == 0) { CalculateScatteringParams(interactive); } CalculateP2PParams(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(app_version, omaha_url, interactive)) { LOG(ERROR) << "Unable to initialize Omaha request params."; return false; } // Set the target channel, if one was provided. if (target_channel.empty()) { LOG(INFO) << "No target channel mandated by policy."; } else { LOG(INFO) << "Setting target channel as mandated: " << target_channel; // Pass in false for powerwash_allowed until we add it to the policy // protobuf. string error_message; if (!omaha_request_params_->SetTargetChannel( target_channel, false, &error_message)) { LOG(ERROR) << "Setting the channel failed: " << error_message; } // Since this is the beginning of a new attempt, update the download // channel. The download channel won't be updated until the next attempt, // even if target channel changes meanwhile, so that how we'll know if we // should cancel the current download attempt if there's such a change in // target channel. omaha_request_params_->UpdateDownloadChannel(); } // Set the DLC module ID list. omaha_request_params_->set_dlc_module_ids(dlc_module_ids_); omaha_request_params_->set_is_install(is_install_); 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 (obey_proxies || 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 = system_state_->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 (system_state_->hardware()->IsOOBEEnabled() && !system_state_->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. system_state_->payload_state()->SetScatteringWaitPeriod( omaha_request_params_->waiting_period()); } void UpdateAttempter::CalculateStagingParams(bool interactive) { bool oobe_complete = system_state_->hardware()->IsOOBEEnabled() && system_state_->hardware()->IsOOBEComplete(nullptr); auto device_policy = system_state_->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, prefs_, &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()); case StagingCase::kSetStagingFromPref: omaha_request_params_->set_waiting_period(staging_wait_time_); 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(); } } void UpdateAttempter::BuildUpdateActions(bool interactive) { CHECK(!processor_->IsRunning()); processor_->set_delegate(this); // Actions: auto update_check_fetcher = std::make_unique( GetProxyResolver(), system_state_->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); auto update_check_action = std::make_unique( system_state_, nullptr, std::move(update_check_fetcher), false); auto response_handler_action = std::make_unique(system_state_); auto update_boot_flags_action = std::make_unique(system_state_->boot_control()); auto download_started_action = std::make_unique( system_state_, new OmahaEvent(OmahaEvent::kTypeUpdateDownloadStarted), std::make_unique(GetProxyResolver(), system_state_->hardware()), false); LibcurlHttpFetcher* download_fetcher = new LibcurlHttpFetcher(GetProxyResolver(), system_state_->hardware()); download_fetcher->set_server_to_check(ServerToCheck::kDownload); if (interactive) download_fetcher->set_max_retry_count(kDownloadMaxRetryCountInteractive); auto download_action = std::make_unique(prefs_, system_state_->boot_control(), system_state_->hardware(), system_state_, download_fetcher, // passes ownership interactive); download_action->set_delegate(this); auto download_finished_action = std::make_unique( system_state_, new OmahaEvent(OmahaEvent::kTypeUpdateDownloadFinished), std::make_unique(GetProxyResolver(), system_state_->hardware()), false); auto filesystem_verifier_action = std::make_unique(); auto update_complete_action = std::make_unique( system_state_, new OmahaEvent(OmahaEvent::kTypeUpdateComplete), std::make_unique(GetProxyResolver(), system_state_->hardware()), false); auto postinstall_runner_action = std::make_unique( system_state_->boot_control(), system_state_->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 = system_state_->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("", "", 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 = system_state_->boot_control()->GetCurrentSlot(); TEST_AND_RETURN_FALSE( install_plan_->LoadPartitionsFromSlots(system_state_->boot_control())); install_plan_->powerwash_required = powerwash; LOG(INFO) << "Using this install plan:"; install_plan_->Dump(); auto install_plan_action = std::make_unique(*install_plan_); auto postinstall_runner_action = std::make_unique( system_state_->boot_control(), system_state_->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. system_state_->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 = system_state_->boot_control()->GetNumSlots(); const BootControlInterface::Slot current_slot = system_state_->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 && system_state_->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) { dlc_module_ids_.clear(); is_install_ = false; bool interactive = !(flags & UpdateAttemptFlags::kFlagNonInteractive); if (interactive && status_ != UpdateStatus::IDLE) { // An update check is either in-progress, or an update has completed and the // system is in UPDATED_NEED_REBOOT. Either way, don't do an interactive // update at this time LOG(INFO) << "Refusing to do an interactive update with an update already " "in progress"; return 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(). } if (forced_update_pending_callback_.get()) { if (!system_state_->dlcservice()->GetInstalled(&dlc_module_ids_)) { dlc_module_ids_.clear(); } // Make sure that a scheduling request is made prior to calling the forced // update pending callback. ScheduleUpdates(); forced_update_pending_callback_->Run(true, interactive); } return true; } bool UpdateAttempter::CheckForInstall(const vector& dlc_module_ids, const string& omaha_url) { dlc_module_ids_ = dlc_module_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) { forced_omaha_url_ = constants::kOmahaDefaultAUTestURL; } else if (omaha_url == kAUTestURLRequest) { forced_omaha_url_ = constants::kOmahaDefaultAUTestURL; } if (!ScheduleUpdates()) { if (forced_update_pending_callback_.get()) { // Make sure that a scheduling request is made prior to calling the forced // update pending callback. ScheduleUpdates(); forced_update_pending_callback_->Run(true, true); return true; } return false; } return true; } bool UpdateAttempter::RebootIfNeeded() { #ifdef __ANDROID__ if (status_ != UpdateStatus::UPDATED_NEED_REBOOT) { LOG(INFO) << "Reboot requested, but status is " << UpdateStatusToString(status_) << ", so not rebooting."; return false; } #endif // __ANDROID__ if (system_state_->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 = system_state_->clock()->GetBootTime().ToInternalValue(); prefs_->SetInt64(kPrefsUpdateCompletedBootTime, value); } bool UpdateAttempter::RebootDirectly() { vector command; command.push_back("/sbin/shutdown"); command.push_back("-r"); command.push_back("now"); LOG(INFO) << "Running \"" << base::JoinString(command, " ") << "\""; int rc = 0; Subprocess::SynchronousExec(command, &rc, 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(forced_app_version_, forced_omaha_url_, params.target_channel, params.target_version_prefix, params.rollback_allowed, /*obey_proxies=*/false, params.interactive); // 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(IsUpdateRunningOrScheduled()); } void UpdateAttempter::UpdateLastCheckedTime() { last_checked_time_ = system_state_->clock()->GetWallclockTime().ToTimeT(); } void UpdateAttempter::UpdateRollbackHappened() { DCHECK(system_state_); DCHECK(system_state_->payload_state()); DCHECK(policy_provider_); if (system_state_->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. system_state_->payload_state()->SetRollbackHappened(false); } } // Delegate methods: void UpdateAttempter::ProcessingDone(const ActionProcessor* processor, ErrorCode code) { LOG(INFO) << "Processing Done."; // Reset cpu shares back to normal. cpu_limiter_.StopLimiter(); // 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); 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) { // For install operation, we do not mark update complete since we do not // need reboot. if (!is_install_) WriteUpdateCompletedMarker(); ReportTimeToUpdateAppliedMetric(); prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0); prefs_->SetString(kPrefsPreviousVersion, omaha_request_params_->app_version()); DeltaPerformer::ResetUpdateProgress(prefs_, false); system_state_->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); system_state_->payload_state()->SetScatteringWaitPeriod(TimeDelta()); system_state_->payload_state()->SetStagingWaitPeriod(TimeDelta()); prefs_->Delete(kPrefsUpdateFirstSeenAt); if (is_install_) { LOG(INFO) << "DLC successfully installed, no reboot needed."; SetStatusAndNotify(UpdateStatus::IDLE); ScheduleUpdates(); return; } 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 + ":"; } // If we just downloaded a rollback image, we should preserve this fact // over the following powerwash. if (install_plan_->is_rollback) { system_state_->payload_state()->SetRollbackHappened(true); system_state_->metrics_reporter()->ReportEnterpriseRollbackMetrics( /*success=*/true, install_plan_->version); } // Expect to reboot into the new version to send the proper metric during // next boot. system_state_->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 (system_state_->payload_state()->GetRollbackVersion().empty()) { LOG(ERROR) << "Can't send metrics because there was no Omaha response"; } } return; } if (ScheduleErrorEventAction()) { return; } LOG(INFO) << "No update."; SetStatusAndNotify(UpdateStatus::IDLE); ScheduleUpdates(); } void UpdateAttempter::ProcessingStopped(const ActionProcessor* processor) { // Reset cpu shares back to normal. cpu_limiter_.StopLimiter(); download_progress_ = 0.0; if (forced_update_pending_callback_.get()) // Clear prior interactive requests once the processor is done. forced_update_pending_callback_->Run(false, false); 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 == DownloadAction::StaticType()) { download_progress_ = 0.0; DownloadAction* 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_system_version_ = install_plan_->system_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: 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. system_state_->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() { system_state_->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(); } } 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; // Update the boot flags so the current slot has higher priority. BootControlInterface* boot_control = system_state_->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. system_state_->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->current_system_version = omaha_request_params_->system_version(); out_status->progress = download_progress_; out_status->new_size_bytes = new_payload_size_; out_status->new_version = new_version_; out_status->new_system_version = new_system_version_; 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 (!system_state_->hardware()->IsNormalBootMode()) flags |= static_cast(ErrorCode::kDevModeFlag); if (install_plan_ && install_plan_->is_resume) flags |= static_cast(ErrorCode::kResumedFlag); if (!system_state_->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 = system_state_->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."; system_state_->payload_state()->UpdateFailed(error_event_->error_code); // Send metrics if it was a rollback. if (install_plan_ && install_plan_->is_rollback) { system_state_->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( system_state_, error_event_.release(), // Pass ownership. std::make_unique(GetProxyResolver(), system_state_->hardware()), false); 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()) { auto ping_action = std::make_unique( system_state_, nullptr, std::make_unique(GetProxyResolver(), system_state_->hardware()), true); 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 // system_state_->payload_state()->UpdateEngineStarted() since it will // delete SystemUpdated marker file. if (system_state_->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(); } } system_state_->payload_state()->UpdateEngineStarted(); StartP2PAtStartup(); } bool UpdateAttempter::StartP2PAtStartup() { if (system_state_ == nullptr || !system_state_->p2p_manager()->IsP2PEnabled()) { LOG(INFO) << "Not starting p2p at startup since it's not enabled."; return false; } if (system_state_->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 (system_state_ == nullptr) return false; if (!system_state_->p2p_manager()->IsP2PEnabled()) { LOG(INFO) << "Not starting p2p since it's not enabled."; return false; } LOG(INFO) << "Ensuring that p2p is running."; if (!system_state_->p2p_manager()->EnsureP2PRunning()) { LOG(ERROR) << "Error starting p2p."; return false; } LOG(INFO) << "Performing p2p housekeeping."; if (!system_state_->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::IsUpdateRunningOrScheduled() { 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 (!system_state_->hardware()->IsOfficialBuild()) { LOG(INFO) << "Non-official build; allowing any update source."; return true; } if (system_state_->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 = system_state_->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 (system_state_->prefs()->Exists(kPrefsUpdateFirstSeenAt)) { if (system_state_->prefs()->GetInt64(kPrefsUpdateFirstSeenAt, &update_first_seen_at_int)) { TimeDelta update_delay = system_state_->clock()->GetWallclockTime() - Time::FromInternalValue(update_first_seen_at_int); system_state_->metrics_reporter() ->ReportEnterpriseUpdateSeenToDownloadDays(has_time_restrictions, update_delay.InDays()); } } } } } // namespace chromeos_update_engine