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 notificatioins 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   if (str == update_engine::kUpdateStatusIdle)
     42     return UpdateEngineClient::UPDATE_STATUS_IDLE;
     43   if (str == update_engine::kUpdateStatusCheckingForUpdate)
     44     return UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE;
     45   if (str == update_engine::kUpdateStatusUpdateAvailable)
     46     return UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE;
     47   if (str == update_engine::kUpdateStatusDownloading)
     48     return UpdateEngineClient::UPDATE_STATUS_DOWNLOADING;
     49   if (str == update_engine::kUpdateStatusVerifying)
     50     return UpdateEngineClient::UPDATE_STATUS_VERIFYING;
     51   if (str == update_engine::kUpdateStatusFinalizing)
     52     return UpdateEngineClient::UPDATE_STATUS_FINALIZING;
     53   if (str == update_engine::kUpdateStatusUpdatedNeedReboot)
     54     return UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT;
     55   if (str == update_engine::kUpdateStatusReportingErrorEvent)
     56     return UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT;
     57   return UpdateEngineClient::UPDATE_STATUS_ERROR;
     58 }
     59 
     60 // Used in UpdateEngineClient::EmptyUpdateCheckCallback().
     61 void EmptyUpdateCheckCallbackBody(
     62     UpdateEngineClient::UpdateCheckResult unused_result) {
     63 }
     64 
     65 bool IsValidChannel(const std::string& channel) {
     66   return channel == kReleaseChannelDev ||
     67       channel == kReleaseChannelBeta ||
     68       channel == kReleaseChannelStable;
     69 }
     70 
     71 }  // namespace
     72 
     73 // The UpdateEngineClient implementation used in production.
     74 class UpdateEngineClientImpl : public UpdateEngineClient {
     75  public:
     76   UpdateEngineClientImpl()
     77       : update_engine_proxy_(NULL), last_status_(), weak_ptr_factory_(this) {}
     78 
     79   virtual ~UpdateEngineClientImpl() {
     80   }
     81 
     82   // UpdateEngineClient implementation:
     83   virtual void AddObserver(Observer* observer) OVERRIDE {
     84     observers_.AddObserver(observer);
     85   }
     86 
     87   virtual void RemoveObserver(Observer* observer) OVERRIDE {
     88     observers_.RemoveObserver(observer);
     89   }
     90 
     91   virtual bool HasObserver(Observer* observer) OVERRIDE {
     92     return observers_.HasObserver(observer);
     93   }
     94 
     95   virtual void RequestUpdateCheck(
     96       const UpdateCheckCallback& callback) OVERRIDE {
     97     dbus::MethodCall method_call(
     98         update_engine::kUpdateEngineInterface,
     99         update_engine::kAttemptUpdate);
    100     dbus::MessageWriter writer(&method_call);
    101     writer.AppendString("");  // Unused.
    102     writer.AppendString("");  // Unused.
    103 
    104     VLOG(1) << "Requesting an update check";
    105     update_engine_proxy_->CallMethod(
    106         &method_call,
    107         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    108         base::Bind(&UpdateEngineClientImpl::OnRequestUpdateCheck,
    109                    weak_ptr_factory_.GetWeakPtr(),
    110                    callback));
    111   }
    112 
    113   virtual void RebootAfterUpdate() OVERRIDE {
    114     dbus::MethodCall method_call(
    115         update_engine::kUpdateEngineInterface,
    116         update_engine::kRebootIfNeeded);
    117 
    118     VLOG(1) << "Requesting a reboot";
    119     update_engine_proxy_->CallMethod(
    120         &method_call,
    121         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    122         base::Bind(&UpdateEngineClientImpl::OnRebootAfterUpdate,
    123                    weak_ptr_factory_.GetWeakPtr()));
    124   }
    125 
    126   virtual Status GetLastStatus() OVERRIDE {
    127     return last_status_;
    128   }
    129 
    130   virtual void SetChannel(const std::string& target_channel,
    131                           bool is_powerwash_allowed) OVERRIDE {
    132     if (!IsValidChannel(target_channel)) {
    133       LOG(ERROR) << "Invalid channel name: " << target_channel;
    134       return;
    135     }
    136 
    137     dbus::MethodCall method_call(
    138         update_engine::kUpdateEngineInterface,
    139         update_engine::kSetChannel);
    140     dbus::MessageWriter writer(&method_call);
    141     writer.AppendString(target_channel);
    142     writer.AppendBool(is_powerwash_allowed);
    143 
    144     VLOG(1) << "Requesting to set channel: "
    145             << "target_channel=" << target_channel << ", "
    146             << "is_powerwash_allowed=" << is_powerwash_allowed;
    147     update_engine_proxy_->CallMethod(
    148         &method_call,
    149         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    150         base::Bind(&UpdateEngineClientImpl::OnSetChannel,
    151                    weak_ptr_factory_.GetWeakPtr()));
    152   }
    153 
    154   virtual void GetChannel(bool get_current_channel,
    155                           const GetChannelCallback& callback) OVERRIDE {
    156     dbus::MethodCall method_call(
    157         update_engine::kUpdateEngineInterface,
    158         update_engine::kGetChannel);
    159     dbus::MessageWriter writer(&method_call);
    160     writer.AppendBool(get_current_channel);
    161 
    162     VLOG(1) << "Requesting to get channel, get_current_channel="
    163             << get_current_channel;
    164     update_engine_proxy_->CallMethod(
    165         &method_call,
    166         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    167         base::Bind(&UpdateEngineClientImpl::OnGetChannel,
    168                    weak_ptr_factory_.GetWeakPtr(),
    169                    callback));
    170   }
    171 
    172  protected:
    173   virtual void Init(dbus::Bus* bus) OVERRIDE {
    174     update_engine_proxy_ = bus->GetObjectProxy(
    175         update_engine::kUpdateEngineServiceName,
    176         dbus::ObjectPath(update_engine::kUpdateEngineServicePath));
    177 
    178     // Monitor the D-Bus signal for brightness changes. Only the power
    179     // manager knows the actual brightness level. We don't cache the
    180     // brightness level in Chrome as it will make things less reliable.
    181     update_engine_proxy_->ConnectToSignal(
    182         update_engine::kUpdateEngineInterface,
    183         update_engine::kStatusUpdate,
    184         base::Bind(&UpdateEngineClientImpl::StatusUpdateReceived,
    185                    weak_ptr_factory_.GetWeakPtr()),
    186         base::Bind(&UpdateEngineClientImpl::StatusUpdateConnected,
    187                    weak_ptr_factory_.GetWeakPtr()));
    188 
    189     // Get update engine status for the initial status. Update engine won't
    190     // send StatusUpdate signal unless there is a status change. If chrome
    191     // crashes after UPDATE_STATUS_UPDATED_NEED_REBOOT status is set,
    192     // restarted chrome would not get this status. See crbug.com/154104.
    193     GetUpdateEngineStatus();
    194   }
    195 
    196  private:
    197   void GetUpdateEngineStatus() {
    198     dbus::MethodCall method_call(
    199         update_engine::kUpdateEngineInterface,
    200         update_engine::kGetStatus);
    201     update_engine_proxy_->CallMethodWithErrorCallback(
    202         &method_call,
    203         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    204         base::Bind(&UpdateEngineClientImpl::OnGetStatus,
    205                    weak_ptr_factory_.GetWeakPtr()),
    206         base::Bind(&UpdateEngineClientImpl::OnGetStatusError,
    207                    weak_ptr_factory_.GetWeakPtr()));
    208   }
    209 
    210   // Called when a response for RequestUpdateCheck() is received.
    211   void OnRequestUpdateCheck(const UpdateCheckCallback& callback,
    212                             dbus::Response* response) {
    213     if (!response) {
    214       LOG(ERROR) << "Failed to request update check";
    215       callback.Run(UPDATE_RESULT_FAILED);
    216       return;
    217     }
    218     callback.Run(UPDATE_RESULT_SUCCESS);
    219   }
    220 
    221   // Called when a response for RebootAfterUpdate() is received.
    222   void OnRebootAfterUpdate(dbus::Response* response) {
    223     if (!response) {
    224       LOG(ERROR) << "Failed to request rebooting after update";
    225       return;
    226     }
    227   }
    228 
    229   // Called when a response for GetStatus is received.
    230   void OnGetStatus(dbus::Response* response) {
    231     if (!response) {
    232       LOG(ERROR) << "Failed to get response for GetStatus request.";
    233       return;
    234     }
    235 
    236     dbus::MessageReader reader(response);
    237     std::string current_operation;
    238     Status status;
    239     if (!(reader.PopInt64(&status.last_checked_time) &&
    240           reader.PopDouble(&status.download_progress) &&
    241           reader.PopString(&current_operation) &&
    242           reader.PopString(&status.new_version) &&
    243           reader.PopInt64(&status.new_size))) {
    244       LOG(ERROR) << "GetStatus had incorrect response: "
    245                  << response->ToString();
    246       return;
    247     }
    248     status.status = UpdateStatusFromString(current_operation);
    249     last_status_ = status;
    250     FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(status));
    251   }
    252 
    253   // Called when GetStatus call failed.
    254   void OnGetStatusError(dbus::ErrorResponse* error) {
    255     LOG(ERROR) << "GetStatus request failed with error: "
    256                << (error ? error->ToString() : "");
    257   }
    258 
    259   // Called when a response for SetReleaseChannel() is received.
    260   void OnSetChannel(dbus::Response* response) {
    261     if (!response) {
    262       LOG(ERROR) << "Failed to request setting channel";
    263       return;
    264     }
    265     VLOG(1) << "Succeeded to set channel";
    266   }
    267 
    268   // Called when a response for GetChannel() is received.
    269   void OnGetChannel(const GetChannelCallback& callback,
    270                     dbus::Response* response) {
    271     if (!response) {
    272       LOG(ERROR) << "Failed to request getting channel";
    273       callback.Run("");
    274       return;
    275     }
    276     dbus::MessageReader reader(response);
    277     std::string channel;
    278     if (!reader.PopString(&channel)) {
    279       LOG(ERROR) << "Incorrect response: " << response->ToString();
    280       callback.Run("");
    281       return;
    282     }
    283     VLOG(1) << "The channel received: " << channel;
    284     callback.Run(channel);
    285   }
    286 
    287   // Called when a status update signal is received.
    288   void StatusUpdateReceived(dbus::Signal* signal) {
    289     VLOG(1) << "Status update signal received: " << signal->ToString();
    290     dbus::MessageReader reader(signal);
    291     int64 last_checked_time = 0;
    292     double progress = 0.0;
    293     std::string current_operation;
    294     std::string new_version;
    295     int64_t new_size = 0;
    296     if (!(reader.PopInt64(&last_checked_time) &&
    297           reader.PopDouble(&progress) &&
    298           reader.PopString(&current_operation) &&
    299           reader.PopString(&new_version) &&
    300           reader.PopInt64(&new_size))) {
    301       LOG(ERROR) << "Status changed signal had incorrect parameters: "
    302                  << signal->ToString();
    303       return;
    304     }
    305     Status status;
    306     status.last_checked_time = last_checked_time;
    307     status.download_progress = progress;
    308     status.status = UpdateStatusFromString(current_operation);
    309     status.new_version = new_version;
    310     status.new_size = new_size;
    311 
    312     last_status_ = status;
    313     FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(status));
    314   }
    315 
    316   // Called when the status update signal is initially connected.
    317   void StatusUpdateConnected(const std::string& interface_name,
    318                              const std::string& signal_name,
    319                              bool success) {
    320     LOG_IF(WARNING, !success)
    321         << "Failed to connect to status updated signal.";
    322   }
    323 
    324   dbus::ObjectProxy* update_engine_proxy_;
    325   ObserverList<Observer> observers_;
    326   Status last_status_;
    327 
    328   // Note: This should remain the last member so it'll be destroyed and
    329   // invalidate its weak pointers before any other members are destroyed.
    330   base::WeakPtrFactory<UpdateEngineClientImpl> weak_ptr_factory_;
    331 
    332   DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientImpl);
    333 };
    334 
    335 // The UpdateEngineClient implementation used on Linux desktop,
    336 // which does nothing.
    337 class UpdateEngineClientStubImpl : public UpdateEngineClient {
    338   // UpdateEngineClient implementation:
    339   virtual void Init(dbus::Bus* bus) OVERRIDE {}
    340   virtual void AddObserver(Observer* observer) OVERRIDE {}
    341   virtual void RemoveObserver(Observer* observer) OVERRIDE {}
    342   virtual bool HasObserver(Observer* observer) OVERRIDE { return false; }
    343 
    344   virtual void RequestUpdateCheck(
    345       const UpdateCheckCallback& callback) OVERRIDE {
    346     callback.Run(UPDATE_RESULT_NOTIMPLEMENTED);
    347   }
    348   virtual void RebootAfterUpdate() OVERRIDE {}
    349   virtual Status GetLastStatus() OVERRIDE { return Status(); }
    350   virtual void SetChannel(const std::string& target_channel,
    351                           bool is_powerwash_allowed) OVERRIDE {
    352     LOG(INFO) << "Requesting to set channel: "
    353               << "target_channel=" << target_channel << ", "
    354               << "is_powerwash_allowed=" << is_powerwash_allowed;
    355   }
    356   virtual void GetChannel(bool get_current_channel,
    357                           const GetChannelCallback& callback) OVERRIDE {
    358     LOG(INFO) << "Requesting to get channel, get_current_channel="
    359               << get_current_channel;
    360     callback.Run(kReleaseChannelBeta);
    361   }
    362 };
    363 
    364 // The UpdateEngineClient implementation used on Linux desktop, which
    365 // tries to emulate real update engine client.
    366 class UpdateEngineClientFakeImpl : public UpdateEngineClientStubImpl {
    367  public:
    368   UpdateEngineClientFakeImpl() : weak_factory_(this) {
    369   }
    370 
    371   virtual ~UpdateEngineClientFakeImpl() {
    372   }
    373 
    374   // UpdateEngineClient implementation:
    375   virtual void AddObserver(Observer* observer) OVERRIDE {
    376     if (observer)
    377       observers_.AddObserver(observer);
    378   }
    379 
    380   virtual void RemoveObserver(Observer* observer) OVERRIDE {
    381     if (observer)
    382       observers_.RemoveObserver(observer);
    383   }
    384 
    385   virtual bool HasObserver(Observer* observer) OVERRIDE {
    386     return observers_.HasObserver(observer);
    387   }
    388 
    389   virtual void RequestUpdateCheck(
    390       const UpdateCheckCallback& callback) OVERRIDE {
    391     if (last_status_.status != UPDATE_STATUS_IDLE) {
    392       callback.Run(UPDATE_RESULT_FAILED);
    393       return;
    394     }
    395     callback.Run(UPDATE_RESULT_SUCCESS);
    396     last_status_.status = UPDATE_STATUS_CHECKING_FOR_UPDATE;
    397     last_status_.download_progress = 0.0;
    398     last_status_.last_checked_time = 0;
    399     last_status_.new_size = 0;
    400     base::MessageLoop::current()->PostDelayedTask(
    401         FROM_HERE,
    402         base::Bind(&UpdateEngineClientFakeImpl::StateTransition,
    403                    weak_factory_.GetWeakPtr()),
    404         base::TimeDelta::FromMilliseconds(kStateTransitionDefaultDelayMs));
    405   }
    406 
    407   virtual Status GetLastStatus() OVERRIDE { return last_status_; }
    408 
    409  private:
    410   void StateTransition() {
    411     UpdateStatusOperation next_status = UPDATE_STATUS_ERROR;
    412     int delay_ms = kStateTransitionDefaultDelayMs;
    413     switch (last_status_.status) {
    414       case UPDATE_STATUS_ERROR:
    415       case UPDATE_STATUS_IDLE:
    416       case UPDATE_STATUS_UPDATED_NEED_REBOOT:
    417       case UPDATE_STATUS_REPORTING_ERROR_EVENT:
    418         return;
    419       case UPDATE_STATUS_CHECKING_FOR_UPDATE:
    420         next_status = UPDATE_STATUS_UPDATE_AVAILABLE;
    421         break;
    422       case UPDATE_STATUS_UPDATE_AVAILABLE:
    423         next_status = UPDATE_STATUS_DOWNLOADING;
    424         break;
    425       case UPDATE_STATUS_DOWNLOADING:
    426         if (last_status_.download_progress >= 1.0) {
    427           next_status = UPDATE_STATUS_VERIFYING;
    428         } else {
    429           next_status = UPDATE_STATUS_DOWNLOADING;
    430           last_status_.download_progress += 0.01;
    431           last_status_.new_size = kDownloadSizeDelta;
    432           delay_ms = kStateTransitionDownloadingDelayMs;
    433         }
    434         break;
    435       case UPDATE_STATUS_VERIFYING:
    436         next_status = UPDATE_STATUS_FINALIZING;
    437         break;
    438       case UPDATE_STATUS_FINALIZING:
    439         next_status = UPDATE_STATUS_IDLE;
    440         break;
    441     }
    442     last_status_.status = next_status;
    443     FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(last_status_));
    444     if (last_status_.status != UPDATE_STATUS_IDLE) {
    445       base::MessageLoop::current()->PostDelayedTask(
    446           FROM_HERE,
    447           base::Bind(&UpdateEngineClientFakeImpl::StateTransition,
    448                      weak_factory_.GetWeakPtr()),
    449           base::TimeDelta::FromMilliseconds(delay_ms));
    450     }
    451   }
    452 
    453   ObserverList<Observer> observers_;
    454   Status last_status_;
    455 
    456   base::WeakPtrFactory<UpdateEngineClientFakeImpl> weak_factory_;
    457 
    458   DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientFakeImpl);
    459 };
    460 
    461 UpdateEngineClient::UpdateEngineClient() {
    462 }
    463 
    464 UpdateEngineClient::~UpdateEngineClient() {
    465 }
    466 
    467 // static
    468 UpdateEngineClient::UpdateCheckCallback
    469 UpdateEngineClient::EmptyUpdateCheckCallback() {
    470   return base::Bind(&EmptyUpdateCheckCallbackBody);
    471 }
    472 
    473 // static
    474 UpdateEngineClient* UpdateEngineClient::Create(
    475     DBusClientImplementationType type) {
    476   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
    477     return new UpdateEngineClientImpl();
    478   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
    479   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestAutoUpdateUI))
    480     return new UpdateEngineClientFakeImpl();
    481   else
    482     return new UpdateEngineClientStubImpl();
    483 }
    484 
    485 }  // namespace chromeos
    486