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/strings/string_util.h"
     10 #include "dbus/bus.h"
     11 #include "dbus/message.h"
     12 #include "dbus/object_path.h"
     13 #include "dbus/object_proxy.h"
     14 #include "third_party/cros_system_api/dbus/service_constants.h"
     15 
     16 namespace chromeos {
     17 
     18 namespace {
     19 
     20 const char kReleaseChannelDev[] = "dev-channel";
     21 const char kReleaseChannelBeta[] = "beta-channel";
     22 const char kReleaseChannelStable[] = "stable-channel";
     23 
     24 // Returns UPDATE_STATUS_ERROR on error.
     25 UpdateEngineClient::UpdateStatusOperation UpdateStatusFromString(
     26     const std::string& str) {
     27   if (str == "UPDATE_STATUS_IDLE")
     28     return UpdateEngineClient::UPDATE_STATUS_IDLE;
     29   if (str == "UPDATE_STATUS_CHECKING_FOR_UPDATE")
     30     return UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE;
     31   if (str == "UPDATE_STATUS_UPDATE_AVAILABLE")
     32     return UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE;
     33   if (str == "UPDATE_STATUS_DOWNLOADING")
     34     return UpdateEngineClient::UPDATE_STATUS_DOWNLOADING;
     35   if (str == "UPDATE_STATUS_VERIFYING")
     36     return UpdateEngineClient::UPDATE_STATUS_VERIFYING;
     37   if (str == "UPDATE_STATUS_FINALIZING")
     38     return UpdateEngineClient::UPDATE_STATUS_FINALIZING;
     39   if (str == "UPDATE_STATUS_UPDATED_NEED_REBOOT")
     40     return UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT;
     41   if (str == "UPDATE_STATUS_REPORTING_ERROR_EVENT")
     42     return UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT;
     43   return UpdateEngineClient::UPDATE_STATUS_ERROR;
     44 }
     45 
     46 // Used in UpdateEngineClient::EmptyUpdateCheckCallback().
     47 void EmptyUpdateCheckCallbackBody(
     48     UpdateEngineClient::UpdateCheckResult unused_result) {
     49 }
     50 
     51 bool IsValidChannel(const std::string& channel) {
     52   return channel == kReleaseChannelDev ||
     53       channel == kReleaseChannelBeta ||
     54       channel == kReleaseChannelStable;
     55 }
     56 
     57 }  // namespace
     58 
     59 // The UpdateEngineClient implementation used in production.
     60 class UpdateEngineClientImpl : public UpdateEngineClient {
     61  public:
     62   explicit UpdateEngineClientImpl(dbus::Bus* bus)
     63       : update_engine_proxy_(NULL),
     64         last_status_(),
     65         weak_ptr_factory_(this) {
     66     update_engine_proxy_ = bus->GetObjectProxy(
     67         update_engine::kUpdateEngineServiceName,
     68         dbus::ObjectPath(update_engine::kUpdateEngineServicePath));
     69 
     70     // Monitor the D-Bus signal for brightness changes. Only the power
     71     // manager knows the actual brightness level. We don't cache the
     72     // brightness level in Chrome as it will make things less reliable.
     73     update_engine_proxy_->ConnectToSignal(
     74         update_engine::kUpdateEngineInterface,
     75         update_engine::kStatusUpdate,
     76         base::Bind(&UpdateEngineClientImpl::StatusUpdateReceived,
     77                    weak_ptr_factory_.GetWeakPtr()),
     78         base::Bind(&UpdateEngineClientImpl::StatusUpdateConnected,
     79                    weak_ptr_factory_.GetWeakPtr()));
     80 
     81     // Get update engine status for the initial status. Update engine won't
     82     // send StatusUpdate signal unless there is a status change. If chrome
     83     // crashes after UPDATE_STATUS_UPDATED_NEED_REBOOT status is set,
     84     // restarted chrome would not get this status. See crbug.com/154104.
     85     GetUpdateEngineStatus();
     86   }
     87 
     88   virtual ~UpdateEngineClientImpl() {
     89   }
     90 
     91   // UpdateEngineClient implementation:
     92   virtual void AddObserver(Observer* observer) OVERRIDE {
     93     observers_.AddObserver(observer);
     94   }
     95 
     96   virtual void RemoveObserver(Observer* observer) OVERRIDE {
     97     observers_.RemoveObserver(observer);
     98   }
     99 
    100   virtual bool HasObserver(Observer* observer) OVERRIDE {
    101     return observers_.HasObserver(observer);
    102   }
    103 
    104   virtual void RequestUpdateCheck(
    105       const UpdateCheckCallback& callback) OVERRIDE {
    106     dbus::MethodCall method_call(
    107         update_engine::kUpdateEngineInterface,
    108         update_engine::kAttemptUpdate);
    109     dbus::MessageWriter writer(&method_call);
    110     writer.AppendString("");  // Unused.
    111     writer.AppendString("");  // Unused.
    112 
    113     VLOG(1) << "Requesting an update check";
    114     update_engine_proxy_->CallMethod(
    115         &method_call,
    116         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    117         base::Bind(&UpdateEngineClientImpl::OnRequestUpdateCheck,
    118                    weak_ptr_factory_.GetWeakPtr(),
    119                    callback));
    120   }
    121 
    122   virtual void RebootAfterUpdate() OVERRIDE {
    123     dbus::MethodCall method_call(
    124         update_engine::kUpdateEngineInterface,
    125         update_engine::kRebootIfNeeded);
    126 
    127     VLOG(1) << "Requesting a reboot";
    128     update_engine_proxy_->CallMethod(
    129         &method_call,
    130         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    131         base::Bind(&UpdateEngineClientImpl::OnRebootAfterUpdate,
    132                    weak_ptr_factory_.GetWeakPtr()));
    133   }
    134 
    135   virtual Status GetLastStatus() OVERRIDE {
    136     return last_status_;
    137   }
    138 
    139   virtual void SetChannel(const std::string& target_channel,
    140                           bool is_powerwash_allowed) OVERRIDE {
    141     if (!IsValidChannel(target_channel)) {
    142       LOG(ERROR) << "Invalid channel name: " << target_channel;
    143       return;
    144     }
    145 
    146     dbus::MethodCall method_call(
    147         update_engine::kUpdateEngineInterface,
    148         update_engine::kSetChannel);
    149     dbus::MessageWriter writer(&method_call);
    150     writer.AppendString(target_channel);
    151     writer.AppendBool(is_powerwash_allowed);
    152 
    153     VLOG(1) << "Requesting to set channel: "
    154             << "target_channel=" << target_channel << ", "
    155             << "is_powerwash_allowed=" << is_powerwash_allowed;
    156     update_engine_proxy_->CallMethod(
    157         &method_call,
    158         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    159         base::Bind(&UpdateEngineClientImpl::OnSetChannel,
    160                    weak_ptr_factory_.GetWeakPtr()));
    161   }
    162 
    163   virtual void GetChannel(bool get_current_channel,
    164                           const GetChannelCallback& callback) OVERRIDE {
    165     dbus::MethodCall method_call(
    166         update_engine::kUpdateEngineInterface,
    167         update_engine::kGetChannel);
    168     dbus::MessageWriter writer(&method_call);
    169     writer.AppendBool(get_current_channel);
    170 
    171     VLOG(1) << "Requesting to get channel, get_current_channel="
    172             << get_current_channel;
    173     update_engine_proxy_->CallMethod(
    174         &method_call,
    175         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    176         base::Bind(&UpdateEngineClientImpl::OnGetChannel,
    177                    weak_ptr_factory_.GetWeakPtr(),
    178                    callback));
    179   }
    180 
    181  private:
    182   void GetUpdateEngineStatus() {
    183     dbus::MethodCall method_call(
    184         update_engine::kUpdateEngineInterface,
    185         update_engine::kGetStatus);
    186     update_engine_proxy_->CallMethodWithErrorCallback(
    187         &method_call,
    188         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    189         base::Bind(&UpdateEngineClientImpl::OnGetStatus,
    190                    weak_ptr_factory_.GetWeakPtr()),
    191         base::Bind(&UpdateEngineClientImpl::OnGetStatusError,
    192                    weak_ptr_factory_.GetWeakPtr()));
    193   }
    194 
    195   // Called when a response for RequestUpdateCheck() is received.
    196   void OnRequestUpdateCheck(const UpdateCheckCallback& callback,
    197                             dbus::Response* response) {
    198     if (!response) {
    199       LOG(ERROR) << "Failed to request update check";
    200       callback.Run(UPDATE_RESULT_FAILED);
    201       return;
    202     }
    203     callback.Run(UPDATE_RESULT_SUCCESS);
    204   }
    205 
    206   // Called when a response for RebootAfterUpdate() is received.
    207   void OnRebootAfterUpdate(dbus::Response* response) {
    208     if (!response) {
    209       LOG(ERROR) << "Failed to request rebooting after update";
    210       return;
    211     }
    212   }
    213 
    214   // Called when a response for GetStatus is received.
    215   void OnGetStatus(dbus::Response* response) {
    216     if (!response) {
    217       LOG(ERROR) << "Failed to get response for GetStatus request.";
    218       return;
    219     }
    220 
    221     dbus::MessageReader reader(response);
    222     std::string current_operation;
    223     Status status;
    224     if (!(reader.PopInt64(&status.last_checked_time) &&
    225           reader.PopDouble(&status.download_progress) &&
    226           reader.PopString(&current_operation) &&
    227           reader.PopString(&status.new_version) &&
    228           reader.PopInt64(&status.new_size))) {
    229       LOG(ERROR) << "GetStatus had incorrect response: "
    230                  << response->ToString();
    231       return;
    232     }
    233     status.status = UpdateStatusFromString(current_operation);
    234     last_status_ = status;
    235     FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(status));
    236   }
    237 
    238   // Called when GetStatus call failed.
    239   void OnGetStatusError(dbus::ErrorResponse* error) {
    240     LOG(ERROR) << "GetStatus request failed with error: " << error->ToString();
    241   }
    242 
    243   // Called when a response for SetReleaseChannel() is received.
    244   void OnSetChannel(dbus::Response* response) {
    245     if (!response) {
    246       LOG(ERROR) << "Failed to request setting channel";
    247       return;
    248     }
    249     VLOG(1) << "Succeeded to set channel";
    250   }
    251 
    252   // Called when a response for GetChannel() is received.
    253   void OnGetChannel(const GetChannelCallback& callback,
    254                     dbus::Response* response) {
    255     if (!response) {
    256       LOG(ERROR) << "Failed to request getting channel";
    257       callback.Run("");
    258       return;
    259     }
    260     dbus::MessageReader reader(response);
    261     std::string channel;
    262     if (!reader.PopString(&channel)) {
    263       LOG(ERROR) << "Incorrect response: " << response->ToString();
    264       callback.Run("");
    265       return;
    266     }
    267     VLOG(1) << "The channel received: " << channel;
    268     callback.Run(channel);
    269   }
    270 
    271   // Called when a status update signal is received.
    272   void StatusUpdateReceived(dbus::Signal* signal) {
    273     VLOG(1) << "Status update signal received: " << signal->ToString();
    274     dbus::MessageReader reader(signal);
    275     int64 last_checked_time = 0;
    276     double progress = 0.0;
    277     std::string current_operation;
    278     std::string new_version;
    279     int64_t new_size = 0;
    280     if (!(reader.PopInt64(&last_checked_time) &&
    281           reader.PopDouble(&progress) &&
    282           reader.PopString(&current_operation) &&
    283           reader.PopString(&new_version) &&
    284           reader.PopInt64(&new_size))) {
    285       LOG(ERROR) << "Status changed signal had incorrect parameters: "
    286                  << signal->ToString();
    287       return;
    288     }
    289     Status status;
    290     status.last_checked_time = last_checked_time;
    291     status.download_progress = progress;
    292     status.status = UpdateStatusFromString(current_operation);
    293     status.new_version = new_version;
    294     status.new_size = new_size;
    295 
    296     last_status_ = status;
    297     FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(status));
    298   }
    299 
    300   // Called when the status update signal is initially connected.
    301   void StatusUpdateConnected(const std::string& interface_name,
    302                              const std::string& signal_name,
    303                              bool success) {
    304     LOG_IF(WARNING, !success)
    305         << "Failed to connect to status updated signal.";
    306   }
    307 
    308   dbus::ObjectProxy* update_engine_proxy_;
    309   ObserverList<Observer> observers_;
    310   Status last_status_;
    311 
    312   // Note: This should remain the last member so it'll be destroyed and
    313   // invalidate its weak pointers before any other members are destroyed.
    314   base::WeakPtrFactory<UpdateEngineClientImpl> weak_ptr_factory_;
    315 
    316   DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientImpl);
    317 };
    318 
    319 // The UpdateEngineClient implementation used on Linux desktop,
    320 // which does nothing.
    321 class UpdateEngineClientStubImpl : public UpdateEngineClient {
    322   // UpdateEngineClient implementation:
    323   virtual void AddObserver(Observer* observer) OVERRIDE {}
    324   virtual void RemoveObserver(Observer* observer) OVERRIDE {}
    325   virtual bool HasObserver(Observer* observer) OVERRIDE { return false; }
    326 
    327   virtual void RequestUpdateCheck(
    328       const UpdateCheckCallback& callback) OVERRIDE {
    329     callback.Run(UPDATE_RESULT_NOTIMPLEMENTED);
    330   }
    331   virtual void RebootAfterUpdate() OVERRIDE {}
    332   virtual Status GetLastStatus() OVERRIDE { return Status(); }
    333   virtual void SetChannel(const std::string& target_channel,
    334                           bool is_powerwash_allowed) OVERRIDE {
    335     LOG(INFO) << "Requesting to set channel: "
    336               << "target_channel=" << target_channel << ", "
    337               << "is_powerwash_allowed=" << is_powerwash_allowed;
    338   }
    339   virtual void GetChannel(bool get_current_channel,
    340                           const GetChannelCallback& callback) OVERRIDE {
    341     LOG(INFO) << "Requesting to get channel, get_current_channel="
    342               << get_current_channel;
    343     callback.Run("beta-channel");
    344   }
    345 };
    346 
    347 UpdateEngineClient::UpdateEngineClient() {
    348 }
    349 
    350 UpdateEngineClient::~UpdateEngineClient() {
    351 }
    352 
    353 // static
    354 UpdateEngineClient::UpdateCheckCallback
    355 UpdateEngineClient::EmptyUpdateCheckCallback() {
    356   return base::Bind(&EmptyUpdateCheckCallbackBody);
    357 }
    358 
    359 // static
    360 UpdateEngineClient* UpdateEngineClient::Create(
    361     DBusClientImplementationType type,
    362     dbus::Bus* bus) {
    363   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
    364     return new UpdateEngineClientImpl(bus);
    365   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
    366   return new UpdateEngineClientStubImpl();
    367 }
    368 
    369 }  // namespace chromeos
    370