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