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