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.
5 #include "chrome/browser/chromeos/login/screens/update_screen.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"
24 using content::BrowserThread;
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;
40 // Defines what part of update progress does download part takes.
41 const int kDownloadProgressIncrement = 60;
43 const char kUpdateDeadlineFile[] = "/tmp/update-check-response-deadline";
45 // Minimum timestep between two consecutive measurements for the
47 const base::TimeDelta kMinTimeStep = base::TimeDelta::FromSeconds(1);
49 // Smooth factor that is used for the average downloading speed
51 // avg_speed = smooth_factor * cur_speed + (1.0 - smooth_factor) * avg_speed.
52 const double kDownloadSpeedSmoothFactor = 0.1;
54 // Minumum allowed value for the average downloading speed.
55 const double kDownloadAverageSpeedDropBound = 1e-8;
57 // An upper bound for possible downloading time left estimations.
58 const double kMaxTimeLeft = 24 * 60 * 60;
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);
68 screen->ExitUpdate(UpdateScreen::REASON_UPDATE_INIT_FAILED);
72 } // anonymous namespace
75 UpdateScreen::InstanceSet& UpdateScreen::GetInstanceSet() {
76 CR_DEFINE_STATIC_LOCAL(std::set<UpdateScreen*>, instance_set, ());
77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // not threadsafe.
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());
89 UpdateScreen* UpdateScreen::Get(ScreenManager* manager) {
90 return static_cast<UpdateScreen*>(
91 manager->GetScreen(WizardController::kUpdateScreenName));
94 UpdateScreen::UpdateScreen(
95 ScreenObserver* screen_observer,
96 UpdateScreenActor* actor)
97 : WizardScreen(screen_observer),
99 reboot_check_delay_(0),
100 is_checking_for_update_(true),
101 is_downloading_update_(false),
102 is_ignore_update_deadlines_(false),
104 ignore_idle_status_(true),
106 is_first_detection_notification_(true),
107 is_first_portal_notification_(true),
108 weak_factory_(this) {
111 actor_->SetDelegate(this);
112 GetInstanceSet().insert(this);
115 UpdateScreen::~UpdateScreen() {
116 DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
117 NetworkPortalDetector::Get()->RemoveObserver(this);
118 GetInstanceSet().erase(this);
120 actor_->SetDelegate(NULL);
123 void UpdateScreen::UpdateStatusChanged(
124 const UpdateEngineClient::Status& status) {
128 if (is_checking_for_update_ &&
129 status.status > UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE) {
130 is_checking_for_update_ = false;
132 if (ignore_idle_status_ && status.status >
133 UpdateEngineClient::UPDATE_STATUS_IDLE) {
134 ignore_idle_status_ = false;
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.
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);
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);
157 case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING:
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);
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);
180 UpdateDownloadingStats(status);
183 case UpdateEngineClient::UPDATE_STATUS_VERIFYING:
184 MakeSureScreenIsShown();
185 actor_->SetProgress(kBeforeVerifyingProgress);
186 actor_->SetProgressMessage(UpdateScreenActor::PROGRESS_MESSAGE_VERIFYING);
187 actor_->ShowProgressMessage(true);
189 case UpdateEngineClient::UPDATE_STATUS_FINALIZING:
190 MakeSureScreenIsShown();
191 actor_->SetProgress(kBeforeFinalizingProgress);
192 actor_->SetProgressMessage(
193 UpdateScreenActor::PROGRESS_MESSAGE_FINALIZING);
194 actor_->ShowProgressMessage(true);
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_),
207 &UpdateScreen::OnWaitForRebootTimeElapsed);
209 ExitUpdate(REASON_UPDATE_NON_CRITICAL);
212 case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK:
213 VLOG(1) << "Attempting rollback";
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.
221 case UpdateEngineClient::UPDATE_STATUS_ERROR:
222 case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT:
223 ExitUpdate(REASON_UPDATE_ENDED);
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;
239 // Wait for the sane detection results.
241 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN) {
245 // Restart portal detection for the first notification about offline state.
247 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE) &&
248 is_first_detection_notification_) {
249 is_first_detection_notification_ = false;
250 base::MessageLoop::current()->PostTask(
253 base::IgnoreResult(&NetworkPortalDetector::StartDetectionIfIdle),
254 base::Unretained(NetworkPortalDetector::Get())));
257 is_first_detection_notification_ = false;
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)
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) {
273 UpdateErrorMessage(network, status);
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()) {
287 state_ = STATE_FIRST_PORTAL_CHECK;
288 is_first_detection_notification_ = true;
289 is_first_portal_notification_ = true;
290 NetworkPortalDetector::Get()->AddAndFireObserver(this);
293 void UpdateScreen::CancelUpdate() {
294 VLOG(1) << "Forced update cancel";
295 ExitUpdate(REASON_UPDATE_CANCELED);
298 void UpdateScreen::Show() {
302 actor_->SetProgress(kBeforeUpdateCheckProgress);
306 void UpdateScreen::Hide() {
312 std::string UpdateScreen::GetName() const {
313 return WizardController::kUpdateScreenName;
316 void UpdateScreen::PrepareToShow() {
318 actor_->PrepareToShow();
321 void UpdateScreen::ExitUpdate(UpdateScreen::ExitReason reason) {
322 DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
323 NetworkPortalDetector::Get()->RemoveObserver(this);
326 case REASON_UPDATE_CANCELED:
327 get_screen_observer()->OnExit(ScreenObserver::UPDATE_NOUPDATE);
329 case REASON_UPDATE_INIT_FAILED:
330 get_screen_observer()->OnExit(
331 ScreenObserver::UPDATE_ERROR_CHECKING_FOR_UPDATE);
333 case REASON_UPDATE_NON_CRITICAL:
334 case REASON_UPDATE_ENDED:
336 UpdateEngineClient* update_engine_client =
337 DBusThreadManager::Get()->GetUpdateEngineClient();
338 switch (update_engine_client->GetLastStatus().status) {
339 case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK:
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.
349 case UpdateEngineClient::UPDATE_STATUS_IDLE:
350 get_screen_observer()->OnExit(ScreenObserver::UPDATE_NOUPDATE);
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);
368 void UpdateScreen::OnWaitForRebootTimeElapsed() {
369 LOG(ERROR) << "Unable to reboot - asking user for a manual reboot.";
370 MakeSureScreenIsShown();
372 actor_->ShowManualRebootInfo();
375 void UpdateScreen::MakeSureScreenIsShown() {
377 get_screen_observer()->ShowCurrentScreen();
380 void UpdateScreen::SetRebootCheckDelay(int seconds) {
382 reboot_timer_.Stop();
383 DCHECK(!reboot_timer_.IsRunning());
384 reboot_check_delay_ = seconds;
387 void UpdateScreen::SetIgnoreIdleStatus(bool ignore_idle_status) {
388 ignore_idle_status_ = ignore_idle_status;
391 void UpdateScreen::UpdateDownloadingStats(
392 const UpdateEngineClient::Status& status) {
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);
401 (download_current_time - download_last_time_).InSecondsF();
402 double download_rate = status.new_size * progress_delta / time_delta;
404 download_last_time_ = download_current_time;
405 download_last_progress_ = status.download_progress;
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;
413 download_average_speed_ =
414 kDownloadSpeedSmoothFactor * download_rate +
415 (1.0 - kDownloadSpeedSmoothFactor) * download_average_speed_;
416 if (download_average_speed_ < kDownloadAverageSpeedDropBound) {
418 (download_current_time - download_start_time_).InSecondsF();
419 download_average_speed_ =
421 (status.download_progress - download_start_progress_) /
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);
430 actor_->ShowEstimatedTimeLeft(true);
431 actor_->SetEstimatedTimeLeft(
432 base::TimeDelta::FromSeconds(static_cast<int64>(time_left)));
435 int download_progress = static_cast<int>(
436 status.download_progress * kDownloadProgressIncrement);
437 actor_->SetProgress(kBeforeDownloadProgress + download_progress);
440 bool UpdateScreen::HasCriticalUpdate() {
441 if (is_ignore_update_deadlines_)
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) ||
454 // TODO(dpolukhin): Analyze file content. Now we can just assume that
455 // if the file exists and not empty, there is critical update.
459 void UpdateScreen::OnActorDestroyed(UpdateScreenActor* actor) {
464 void UpdateScreen::OnConnectToNetworkRequested() {
465 if (state_ == STATE_ERROR) {
466 LOG(WARNING) << "Hiding error message since AP was reselected";
471 ErrorScreen* UpdateScreen::GetErrorScreen() {
472 return get_screen_observer()->GetErrorScreen();
475 void UpdateScreen::StartUpdateCheck() {
476 NetworkPortalDetector::Get()->RemoveObserver(this);
477 if (state_ == STATE_ERROR)
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));
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();
493 void UpdateScreen::HideErrorMessage() {
494 LOG(WARNING) << "UpdateScreen::HideErrorMessage()";
495 get_screen_observer()->HideErrorScreen(this);
498 void UpdateScreen::UpdateErrorMessage(
499 const NetworkState* network,
500 const NetworkPortalDetector::CaptivePortalStatus status) {
502 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE:
505 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN:
506 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE:
507 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_OFFLINE,
510 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL:
512 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PORTAL,
514 if (is_first_portal_notification_) {
515 is_first_portal_notification_ = false;
516 GetErrorScreen()->FixCaptivePortal();
519 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED:
520 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PROXY,
529 } // namespace chromeos