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/error_screens_histogram_helper.h"
15 #include "chrome/browser/chromeos/login/screen_manager.h"
16 #include "chrome/browser/chromeos/login/screens/base_screen_delegate.h"
17 #include "chrome/browser/chromeos/login/screens/error_screen.h"
18 #include "chrome/browser/chromeos/login/screens/update_screen_actor.h"
19 #include "chrome/browser/chromeos/login/startup_utils.h"
20 #include "chrome/browser/chromeos/login/wizard_controller.h"
21 #include "chromeos/dbus/dbus_thread_manager.h"
22 #include "chromeos/network/network_state.h"
23 #include "content/public/browser/browser_thread.h"
25 using content::BrowserThread;
26 using pairing_chromeos::HostPairingController;
32 // Progress bar stages. Each represents progress bar value
33 // at the beginning of each stage.
34 // TODO(nkostylev): Base stage progress values on approximate time.
35 // TODO(nkostylev): Animate progress during each state.
36 const int kBeforeUpdateCheckProgress = 7;
37 const int kBeforeDownloadProgress = 14;
38 const int kBeforeVerifyingProgress = 74;
39 const int kBeforeFinalizingProgress = 81;
40 const int kProgressComplete = 100;
42 // Defines what part of update progress does download part takes.
43 const int kDownloadProgressIncrement = 60;
45 const char kUpdateDeadlineFile[] = "/tmp/update-check-response-deadline";
47 // Minimum timestep between two consecutive measurements for the
49 const base::TimeDelta kMinTimeStep = base::TimeDelta::FromSeconds(1);
51 // Smooth factor that is used for the average downloading speed
53 // avg_speed = smooth_factor * cur_speed + (1.0 - smooth_factor) * avg_speed.
54 const double kDownloadSpeedSmoothFactor = 0.1;
56 // Minumum allowed value for the average downloading speed.
57 const double kDownloadAverageSpeedDropBound = 1e-8;
59 // An upper bound for possible downloading time left estimations.
60 const double kMaxTimeLeft = 24 * 60 * 60;
62 // Invoked from call to RequestUpdateCheck upon completion of the DBus call.
63 void StartUpdateCallback(UpdateScreen* screen,
64 UpdateEngineClient::UpdateCheckResult result) {
65 VLOG(1) << "Callback from RequestUpdateCheck, result " << result;
66 if (UpdateScreen::HasInstance(screen)) {
67 if (result == UpdateEngineClient::UPDATE_RESULT_SUCCESS)
68 screen->SetIgnoreIdleStatus(false);
70 screen->ExitUpdate(UpdateScreen::REASON_UPDATE_INIT_FAILED);
74 } // anonymous namespace
77 UpdateScreen::InstanceSet& UpdateScreen::GetInstanceSet() {
78 CR_DEFINE_STATIC_LOCAL(std::set<UpdateScreen*>, instance_set, ());
79 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // not threadsafe.
84 bool UpdateScreen::HasInstance(UpdateScreen* inst) {
85 InstanceSet& instance_set = GetInstanceSet();
86 InstanceSet::iterator found = instance_set.find(inst);
87 return (found != instance_set.end());
91 UpdateScreen* UpdateScreen::Get(ScreenManager* manager) {
92 return static_cast<UpdateScreen*>(
93 manager->GetScreen(WizardController::kUpdateScreenName));
96 UpdateScreen::UpdateScreen(BaseScreenDelegate* base_screen_delegate,
97 UpdateScreenActor* actor,
98 HostPairingController* remora_controller)
99 : BaseScreen(base_screen_delegate),
101 reboot_check_delay_(0),
102 is_checking_for_update_(true),
103 is_downloading_update_(false),
104 is_ignore_update_deadlines_(false),
106 ignore_idle_status_(true),
108 remora_controller_(remora_controller),
109 is_first_detection_notification_(true),
110 is_first_portal_notification_(true),
111 histogram_helper_(new ErrorScreensHistogramHelper("Update")),
112 weak_factory_(this) {
115 actor_->SetDelegate(this);
116 GetInstanceSet().insert(this);
119 UpdateScreen::~UpdateScreen() {
120 DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
121 NetworkPortalDetector::Get()->RemoveObserver(this);
122 GetInstanceSet().erase(this);
124 actor_->SetDelegate(NULL);
127 void UpdateScreen::UpdateStatusChanged(
128 const UpdateEngineClient::Status& status) {
132 if (is_checking_for_update_ &&
133 status.status > UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE) {
134 is_checking_for_update_ = false;
136 if (ignore_idle_status_ && status.status >
137 UpdateEngineClient::UPDATE_STATUS_IDLE) {
138 ignore_idle_status_ = false;
141 switch (status.status) {
142 case UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE:
143 // Do nothing in these cases, we don't want to notify the user of the
144 // check unless there is an update.
145 SetHostPairingControllerStatus(
146 HostPairingController::UPDATE_STATUS_UPDATING);
148 case UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE:
149 MakeSureScreenIsShown();
150 actor_->SetProgress(kBeforeDownloadProgress);
151 actor_->ShowEstimatedTimeLeft(false);
152 if (!HasCriticalUpdate()) {
153 VLOG(1) << "Noncritical update available: " << status.new_version;
154 ExitUpdate(REASON_UPDATE_NON_CRITICAL);
156 VLOG(1) << "Critical update available: " << status.new_version;
157 actor_->SetProgressMessage(
158 UpdateScreenActor::PROGRESS_MESSAGE_UPDATE_AVAILABLE);
159 actor_->ShowProgressMessage(true);
160 actor_->ShowCurtain(false);
163 case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING:
165 MakeSureScreenIsShown();
166 if (!is_downloading_update_) {
167 // Because update engine doesn't send UPDATE_STATUS_UPDATE_AVAILABLE
168 // we need to is update critical on first downloading notification.
169 is_downloading_update_ = true;
170 download_start_time_ = download_last_time_ = base::Time::Now();
171 download_start_progress_ = status.download_progress;
172 download_last_progress_ = status.download_progress;
173 is_download_average_speed_computed_ = false;
174 download_average_speed_ = 0.0;
175 if (!HasCriticalUpdate()) {
176 VLOG(1) << "Non-critical update available: " << status.new_version;
177 ExitUpdate(REASON_UPDATE_NON_CRITICAL);
179 VLOG(1) << "Critical update available: " << status.new_version;
180 actor_->SetProgressMessage(
181 UpdateScreenActor::PROGRESS_MESSAGE_INSTALLING_UPDATE);
182 actor_->ShowProgressMessage(true);
183 actor_->ShowCurtain(false);
186 UpdateDownloadingStats(status);
189 case UpdateEngineClient::UPDATE_STATUS_VERIFYING:
190 MakeSureScreenIsShown();
191 actor_->SetProgress(kBeforeVerifyingProgress);
192 actor_->SetProgressMessage(UpdateScreenActor::PROGRESS_MESSAGE_VERIFYING);
193 actor_->ShowProgressMessage(true);
195 case UpdateEngineClient::UPDATE_STATUS_FINALIZING:
196 MakeSureScreenIsShown();
197 actor_->SetProgress(kBeforeFinalizingProgress);
198 actor_->SetProgressMessage(
199 UpdateScreenActor::PROGRESS_MESSAGE_FINALIZING);
200 actor_->ShowProgressMessage(true);
202 case UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT:
203 MakeSureScreenIsShown();
204 actor_->SetProgress(kProgressComplete);
205 actor_->ShowEstimatedTimeLeft(false);
206 if (HasCriticalUpdate()) {
207 actor_->ShowCurtain(false);
208 VLOG(1) << "Initiate reboot after update";
209 SetHostPairingControllerStatus(
210 HostPairingController::UPDATE_STATUS_REBOOTING);
211 DBusThreadManager::Get()->GetUpdateEngineClient()->RebootAfterUpdate();
212 reboot_timer_.Start(FROM_HERE,
213 base::TimeDelta::FromSeconds(reboot_check_delay_),
215 &UpdateScreen::OnWaitForRebootTimeElapsed);
217 ExitUpdate(REASON_UPDATE_NON_CRITICAL);
220 case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK:
221 VLOG(1) << "Attempting rollback";
223 case UpdateEngineClient::UPDATE_STATUS_IDLE:
224 if (ignore_idle_status_) {
225 // It is first IDLE status that is sent before we initiated the check.
229 case UpdateEngineClient::UPDATE_STATUS_ERROR:
230 case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT:
231 ExitUpdate(REASON_UPDATE_ENDED);
239 void UpdateScreen::OnPortalDetectionCompleted(
240 const NetworkState* network,
241 const NetworkPortalDetector::CaptivePortalState& state) {
242 LOG(WARNING) << "UpdateScreen::PortalDetectionCompleted(): "
243 << "network=" << (network ? network->path() : "") << ", "
244 << "state.status=" << state.status << ", "
245 << "state.response_code=" << state.response_code;
247 // Wait for the sane detection results.
249 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN) {
253 // Restart portal detection for the first notification about offline state.
255 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE) &&
256 is_first_detection_notification_) {
257 is_first_detection_notification_ = false;
258 base::MessageLoop::current()->PostTask(
261 base::IgnoreResult(&NetworkPortalDetector::StartDetectionIfIdle),
262 base::Unretained(NetworkPortalDetector::Get())));
265 is_first_detection_notification_ = false;
267 NetworkPortalDetector::CaptivePortalStatus status = state.status;
268 if (state_ == STATE_ERROR) {
269 // In the case of online state hide error message and proceed to
270 // the update stage. Otherwise, update error message content.
271 if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE)
274 UpdateErrorMessage(network, status);
275 } else if (state_ == STATE_FIRST_PORTAL_CHECK) {
276 // In the case of online state immediately proceed to the update
277 // stage. Otherwise, prepare and show error message.
278 if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE) {
281 UpdateErrorMessage(network, status);
287 void UpdateScreen::StartNetworkCheck() {
288 // If portal detector is enabled and portal detection before AU is
289 // allowed, initiate network state check. Otherwise, directly
290 // proceed to update.
291 if (!NetworkPortalDetector::Get()->IsEnabled()) {
295 state_ = STATE_FIRST_PORTAL_CHECK;
296 is_first_detection_notification_ = true;
297 is_first_portal_notification_ = true;
298 NetworkPortalDetector::Get()->AddAndFireObserver(this);
301 void UpdateScreen::CancelUpdate() {
302 VLOG(1) << "Forced update cancel";
303 ExitUpdate(REASON_UPDATE_CANCELED);
306 void UpdateScreen::Show() {
308 histogram_helper_->OnScreenShow();
311 actor_->SetProgress(kBeforeUpdateCheckProgress);
315 void UpdateScreen::Hide() {
321 std::string UpdateScreen::GetName() const {
322 return WizardController::kUpdateScreenName;
325 void UpdateScreen::PrepareToShow() {
327 actor_->PrepareToShow();
330 void UpdateScreen::ExitUpdate(UpdateScreen::ExitReason reason) {
331 DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
332 NetworkPortalDetector::Get()->RemoveObserver(this);
333 SetHostPairingControllerStatus(HostPairingController::UPDATE_STATUS_UPDATED);
337 case REASON_UPDATE_CANCELED:
338 get_base_screen_delegate()->OnExit(BaseScreenDelegate::UPDATE_NOUPDATE);
340 case REASON_UPDATE_INIT_FAILED:
341 get_base_screen_delegate()->OnExit(
342 BaseScreenDelegate::UPDATE_ERROR_CHECKING_FOR_UPDATE);
344 case REASON_UPDATE_NON_CRITICAL:
345 case REASON_UPDATE_ENDED:
347 UpdateEngineClient* update_engine_client =
348 DBusThreadManager::Get()->GetUpdateEngineClient();
349 switch (update_engine_client->GetLastStatus().status) {
350 case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK:
352 case UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE:
353 case UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT:
354 case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING:
355 case UpdateEngineClient::UPDATE_STATUS_FINALIZING:
356 case UpdateEngineClient::UPDATE_STATUS_VERIFYING:
357 DCHECK(!HasCriticalUpdate());
358 // Noncritical update, just exit screen as if there is no update.
360 case UpdateEngineClient::UPDATE_STATUS_IDLE:
361 get_base_screen_delegate()->OnExit(
362 BaseScreenDelegate::UPDATE_NOUPDATE);
364 case UpdateEngineClient::UPDATE_STATUS_ERROR:
365 case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT:
366 get_base_screen_delegate()->OnExit(
367 is_checking_for_update_
368 ? BaseScreenDelegate::UPDATE_ERROR_CHECKING_FOR_UPDATE
369 : BaseScreenDelegate::UPDATE_ERROR_UPDATING);
381 void UpdateScreen::OnWaitForRebootTimeElapsed() {
382 LOG(ERROR) << "Unable to reboot - asking user for a manual reboot.";
383 MakeSureScreenIsShown();
385 actor_->ShowManualRebootInfo();
388 void UpdateScreen::MakeSureScreenIsShown() {
390 get_base_screen_delegate()->ShowCurrentScreen();
393 void UpdateScreen::SetRebootCheckDelay(int seconds) {
395 reboot_timer_.Stop();
396 DCHECK(!reboot_timer_.IsRunning());
397 reboot_check_delay_ = seconds;
400 void UpdateScreen::SetIgnoreIdleStatus(bool ignore_idle_status) {
401 ignore_idle_status_ = ignore_idle_status;
404 void UpdateScreen::UpdateDownloadingStats(
405 const UpdateEngineClient::Status& status) {
408 base::Time download_current_time = base::Time::Now();
409 if (download_current_time >= download_last_time_ + kMinTimeStep) {
410 // Estimate downloading rate.
411 double progress_delta =
412 std::max(status.download_progress - download_last_progress_, 0.0);
414 (download_current_time - download_last_time_).InSecondsF();
415 double download_rate = status.new_size * progress_delta / time_delta;
417 download_last_time_ = download_current_time;
418 download_last_progress_ = status.download_progress;
420 // Estimate time left.
421 double progress_left = std::max(1.0 - status.download_progress, 0.0);
422 if (!is_download_average_speed_computed_) {
423 download_average_speed_ = download_rate;
424 is_download_average_speed_computed_ = true;
426 download_average_speed_ =
427 kDownloadSpeedSmoothFactor * download_rate +
428 (1.0 - kDownloadSpeedSmoothFactor) * download_average_speed_;
429 if (download_average_speed_ < kDownloadAverageSpeedDropBound) {
431 (download_current_time - download_start_time_).InSecondsF();
432 download_average_speed_ =
434 (status.download_progress - download_start_progress_) /
437 double work_left = progress_left * status.new_size;
438 double time_left = work_left / download_average_speed_;
439 // |time_left| may be large enough or even +infinity. So we must
440 // |bound possible estimations.
441 time_left = std::min(time_left, kMaxTimeLeft);
443 actor_->ShowEstimatedTimeLeft(true);
444 actor_->SetEstimatedTimeLeft(
445 base::TimeDelta::FromSeconds(static_cast<int64>(time_left)));
448 int download_progress = static_cast<int>(
449 status.download_progress * kDownloadProgressIncrement);
450 actor_->SetProgress(kBeforeDownloadProgress + download_progress);
453 bool UpdateScreen::HasCriticalUpdate() {
454 if (is_ignore_update_deadlines_)
457 std::string deadline;
458 // Checking for update flag file causes us to do blocking IO on UI thread.
459 // Temporarily allow it until we fix http://crosbug.com/11106
460 base::ThreadRestrictions::ScopedAllowIO allow_io;
461 base::FilePath update_deadline_file_path(kUpdateDeadlineFile);
462 if (!base::ReadFileToString(update_deadline_file_path, &deadline) ||
467 // TODO(dpolukhin): Analyze file content. Now we can just assume that
468 // if the file exists and not empty, there is critical update.
472 void UpdateScreen::OnActorDestroyed(UpdateScreenActor* actor) {
477 void UpdateScreen::OnConnectToNetworkRequested() {
478 if (state_ == STATE_ERROR) {
479 LOG(WARNING) << "Hiding error message since AP was reselected";
484 ErrorScreen* UpdateScreen::GetErrorScreen() {
485 return get_base_screen_delegate()->GetErrorScreen();
488 void UpdateScreen::StartUpdateCheck() {
489 NetworkPortalDetector::Get()->RemoveObserver(this);
490 if (state_ == STATE_ERROR)
492 state_ = STATE_UPDATE;
493 DBusThreadManager::Get()->GetUpdateEngineClient()->AddObserver(this);
494 VLOG(1) << "Initiate update check";
495 DBusThreadManager::Get()->GetUpdateEngineClient()->RequestUpdateCheck(
496 base::Bind(StartUpdateCallback, this));
499 void UpdateScreen::ShowErrorMessage() {
500 LOG(WARNING) << "UpdateScreen::ShowErrorMessage()";
501 state_ = STATE_ERROR;
502 GetErrorScreen()->SetUIState(ErrorScreen::UI_STATE_UPDATE);
503 get_base_screen_delegate()->ShowErrorScreen();
504 histogram_helper_->OnErrorShow(GetErrorScreen()->GetErrorState());
507 void UpdateScreen::HideErrorMessage() {
508 LOG(WARNING) << "UpdateScreen::HideErrorMessage()";
509 get_base_screen_delegate()->HideErrorScreen(this);
510 histogram_helper_->OnErrorHide();
513 void UpdateScreen::UpdateErrorMessage(
514 const NetworkState* network,
515 const NetworkPortalDetector::CaptivePortalStatus status) {
517 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE:
520 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN:
521 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE:
522 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_OFFLINE,
525 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL:
527 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PORTAL,
529 if (is_first_portal_notification_) {
530 is_first_portal_notification_ = false;
531 GetErrorScreen()->FixCaptivePortal();
534 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED:
535 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PROXY,
544 void UpdateScreen::SetHostPairingControllerStatus(
545 HostPairingController::UpdateStatus update_status) {
546 if (remora_controller_) {
547 remora_controller_->OnUpdateStatusChanged(update_status);
551 } // namespace chromeos