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.
5 #include "chromeos/dbus/update_engine_client.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"
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"
23 const char kReleaseChannelDev[] = "dev-channel";
24 const char kReleaseChannelBeta[] = "beta-channel";
25 const char kReleaseChannelStable[] = "stable-channel";
27 // Delay between successive state transitions during AU.
28 const int kStateTransitionDefaultDelayMs = 3000;
30 // Delay between successive notificatioins about downloading progress
32 const int kStateTransitionDownloadingDelayMs = 250;
34 // Size of parts of a "new" image which are downloaded each
35 // |kStateTransitionDownloadingDelayMs| during fake AU.
36 const int64_t kDownloadSizeDelta = 1 << 19;
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;
60 // Used in UpdateEngineClient::EmptyUpdateCheckCallback().
61 void EmptyUpdateCheckCallbackBody(
62 UpdateEngineClient::UpdateCheckResult unused_result) {
65 bool IsValidChannel(const std::string& channel) {
66 return channel == kReleaseChannelDev ||
67 channel == kReleaseChannelBeta ||
68 channel == kReleaseChannelStable;
73 // The UpdateEngineClient implementation used in production.
74 class UpdateEngineClientImpl : public UpdateEngineClient {
76 UpdateEngineClientImpl()
77 : update_engine_proxy_(NULL), last_status_(), weak_ptr_factory_(this) {}
79 virtual ~UpdateEngineClientImpl() {
82 // UpdateEngineClient implementation:
83 virtual void AddObserver(Observer* observer) OVERRIDE {
84 observers_.AddObserver(observer);
87 virtual void RemoveObserver(Observer* observer) OVERRIDE {
88 observers_.RemoveObserver(observer);
91 virtual bool HasObserver(Observer* observer) OVERRIDE {
92 return observers_.HasObserver(observer);
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.
104 VLOG(1) << "Requesting an update check";
105 update_engine_proxy_->CallMethod(
107 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
108 base::Bind(&UpdateEngineClientImpl::OnRequestUpdateCheck,
109 weak_ptr_factory_.GetWeakPtr(),
113 virtual void RebootAfterUpdate() OVERRIDE {
114 dbus::MethodCall method_call(
115 update_engine::kUpdateEngineInterface,
116 update_engine::kRebootIfNeeded);
118 VLOG(1) << "Requesting a reboot";
119 update_engine_proxy_->CallMethod(
121 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
122 base::Bind(&UpdateEngineClientImpl::OnRebootAfterUpdate,
123 weak_ptr_factory_.GetWeakPtr()));
126 virtual Status GetLastStatus() OVERRIDE {
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;
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);
144 VLOG(1) << "Requesting to set channel: "
145 << "target_channel=" << target_channel << ", "
146 << "is_powerwash_allowed=" << is_powerwash_allowed;
147 update_engine_proxy_->CallMethod(
149 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
150 base::Bind(&UpdateEngineClientImpl::OnSetChannel,
151 weak_ptr_factory_.GetWeakPtr()));
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);
162 VLOG(1) << "Requesting to get channel, get_current_channel="
163 << get_current_channel;
164 update_engine_proxy_->CallMethod(
166 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
167 base::Bind(&UpdateEngineClientImpl::OnGetChannel,
168 weak_ptr_factory_.GetWeakPtr(),
173 virtual void Init(dbus::Bus* bus) OVERRIDE {
174 update_engine_proxy_ = bus->GetObjectProxy(
175 update_engine::kUpdateEngineServiceName,
176 dbus::ObjectPath(update_engine::kUpdateEngineServicePath));
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()));
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();
197 void GetUpdateEngineStatus() {
198 dbus::MethodCall method_call(
199 update_engine::kUpdateEngineInterface,
200 update_engine::kGetStatus);
201 update_engine_proxy_->CallMethodWithErrorCallback(
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()));
210 // Called when a response for RequestUpdateCheck() is received.
211 void OnRequestUpdateCheck(const UpdateCheckCallback& callback,
212 dbus::Response* response) {
214 LOG(ERROR) << "Failed to request update check";
215 callback.Run(UPDATE_RESULT_FAILED);
218 callback.Run(UPDATE_RESULT_SUCCESS);
221 // Called when a response for RebootAfterUpdate() is received.
222 void OnRebootAfterUpdate(dbus::Response* response) {
224 LOG(ERROR) << "Failed to request rebooting after update";
229 // Called when a response for GetStatus is received.
230 void OnGetStatus(dbus::Response* response) {
232 LOG(ERROR) << "Failed to get response for GetStatus request.";
236 dbus::MessageReader reader(response);
237 std::string current_operation;
239 if (!(reader.PopInt64(&status.last_checked_time) &&
240 reader.PopDouble(&status.download_progress) &&
241 reader.PopString(¤t_operation) &&
242 reader.PopString(&status.new_version) &&
243 reader.PopInt64(&status.new_size))) {
244 LOG(ERROR) << "GetStatus had incorrect response: "
245 << response->ToString();
248 status.status = UpdateStatusFromString(current_operation);
249 last_status_ = status;
250 FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(status));
253 // Called when GetStatus call failed.
254 void OnGetStatusError(dbus::ErrorResponse* error) {
255 LOG(ERROR) << "GetStatus request failed with error: " << error->ToString();
258 // Called when a response for SetReleaseChannel() is received.
259 void OnSetChannel(dbus::Response* response) {
261 LOG(ERROR) << "Failed to request setting channel";
264 VLOG(1) << "Succeeded to set channel";
267 // Called when a response for GetChannel() is received.
268 void OnGetChannel(const GetChannelCallback& callback,
269 dbus::Response* response) {
271 LOG(ERROR) << "Failed to request getting channel";
275 dbus::MessageReader reader(response);
277 if (!reader.PopString(&channel)) {
278 LOG(ERROR) << "Incorrect response: " << response->ToString();
282 VLOG(1) << "The channel received: " << channel;
283 callback.Run(channel);
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(¤t_operation) &&
298 reader.PopString(&new_version) &&
299 reader.PopInt64(&new_size))) {
300 LOG(ERROR) << "Status changed signal had incorrect parameters: "
301 << signal->ToString();
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;
311 last_status_ = status;
312 FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(status));
315 // Called when the status update signal is initially connected.
316 void StatusUpdateConnected(const std::string& interface_name,
317 const std::string& signal_name,
319 LOG_IF(WARNING, !success)
320 << "Failed to connect to status updated signal.";
323 dbus::ObjectProxy* update_engine_proxy_;
324 ObserverList<Observer> observers_;
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_;
331 DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientImpl);
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; }
343 virtual void RequestUpdateCheck(
344 const UpdateCheckCallback& callback) OVERRIDE {
345 callback.Run(UPDATE_RESULT_NOTIMPLEMENTED);
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;
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);
363 // The UpdateEngineClient implementation used on Linux desktop, which
364 // tries to emulate real update engine client.
365 class UpdateEngineClientFakeImpl : public UpdateEngineClientStubImpl {
367 UpdateEngineClientFakeImpl() : weak_factory_(this) {
370 virtual ~UpdateEngineClientFakeImpl() {
373 // UpdateEngineClient implementation:
374 virtual void AddObserver(Observer* observer) OVERRIDE {
376 observers_.AddObserver(observer);
379 virtual void RemoveObserver(Observer* observer) OVERRIDE {
381 observers_.RemoveObserver(observer);
384 virtual bool HasObserver(Observer* observer) OVERRIDE {
385 return observers_.HasObserver(observer);
388 virtual void RequestUpdateCheck(
389 const UpdateCheckCallback& callback) OVERRIDE {
390 if (last_status_.status != UPDATE_STATUS_IDLE) {
391 callback.Run(UPDATE_RESULT_FAILED);
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(
401 base::Bind(&UpdateEngineClientFakeImpl::StateTransition,
402 weak_factory_.GetWeakPtr()),
403 base::TimeDelta::FromMilliseconds(kStateTransitionDefaultDelayMs));
406 virtual Status GetLastStatus() OVERRIDE { return last_status_; }
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:
418 case UPDATE_STATUS_CHECKING_FOR_UPDATE:
419 next_status = UPDATE_STATUS_UPDATE_AVAILABLE;
421 case UPDATE_STATUS_UPDATE_AVAILABLE:
422 next_status = UPDATE_STATUS_DOWNLOADING;
424 case UPDATE_STATUS_DOWNLOADING:
425 if (last_status_.download_progress >= 1.0) {
426 next_status = UPDATE_STATUS_VERIFYING;
428 next_status = UPDATE_STATUS_DOWNLOADING;
429 last_status_.download_progress += 0.01;
430 last_status_.new_size = kDownloadSizeDelta;
431 delay_ms = kStateTransitionDownloadingDelayMs;
434 case UPDATE_STATUS_VERIFYING:
435 next_status = UPDATE_STATUS_FINALIZING;
437 case UPDATE_STATUS_FINALIZING:
438 next_status = UPDATE_STATUS_IDLE;
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(
446 base::Bind(&UpdateEngineClientFakeImpl::StateTransition,
447 weak_factory_.GetWeakPtr()),
448 base::TimeDelta::FromMilliseconds(delay_ms));
452 ObserverList<Observer> observers_;
455 base::WeakPtrFactory<UpdateEngineClientFakeImpl> weak_factory_;
457 DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientFakeImpl);
460 UpdateEngineClient::UpdateEngineClient() {
463 UpdateEngineClient::~UpdateEngineClient() {
467 UpdateEngineClient::UpdateCheckCallback
468 UpdateEngineClient::EmptyUpdateCheckCallback() {
469 return base::Bind(&EmptyUpdateCheckCallbackBody);
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();
481 return new UpdateEngineClientStubImpl();
484 } // namespace chromeos