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 "chrome/browser/chromeos/login/screens/update_screen.h" 6 7 #include <algorithm> 8 9 #include "base/bind.h" 10 #include "base/files/file_util.h" 11 #include "base/logging.h" 12 #include "base/message_loop/message_loop.h" 13 #include "base/threading/thread_restrictions.h" 14 #include "chrome/browser/chromeos/login/screen_manager.h" 15 #include "chrome/browser/chromeos/login/screens/error_screen.h" 16 #include "chrome/browser/chromeos/login/screens/screen_observer.h" 17 #include "chrome/browser/chromeos/login/screens/update_screen_actor.h" 18 #include "chrome/browser/chromeos/login/startup_utils.h" 19 #include "chrome/browser/chromeos/login/wizard_controller.h" 20 #include "chromeos/dbus/dbus_thread_manager.h" 21 #include "chromeos/network/network_state.h" 22 #include "content/public/browser/browser_thread.h" 23 24 using content::BrowserThread; 25 26 namespace chromeos { 27 28 namespace { 29 30 // Progress bar stages. Each represents progress bar value 31 // at the beginning of each stage. 32 // TODO(nkostylev): Base stage progress values on approximate time. 33 // TODO(nkostylev): Animate progress during each state. 34 const int kBeforeUpdateCheckProgress = 7; 35 const int kBeforeDownloadProgress = 14; 36 const int kBeforeVerifyingProgress = 74; 37 const int kBeforeFinalizingProgress = 81; 38 const int kProgressComplete = 100; 39 40 // Defines what part of update progress does download part takes. 41 const int kDownloadProgressIncrement = 60; 42 43 const char kUpdateDeadlineFile[] = "/tmp/update-check-response-deadline"; 44 45 // Minimum timestep between two consecutive measurements for the 46 // download rate. 47 const base::TimeDelta kMinTimeStep = base::TimeDelta::FromSeconds(1); 48 49 // Smooth factor that is used for the average downloading speed 50 // estimation. 51 // avg_speed = smooth_factor * cur_speed + (1.0 - smooth_factor) * avg_speed. 52 const double kDownloadSpeedSmoothFactor = 0.1; 53 54 // Minumum allowed value for the average downloading speed. 55 const double kDownloadAverageSpeedDropBound = 1e-8; 56 57 // An upper bound for possible downloading time left estimations. 58 const double kMaxTimeLeft = 24 * 60 * 60; 59 60 // Invoked from call to RequestUpdateCheck upon completion of the DBus call. 61 void StartUpdateCallback(UpdateScreen* screen, 62 UpdateEngineClient::UpdateCheckResult result) { 63 VLOG(1) << "Callback from RequestUpdateCheck, result " << result; 64 if (UpdateScreen::HasInstance(screen)) { 65 if (result == UpdateEngineClient::UPDATE_RESULT_SUCCESS) 66 screen->SetIgnoreIdleStatus(false); 67 else 68 screen->ExitUpdate(UpdateScreen::REASON_UPDATE_INIT_FAILED); 69 } 70 } 71 72 } // anonymous namespace 73 74 // static 75 UpdateScreen::InstanceSet& UpdateScreen::GetInstanceSet() { 76 CR_DEFINE_STATIC_LOCAL(std::set<UpdateScreen*>, instance_set, ()); 77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // not threadsafe. 78 return instance_set; 79 } 80 81 // static 82 bool UpdateScreen::HasInstance(UpdateScreen* inst) { 83 InstanceSet& instance_set = GetInstanceSet(); 84 InstanceSet::iterator found = instance_set.find(inst); 85 return (found != instance_set.end()); 86 } 87 88 // static 89 UpdateScreen* UpdateScreen::Get(ScreenManager* manager) { 90 return static_cast<UpdateScreen*>( 91 manager->GetScreen(WizardController::kUpdateScreenName)); 92 } 93 94 UpdateScreen::UpdateScreen( 95 ScreenObserver* screen_observer, 96 UpdateScreenActor* actor) 97 : WizardScreen(screen_observer), 98 state_(STATE_IDLE), 99 reboot_check_delay_(0), 100 is_checking_for_update_(true), 101 is_downloading_update_(false), 102 is_ignore_update_deadlines_(false), 103 is_shown_(false), 104 ignore_idle_status_(true), 105 actor_(actor), 106 is_first_detection_notification_(true), 107 is_first_portal_notification_(true), 108 weak_factory_(this) { 109 DCHECK(actor_); 110 if (actor_) 111 actor_->SetDelegate(this); 112 GetInstanceSet().insert(this); 113 } 114 115 UpdateScreen::~UpdateScreen() { 116 DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this); 117 NetworkPortalDetector::Get()->RemoveObserver(this); 118 GetInstanceSet().erase(this); 119 if (actor_) 120 actor_->SetDelegate(NULL); 121 } 122 123 void UpdateScreen::UpdateStatusChanged( 124 const UpdateEngineClient::Status& status) { 125 if (!actor_) 126 return; 127 128 if (is_checking_for_update_ && 129 status.status > UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE) { 130 is_checking_for_update_ = false; 131 } 132 if (ignore_idle_status_ && status.status > 133 UpdateEngineClient::UPDATE_STATUS_IDLE) { 134 ignore_idle_status_ = false; 135 } 136 137 switch (status.status) { 138 case UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE: 139 // Do nothing in these cases, we don't want to notify the user of the 140 // check unless there is an update. 141 break; 142 case UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE: 143 MakeSureScreenIsShown(); 144 actor_->SetProgress(kBeforeDownloadProgress); 145 actor_->ShowEstimatedTimeLeft(false); 146 if (!HasCriticalUpdate()) { 147 VLOG(1) << "Noncritical update available: " << status.new_version; 148 ExitUpdate(REASON_UPDATE_NON_CRITICAL); 149 } else { 150 VLOG(1) << "Critical update available: " << status.new_version; 151 actor_->SetProgressMessage( 152 UpdateScreenActor::PROGRESS_MESSAGE_UPDATE_AVAILABLE); 153 actor_->ShowProgressMessage(true); 154 actor_->ShowCurtain(false); 155 } 156 break; 157 case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING: 158 { 159 MakeSureScreenIsShown(); 160 if (!is_downloading_update_) { 161 // Because update engine doesn't send UPDATE_STATUS_UPDATE_AVAILABLE 162 // we need to is update critical on first downloading notification. 163 is_downloading_update_ = true; 164 download_start_time_ = download_last_time_ = base::Time::Now(); 165 download_start_progress_ = status.download_progress; 166 download_last_progress_ = status.download_progress; 167 is_download_average_speed_computed_ = false; 168 download_average_speed_ = 0.0; 169 if (!HasCriticalUpdate()) { 170 VLOG(1) << "Non-critical update available: " << status.new_version; 171 ExitUpdate(REASON_UPDATE_NON_CRITICAL); 172 } else { 173 VLOG(1) << "Critical update available: " << status.new_version; 174 actor_->SetProgressMessage( 175 UpdateScreenActor::PROGRESS_MESSAGE_INSTALLING_UPDATE); 176 actor_->ShowProgressMessage(true); 177 actor_->ShowCurtain(false); 178 } 179 } 180 UpdateDownloadingStats(status); 181 } 182 break; 183 case UpdateEngineClient::UPDATE_STATUS_VERIFYING: 184 MakeSureScreenIsShown(); 185 actor_->SetProgress(kBeforeVerifyingProgress); 186 actor_->SetProgressMessage(UpdateScreenActor::PROGRESS_MESSAGE_VERIFYING); 187 actor_->ShowProgressMessage(true); 188 break; 189 case UpdateEngineClient::UPDATE_STATUS_FINALIZING: 190 MakeSureScreenIsShown(); 191 actor_->SetProgress(kBeforeFinalizingProgress); 192 actor_->SetProgressMessage( 193 UpdateScreenActor::PROGRESS_MESSAGE_FINALIZING); 194 actor_->ShowProgressMessage(true); 195 break; 196 case UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT: 197 MakeSureScreenIsShown(); 198 actor_->SetProgress(kProgressComplete); 199 actor_->ShowEstimatedTimeLeft(false); 200 if (HasCriticalUpdate()) { 201 actor_->ShowCurtain(false); 202 VLOG(1) << "Initiate reboot after update"; 203 DBusThreadManager::Get()->GetUpdateEngineClient()->RebootAfterUpdate(); 204 reboot_timer_.Start(FROM_HERE, 205 base::TimeDelta::FromSeconds(reboot_check_delay_), 206 this, 207 &UpdateScreen::OnWaitForRebootTimeElapsed); 208 } else { 209 ExitUpdate(REASON_UPDATE_NON_CRITICAL); 210 } 211 break; 212 case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK: 213 VLOG(1) << "Attempting rollback"; 214 break; 215 case UpdateEngineClient::UPDATE_STATUS_IDLE: 216 if (ignore_idle_status_) { 217 // It is first IDLE status that is sent before we initiated the check. 218 break; 219 } 220 // else no break 221 case UpdateEngineClient::UPDATE_STATUS_ERROR: 222 case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT: 223 ExitUpdate(REASON_UPDATE_ENDED); 224 break; 225 default: 226 NOTREACHED(); 227 break; 228 } 229 } 230 231 void UpdateScreen::OnPortalDetectionCompleted( 232 const NetworkState* network, 233 const NetworkPortalDetector::CaptivePortalState& state) { 234 LOG(WARNING) << "UpdateScreen::PortalDetectionCompleted(): " 235 << "network=" << (network ? network->path() : "") << ", " 236 << "state.status=" << state.status << ", " 237 << "state.response_code=" << state.response_code; 238 239 // Wait for the sane detection results. 240 if (network && 241 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN) { 242 return; 243 } 244 245 // Restart portal detection for the first notification about offline state. 246 if ((!network || 247 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE) && 248 is_first_detection_notification_) { 249 is_first_detection_notification_ = false; 250 base::MessageLoop::current()->PostTask( 251 FROM_HERE, 252 base::Bind( 253 base::IgnoreResult(&NetworkPortalDetector::StartDetectionIfIdle), 254 base::Unretained(NetworkPortalDetector::Get()))); 255 return; 256 } 257 is_first_detection_notification_ = false; 258 259 NetworkPortalDetector::CaptivePortalStatus status = state.status; 260 if (state_ == STATE_ERROR) { 261 // In the case of online state hide error message and proceed to 262 // the update stage. Otherwise, update error message content. 263 if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE) 264 StartUpdateCheck(); 265 else 266 UpdateErrorMessage(network, status); 267 } else if (state_ == STATE_FIRST_PORTAL_CHECK) { 268 // In the case of online state immediately proceed to the update 269 // stage. Otherwise, prepare and show error message. 270 if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE) { 271 StartUpdateCheck(); 272 } else { 273 UpdateErrorMessage(network, status); 274 ShowErrorMessage(); 275 } 276 } 277 } 278 279 void UpdateScreen::StartNetworkCheck() { 280 // If portal detector is enabled and portal detection before AU is 281 // allowed, initiate network state check. Otherwise, directly 282 // proceed to update. 283 if (!NetworkPortalDetector::Get()->IsEnabled()) { 284 StartUpdateCheck(); 285 return; 286 } 287 state_ = STATE_FIRST_PORTAL_CHECK; 288 is_first_detection_notification_ = true; 289 is_first_portal_notification_ = true; 290 NetworkPortalDetector::Get()->AddAndFireObserver(this); 291 } 292 293 void UpdateScreen::CancelUpdate() { 294 VLOG(1) << "Forced update cancel"; 295 ExitUpdate(REASON_UPDATE_CANCELED); 296 } 297 298 void UpdateScreen::Show() { 299 is_shown_ = true; 300 if (actor_) { 301 actor_->Show(); 302 actor_->SetProgress(kBeforeUpdateCheckProgress); 303 } 304 } 305 306 void UpdateScreen::Hide() { 307 if (actor_) 308 actor_->Hide(); 309 is_shown_ = false; 310 } 311 312 std::string UpdateScreen::GetName() const { 313 return WizardController::kUpdateScreenName; 314 } 315 316 void UpdateScreen::PrepareToShow() { 317 if (actor_) 318 actor_->PrepareToShow(); 319 } 320 321 void UpdateScreen::ExitUpdate(UpdateScreen::ExitReason reason) { 322 DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this); 323 NetworkPortalDetector::Get()->RemoveObserver(this); 324 325 switch (reason) { 326 case REASON_UPDATE_CANCELED: 327 get_screen_observer()->OnExit(ScreenObserver::UPDATE_NOUPDATE); 328 break; 329 case REASON_UPDATE_INIT_FAILED: 330 get_screen_observer()->OnExit( 331 ScreenObserver::UPDATE_ERROR_CHECKING_FOR_UPDATE); 332 break; 333 case REASON_UPDATE_NON_CRITICAL: 334 case REASON_UPDATE_ENDED: 335 { 336 UpdateEngineClient* update_engine_client = 337 DBusThreadManager::Get()->GetUpdateEngineClient(); 338 switch (update_engine_client->GetLastStatus().status) { 339 case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK: 340 break; 341 case UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE: 342 case UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT: 343 case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING: 344 case UpdateEngineClient::UPDATE_STATUS_FINALIZING: 345 case UpdateEngineClient::UPDATE_STATUS_VERIFYING: 346 DCHECK(!HasCriticalUpdate()); 347 // Noncritical update, just exit screen as if there is no update. 348 // no break 349 case UpdateEngineClient::UPDATE_STATUS_IDLE: 350 get_screen_observer()->OnExit(ScreenObserver::UPDATE_NOUPDATE); 351 break; 352 case UpdateEngineClient::UPDATE_STATUS_ERROR: 353 case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT: 354 get_screen_observer()->OnExit(is_checking_for_update_ ? 355 ScreenObserver::UPDATE_ERROR_CHECKING_FOR_UPDATE : 356 ScreenObserver::UPDATE_ERROR_UPDATING); 357 break; 358 default: 359 NOTREACHED(); 360 } 361 } 362 break; 363 default: 364 NOTREACHED(); 365 } 366 } 367 368 void UpdateScreen::OnWaitForRebootTimeElapsed() { 369 LOG(ERROR) << "Unable to reboot - asking user for a manual reboot."; 370 MakeSureScreenIsShown(); 371 if (actor_) 372 actor_->ShowManualRebootInfo(); 373 } 374 375 void UpdateScreen::MakeSureScreenIsShown() { 376 if (!is_shown_) 377 get_screen_observer()->ShowCurrentScreen(); 378 } 379 380 void UpdateScreen::SetRebootCheckDelay(int seconds) { 381 if (seconds <= 0) 382 reboot_timer_.Stop(); 383 DCHECK(!reboot_timer_.IsRunning()); 384 reboot_check_delay_ = seconds; 385 } 386 387 void UpdateScreen::SetIgnoreIdleStatus(bool ignore_idle_status) { 388 ignore_idle_status_ = ignore_idle_status; 389 } 390 391 void UpdateScreen::UpdateDownloadingStats( 392 const UpdateEngineClient::Status& status) { 393 if (!actor_) 394 return; 395 base::Time download_current_time = base::Time::Now(); 396 if (download_current_time >= download_last_time_ + kMinTimeStep) { 397 // Estimate downloading rate. 398 double progress_delta = 399 std::max(status.download_progress - download_last_progress_, 0.0); 400 double time_delta = 401 (download_current_time - download_last_time_).InSecondsF(); 402 double download_rate = status.new_size * progress_delta / time_delta; 403 404 download_last_time_ = download_current_time; 405 download_last_progress_ = status.download_progress; 406 407 // Estimate time left. 408 double progress_left = std::max(1.0 - status.download_progress, 0.0); 409 if (!is_download_average_speed_computed_) { 410 download_average_speed_ = download_rate; 411 is_download_average_speed_computed_ = true; 412 } 413 download_average_speed_ = 414 kDownloadSpeedSmoothFactor * download_rate + 415 (1.0 - kDownloadSpeedSmoothFactor) * download_average_speed_; 416 if (download_average_speed_ < kDownloadAverageSpeedDropBound) { 417 time_delta = 418 (download_current_time - download_start_time_).InSecondsF(); 419 download_average_speed_ = 420 status.new_size * 421 (status.download_progress - download_start_progress_) / 422 time_delta; 423 } 424 double work_left = progress_left * status.new_size; 425 double time_left = work_left / download_average_speed_; 426 // |time_left| may be large enough or even +infinity. So we must 427 // |bound possible estimations. 428 time_left = std::min(time_left, kMaxTimeLeft); 429 430 actor_->ShowEstimatedTimeLeft(true); 431 actor_->SetEstimatedTimeLeft( 432 base::TimeDelta::FromSeconds(static_cast<int64>(time_left))); 433 } 434 435 int download_progress = static_cast<int>( 436 status.download_progress * kDownloadProgressIncrement); 437 actor_->SetProgress(kBeforeDownloadProgress + download_progress); 438 } 439 440 bool UpdateScreen::HasCriticalUpdate() { 441 if (is_ignore_update_deadlines_) 442 return true; 443 444 std::string deadline; 445 // Checking for update flag file causes us to do blocking IO on UI thread. 446 // Temporarily allow it until we fix http://crosbug.com/11106 447 base::ThreadRestrictions::ScopedAllowIO allow_io; 448 base::FilePath update_deadline_file_path(kUpdateDeadlineFile); 449 if (!base::ReadFileToString(update_deadline_file_path, &deadline) || 450 deadline.empty()) { 451 return false; 452 } 453 454 // TODO(dpolukhin): Analyze file content. Now we can just assume that 455 // if the file exists and not empty, there is critical update. 456 return true; 457 } 458 459 void UpdateScreen::OnActorDestroyed(UpdateScreenActor* actor) { 460 if (actor_ == actor) 461 actor_ = NULL; 462 } 463 464 void UpdateScreen::OnConnectToNetworkRequested() { 465 if (state_ == STATE_ERROR) { 466 LOG(WARNING) << "Hiding error message since AP was reselected"; 467 StartUpdateCheck(); 468 } 469 } 470 471 ErrorScreen* UpdateScreen::GetErrorScreen() { 472 return get_screen_observer()->GetErrorScreen(); 473 } 474 475 void UpdateScreen::StartUpdateCheck() { 476 NetworkPortalDetector::Get()->RemoveObserver(this); 477 if (state_ == STATE_ERROR) 478 HideErrorMessage(); 479 state_ = STATE_UPDATE; 480 DBusThreadManager::Get()->GetUpdateEngineClient()->AddObserver(this); 481 VLOG(1) << "Initiate update check"; 482 DBusThreadManager::Get()->GetUpdateEngineClient()->RequestUpdateCheck( 483 base::Bind(StartUpdateCallback, this)); 484 } 485 486 void UpdateScreen::ShowErrorMessage() { 487 LOG(WARNING) << "UpdateScreen::ShowErrorMessage()"; 488 state_ = STATE_ERROR; 489 GetErrorScreen()->SetUIState(ErrorScreen::UI_STATE_UPDATE); 490 get_screen_observer()->ShowErrorScreen(); 491 } 492 493 void UpdateScreen::HideErrorMessage() { 494 LOG(WARNING) << "UpdateScreen::HideErrorMessage()"; 495 get_screen_observer()->HideErrorScreen(this); 496 } 497 498 void UpdateScreen::UpdateErrorMessage( 499 const NetworkState* network, 500 const NetworkPortalDetector::CaptivePortalStatus status) { 501 switch (status) { 502 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE: 503 NOTREACHED(); 504 break; 505 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN: 506 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE: 507 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_OFFLINE, 508 std::string()); 509 break; 510 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL: 511 DCHECK(network); 512 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PORTAL, 513 network->name()); 514 if (is_first_portal_notification_) { 515 is_first_portal_notification_ = false; 516 GetErrorScreen()->FixCaptivePortal(); 517 } 518 break; 519 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED: 520 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PROXY, 521 std::string()); 522 break; 523 default: 524 NOTREACHED(); 525 break; 526 } 527 } 528 529 } // namespace chromeos 530