- add sources.
[platform/framework/web/crosswalk.git] / src / chromeos / dbus / update_engine_client.cc
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: " << 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.
336 class 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.
365 class 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
460 UpdateEngineClient::UpdateEngineClient() {
461 }
462
463 UpdateEngineClient::~UpdateEngineClient() {
464 }
465
466 // static
467 UpdateEngineClient::UpdateCheckCallback
468 UpdateEngineClient::EmptyUpdateCheckCallback() {
469   return base::Bind(&EmptyUpdateCheckCallbackBody);
470 }
471
472 // static
473 UpdateEngineClient* 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