update_engine_client.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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
19namespace chromeos {
20
21namespace {
22
23const char kReleaseChannelDev[] = "dev-channel";
24const char kReleaseChannelBeta[] = "beta-channel";
25const char kReleaseChannelStable[] = "stable-channel";
26
27// Delay between successive state transitions during AU.
28const int kStateTransitionDefaultDelayMs = 3000;
29
30// Delay between successive notificatioins about downloading progress
31// during fake AU.
32const int kStateTransitionDownloadingDelayMs = 250;
33
34// Size of parts of a "new" image which are downloaded each
35// |kStateTransitionDownloadingDelayMs| during fake AU.
36const int64_t kDownloadSizeDelta = 1 << 19;
37
38// Returns UPDATE_STATUS_ERROR on error.
39UpdateEngineClient::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().
61void EmptyUpdateCheckCallbackBody(
62    UpdateEngineClient::UpdateCheckResult unused_result) {
63}
64
65bool 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.
74class 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: " << error->ToString();
256  }
257
258  // Called when a response for SetReleaseChannel() is received.
259  void OnSetChannel(dbus::Response* response) {
260    if (!response) {
261      LOG(ERROR) << "Failed to request setting channel";
262      return;
263    }
264    VLOG(1) << "Succeeded to set channel";
265  }
266
267  // Called when a response for GetChannel() is received.
268  void OnGetChannel(const GetChannelCallback& callback,
269                    dbus::Response* response) {
270    if (!response) {
271      LOG(ERROR) << "Failed to request getting channel";
272      callback.Run("");
273      return;
274    }
275    dbus::MessageReader reader(response);
276    std::string channel;
277    if (!reader.PopString(&channel)) {
278      LOG(ERROR) << "Incorrect response: " << response->ToString();
279      callback.Run("");
280      return;
281    }
282    VLOG(1) << "The channel received: " << channel;
283    callback.Run(channel);
284  }
285
286  // Called when a status update signal is received.
287  void StatusUpdateReceived(dbus::Signal* signal) {
288    VLOG(1) << "Status update signal received: " << signal->ToString();
289    dbus::MessageReader reader(signal);
290    int64 last_checked_time = 0;
291    double progress = 0.0;
292    std::string current_operation;
293    std::string new_version;
294    int64_t new_size = 0;
295    if (!(reader.PopInt64(&last_checked_time) &&
296          reader.PopDouble(&progress) &&
297          reader.PopString(&current_operation) &&
298          reader.PopString(&new_version) &&
299          reader.PopInt64(&new_size))) {
300      LOG(ERROR) << "Status changed signal had incorrect parameters: "
301                 << signal->ToString();
302      return;
303    }
304    Status status;
305    status.last_checked_time = last_checked_time;
306    status.download_progress = progress;
307    status.status = UpdateStatusFromString(current_operation);
308    status.new_version = new_version;
309    status.new_size = new_size;
310
311    last_status_ = status;
312    FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(status));
313  }
314
315  // Called when the status update signal is initially connected.
316  void StatusUpdateConnected(const std::string& interface_name,
317                             const std::string& signal_name,
318                             bool success) {
319    LOG_IF(WARNING, !success)
320        << "Failed to connect to status updated signal.";
321  }
322
323  dbus::ObjectProxy* update_engine_proxy_;
324  ObserverList<Observer> observers_;
325  Status last_status_;
326
327  // Note: This should remain the last member so it'll be destroyed and
328  // invalidate its weak pointers before any other members are destroyed.
329  base::WeakPtrFactory<UpdateEngineClientImpl> weak_ptr_factory_;
330
331  DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientImpl);
332};
333
334// The UpdateEngineClient implementation used on Linux desktop,
335// which does nothing.
336class UpdateEngineClientStubImpl : public UpdateEngineClient {
337  // UpdateEngineClient implementation:
338  virtual void Init(dbus::Bus* bus) OVERRIDE {}
339  virtual void AddObserver(Observer* observer) OVERRIDE {}
340  virtual void RemoveObserver(Observer* observer) OVERRIDE {}
341  virtual bool HasObserver(Observer* observer) OVERRIDE { return false; }
342
343  virtual void RequestUpdateCheck(
344      const UpdateCheckCallback& callback) OVERRIDE {
345    callback.Run(UPDATE_RESULT_NOTIMPLEMENTED);
346  }
347  virtual void RebootAfterUpdate() OVERRIDE {}
348  virtual Status GetLastStatus() OVERRIDE { return Status(); }
349  virtual void SetChannel(const std::string& target_channel,
350                          bool is_powerwash_allowed) OVERRIDE {
351    LOG(INFO) << "Requesting to set channel: "
352              << "target_channel=" << target_channel << ", "
353              << "is_powerwash_allowed=" << is_powerwash_allowed;
354  }
355  virtual void GetChannel(bool get_current_channel,
356                          const GetChannelCallback& callback) OVERRIDE {
357    LOG(INFO) << "Requesting to get channel, get_current_channel="
358              << get_current_channel;
359    callback.Run(kReleaseChannelBeta);
360  }
361};
362
363// The UpdateEngineClient implementation used on Linux desktop, which
364// tries to emulate real update engine client.
365class UpdateEngineClientFakeImpl : public UpdateEngineClientStubImpl {
366 public:
367  UpdateEngineClientFakeImpl() : weak_factory_(this) {
368  }
369
370  virtual ~UpdateEngineClientFakeImpl() {
371  }
372
373  // UpdateEngineClient implementation:
374  virtual void AddObserver(Observer* observer) OVERRIDE {
375    if (observer)
376      observers_.AddObserver(observer);
377  }
378
379  virtual void RemoveObserver(Observer* observer) OVERRIDE {
380    if (observer)
381      observers_.RemoveObserver(observer);
382  }
383
384  virtual bool HasObserver(Observer* observer) OVERRIDE {
385    return observers_.HasObserver(observer);
386  }
387
388  virtual void RequestUpdateCheck(
389      const UpdateCheckCallback& callback) OVERRIDE {
390    if (last_status_.status != UPDATE_STATUS_IDLE) {
391      callback.Run(UPDATE_RESULT_FAILED);
392      return;
393    }
394    callback.Run(UPDATE_RESULT_SUCCESS);
395    last_status_.status = UPDATE_STATUS_CHECKING_FOR_UPDATE;
396    last_status_.download_progress = 0.0;
397    last_status_.last_checked_time = 0;
398    last_status_.new_size = 0;
399    base::MessageLoop::current()->PostDelayedTask(
400        FROM_HERE,
401        base::Bind(&UpdateEngineClientFakeImpl::StateTransition,
402                   weak_factory_.GetWeakPtr()),
403        base::TimeDelta::FromMilliseconds(kStateTransitionDefaultDelayMs));
404  }
405
406  virtual Status GetLastStatus() OVERRIDE { return last_status_; }
407
408 private:
409  void StateTransition() {
410    UpdateStatusOperation next_status = UPDATE_STATUS_ERROR;
411    int delay_ms = kStateTransitionDefaultDelayMs;
412    switch (last_status_.status) {
413      case UPDATE_STATUS_ERROR:
414      case UPDATE_STATUS_IDLE:
415      case UPDATE_STATUS_UPDATED_NEED_REBOOT:
416      case UPDATE_STATUS_REPORTING_ERROR_EVENT:
417        return;
418      case UPDATE_STATUS_CHECKING_FOR_UPDATE:
419        next_status = UPDATE_STATUS_UPDATE_AVAILABLE;
420        break;
421      case UPDATE_STATUS_UPDATE_AVAILABLE:
422        next_status = UPDATE_STATUS_DOWNLOADING;
423        break;
424      case UPDATE_STATUS_DOWNLOADING:
425        if (last_status_.download_progress >= 1.0) {
426          next_status = UPDATE_STATUS_VERIFYING;
427        } else {
428          next_status = UPDATE_STATUS_DOWNLOADING;
429          last_status_.download_progress += 0.01;
430          last_status_.new_size = kDownloadSizeDelta;
431          delay_ms = kStateTransitionDownloadingDelayMs;
432        }
433        break;
434      case UPDATE_STATUS_VERIFYING:
435        next_status = UPDATE_STATUS_FINALIZING;
436        break;
437      case UPDATE_STATUS_FINALIZING:
438        next_status = UPDATE_STATUS_IDLE;
439        break;
440    }
441    last_status_.status = next_status;
442    FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(last_status_));
443    if (last_status_.status != UPDATE_STATUS_IDLE) {
444      base::MessageLoop::current()->PostDelayedTask(
445          FROM_HERE,
446          base::Bind(&UpdateEngineClientFakeImpl::StateTransition,
447                     weak_factory_.GetWeakPtr()),
448          base::TimeDelta::FromMilliseconds(delay_ms));
449    }
450  }
451
452  ObserverList<Observer> observers_;
453  Status last_status_;
454
455  base::WeakPtrFactory<UpdateEngineClientFakeImpl> weak_factory_;
456
457  DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientFakeImpl);
458};
459
460UpdateEngineClient::UpdateEngineClient() {
461}
462
463UpdateEngineClient::~UpdateEngineClient() {
464}
465
466// static
467UpdateEngineClient::UpdateCheckCallback
468UpdateEngineClient::EmptyUpdateCheckCallback() {
469  return base::Bind(&EmptyUpdateCheckCallbackBody);
470}
471
472// static
473UpdateEngineClient* UpdateEngineClient::Create(
474    DBusClientImplementationType type) {
475  if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
476    return new UpdateEngineClientImpl();
477  DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
478  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestAutoUpdateUI))
479    return new UpdateEngineClientFakeImpl();
480  else
481    return new UpdateEngineClientStubImpl();
482}
483
484}  // namespace chromeos
485