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