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