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/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
;
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
48 const base::TimeDelta kMinTimeStep
= base::TimeDelta::FromSeconds(1);
50 // Smooth factor that is used for the average downloading speed
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);
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
82 UpdateScreen::InstanceSet
& UpdateScreen::GetInstanceSet() {
83 CR_DEFINE_STATIC_LOCAL(std::set
<UpdateScreen
*>, instance_set
, ());
84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
)); // not threadsafe.
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
),
100 reboot_check_delay_(0),
101 is_checking_for_update_(true),
102 is_downloading_update_(false),
103 is_ignore_update_deadlines_(false),
105 ignore_idle_status_(true),
107 is_first_detection_notification_(true),
108 is_first_portal_notification_(true),
109 weak_factory_(this) {
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);
121 actor_
->SetDelegate(NULL
);
124 void UpdateScreen::UpdateStatusChanged(
125 const UpdateEngineClient::Status
& status
) {
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.
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
);
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);
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
);
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
);
188 case UpdateEngineClient::UPDATE_STATUS_VERIFYING
:
189 MakeSureScreenIsShown();
190 actor_
->SetProgress(kBeforeVerifyingProgress
);
191 actor_
->SetProgressMessage(UpdateScreenActor::PROGRESS_MESSAGE_VERIFYING
);
192 actor_
->ShowProgressMessage(true);
194 case UpdateEngineClient::UPDATE_STATUS_FINALIZING
:
195 MakeSureScreenIsShown();
196 actor_
->SetProgress(kBeforeFinalizingProgress
);
197 actor_
->SetProgressMessage(
198 UpdateScreenActor::PROGRESS_MESSAGE_FINALIZING
);
199 actor_
->ShowProgressMessage(true);
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_
),
214 &UpdateScreen::OnWaitForRebootTimeElapsed
);
216 ExitUpdate(REASON_UPDATE_NON_CRITICAL
);
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.
226 case UpdateEngineClient::UPDATE_STATUS_ERROR
:
227 case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT
:
228 ExitUpdate(REASON_UPDATE_ENDED
);
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.
246 state
.status
== NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN
) {
250 // Restart portal detection for the first notification about offline state.
252 state
.status
== NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE
) &&
253 is_first_detection_notification_
) {
254 is_first_detection_notification_
= false;
255 base::MessageLoop::current()->PostTask(
258 base::IgnoreResult(&NetworkPortalDetector::StartDetectionIfIdle
),
259 base::Unretained(NetworkPortalDetector::Get())));
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
)
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
) {
278 UpdateErrorMessage(network
, status
);
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()) {
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() {
308 actor_
->SetProgress(kBeforeUpdateCheckProgress
);
312 void UpdateScreen::Hide() {
318 std::string
UpdateScreen::GetName() const {
319 return WizardController::kUpdateScreenName
;
322 void UpdateScreen::PrepareToShow() {
324 actor_
->PrepareToShow();
327 void UpdateScreen::ExitUpdate(UpdateScreen::ExitReason reason
) {
328 DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
329 NetworkPortalDetector::Get()->RemoveObserver(this);
332 case REASON_UPDATE_CANCELED
:
333 get_screen_observer()->OnExit(ScreenObserver::UPDATE_NOUPDATE
);
335 case REASON_UPDATE_INIT_FAILED
:
336 get_screen_observer()->OnExit(
337 ScreenObserver::UPDATE_ERROR_CHECKING_FOR_UPDATE
);
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.
353 case UpdateEngineClient::UPDATE_STATUS_IDLE
:
354 get_screen_observer()->OnExit(ScreenObserver::UPDATE_NOUPDATE
);
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
);
372 void UpdateScreen::OnWaitForRebootTimeElapsed() {
373 LOG(ERROR
) << "Unable to reboot - asking user for a manual reboot.";
374 MakeSureScreenIsShown();
376 actor_
->ShowManualRebootInfo();
379 void UpdateScreen::MakeSureScreenIsShown() {
381 get_screen_observer()->ShowCurrentScreen();
384 void UpdateScreen::SetRebootCheckDelay(int seconds
) {
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
) {
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);
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
) {
422 (download_current_time
- download_start_time_
).InSecondsF();
423 download_average_speed_
=
425 (status
.download_progress
- download_start_progress_
) /
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_
)
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
) ||
458 // TODO(dpolukhin): Analyze file content. Now we can just assume that
459 // if the file exists and not empty, there is critical update.
463 void UpdateScreen::OnActorDestroyed(UpdateScreenActor
* actor
) {
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";
476 ErrorScreen
* UpdateScreen::GetErrorScreen() {
477 return get_screen_observer()->GetErrorScreen();
480 void UpdateScreen::StartUpdateCheck() {
481 NetworkPortalDetector::Get()->RemoveObserver(this);
482 if (state_
== STATE_ERROR
)
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
) {
507 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE
:
510 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN
:
511 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE
:
512 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_OFFLINE
,
515 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL
:
517 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PORTAL
,
519 if (is_first_portal_notification_
) {
520 is_first_portal_notification_
= false;
521 GetErrorScreen()->FixCaptivePortal();
524 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED
:
525 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PROXY
,
534 } // namespace chromeos