// // 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 <string> #include <base/bind.h> #include <base/strings/stringprintf.h> #include <base/time/time.h> #include <update_engine/dbus-constants.h> #include "update_engine/common/clock_interface.h" #include "update_engine/common/prefs.h" #include "update_engine/omaha_request_params.h" #include "update_engine/update_attempter.h" using base::StringPrintf; using base::Time; using base::TimeDelta; using chromeos_update_engine::OmahaRequestParams; using chromeos_update_engine::SystemState; using std::string; 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, SystemState* system_state) : Variable<T>(name, mode), system_state_(system_state) {} protected: // The system state used for pulling information from the updater. inline SystemState* system_state() const { return system_state_; } private: SystemState* const system_state_; }; // Helper class for issuing a GetStatus() to the UpdateAttempter. class GetStatusHelper { public: GetStatusHelper(SystemState* system_state, string* errmsg) { is_success_ = system_state->update_attempter()->GetStatus( &last_checked_time_, &progress_, &update_status_, &new_version_, &payload_size_); 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 last_checked_time_; } inline double progress() { return progress_; } inline const string& update_status() { return update_status_; } inline const string& new_version() { return new_version_; } inline int64_t payload_size() { return payload_size_; } private: bool is_success_; int64_t last_checked_time_; double progress_; string update_status_; string new_version_; int64_t payload_size_; }; // A variable reporting the time when a last update check was issued. class LastCheckedTimeVariable : public UpdaterVariableBase<Time> { public: LastCheckedTimeVariable(const string& name, SystemState* system_state) : UpdaterVariableBase<Time>(name, kVariableModePoll, system_state) {} private: const Time* GetValue(TimeDelta /* timeout */, string* errmsg) override { GetStatusHelper raw(system_state(), 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: ProgressVariable(const string& name, SystemState* system_state) : UpdaterVariableBase<double>(name, kVariableModePoll, system_state) {} private: const double* GetValue(TimeDelta /* timeout */, string* errmsg) override { GetStatusHelper raw(system_state(), 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: StageVariable(const string& name, SystemState* system_state) : UpdaterVariableBase<Stage>(name, kVariableModePoll, system_state) {} 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}, { // NOLINT(whitespace/braces) update_engine::kUpdateStatusReportingErrorEvent, Stage::kReportingErrorEvent }, {update_engine::kUpdateStatusAttemptingRollback, Stage::kAttemptingRollback}, }; const Stage* StageVariable::GetValue(TimeDelta /* timeout */, string* errmsg) { GetStatusHelper raw(system_state(), 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: NewVersionVariable(const string& name, SystemState* system_state) : UpdaterVariableBase<string>(name, kVariableModePoll, system_state) {} private: const string* GetValue(TimeDelta /* timeout */, string* errmsg) override { GetStatusHelper raw(system_state(), 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<int64_t> { public: PayloadSizeVariable(const string& name, SystemState* system_state) : UpdaterVariableBase<int64_t>(name, kVariableModePoll, system_state) {} private: const int64_t* GetValue(TimeDelta /* timeout */, string* errmsg) override { GetStatusHelper raw(system_state(), errmsg); if (!raw.is_success()) return nullptr; if (raw.payload_size() < 0) { if (errmsg) *errmsg = string("Invalid payload size: %" PRId64, raw.payload_size()); return nullptr; } return new int64_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: UpdateCompletedTimeVariable(const string& name, SystemState* system_state) : UpdaterVariableBase<Time>(name, kVariableModePoll, system_state) {} private: const Time* GetValue(TimeDelta /* timeout */, string* errmsg) override { Time update_boottime; if (!system_state()->update_attempter()->GetBootTimeAtUpdate( &update_boottime)) { if (errmsg) *errmsg = "Update completed time could not be read"; return nullptr; } chromeos_update_engine::ClockInterface* clock = system_state()->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: CurrChannelVariable(const string& name, SystemState* system_state) : UpdaterVariableBase<string>(name, kVariableModePoll, system_state) {} private: const string* GetValue(TimeDelta /* timeout */, string* errmsg) override { OmahaRequestParams* request_params = system_state()->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: NewChannelVariable(const string& name, SystemState* system_state) : UpdaterVariableBase<string>(name, kVariableModePoll, system_state) {} private: const string* GetValue(TimeDelta /* timeout */, string* errmsg) override { OmahaRequestParams* request_params = system_state()->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, chromeos_update_engine::PrefsInterface* prefs, const char* key, bool default_val) : AsyncCopyVariable<bool>(name), prefs_(prefs), key_(key), default_val_(default_val) { prefs->AddObserver(key, this); OnPrefSet(key); } ~BooleanPrefVariable() { 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_; if (prefs_ && 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_); } chromeos_update_engine::PrefsInterface* prefs_; // 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: ConsecutiveFailedUpdateChecksVariable(const string& name, SystemState* system_state) : UpdaterVariableBase<unsigned int>(name, kVariableModePoll, system_state) {} private: const unsigned int* GetValue(TimeDelta /* timeout */, string* /* errmsg */) override { return new unsigned int( system_state()->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: ServerDictatedPollIntervalVariable(const string& name, SystemState* system_state) : UpdaterVariableBase<unsigned int>(name, kVariableModePoll, system_state) {} private: const unsigned int* GetValue(TimeDelta /* timeout */, string* /* errmsg */) override { return new unsigned int( system_state()->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: ForcedUpdateRequestedVariable(const string& name, SystemState* system_state) : UpdaterVariableBase<UpdateRequestStatus>::UpdaterVariableBase( name, kVariableModeAsync, system_state) { system_state->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 is_interactive) { UpdateRequestStatus new_value = UpdateRequestStatus::kNone; if (forced_update_requested) new_value = (is_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); }; // RealUpdaterProvider methods. RealUpdaterProvider::RealUpdaterProvider(SystemState* system_state) : system_state_(system_state), var_updater_started_time_("updater_started_time", system_state->clock()->GetWallclockTime()), var_last_checked_time_( new LastCheckedTimeVariable("last_checked_time", system_state_)), var_update_completed_time_( new UpdateCompletedTimeVariable("update_completed_time", system_state_)), var_progress_(new ProgressVariable("progress", system_state_)), var_stage_(new StageVariable("stage", system_state_)), var_new_version_(new NewVersionVariable("new_version", system_state_)), var_payload_size_(new PayloadSizeVariable("payload_size", system_state_)), var_curr_channel_(new CurrChannelVariable("curr_channel", system_state_)), var_new_channel_(new NewChannelVariable("new_channel", system_state_)), var_p2p_enabled_( new BooleanPrefVariable("p2p_enabled", system_state_->prefs(), chromeos_update_engine::kPrefsP2PEnabled, false)), var_cellular_enabled_( new BooleanPrefVariable( "cellular_enabled", system_state_->prefs(), chromeos_update_engine::kPrefsUpdateOverCellularPermission, false)), var_consecutive_failed_update_checks_( new ConsecutiveFailedUpdateChecksVariable( "consecutive_failed_update_checks", system_state_)), var_server_dictated_poll_interval_( new ServerDictatedPollIntervalVariable( "server_dictated_poll_interval", system_state_)), var_forced_update_requested_( new ForcedUpdateRequestedVariable( "forced_update_requested", system_state_)) {} } // namespace chromeos_update_manager