• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
GetInstanceSet()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
HasInstance(UpdateScreen * inst)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 
UpdateScreen(WizardScreenDelegate * delegate)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 
~UpdateScreen()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 
UpdateStatusChanged(UpdateLibrary * library)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.
StartUpdateCallback(void * user_data,UpdateResult result,const char * msg)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 
StartUpdate()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 
CancelUpdate()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 
Show()199 void UpdateScreen::Show() {
200   DefaultViewScreen<UpdateView>::Show();
201   view()->set_controller(this);
202   is_downloading_update_ = false;
203   view()->SetProgress(kBeforeUpdateCheckProgress);
204 }
205 
ExitUpdate(UpdateScreen::ExitReason reason)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 
OnWaitForRebootTimeElapsed()250 void UpdateScreen::OnWaitForRebootTimeElapsed() {
251   LOG(ERROR) << "Unable to reboot - asking user for a manual reboot.";
252   MakeSureScreenIsShown();
253   view()->ShowManualRebootInfo();
254 }
255 
MakeSureScreenIsShown()256 void UpdateScreen::MakeSureScreenIsShown() {
257   if (!view()) {
258     delegate()->ShowCurrentScreen();
259   }
260 }
261 
SetRebootCheckDelay(int seconds)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 
HasCriticalUpdate()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 
SetAllUpdatesCritical(bool is_critical)288 void UpdateScreen::SetAllUpdatesCritical(bool is_critical) {
289   is_all_updates_critical_ = is_critical;
290 }
291 
292 }  // namespace chromeos
293