Home | History | Annotate | Download | only in login
      1 // Copyright (c) 2011 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/update_screen.h"
      6 
      7 #include "base/file_util.h"
      8 #include "base/logging.h"
      9 #include "base/threading/thread_restrictions.h"
     10 #include "chrome/browser/chromeos/cros/cros_library.h"
     11 #include "chrome/browser/chromeos/login/screen_observer.h"
     12 #include "chrome/browser/chromeos/login/update_view.h"
     13 #include "chrome/browser/chromeos/login/wizard_controller.h"
     14 #include "content/browser/browser_thread.h"
     15 
     16 namespace {
     17 
     18 // Progress bar stages. Each represents progress bar value
     19 // at the beginning of each stage.
     20 // TODO(nkostylev): Base stage progress values on approximate time.
     21 // TODO(nkostylev): Animate progress during each state.
     22 const int kBeforeUpdateCheckProgress = 7;
     23 const int kBeforeDownloadProgress = 14;
     24 const int kBeforeVerifyingProgress = 74;
     25 const int kBeforeFinalizingProgress = 81;
     26 const int kProgressComplete = 100;
     27 
     28 // Defines what part of update progress does download part takes.
     29 const int kDownloadProgressIncrement = 60;
     30 
     31 // Considering 10px shadow from each side.
     32 const int kUpdateScreenWidth = 580;
     33 const int kUpdateScreenHeight = 305;
     34 
     35 const char kUpdateDeadlineFile[] = "/tmp/update-check-response-deadline";
     36 
     37 }  // anonymous namespace
     38 
     39 namespace chromeos {
     40 
     41 
     42 // static
     43 UpdateScreen::InstanceSet& UpdateScreen::GetInstanceSet() {
     44   static std::set<UpdateScreen*> instance_set;
     45   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));  // not threadsafe.
     46   return instance_set;
     47 }
     48 
     49 // static
     50 bool UpdateScreen::HasInstance(UpdateScreen* inst) {
     51   InstanceSet& instance_set = GetInstanceSet();
     52   InstanceSet::iterator found = instance_set.find(inst);
     53   return (found != instance_set.end());
     54 }
     55 
     56 UpdateScreen::UpdateScreen(WizardScreenDelegate* delegate)
     57     : DefaultViewScreen<chromeos::UpdateView>(delegate,
     58                                               kUpdateScreenWidth,
     59                                               kUpdateScreenHeight),
     60       checking_for_update_(true),
     61       reboot_check_delay_(0),
     62       is_downloading_update_(false),
     63       is_all_updates_critical_(true) { // See http://crosbug.com/10068
     64   GetInstanceSet().insert(this);
     65 }
     66 
     67 UpdateScreen::~UpdateScreen() {
     68   // Remove pointer to this object from view.
     69   if (view())
     70     view()->set_controller(NULL);
     71   CrosLibrary::Get()->GetUpdateLibrary()->RemoveObserver(this);
     72   GetInstanceSet().erase(this);
     73 }
     74 
     75 void UpdateScreen::UpdateStatusChanged(UpdateLibrary* library) {
     76   UpdateStatusOperation status = library->status().status;
     77   if (checking_for_update_ && status > UPDATE_STATUS_CHECKING_FOR_UPDATE) {
     78     checking_for_update_ = false;
     79   }
     80 
     81   switch (status) {
     82     case UPDATE_STATUS_CHECKING_FOR_UPDATE:
     83       // Do nothing in these cases, we don't want to notify the user of the
     84       // check unless there is an update.
     85       break;
     86     case UPDATE_STATUS_UPDATE_AVAILABLE:
     87       MakeSureScreenIsShown();
     88       view()->SetProgress(kBeforeDownloadProgress);
     89       if (!HasCriticalUpdate()) {
     90         LOG(INFO) << "Noncritical update available: "
     91                   << library->status().new_version;
     92         ExitUpdate(REASON_UPDATE_NON_CRITICAL);
     93       } else {
     94         LOG(INFO) << "Critical update available: "
     95                   << library->status().new_version;
     96         view()->ShowPreparingUpdatesInfo(true);
     97         view()->ShowCurtain(false);
     98       }
     99       break;
    100     case UPDATE_STATUS_DOWNLOADING:
    101       {
    102         MakeSureScreenIsShown();
    103         if (!is_downloading_update_) {
    104           // Because update engine doesn't send UPDATE_STATUS_UPDATE_AVAILABLE
    105           // we need to is update critical on first downloading notification.
    106           is_downloading_update_ = true;
    107           if (!HasCriticalUpdate()) {
    108             LOG(INFO) << "Non-critical update available: "
    109                       << library->status().new_version;
    110             ExitUpdate(REASON_UPDATE_NON_CRITICAL);
    111           } else {
    112             LOG(INFO) << "Critical update available: "
    113                       << library->status().new_version;
    114           }
    115         }
    116         view()->ShowPreparingUpdatesInfo(false);
    117         view()->ShowCurtain(false);
    118         int download_progress = static_cast<int>(
    119             library->status().download_progress * kDownloadProgressIncrement);
    120         view()->SetProgress(kBeforeDownloadProgress + download_progress);
    121       }
    122       break;
    123     case UPDATE_STATUS_VERIFYING:
    124       MakeSureScreenIsShown();
    125       view()->SetProgress(kBeforeVerifyingProgress);
    126       break;
    127     case UPDATE_STATUS_FINALIZING:
    128       MakeSureScreenIsShown();
    129       view()->SetProgress(kBeforeFinalizingProgress);
    130       break;
    131     case UPDATE_STATUS_UPDATED_NEED_REBOOT:
    132       MakeSureScreenIsShown();
    133       // Make sure that first OOBE stage won't be shown after reboot.
    134       WizardController::MarkOobeCompleted();
    135       view()->SetProgress(kProgressComplete);
    136       if (HasCriticalUpdate()) {
    137         view()->ShowCurtain(false);
    138         VLOG(1) << "Initiate reboot after update";
    139         CrosLibrary::Get()->GetUpdateLibrary()->RebootAfterUpdate();
    140         reboot_timer_.Start(base::TimeDelta::FromSeconds(reboot_check_delay_),
    141                             this,
    142                             &UpdateScreen::OnWaitForRebootTimeElapsed);
    143       } else {
    144         ExitUpdate(REASON_UPDATE_NON_CRITICAL);
    145       }
    146       break;
    147     case UPDATE_STATUS_IDLE:
    148     case UPDATE_STATUS_ERROR:
    149     case UPDATE_STATUS_REPORTING_ERROR_EVENT:
    150       ExitUpdate(REASON_UPDATE_ENDED);
    151       break;
    152     default:
    153       NOTREACHED();
    154       break;
    155   }
    156 }
    157 
    158 namespace {
    159 // Invoked from call to RequestUpdateCheck upon completion of the DBus call.
    160 void StartUpdateCallback(void* user_data,
    161                          UpdateResult result,
    162                          const char* msg) {
    163   if (result != UPDATE_RESULT_SUCCESS) {
    164     DCHECK(user_data);
    165     UpdateScreen* screen = static_cast<UpdateScreen*>(user_data);
    166     if (UpdateScreen::HasInstance(screen))
    167       screen->ExitUpdate(UpdateScreen::REASON_UPDATE_INIT_FAILED);
    168   }
    169 }
    170 }  // namespace
    171 
    172 void UpdateScreen::StartUpdate() {
    173   // Reset view if view was created.
    174   if (view()) {
    175     view()->Reset();
    176     view()->set_controller(this);
    177     is_downloading_update_ = false;
    178     view()->SetProgress(kBeforeUpdateCheckProgress);
    179   }
    180 
    181   if (!CrosLibrary::Get()->EnsureLoaded()) {
    182     LOG(ERROR) << "Error loading CrosLibrary";
    183     ExitUpdate(REASON_UPDATE_INIT_FAILED);
    184   } else {
    185     CrosLibrary::Get()->GetUpdateLibrary()->AddObserver(this);
    186     VLOG(1) << "Initiate update check";
    187     CrosLibrary::Get()->GetUpdateLibrary()->RequestUpdateCheck(
    188         StartUpdateCallback, this);
    189   }
    190 }
    191 
    192 void UpdateScreen::CancelUpdate() {
    193   // Screen has longer lifetime than it's view.
    194   // View is deleted after wizard proceeds to the next screen.
    195   if (view())
    196     ExitUpdate(REASON_UPDATE_CANCELED);
    197 }
    198 
    199 void UpdateScreen::Show() {
    200   DefaultViewScreen<UpdateView>::Show();
    201   view()->set_controller(this);
    202   is_downloading_update_ = false;
    203   view()->SetProgress(kBeforeUpdateCheckProgress);
    204 }
    205 
    206 void UpdateScreen::ExitUpdate(UpdateScreen::ExitReason reason) {
    207   ScreenObserver* observer = delegate()->GetObserver(this);
    208   if (CrosLibrary::Get()->EnsureLoaded())
    209     CrosLibrary::Get()->GetUpdateLibrary()->RemoveObserver(this);
    210 
    211   switch(reason) {
    212     case REASON_UPDATE_CANCELED:
    213       observer->OnExit(ScreenObserver::UPDATE_NOUPDATE);
    214       break;
    215     case REASON_UPDATE_INIT_FAILED:
    216       observer->OnExit(ScreenObserver::UPDATE_ERROR_CHECKING_FOR_UPDATE);
    217       break;
    218     case REASON_UPDATE_NON_CRITICAL:
    219     case REASON_UPDATE_ENDED:
    220       {
    221         UpdateLibrary* update_library = CrosLibrary::Get()->GetUpdateLibrary();
    222         switch (update_library->status().status) {
    223           case UPDATE_STATUS_UPDATE_AVAILABLE:
    224           case UPDATE_STATUS_UPDATED_NEED_REBOOT:
    225           case UPDATE_STATUS_DOWNLOADING:
    226           case UPDATE_STATUS_FINALIZING:
    227           case UPDATE_STATUS_VERIFYING:
    228             DCHECK(!HasCriticalUpdate());
    229             // Noncritical update, just exit screen as if there is no update.
    230             // no break
    231           case UPDATE_STATUS_IDLE:
    232             observer->OnExit(ScreenObserver::UPDATE_NOUPDATE);
    233             break;
    234           case UPDATE_STATUS_ERROR:
    235           case UPDATE_STATUS_REPORTING_ERROR_EVENT:
    236             observer->OnExit(checking_for_update_ ?
    237                 ScreenObserver::UPDATE_ERROR_CHECKING_FOR_UPDATE :
    238                 ScreenObserver::UPDATE_ERROR_UPDATING);
    239             break;
    240           default:
    241             NOTREACHED();
    242         }
    243       }
    244       break;
    245     default:
    246       NOTREACHED();
    247   }
    248 }
    249 
    250 void UpdateScreen::OnWaitForRebootTimeElapsed() {
    251   LOG(ERROR) << "Unable to reboot - asking user for a manual reboot.";
    252   MakeSureScreenIsShown();
    253   view()->ShowManualRebootInfo();
    254 }
    255 
    256 void UpdateScreen::MakeSureScreenIsShown() {
    257   if (!view()) {
    258     delegate()->ShowCurrentScreen();
    259   }
    260 }
    261 
    262 void UpdateScreen::SetRebootCheckDelay(int seconds) {
    263   if (seconds <= 0)
    264     reboot_timer_.Stop();
    265   DCHECK(!reboot_timer_.IsRunning());
    266   reboot_check_delay_ = seconds;
    267 }
    268 
    269 bool UpdateScreen::HasCriticalUpdate() {
    270   if (is_all_updates_critical_)
    271     return true;
    272 
    273   std::string deadline;
    274   // Checking for update flag file causes us to do blocking IO on UI thread.
    275   // Temporarily allow it until we fix http://crosbug.com/11106
    276   base::ThreadRestrictions::ScopedAllowIO allow_io;
    277   FilePath update_deadline_file_path(kUpdateDeadlineFile);
    278   if (!file_util::ReadFileToString(update_deadline_file_path, &deadline) ||
    279       deadline.empty()) {
    280     return false;
    281   }
    282 
    283   // TODO(dpolukhin): Analyze file content. Now we can just assume that
    284   // if the file exists and not empty, there is critical update.
    285   return true;
    286 }
    287 
    288 void UpdateScreen::SetAllUpdatesCritical(bool is_critical) {
    289   is_all_updates_critical_ = is_critical;
    290 }
    291 
    292 }  // namespace chromeos
    293