Home | History | Annotate | Download | only in dbus
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chromeos/dbus/update_engine_client.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "base/command_line.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/strings/string_util.h"
     12 #include "chromeos/chromeos_switches.h"
     13 #include "dbus/bus.h"
     14 #include "dbus/message.h"
     15 #include "dbus/object_path.h"
     16 #include "dbus/object_proxy.h"
     17 #include "third_party/cros_system_api/dbus/service_constants.h"
     18 
     19 namespace chromeos {
     20 
     21 namespace {
     22 
     23 const char kReleaseChannelDev[] = "dev-channel";
     24 const char kReleaseChannelBeta[] = "beta-channel";
     25 const char kReleaseChannelStable[] = "stable-channel";
     26 
     27 // Delay between successive state transitions during AU.
     28 const int kStateTransitionDefaultDelayMs = 3000;
     29 
     30 // Delay between successive notifications about downloading progress
     31 // during fake AU.
     32 const int kStateTransitionDownloadingDelayMs = 250;
     33 
     34 // Size of parts of a "new" image which are downloaded each
     35 // |kStateTransitionDownloadingDelayMs| during fake AU.
     36 const int64_t kDownloadSizeDelta = 1 << 19;
     37 
     38 // Returns UPDATE_STATUS_ERROR on error.
     39 UpdateEngineClient::UpdateStatusOperation UpdateStatusFromString(
     40     const std::string& str) {
     41   VLOG(1) << "UpdateStatusFromString got " << str << " as input.";
     42   if (str == update_engine::kUpdateStatusIdle)
     43     return UpdateEngineClient::UPDATE_STATUS_IDLE;
     44   if (str == update_engine::kUpdateStatusCheckingForUpdate)
     45     return UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE;
     46   if (str == update_engine::kUpdateStatusUpdateAvailable)
     47     return UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE;
     48   if (str == update_engine::kUpdateStatusDownloading)
     49     return UpdateEngineClient::UPDATE_STATUS_DOWNLOADING;
     50   if (str == update_engine::kUpdateStatusVerifying)
     51     return UpdateEngineClient::UPDATE_STATUS_VERIFYING;
     52   if (str == update_engine::kUpdateStatusFinalizing)
     53     return UpdateEngineClient::UPDATE_STATUS_FINALIZING;
     54   if (str == update_engine::kUpdateStatusUpdatedNeedReboot)
     55     return UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT;
     56   if (str == update_engine::kUpdateStatusReportingErrorEvent)
     57     return UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT;
     58   if (str == update_engine::kUpdateStatusAttemptingRollback)
     59     return UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK;
     60   return UpdateEngineClient::UPDATE_STATUS_ERROR;
     61 }
     62 
     63 // Used in UpdateEngineClient::EmptyUpdateCheckCallback().
     64 void EmptyUpdateCheckCallbackBody(
     65     UpdateEngineClient::UpdateCheckResult unused_result) {
     66 }
     67 
     68 bool IsValidChannel(const std::string& channel) {
     69   return channel == kReleaseChannelDev ||
     70       channel == kReleaseChannelBeta ||
     71       channel == kReleaseChannelStable;
     72 }
     73 
     74 }  // namespace
     75 
     76 // The UpdateEngineClient implementation used in production.
     77 class UpdateEngineClientImpl : public UpdateEngineClient {
     78  public:
     79   UpdateEngineClientImpl()
     80       : update_engine_proxy_(NULL), last_status_(), weak_ptr_factory_(this) {}
     81 
     82   virtual ~UpdateEngineClientImpl() {
     83   }
     84 
     85   // UpdateEngineClient implementation:
     86   virtual void AddObserver(Observer* observer) OVERRIDE {
     87     observers_.AddObserver(observer);
     88   }
     89 
     90   virtual void RemoveObserver(Observer* observer) OVERRIDE {
     91     observers_.RemoveObserver(observer);
     92   }
     93 
     94   virtual bool HasObserver(Observer* observer) OVERRIDE {
     95     return observers_.HasObserver(observer);
     96   }
     97 
     98   virtual void RequestUpdateCheck(
     99       const UpdateCheckCallback& callback) OVERRIDE {
    100     dbus::MethodCall method_call(
    101         update_engine::kUpdateEngineInterface,
    102         update_engine::kAttemptUpdate);
    103     dbus::MessageWriter writer(&method_call);
    104     writer.AppendString("");  // Unused.
    105     writer.AppendString("");  // Unused.
    106 
    107     VLOG(1) << "Requesting an update check";
    108     update_engine_proxy_->CallMethod(
    109         &method_call,
    110         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    111         base::Bind(&UpdateEngineClientImpl::OnRequestUpdateCheck,
    112                    weak_ptr_factory_.GetWeakPtr(),
    113                    callback));
    114   }
    115 
    116   virtual void RebootAfterUpdate() OVERRIDE {
    117     dbus::MethodCall method_call(
    118         update_engine::kUpdateEngineInterface,
    119         update_engine::kRebootIfNeeded);
    120 
    121     VLOG(1) << "Requesting a reboot";
    122     update_engine_proxy_->CallMethod(
    123         &method_call,
    124         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    125         base::Bind(&UpdateEngineClientImpl::OnRebootAfterUpdate,
    126                    weak_ptr_factory_.GetWeakPtr()));
    127   }
    128 
    129   virtual void Rollback() OVERRIDE {
    130     VLOG(1) << "Requesting a rollback";
    131      dbus::MethodCall method_call(
    132         update_engine::kUpdateEngineInterface,
    133         update_engine::kAttemptRollback);
    134     dbus::MessageWriter writer(&method_call);
    135     writer.AppendBool(true /* powerwash */);
    136 
    137     update_engine_proxy_->CallMethod(
    138         &method_call,
    139         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    140         base::Bind(&UpdateEngineClientImpl::OnRollback,
    141                    weak_ptr_factory_.GetWeakPtr()));
    142   }
    143 
    144 
    145   virtual void CanRollbackCheck(
    146       const RollbackCheckCallback& callback) OVERRIDE {
    147     dbus::MethodCall method_call(
    148         update_engine::kUpdateEngineInterface,
    149         update_engine::kCanRollback);
    150 
    151     VLOG(1) << "Requesting to get rollback availability status";
    152     update_engine_proxy_->CallMethod(
    153         &method_call,
    154         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    155         base::Bind(&UpdateEngineClientImpl::OnCanRollbackCheck,
    156                    weak_ptr_factory_.GetWeakPtr(),
    157                    callback));
    158   }
    159 
    160   virtual Status GetLastStatus() OVERRIDE {
    161     return last_status_;
    162   }
    163 
    164   virtual void SetChannel(const std::string& target_channel,
    165                           bool is_powerwash_allowed) OVERRIDE {
    166     if (!IsValidChannel(target_channel)) {
    167       LOG(ERROR) << "Invalid channel name: " << target_channel;
    168       return;
    169     }
    170 
    171     dbus::MethodCall method_call(
    172         update_engine::kUpdateEngineInterface,
    173         update_engine::kSetChannel);
    174     dbus::MessageWriter writer(&method_call);
    175     writer.AppendString(target_channel);
    176     writer.AppendBool(is_powerwash_allowed);
    177 
    178     VLOG(1) << "Requesting to set channel: "
    179             << "target_channel=" << target_channel << ", "
    180             << "is_powerwash_allowed=" << is_powerwash_allowed;
    181     update_engine_proxy_->CallMethod(
    182         &method_call,
    183         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    184         base::Bind(&UpdateEngineClientImpl::OnSetChannel,
    185                    weak_ptr_factory_.GetWeakPtr()));
    186   }
    187 
    188   virtual void GetChannel(bool get_current_channel,
    189                           const GetChannelCallback& callback) OVERRIDE {
    190     dbus::MethodCall method_call(
    191         update_engine::kUpdateEngineInterface,
    192         update_engine::kGetChannel);
    193     dbus::MessageWriter writer(&method_call);
    194     writer.AppendBool(get_current_channel);
    195 
    196     VLOG(1) << "Requesting to get channel, get_current_channel="
    197             << get_current_channel;
    198     update_engine_proxy_->CallMethod(
    199         &method_call,
    200         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    201         base::Bind(&UpdateEngineClientImpl::OnGetChannel,
    202                    weak_ptr_factory_.GetWeakPtr(),
    203                    callback));
    204   }
    205 
    206  protected:
    207   virtual void Init(dbus::Bus* bus) OVERRIDE {
    208     update_engine_proxy_ = bus->GetObjectProxy(
    209         update_engine::kUpdateEngineServiceName,
    210         dbus::ObjectPath(update_engine::kUpdateEngineServicePath));
    211 
    212     // Monitor the D-Bus signal for brightness changes. Only the power
    213     // manager knows the actual brightness level. We don't cache the
    214     // brightness level in Chrome as it will make things less reliable.
    215     update_engine_proxy_->ConnectToSignal(
    216         update_engine::kUpdateEngineInterface,
    217         update_engine::kStatusUpdate,
    218         base::Bind(&UpdateEngineClientImpl::StatusUpdateReceived,
    219                    weak_ptr_factory_.GetWeakPtr()),
    220         base::Bind(&UpdateEngineClientImpl::StatusUpdateConnected,
    221                    weak_ptr_factory_.GetWeakPtr()));
    222 
    223     // Get update engine status for the initial status. Update engine won't
    224     // send StatusUpdate signal unless there is a status change. If chrome
    225     // crashes after UPDATE_STATUS_UPDATED_NEED_REBOOT status is set,
    226     // restarted chrome would not get this status. See crbug.com/154104.
    227     GetUpdateEngineStatus();
    228   }
    229 
    230  private:
    231   void GetUpdateEngineStatus() {
    232     dbus::MethodCall method_call(
    233         update_engine::kUpdateEngineInterface,
    234         update_engine::kGetStatus);
    235     update_engine_proxy_->CallMethodWithErrorCallback(
    236         &method_call,
    237         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    238         base::Bind(&UpdateEngineClientImpl::OnGetStatus,
    239                    weak_ptr_factory_.GetWeakPtr()),
    240         base::Bind(&UpdateEngineClientImpl::OnGetStatusError,
    241                    weak_ptr_factory_.GetWeakPtr()));
    242   }
    243 
    244   // Called when a response for RequestUpdateCheck() is received.
    245   void OnRequestUpdateCheck(const UpdateCheckCallback& callback,
    246                             dbus::Response* response) {
    247     if (!response) {
    248       LOG(ERROR) << "Failed to request update check";
    249       callback.Run(UPDATE_RESULT_FAILED);
    250       return;
    251     }
    252     callback.Run(UPDATE_RESULT_SUCCESS);
    253   }
    254 
    255   // Called when a response for RebootAfterUpdate() is received.
    256   void OnRebootAfterUpdate(dbus::Response* response) {
    257     if (!response) {
    258       LOG(ERROR) << "Failed to request rebooting after update";
    259       return;
    260     }
    261   }
    262 
    263   // Called when a response for Rollback() is received.
    264   void OnRollback(dbus::Response* response) {
    265     if (!response) {
    266       LOG(ERROR) << "Failed to rollback";
    267       return;
    268     }
    269   }
    270 
    271   // Called when a response for CanRollbackCheck() is received.
    272   void OnCanRollbackCheck(const RollbackCheckCallback& callback,
    273                           dbus::Response* response) {
    274     if (!response) {
    275       LOG(ERROR) << "Failed to request rollback availability status";
    276       callback.Run(false);
    277       return;
    278     }
    279     dbus::MessageReader reader(response);
    280     bool can_rollback;
    281     if (!reader.PopBool(&can_rollback)) {
    282       LOG(ERROR) << "Incorrect response: " << response->ToString();
    283       callback.Run(false);
    284       return;
    285     }
    286     VLOG(1) << "Rollback availability status received: " << can_rollback;
    287     callback.Run(can_rollback);
    288   }
    289 
    290   // Called when a response for GetStatus is received.
    291   void OnGetStatus(dbus::Response* response) {
    292     if (!response) {
    293       LOG(ERROR) << "Failed to get response for GetStatus request.";
    294       return;
    295     }
    296 
    297     dbus::MessageReader reader(response);
    298     std::string current_operation;
    299     Status status;
    300     if (!(reader.PopInt64(&status.last_checked_time) &&
    301           reader.PopDouble(&status.download_progress) &&
    302           reader.PopString(&current_operation) &&
    303           reader.PopString(&status.new_version) &&
    304           reader.PopInt64(&status.new_size))) {
    305       LOG(ERROR) << "GetStatus had incorrect response: "
    306                  << response->ToString();
    307       return;
    308     }
    309     status.status = UpdateStatusFromString(current_operation);
    310     last_status_ = status;
    311     FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(status));
    312   }
    313 
    314   // Called when GetStatus call failed.
    315   void OnGetStatusError(dbus::ErrorResponse* error) {
    316     LOG(ERROR) << "GetStatus request failed with error: "
    317                << (error ? error->ToString() : "");
    318   }
    319 
    320   // Called when a response for SetReleaseChannel() is received.
    321   void OnSetChannel(dbus::Response* response) {
    322     if (!response) {
    323       LOG(ERROR) << "Failed to request setting channel";
    324       return;
    325     }
    326     VLOG(1) << "Succeeded to set channel";
    327   }
    328 
    329   // Called when a response for GetChannel() is received.
    330   void OnGetChannel(const GetChannelCallback& callback,
    331                     dbus::Response* response) {
    332     if (!response) {
    333       LOG(ERROR) << "Failed to request getting channel";
    334       callback.Run("");
    335       return;
    336     }
    337     dbus::MessageReader reader(response);
    338     std::string channel;
    339     if (!reader.PopString(&channel)) {
    340       LOG(ERROR) << "Incorrect response: " << response->ToString();
    341       callback.Run("");
    342       return;
    343     }
    344     VLOG(1) << "The channel received: " << channel;
    345     callback.Run(channel);
    346   }
    347 
    348   // Called when a status update signal is received.
    349   void StatusUpdateReceived(dbus::Signal* signal) {
    350     VLOG(1) << "Status update signal received: " << signal->ToString();
    351     dbus::MessageReader reader(signal);
    352     int64 last_checked_time = 0;
    353     double progress = 0.0;
    354     std::string current_operation;
    355     std::string new_version;
    356     int64_t new_size = 0;
    357     if (!(reader.PopInt64(&last_checked_time) &&
    358           reader.PopDouble(&progress) &&
    359           reader.PopString(&current_operation) &&
    360           reader.PopString(&new_version) &&
    361           reader.PopInt64(&new_size))) {
    362       LOG(ERROR) << "Status changed signal had incorrect parameters: "
    363                  << signal->ToString();
    364       return;
    365     }
    366     Status status;
    367     status.last_checked_time = last_checked_time;
    368     status.download_progress = progress;
    369     status.status = UpdateStatusFromString(current_operation);
    370     status.new_version = new_version;
    371     status.new_size = new_size;
    372 
    373     last_status_ = status;
    374     FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(status));
    375   }
    376 
    377   // Called when the status update signal is initially connected.
    378   void StatusUpdateConnected(const std::string& interface_name,
    379                              const std::string& signal_name,
    380                              bool success) {
    381     LOG_IF(WARNING, !success)
    382         << "Failed to connect to status updated signal.";
    383   }
    384 
    385   dbus::ObjectProxy* update_engine_proxy_;
    386   ObserverList<Observer> observers_;
    387   Status last_status_;
    388 
    389   // Note: This should remain the last member so it'll be destroyed and
    390   // invalidate its weak pointers before any other members are destroyed.
    391   base::WeakPtrFactory<UpdateEngineClientImpl> weak_ptr_factory_;
    392 
    393   DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientImpl);
    394 };
    395 
    396 // The UpdateEngineClient implementation used on Linux desktop,
    397 // which does nothing.
    398 class UpdateEngineClientStubImpl : public UpdateEngineClient {
    399  public:
    400   UpdateEngineClientStubImpl()
    401       : current_channel_(kReleaseChannelBeta),
    402         target_channel_(kReleaseChannelBeta) {}
    403 
    404   // UpdateEngineClient implementation:
    405   virtual void Init(dbus::Bus* bus) OVERRIDE {}
    406   virtual void AddObserver(Observer* observer) OVERRIDE {}
    407   virtual void RemoveObserver(Observer* observer) OVERRIDE {}
    408   virtual bool HasObserver(Observer* observer) OVERRIDE { return false; }
    409 
    410   virtual void RequestUpdateCheck(
    411       const UpdateCheckCallback& callback) OVERRIDE {
    412     callback.Run(UPDATE_RESULT_NOTIMPLEMENTED);
    413   }
    414   virtual void RebootAfterUpdate() OVERRIDE {}
    415   virtual void Rollback() OVERRIDE {}
    416   virtual void CanRollbackCheck(
    417       const RollbackCheckCallback& callback) OVERRIDE {
    418     callback.Run(true);
    419   }
    420   virtual Status GetLastStatus() OVERRIDE { return Status(); }
    421   virtual void SetChannel(const std::string& target_channel,
    422                           bool is_powerwash_allowed) OVERRIDE {
    423     VLOG(1) << "Requesting to set channel: "
    424             << "target_channel=" << target_channel << ", "
    425             << "is_powerwash_allowed=" << is_powerwash_allowed;
    426     target_channel_ = target_channel;
    427   }
    428   virtual void GetChannel(bool get_current_channel,
    429                           const GetChannelCallback& callback) OVERRIDE {
    430     VLOG(1) << "Requesting to get channel, get_current_channel="
    431             << get_current_channel;
    432     if (get_current_channel)
    433       callback.Run(current_channel_);
    434     else
    435       callback.Run(target_channel_);
    436   }
    437 
    438   std::string current_channel_;
    439   std::string target_channel_;
    440 };
    441 
    442 // The UpdateEngineClient implementation used on Linux desktop, which
    443 // tries to emulate real update engine client.
    444 class UpdateEngineClientFakeImpl : public UpdateEngineClientStubImpl {
    445  public:
    446   UpdateEngineClientFakeImpl() : weak_factory_(this) {
    447   }
    448 
    449   virtual ~UpdateEngineClientFakeImpl() {
    450   }
    451 
    452   // UpdateEngineClient implementation:
    453   virtual void AddObserver(Observer* observer) OVERRIDE {
    454     if (observer)
    455       observers_.AddObserver(observer);
    456   }
    457 
    458   virtual void RemoveObserver(Observer* observer) OVERRIDE {
    459     if (observer)
    460       observers_.RemoveObserver(observer);
    461   }
    462 
    463   virtual bool HasObserver(Observer* observer) OVERRIDE {
    464     return observers_.HasObserver(observer);
    465   }
    466 
    467   virtual void RequestUpdateCheck(
    468       const UpdateCheckCallback& callback) OVERRIDE {
    469     if (last_status_.status != UPDATE_STATUS_IDLE) {
    470       callback.Run(UPDATE_RESULT_FAILED);
    471       return;
    472     }
    473     callback.Run(UPDATE_RESULT_SUCCESS);
    474     last_status_.status = UPDATE_STATUS_CHECKING_FOR_UPDATE;
    475     last_status_.download_progress = 0.0;
    476     last_status_.last_checked_time = 0;
    477     last_status_.new_size = 0;
    478     base::MessageLoop::current()->PostDelayedTask(
    479         FROM_HERE,
    480         base::Bind(&UpdateEngineClientFakeImpl::StateTransition,
    481                    weak_factory_.GetWeakPtr()),
    482         base::TimeDelta::FromMilliseconds(kStateTransitionDefaultDelayMs));
    483   }
    484 
    485   virtual Status GetLastStatus() OVERRIDE { return last_status_; }
    486 
    487  private:
    488   void StateTransition() {
    489     UpdateStatusOperation next_status = UPDATE_STATUS_ERROR;
    490     int delay_ms = kStateTransitionDefaultDelayMs;
    491     switch (last_status_.status) {
    492       case UPDATE_STATUS_ERROR:
    493       case UPDATE_STATUS_IDLE:
    494       case UPDATE_STATUS_UPDATED_NEED_REBOOT:
    495       case UPDATE_STATUS_REPORTING_ERROR_EVENT:
    496       case UPDATE_STATUS_ATTEMPTING_ROLLBACK:
    497         return;
    498       case UPDATE_STATUS_CHECKING_FOR_UPDATE:
    499         next_status = UPDATE_STATUS_UPDATE_AVAILABLE;
    500         break;
    501       case UPDATE_STATUS_UPDATE_AVAILABLE:
    502         next_status = UPDATE_STATUS_DOWNLOADING;
    503         break;
    504       case UPDATE_STATUS_DOWNLOADING:
    505         if (last_status_.download_progress >= 1.0) {
    506           next_status = UPDATE_STATUS_VERIFYING;
    507         } else {
    508           next_status = UPDATE_STATUS_DOWNLOADING;
    509           last_status_.download_progress += 0.01;
    510           last_status_.new_size = kDownloadSizeDelta;
    511           delay_ms = kStateTransitionDownloadingDelayMs;
    512         }
    513         break;
    514       case UPDATE_STATUS_VERIFYING:
    515         next_status = UPDATE_STATUS_FINALIZING;
    516         break;
    517       case UPDATE_STATUS_FINALIZING:
    518         next_status = UPDATE_STATUS_IDLE;
    519         break;
    520     }
    521     last_status_.status = next_status;
    522     FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(last_status_));
    523     if (last_status_.status != UPDATE_STATUS_IDLE) {
    524       base::MessageLoop::current()->PostDelayedTask(
    525           FROM_HERE,
    526           base::Bind(&UpdateEngineClientFakeImpl::StateTransition,
    527                      weak_factory_.GetWeakPtr()),
    528           base::TimeDelta::FromMilliseconds(delay_ms));
    529     }
    530   }
    531 
    532   ObserverList<Observer> observers_;
    533   Status last_status_;
    534 
    535   base::WeakPtrFactory<UpdateEngineClientFakeImpl> weak_factory_;
    536 
    537   DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientFakeImpl);
    538 };
    539 
    540 UpdateEngineClient::UpdateEngineClient() {
    541 }
    542 
    543 UpdateEngineClient::~UpdateEngineClient() {
    544 }
    545 
    546 // static
    547 UpdateEngineClient::UpdateCheckCallback
    548 UpdateEngineClient::EmptyUpdateCheckCallback() {
    549   return base::Bind(&EmptyUpdateCheckCallbackBody);
    550 }
    551 
    552 // static
    553 UpdateEngineClient* UpdateEngineClient::Create(
    554     DBusClientImplementationType type) {
    555   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
    556     return new UpdateEngineClientImpl();
    557   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
    558   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestAutoUpdateUI))
    559     return new UpdateEngineClientFakeImpl();
    560   else
    561     return new UpdateEngineClientStubImpl();
    562 }
    563 
    564 }  // namespace chromeos
    565