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(¤t_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(¤t_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