Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / login / screens / update_screen.cc
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   if (state_ == STATE_ERROR) {
459     LOG(WARNING) << "Hiding error message since AP was reselected";
460     StartUpdateCheck();
461   }
462 }
463
464 ErrorScreen* UpdateScreen::GetErrorScreen() {
465   return get_screen_observer()->GetErrorScreen();
466 }
467
468 void UpdateScreen::StartUpdateCheck() {
469   NetworkPortalDetector::Get()->RemoveObserver(this);
470   if (state_ == STATE_ERROR)
471     HideErrorMessage();
472   state_ = STATE_UPDATE;
473   DBusThreadManager::Get()->GetUpdateEngineClient()->AddObserver(this);
474   VLOG(1) << "Initiate update check";
475   DBusThreadManager::Get()->GetUpdateEngineClient()->RequestUpdateCheck(
476       base::Bind(StartUpdateCallback, this));
477 }
478
479 void UpdateScreen::ShowErrorMessage() {
480   LOG(WARNING) << "UpdateScreen::ShowErrorMessage()";
481   state_ = STATE_ERROR;
482   GetErrorScreen()->SetUIState(ErrorScreen::UI_STATE_UPDATE);
483   get_screen_observer()->ShowErrorScreen();
484 }
485
486 void UpdateScreen::HideErrorMessage() {
487   LOG(WARNING) << "UpdateScreen::HideErrorMessage()";
488   get_screen_observer()->HideErrorScreen(this);
489 }
490
491 void UpdateScreen::UpdateErrorMessage(
492     const NetworkState* network,
493     const NetworkPortalDetector::CaptivePortalStatus status) {
494   switch (status) {
495     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE:
496       NOTREACHED();
497       break;
498     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN:
499     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE:
500       GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_OFFLINE,
501                                       std::string());
502       break;
503     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL:
504       DCHECK(network);
505       GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PORTAL,
506                                       network->name());
507       if (is_first_portal_notification_) {
508         is_first_portal_notification_ = false;
509         GetErrorScreen()->FixCaptivePortal();
510       }
511       break;
512     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED:
513       GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PROXY,
514                                       std::string());
515       break;
516     default:
517       NOTREACHED();
518       break;
519   }
520 }
521
522 }  // namespace chromeos