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 notifications 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  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().
64void EmptyUpdateCheckCallbackBody(
65    UpdateEngineClient::UpdateCheckResult unused_result) {
66}
67
68bool 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.
77class 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.
398class 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.
444class 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
540UpdateEngineClient::UpdateEngineClient() {
541}
542
543UpdateEngineClient::~UpdateEngineClient() {
544}
545
546// static
547UpdateEngineClient::UpdateCheckCallback
548UpdateEngineClient::EmptyUpdateCheckCallback() {
549  return base::Bind(&EmptyUpdateCheckCallbackBody);
550}
551
552// static
553UpdateEngineClient* 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