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