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(¤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: " 256 << (error ? error->ToString() : ""); 257 } 258 259 // Called when a response for SetReleaseChannel() is received. 260 void OnSetChannel(dbus::Response* response) { 261 if (!response) { 262 LOG(ERROR) << "Failed to request setting channel"; 263 return; 264 } 265 VLOG(1) << "Succeeded to set channel"; 266 } 267 268 // Called when a response for GetChannel() is received. 269 void OnGetChannel(const GetChannelCallback& callback, 270 dbus::Response* response) { 271 if (!response) { 272 LOG(ERROR) << "Failed to request getting channel"; 273 callback.Run(""); 274 return; 275 } 276 dbus::MessageReader reader(response); 277 std::string channel; 278 if (!reader.PopString(&channel)) { 279 LOG(ERROR) << "Incorrect response: " << response->ToString(); 280 callback.Run(""); 281 return; 282 } 283 VLOG(1) << "The channel received: " << channel; 284 callback.Run(channel); 285 } 286 287 // Called when a status update signal is received. 288 void StatusUpdateReceived(dbus::Signal* signal) { 289 VLOG(1) << "Status update signal received: " << signal->ToString(); 290 dbus::MessageReader reader(signal); 291 int64 last_checked_time = 0; 292 double progress = 0.0; 293 std::string current_operation; 294 std::string new_version; 295 int64_t new_size = 0; 296 if (!(reader.PopInt64(&last_checked_time) && 297 reader.PopDouble(&progress) && 298 reader.PopString(¤t_operation) && 299 reader.PopString(&new_version) && 300 reader.PopInt64(&new_size))) { 301 LOG(ERROR) << "Status changed signal had incorrect parameters: " 302 << signal->ToString(); 303 return; 304 } 305 Status status; 306 status.last_checked_time = last_checked_time; 307 status.download_progress = progress; 308 status.status = UpdateStatusFromString(current_operation); 309 status.new_version = new_version; 310 status.new_size = new_size; 311 312 last_status_ = status; 313 FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(status)); 314 } 315 316 // Called when the status update signal is initially connected. 317 void StatusUpdateConnected(const std::string& interface_name, 318 const std::string& signal_name, 319 bool success) { 320 LOG_IF(WARNING, !success) 321 << "Failed to connect to status updated signal."; 322 } 323 324 dbus::ObjectProxy* update_engine_proxy_; 325 ObserverList<Observer> observers_; 326 Status last_status_; 327 328 // Note: This should remain the last member so it'll be destroyed and 329 // invalidate its weak pointers before any other members are destroyed. 330 base::WeakPtrFactory<UpdateEngineClientImpl> weak_ptr_factory_; 331 332 DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientImpl); 333 }; 334 335 // The UpdateEngineClient implementation used on Linux desktop, 336 // which does nothing. 337 class UpdateEngineClientStubImpl : public UpdateEngineClient { 338 // UpdateEngineClient implementation: 339 virtual void Init(dbus::Bus* bus) OVERRIDE {} 340 virtual void AddObserver(Observer* observer) OVERRIDE {} 341 virtual void RemoveObserver(Observer* observer) OVERRIDE {} 342 virtual bool HasObserver(Observer* observer) OVERRIDE { return false; } 343 344 virtual void RequestUpdateCheck( 345 const UpdateCheckCallback& callback) OVERRIDE { 346 callback.Run(UPDATE_RESULT_NOTIMPLEMENTED); 347 } 348 virtual void RebootAfterUpdate() OVERRIDE {} 349 virtual Status GetLastStatus() OVERRIDE { return Status(); } 350 virtual void SetChannel(const std::string& target_channel, 351 bool is_powerwash_allowed) OVERRIDE { 352 LOG(INFO) << "Requesting to set channel: " 353 << "target_channel=" << target_channel << ", " 354 << "is_powerwash_allowed=" << is_powerwash_allowed; 355 } 356 virtual void GetChannel(bool get_current_channel, 357 const GetChannelCallback& callback) OVERRIDE { 358 LOG(INFO) << "Requesting to get channel, get_current_channel=" 359 << get_current_channel; 360 callback.Run(kReleaseChannelBeta); 361 } 362 }; 363 364 // The UpdateEngineClient implementation used on Linux desktop, which 365 // tries to emulate real update engine client. 366 class UpdateEngineClientFakeImpl : public UpdateEngineClientStubImpl { 367 public: 368 UpdateEngineClientFakeImpl() : weak_factory_(this) { 369 } 370 371 virtual ~UpdateEngineClientFakeImpl() { 372 } 373 374 // UpdateEngineClient implementation: 375 virtual void AddObserver(Observer* observer) OVERRIDE { 376 if (observer) 377 observers_.AddObserver(observer); 378 } 379 380 virtual void RemoveObserver(Observer* observer) OVERRIDE { 381 if (observer) 382 observers_.RemoveObserver(observer); 383 } 384 385 virtual bool HasObserver(Observer* observer) OVERRIDE { 386 return observers_.HasObserver(observer); 387 } 388 389 virtual void RequestUpdateCheck( 390 const UpdateCheckCallback& callback) OVERRIDE { 391 if (last_status_.status != UPDATE_STATUS_IDLE) { 392 callback.Run(UPDATE_RESULT_FAILED); 393 return; 394 } 395 callback.Run(UPDATE_RESULT_SUCCESS); 396 last_status_.status = UPDATE_STATUS_CHECKING_FOR_UPDATE; 397 last_status_.download_progress = 0.0; 398 last_status_.last_checked_time = 0; 399 last_status_.new_size = 0; 400 base::MessageLoop::current()->PostDelayedTask( 401 FROM_HERE, 402 base::Bind(&UpdateEngineClientFakeImpl::StateTransition, 403 weak_factory_.GetWeakPtr()), 404 base::TimeDelta::FromMilliseconds(kStateTransitionDefaultDelayMs)); 405 } 406 407 virtual Status GetLastStatus() OVERRIDE { return last_status_; } 408 409 private: 410 void StateTransition() { 411 UpdateStatusOperation next_status = UPDATE_STATUS_ERROR; 412 int delay_ms = kStateTransitionDefaultDelayMs; 413 switch (last_status_.status) { 414 case UPDATE_STATUS_ERROR: 415 case UPDATE_STATUS_IDLE: 416 case UPDATE_STATUS_UPDATED_NEED_REBOOT: 417 case UPDATE_STATUS_REPORTING_ERROR_EVENT: 418 return; 419 case UPDATE_STATUS_CHECKING_FOR_UPDATE: 420 next_status = UPDATE_STATUS_UPDATE_AVAILABLE; 421 break; 422 case UPDATE_STATUS_UPDATE_AVAILABLE: 423 next_status = UPDATE_STATUS_DOWNLOADING; 424 break; 425 case UPDATE_STATUS_DOWNLOADING: 426 if (last_status_.download_progress >= 1.0) { 427 next_status = UPDATE_STATUS_VERIFYING; 428 } else { 429 next_status = UPDATE_STATUS_DOWNLOADING; 430 last_status_.download_progress += 0.01; 431 last_status_.new_size = kDownloadSizeDelta; 432 delay_ms = kStateTransitionDownloadingDelayMs; 433 } 434 break; 435 case UPDATE_STATUS_VERIFYING: 436 next_status = UPDATE_STATUS_FINALIZING; 437 break; 438 case UPDATE_STATUS_FINALIZING: 439 next_status = UPDATE_STATUS_IDLE; 440 break; 441 } 442 last_status_.status = next_status; 443 FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(last_status_)); 444 if (last_status_.status != UPDATE_STATUS_IDLE) { 445 base::MessageLoop::current()->PostDelayedTask( 446 FROM_HERE, 447 base::Bind(&UpdateEngineClientFakeImpl::StateTransition, 448 weak_factory_.GetWeakPtr()), 449 base::TimeDelta::FromMilliseconds(delay_ms)); 450 } 451 } 452 453 ObserverList<Observer> observers_; 454 Status last_status_; 455 456 base::WeakPtrFactory<UpdateEngineClientFakeImpl> weak_factory_; 457 458 DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientFakeImpl); 459 }; 460 461 UpdateEngineClient::UpdateEngineClient() { 462 } 463 464 UpdateEngineClient::~UpdateEngineClient() { 465 } 466 467 // static 468 UpdateEngineClient::UpdateCheckCallback 469 UpdateEngineClient::EmptyUpdateCheckCallback() { 470 return base::Bind(&EmptyUpdateCheckCallbackBody); 471 } 472 473 // static 474 UpdateEngineClient* UpdateEngineClient::Create( 475 DBusClientImplementationType type) { 476 if (type == REAL_DBUS_CLIENT_IMPLEMENTATION) 477 return new UpdateEngineClientImpl(); 478 DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type); 479 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestAutoUpdateUI)) 480 return new UpdateEngineClientFakeImpl(); 481 else 482 return new UpdateEngineClientStubImpl(); 483 } 484 485 } // namespace chromeos 486