| // |
| // Copyright (C) 2014 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_manager/real_updater_provider.h" |
| |
| #include <inttypes.h> |
| |
| #include <algorithm> |
| #include <string> |
| |
| #include <base/bind.h> |
| #include <base/strings/stringprintf.h> |
| #include <base/time/time.h> |
| #include <update_engine/dbus-constants.h> |
| |
| #include "update_engine/client_library/include/update_engine/update_status.h" |
| #include "update_engine/common/prefs.h" |
| #include "update_engine/common/system_state.h" |
| #include "update_engine/cros/omaha_request_params.h" |
| #include "update_engine/cros/update_attempter.h" |
| #include "update_engine/update_status_utils.h" |
| |
| using base::StringPrintf; |
| using base::Time; |
| using base::TimeDelta; |
| using chromeos_update_engine::OmahaRequestParams; |
| using chromeos_update_engine::SystemState; |
| using std::string; |
| using update_engine::UpdateAttemptFlags; |
| using update_engine::UpdateEngineStatus; |
| |
| namespace chromeos_update_manager { |
| |
| // A templated base class for all update related variables. Provides uniform |
| // construction and a system state handle. |
| template <typename T> |
| class UpdaterVariableBase : public Variable<T> { |
| public: |
| UpdaterVariableBase(const string& name, VariableMode mode) |
| : Variable<T>(name, mode) {} |
| }; |
| |
| // Helper class for issuing a GetStatus() to the UpdateAttempter. |
| class GetStatusHelper { |
| public: |
| explicit GetStatusHelper(string* errmsg) { |
| is_success_ = SystemState::Get()->update_attempter()->GetStatus( |
| &update_engine_status_); |
| if (!is_success_ && errmsg) { |
| *errmsg = "Failed to get a status update from the update engine"; |
| } |
| } |
| |
| inline bool is_success() { return is_success_; } |
| inline int64_t last_checked_time() { |
| return update_engine_status_.last_checked_time; |
| } |
| inline double progress() { return update_engine_status_.progress; } |
| inline const string update_status() { |
| return chromeos_update_engine::UpdateStatusToString( |
| update_engine_status_.status); |
| } |
| inline const string& new_version() { |
| return update_engine_status_.new_version; |
| } |
| inline uint64_t payload_size() { |
| return update_engine_status_.new_size_bytes; |
| } |
| |
| private: |
| bool is_success_; |
| UpdateEngineStatus update_engine_status_; |
| }; |
| |
| // A variable reporting the time when a last update check was issued. |
| class LastCheckedTimeVariable : public UpdaterVariableBase<Time> { |
| public: |
| explicit LastCheckedTimeVariable(const string& name) |
| : UpdaterVariableBase<Time>(name, kVariableModePoll) {} |
| |
| private: |
| const Time* GetValue(TimeDelta /* timeout */, string* errmsg) override { |
| GetStatusHelper raw(errmsg); |
| if (!raw.is_success()) |
| return nullptr; |
| |
| return new Time(Time::FromTimeT(raw.last_checked_time())); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(LastCheckedTimeVariable); |
| }; |
| |
| // A variable reporting the update (download) progress as a decimal fraction |
| // between 0.0 and 1.0. |
| class ProgressVariable : public UpdaterVariableBase<double> { |
| public: |
| explicit ProgressVariable(const string& name) |
| : UpdaterVariableBase<double>(name, kVariableModePoll) {} |
| |
| private: |
| const double* GetValue(TimeDelta /* timeout */, string* errmsg) override { |
| GetStatusHelper raw(errmsg); |
| if (!raw.is_success()) |
| return nullptr; |
| |
| if (raw.progress() < 0.0 || raw.progress() > 1.0) { |
| if (errmsg) { |
| *errmsg = |
| StringPrintf("Invalid progress value received: %f", raw.progress()); |
| } |
| return nullptr; |
| } |
| |
| return new double(raw.progress()); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(ProgressVariable); |
| }; |
| |
| // A variable reporting the stage in which the update process is. |
| class StageVariable : public UpdaterVariableBase<Stage> { |
| public: |
| explicit StageVariable(const string& name) |
| : UpdaterVariableBase<Stage>(name, kVariableModePoll) {} |
| |
| private: |
| struct CurrOpStrToStage { |
| const char* str; |
| Stage stage; |
| }; |
| static const CurrOpStrToStage curr_op_str_to_stage[]; |
| |
| // Note: the method is defined outside the class so arraysize can work. |
| const Stage* GetValue(TimeDelta /* timeout */, string* errmsg) override; |
| |
| DISALLOW_COPY_AND_ASSIGN(StageVariable); |
| }; |
| |
| const StageVariable::CurrOpStrToStage StageVariable::curr_op_str_to_stage[] = { |
| {update_engine::kUpdateStatusIdle, Stage::kIdle}, |
| {update_engine::kUpdateStatusCheckingForUpdate, Stage::kCheckingForUpdate}, |
| {update_engine::kUpdateStatusUpdateAvailable, Stage::kUpdateAvailable}, |
| {update_engine::kUpdateStatusDownloading, Stage::kDownloading}, |
| {update_engine::kUpdateStatusVerifying, Stage::kVerifying}, |
| {update_engine::kUpdateStatusFinalizing, Stage::kFinalizing}, |
| {update_engine::kUpdateStatusUpdatedNeedReboot, Stage::kUpdatedNeedReboot}, |
| {update_engine::kUpdateStatusReportingErrorEvent, |
| Stage::kReportingErrorEvent}, |
| {update_engine::kUpdateStatusAttemptingRollback, |
| Stage::kAttemptingRollback}, |
| {update_engine::kUpdateStatusCleanupPreviousUpdate, |
| Stage::kCleanupPreviousUpdate}, |
| }; |
| |
| const Stage* StageVariable::GetValue(TimeDelta /* timeout */, string* errmsg) { |
| GetStatusHelper raw(errmsg); |
| if (!raw.is_success()) |
| return nullptr; |
| |
| for (auto& key_val : curr_op_str_to_stage) |
| if (raw.update_status() == key_val.str) |
| return new Stage(key_val.stage); |
| |
| if (errmsg) |
| *errmsg = string("Unknown update status: ") + raw.update_status(); |
| return nullptr; |
| } |
| |
| // A variable reporting the version number that an update is updating to. |
| class NewVersionVariable : public UpdaterVariableBase<string> { |
| public: |
| explicit NewVersionVariable(const string& name) |
| : UpdaterVariableBase<string>(name, kVariableModePoll) {} |
| |
| private: |
| const string* GetValue(TimeDelta /* timeout */, string* errmsg) override { |
| GetStatusHelper raw(errmsg); |
| if (!raw.is_success()) |
| return nullptr; |
| |
| return new string(raw.new_version()); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(NewVersionVariable); |
| }; |
| |
| // A variable reporting the size of the update being processed in bytes. |
| class PayloadSizeVariable : public UpdaterVariableBase<uint64_t> { |
| public: |
| explicit PayloadSizeVariable(const string& name) |
| : UpdaterVariableBase<uint64_t>(name, kVariableModePoll) {} |
| |
| private: |
| const uint64_t* GetValue(TimeDelta /* timeout */, string* errmsg) override { |
| GetStatusHelper raw(errmsg); |
| if (!raw.is_success()) |
| return nullptr; |
| |
| return new uint64_t(raw.payload_size()); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(PayloadSizeVariable); |
| }; |
| |
| // A variable reporting the point in time an update last completed in the |
| // current boot cycle. |
| // |
| // TODO(garnold) In general, both the current boottime and wallclock time |
| // readings should come from the time provider and be moderated by the |
| // evaluation context, so that they are uniform throughout the evaluation of a |
| // policy request. |
| class UpdateCompletedTimeVariable : public UpdaterVariableBase<Time> { |
| public: |
| explicit UpdateCompletedTimeVariable(const string& name) |
| : UpdaterVariableBase<Time>(name, kVariableModePoll) {} |
| |
| private: |
| const Time* GetValue(TimeDelta /* timeout */, string* errmsg) override { |
| Time update_boottime; |
| if (!SystemState::Get()->update_attempter()->GetBootTimeAtUpdate( |
| &update_boottime)) { |
| if (errmsg) |
| *errmsg = "Update completed time could not be read"; |
| return nullptr; |
| } |
| |
| const auto* clock = SystemState::Get()->clock(); |
| Time curr_boottime = clock->GetBootTime(); |
| if (curr_boottime < update_boottime) { |
| if (errmsg) |
| *errmsg = "Update completed time more recent than current time"; |
| return nullptr; |
| } |
| TimeDelta duration_since_update = curr_boottime - update_boottime; |
| return new Time(clock->GetWallclockTime() - duration_since_update); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(UpdateCompletedTimeVariable); |
| }; |
| |
| // Variables reporting the current image channel. |
| class CurrChannelVariable : public UpdaterVariableBase<string> { |
| public: |
| explicit CurrChannelVariable(const string& name) |
| : UpdaterVariableBase<string>(name, kVariableModePoll) {} |
| |
| private: |
| const string* GetValue(TimeDelta /* timeout */, string* errmsg) override { |
| OmahaRequestParams* request_params = SystemState::Get()->request_params(); |
| string channel = request_params->current_channel(); |
| if (channel.empty()) { |
| if (errmsg) |
| *errmsg = "No current channel"; |
| return nullptr; |
| } |
| return new string(channel); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(CurrChannelVariable); |
| }; |
| |
| // Variables reporting the new image channel. |
| class NewChannelVariable : public UpdaterVariableBase<string> { |
| public: |
| explicit NewChannelVariable(const string& name) |
| : UpdaterVariableBase<string>(name, kVariableModePoll) {} |
| |
| private: |
| const string* GetValue(TimeDelta /* timeout */, string* errmsg) override { |
| OmahaRequestParams* request_params = SystemState::Get()->request_params(); |
| string channel = request_params->target_channel(); |
| if (channel.empty()) { |
| if (errmsg) |
| *errmsg = "No new channel"; |
| return nullptr; |
| } |
| return new string(channel); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(NewChannelVariable); |
| }; |
| |
| // A variable class for reading Boolean prefs values. |
| class BooleanPrefVariable |
| : public AsyncCopyVariable<bool>, |
| public chromeos_update_engine::PrefsInterface::ObserverInterface { |
| public: |
| BooleanPrefVariable(const string& name, |
| const char* key, |
| bool default_val) |
| : AsyncCopyVariable<bool>(name), |
| key_(key), |
| default_val_(default_val) { |
| SystemState::Get()->prefs()->AddObserver(key, this); |
| OnPrefSet(key); |
| } |
| ~BooleanPrefVariable() { |
| SystemState::Get()->prefs()->RemoveObserver(key_, this); |
| } |
| |
| private: |
| // Reads the actual value from the Prefs instance and updates the Variable |
| // value. |
| void OnPrefSet(const string& key) override { |
| bool result = default_val_; |
| auto* prefs = SystemState::Get()->prefs(); |
| if (prefs->Exists(key_) && !prefs->GetBoolean(key_, &result)) |
| result = default_val_; |
| // AsyncCopyVariable will take care of values that didn't change. |
| SetValue(result); |
| } |
| |
| void OnPrefDeleted(const string& key) override { SetValue(default_val_); } |
| |
| // The Boolean preference key and default value. |
| const char* const key_; |
| const bool default_val_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BooleanPrefVariable); |
| }; |
| |
| // A variable returning the number of consecutive failed update checks. |
| class ConsecutiveFailedUpdateChecksVariable |
| : public UpdaterVariableBase<unsigned int> { |
| public: |
| explicit ConsecutiveFailedUpdateChecksVariable(const string& name) |
| : UpdaterVariableBase<unsigned int>(name, kVariableModePoll) {} |
| |
| private: |
| const unsigned int* GetValue(TimeDelta /* timeout */, |
| string* /* errmsg */) override { |
| // NOLINTNEXTLINE(readability/casting) |
| return new unsigned int(SystemState::Get() |
| ->update_attempter() |
| ->consecutive_failed_update_checks()); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(ConsecutiveFailedUpdateChecksVariable); |
| }; |
| |
| // A variable returning the server-dictated poll interval. |
| class ServerDictatedPollIntervalVariable |
| : public UpdaterVariableBase<unsigned int> { |
| public: |
| explicit ServerDictatedPollIntervalVariable(const string& name) |
| : UpdaterVariableBase<unsigned int>(name, kVariableModePoll) {} |
| |
| private: |
| const unsigned int* GetValue(TimeDelta /* timeout */, |
| string* /* errmsg */) override { |
| // NOLINTNEXTLINE(readability/casting) |
| return new unsigned int(SystemState::Get() |
| ->update_attempter() |
| ->server_dictated_poll_interval()); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(ServerDictatedPollIntervalVariable); |
| }; |
| |
| // An async variable that tracks changes to forced update requests. |
| class ForcedUpdateRequestedVariable |
| : public UpdaterVariableBase<UpdateRequestStatus> { |
| public: |
| explicit ForcedUpdateRequestedVariable(const string& name) |
| : UpdaterVariableBase<UpdateRequestStatus>::UpdaterVariableBase( |
| name, kVariableModeAsync) { |
| SystemState::Get()->update_attempter()->set_forced_update_pending_callback( |
| new base::Callback<void(bool, bool)>( // NOLINT(readability/function) |
| base::Bind(&ForcedUpdateRequestedVariable::Reset, |
| base::Unretained(this)))); |
| } |
| |
| private: |
| const UpdateRequestStatus* GetValue(TimeDelta /* timeout */, |
| string* /* errmsg */) override { |
| return new UpdateRequestStatus(update_request_status_); |
| } |
| |
| void Reset(bool forced_update_requested, bool interactive) { |
| UpdateRequestStatus new_value = UpdateRequestStatus::kNone; |
| if (forced_update_requested) |
| new_value = (interactive ? UpdateRequestStatus::kInteractive |
| : UpdateRequestStatus::kPeriodic); |
| if (update_request_status_ != new_value) { |
| update_request_status_ = new_value; |
| NotifyValueChanged(); |
| } |
| } |
| |
| UpdateRequestStatus update_request_status_ = UpdateRequestStatus::kNone; |
| |
| DISALLOW_COPY_AND_ASSIGN(ForcedUpdateRequestedVariable); |
| }; |
| |
| // A variable returning the current update restrictions that are in effect. |
| class UpdateRestrictionsVariable |
| : public UpdaterVariableBase<UpdateRestrictions> { |
| public: |
| explicit UpdateRestrictionsVariable(const string& name) |
| : UpdaterVariableBase<UpdateRestrictions>(name, kVariableModePoll) {} |
| |
| private: |
| const UpdateRestrictions* GetValue(TimeDelta /* timeout */, |
| string* /* errmsg */) override { |
| UpdateAttemptFlags attempt_flags = |
| SystemState::Get()->update_attempter()->GetCurrentUpdateAttemptFlags(); |
| UpdateRestrictions restriction_flags = UpdateRestrictions::kNone; |
| // Don't blindly copy the whole value, test and set bits that should |
| // transfer from one set of flags to the other. |
| if (attempt_flags & UpdateAttemptFlags::kFlagRestrictDownload) { |
| restriction_flags = static_cast<UpdateRestrictions>( |
| restriction_flags | UpdateRestrictions::kRestrictDownloading); |
| } |
| |
| return new UpdateRestrictions(restriction_flags); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(UpdateRestrictionsVariable); |
| }; |
| |
| // A variable class for reading timeout interval prefs value. |
| class TestUpdateCheckIntervalTimeoutVariable : public Variable<int64_t> { |
| public: |
| explicit TestUpdateCheckIntervalTimeoutVariable(const string& name) |
| : Variable<int64_t>(name, kVariableModePoll), read_count_(0) { |
| SetMissingOk(); |
| } |
| ~TestUpdateCheckIntervalTimeoutVariable() = default; |
| |
| private: |
| const int64_t* GetValue(TimeDelta /* timeout */, |
| string* /* errmsg */) override { |
| auto key = chromeos_update_engine::kPrefsTestUpdateCheckIntervalTimeout; |
| auto* prefs = SystemState::Get()->prefs(); |
| int64_t result; |
| if (prefs->Exists(key) && prefs->GetInt64(key, &result)) { |
| // This specific value is used for testing only. So it should not be kept |
| // around and should be deleted after a few reads. |
| if (++read_count_ > 5) |
| prefs->Delete(key); |
| |
| // Limit the timeout interval to 10 minutes so it is not abused if it is |
| // seen on official images. |
| return new int64_t(std::min(result, static_cast<int64_t>(10 * 60))); |
| } |
| return nullptr; |
| } |
| |
| // Counts how many times this variable is read. This is used to delete the |
| // underlying file defining the variable after a certain number of reads in |
| // order to prevent any abuse of this variable. |
| int read_count_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestUpdateCheckIntervalTimeoutVariable); |
| }; |
| |
| // RealUpdaterProvider methods. |
| |
| RealUpdaterProvider::RealUpdaterProvider() |
| : var_updater_started_time_( |
| "updater_started_time", |
| SystemState::Get()->clock()->GetWallclockTime()), |
| var_last_checked_time_(new LastCheckedTimeVariable("last_checked_time")), |
| var_update_completed_time_( |
| new UpdateCompletedTimeVariable("update_completed_time")), |
| var_progress_(new ProgressVariable("progress")), |
| var_stage_(new StageVariable("stage")), |
| var_new_version_(new NewVersionVariable("new_version")), |
| var_payload_size_(new PayloadSizeVariable("payload_size")), |
| var_curr_channel_(new CurrChannelVariable("curr_channel")), |
| var_new_channel_(new NewChannelVariable("new_channel")), |
| var_p2p_enabled_(new BooleanPrefVariable( |
| "p2p_enabled", chromeos_update_engine::kPrefsP2PEnabled, false)), |
| var_cellular_enabled_(new BooleanPrefVariable( |
| "cellular_enabled", |
| chromeos_update_engine::kPrefsUpdateOverCellularPermission, |
| false)), |
| var_consecutive_failed_update_checks_( |
| new ConsecutiveFailedUpdateChecksVariable( |
| "consecutive_failed_update_checks")), |
| var_server_dictated_poll_interval_(new ServerDictatedPollIntervalVariable( |
| "server_dictated_poll_interval")), |
| var_forced_update_requested_( |
| new ForcedUpdateRequestedVariable("forced_update_requested")), |
| var_update_restrictions_( |
| new UpdateRestrictionsVariable("update_restrictions")), |
| var_test_update_check_interval_timeout_( |
| new TestUpdateCheckIntervalTimeoutVariable( |
| "test_update_check_interval_timeout")) {} |
| } // namespace chromeos_update_manager |