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