Home | History | Annotate | Download | only in screens
      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