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 } // anonymous namespace
76 UpdateScreen::InstanceSet
& UpdateScreen::GetInstanceSet() {
77 CR_DEFINE_STATIC_LOCAL(std::set
<UpdateScreen
*>, instance_set
, ());
78 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
)); // not threadsafe.
83 bool UpdateScreen::HasInstance(UpdateScreen
* inst
) {
84 InstanceSet
& instance_set
= GetInstanceSet();
85 InstanceSet::iterator found
= instance_set
.find(inst
);
86 return (found
!= instance_set
.end());
89 UpdateScreen::UpdateScreen(
90 ScreenObserver
* screen_observer
,
91 UpdateScreenActor
* actor
)
92 : WizardScreen(screen_observer
),
94 reboot_check_delay_(0),
95 is_checking_for_update_(true),
96 is_downloading_update_(false),
97 is_ignore_update_deadlines_(false),
99 ignore_idle_status_(true),
101 is_first_detection_notification_(true),
102 is_first_portal_notification_(true),
103 weak_factory_(this) {
106 actor_
->SetDelegate(this);
107 GetInstanceSet().insert(this);
110 UpdateScreen::~UpdateScreen() {
111 DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
112 NetworkPortalDetector::Get()->RemoveObserver(this);
113 GetInstanceSet().erase(this);
115 actor_
->SetDelegate(NULL
);
118 void UpdateScreen::UpdateStatusChanged(
119 const UpdateEngineClient::Status
& status
) {
123 if (is_checking_for_update_
&&
124 status
.status
> UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE
) {
125 is_checking_for_update_
= false;
127 if (ignore_idle_status_
&& status
.status
>
128 UpdateEngineClient::UPDATE_STATUS_IDLE
) {
129 ignore_idle_status_
= false;
132 switch (status
.status
) {
133 case UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE
:
134 // Do nothing in these cases, we don't want to notify the user of the
135 // check unless there is an update.
137 case UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE
:
138 MakeSureScreenIsShown();
139 actor_
->SetProgress(kBeforeDownloadProgress
);
140 actor_
->ShowEstimatedTimeLeft(false);
141 if (!HasCriticalUpdate()) {
142 VLOG(1) << "Noncritical update available: " << status
.new_version
;
143 ExitUpdate(REASON_UPDATE_NON_CRITICAL
);
145 VLOG(1) << "Critical update available: " << status
.new_version
;
146 actor_
->SetProgressMessage(
147 UpdateScreenActor::PROGRESS_MESSAGE_UPDATE_AVAILABLE
);
148 actor_
->ShowProgressMessage(true);
149 actor_
->ShowCurtain(false);
152 case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING
:
154 MakeSureScreenIsShown();
155 if (!is_downloading_update_
) {
156 // Because update engine doesn't send UPDATE_STATUS_UPDATE_AVAILABLE
157 // we need to is update critical on first downloading notification.
158 is_downloading_update_
= true;
159 download_start_time_
= download_last_time_
= base::Time::Now();
160 download_start_progress_
= status
.download_progress
;
161 download_last_progress_
= status
.download_progress
;
162 is_download_average_speed_computed_
= false;
163 download_average_speed_
= 0.0;
164 if (!HasCriticalUpdate()) {
165 VLOG(1) << "Non-critical update available: " << status
.new_version
;
166 ExitUpdate(REASON_UPDATE_NON_CRITICAL
);
168 VLOG(1) << "Critical update available: " << status
.new_version
;
169 actor_
->SetProgressMessage(
170 UpdateScreenActor::PROGRESS_MESSAGE_INSTALLING_UPDATE
);
171 actor_
->ShowProgressMessage(true);
172 actor_
->ShowCurtain(false);
175 UpdateDownloadingStats(status
);
178 case UpdateEngineClient::UPDATE_STATUS_VERIFYING
:
179 MakeSureScreenIsShown();
180 actor_
->SetProgress(kBeforeVerifyingProgress
);
181 actor_
->SetProgressMessage(UpdateScreenActor::PROGRESS_MESSAGE_VERIFYING
);
182 actor_
->ShowProgressMessage(true);
184 case UpdateEngineClient::UPDATE_STATUS_FINALIZING
:
185 MakeSureScreenIsShown();
186 actor_
->SetProgress(kBeforeFinalizingProgress
);
187 actor_
->SetProgressMessage(
188 UpdateScreenActor::PROGRESS_MESSAGE_FINALIZING
);
189 actor_
->ShowProgressMessage(true);
191 case UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT
:
192 MakeSureScreenIsShown();
193 // Make sure that first OOBE stage won't be shown after reboot.
194 StartupUtils::MarkOobeCompleted();
195 actor_
->SetProgress(kProgressComplete
);
196 actor_
->ShowEstimatedTimeLeft(false);
197 if (HasCriticalUpdate()) {
198 actor_
->ShowCurtain(false);
199 VLOG(1) << "Initiate reboot after update";
200 DBusThreadManager::Get()->GetUpdateEngineClient()->RebootAfterUpdate();
201 reboot_timer_
.Start(FROM_HERE
,
202 base::TimeDelta::FromSeconds(reboot_check_delay_
),
204 &UpdateScreen::OnWaitForRebootTimeElapsed
);
206 ExitUpdate(REASON_UPDATE_NON_CRITICAL
);
209 case UpdateEngineClient::UPDATE_STATUS_IDLE
:
210 if (ignore_idle_status_
) {
211 // It is first IDLE status that is sent before we initiated the check.
216 case UpdateEngineClient::UPDATE_STATUS_ERROR
:
217 case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT
:
218 ExitUpdate(REASON_UPDATE_ENDED
);
226 void UpdateScreen::OnPortalDetectionCompleted(
227 const NetworkState
* network
,
228 const NetworkPortalDetector::CaptivePortalState
& state
) {
229 LOG(WARNING
) << "UpdateScreen::PortalDetectionCompleted(): "
230 << "network=" << (network
? network
->path() : "") << ", "
231 << "state.status=" << state
.status
<< ", "
232 << "state.response_code=" << state
.response_code
;
234 // Wait for the sane detection results.
236 state
.status
== NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN
) {
240 // Restart portal detection for the first notification about offline state.
242 state
.status
== NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE
) &&
243 is_first_detection_notification_
) {
244 is_first_detection_notification_
= false;
245 base::MessageLoop::current()->PostTask(
248 base::IgnoreResult(&NetworkPortalDetector::StartDetectionIfIdle
),
249 base::Unretained(NetworkPortalDetector::Get())));
252 is_first_detection_notification_
= false;
254 NetworkPortalDetector::CaptivePortalStatus status
= state
.status
;
255 if (state_
== STATE_ERROR
) {
256 // In the case of online state hide error message and proceed to
257 // the update stage. Otherwise, update error message content.
258 if (status
== NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE
)
261 UpdateErrorMessage(network
, status
);
262 } else if (state_
== STATE_FIRST_PORTAL_CHECK
) {
263 // In the case of online state immediately proceed to the update
264 // stage. Otherwise, prepare and show error message.
265 if (status
== NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE
) {
268 UpdateErrorMessage(network
, status
);
274 void UpdateScreen::StartNetworkCheck() {
275 // If portal detector is enabled and portal detection before AU is
276 // allowed, initiate network state check. Otherwise, directly
277 // proceed to update.
278 if (!NetworkPortalDetector::Get()->IsEnabled()) {
282 state_
= STATE_FIRST_PORTAL_CHECK
;
283 is_first_detection_notification_
= true;
284 is_first_portal_notification_
= true;
285 NetworkPortalDetector::Get()->AddAndFireObserver(this);
288 void UpdateScreen::CancelUpdate() {
289 VLOG(1) << "Forced update cancel";
290 ExitUpdate(REASON_UPDATE_CANCELED
);
293 void UpdateScreen::Show() {
297 actor_
->SetProgress(kBeforeUpdateCheckProgress
);
301 void UpdateScreen::Hide() {
307 std::string
UpdateScreen::GetName() const {
308 return WizardController::kUpdateScreenName
;
311 void UpdateScreen::PrepareToShow() {
313 actor_
->PrepareToShow();
316 void UpdateScreen::ExitUpdate(UpdateScreen::ExitReason reason
) {
317 DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
318 NetworkPortalDetector::Get()->RemoveObserver(this);
321 case REASON_UPDATE_CANCELED
:
322 get_screen_observer()->OnExit(ScreenObserver::UPDATE_NOUPDATE
);
324 case REASON_UPDATE_INIT_FAILED
:
325 get_screen_observer()->OnExit(
326 ScreenObserver::UPDATE_ERROR_CHECKING_FOR_UPDATE
);
328 case REASON_UPDATE_NON_CRITICAL
:
329 case REASON_UPDATE_ENDED
:
331 UpdateEngineClient
* update_engine_client
=
332 DBusThreadManager::Get()->GetUpdateEngineClient();
333 switch (update_engine_client
->GetLastStatus().status
) {
334 case UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE
:
335 case UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT
:
336 case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING
:
337 case UpdateEngineClient::UPDATE_STATUS_FINALIZING
:
338 case UpdateEngineClient::UPDATE_STATUS_VERIFYING
:
339 DCHECK(!HasCriticalUpdate());
340 // Noncritical update, just exit screen as if there is no update.
342 case UpdateEngineClient::UPDATE_STATUS_IDLE
:
343 get_screen_observer()->OnExit(ScreenObserver::UPDATE_NOUPDATE
);
345 case UpdateEngineClient::UPDATE_STATUS_ERROR
:
346 case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT
:
347 get_screen_observer()->OnExit(is_checking_for_update_
?
348 ScreenObserver::UPDATE_ERROR_CHECKING_FOR_UPDATE
:
349 ScreenObserver::UPDATE_ERROR_UPDATING
);
361 void UpdateScreen::OnWaitForRebootTimeElapsed() {
362 LOG(ERROR
) << "Unable to reboot - asking user for a manual reboot.";
363 MakeSureScreenIsShown();
365 actor_
->ShowManualRebootInfo();
368 void UpdateScreen::MakeSureScreenIsShown() {
370 get_screen_observer()->ShowCurrentScreen();
373 void UpdateScreen::SetRebootCheckDelay(int seconds
) {
375 reboot_timer_
.Stop();
376 DCHECK(!reboot_timer_
.IsRunning());
377 reboot_check_delay_
= seconds
;
380 void UpdateScreen::SetIgnoreIdleStatus(bool ignore_idle_status
) {
381 ignore_idle_status_
= ignore_idle_status
;
384 void UpdateScreen::UpdateDownloadingStats(
385 const UpdateEngineClient::Status
& status
) {
388 base::Time download_current_time
= base::Time::Now();
389 if (download_current_time
>= download_last_time_
+ kMinTimeStep
) {
390 // Estimate downloading rate.
391 double progress_delta
=
392 std::max(status
.download_progress
- download_last_progress_
, 0.0);
394 (download_current_time
- download_last_time_
).InSecondsF();
395 double download_rate
= status
.new_size
* progress_delta
/ time_delta
;
397 download_last_time_
= download_current_time
;
398 download_last_progress_
= status
.download_progress
;
400 // Estimate time left.
401 double progress_left
= std::max(1.0 - status
.download_progress
, 0.0);
402 if (!is_download_average_speed_computed_
) {
403 download_average_speed_
= download_rate
;
404 is_download_average_speed_computed_
= true;
406 download_average_speed_
=
407 kDownloadSpeedSmoothFactor
* download_rate
+
408 (1.0 - kDownloadSpeedSmoothFactor
) * download_average_speed_
;
409 if (download_average_speed_
< kDownloadAverageSpeedDropBound
) {
411 (download_current_time
- download_start_time_
).InSecondsF();
412 download_average_speed_
=
414 (status
.download_progress
- download_start_progress_
) /
417 double work_left
= progress_left
* status
.new_size
;
418 double time_left
= work_left
/ download_average_speed_
;
419 // |time_left| may be large enough or even +infinity. So we must
420 // |bound possible estimations.
421 time_left
= std::min(time_left
, kMaxTimeLeft
);
423 actor_
->ShowEstimatedTimeLeft(true);
424 actor_
->SetEstimatedTimeLeft(
425 base::TimeDelta::FromSeconds(static_cast<int64
>(time_left
)));
428 int download_progress
= static_cast<int>(
429 status
.download_progress
* kDownloadProgressIncrement
);
430 actor_
->SetProgress(kBeforeDownloadProgress
+ download_progress
);
433 bool UpdateScreen::HasCriticalUpdate() {
434 if (is_ignore_update_deadlines_
)
437 std::string deadline
;
438 // Checking for update flag file causes us to do blocking IO on UI thread.
439 // Temporarily allow it until we fix http://crosbug.com/11106
440 base::ThreadRestrictions::ScopedAllowIO allow_io
;
441 base::FilePath
update_deadline_file_path(kUpdateDeadlineFile
);
442 if (!base::ReadFileToString(update_deadline_file_path
, &deadline
) ||
447 // TODO(dpolukhin): Analyze file content. Now we can just assume that
448 // if the file exists and not empty, there is critical update.
452 void UpdateScreen::OnActorDestroyed(UpdateScreenActor
* actor
) {
457 void UpdateScreen::OnConnectToNetworkRequested(
458 const std::string
& service_path
) {
459 if (state_
== STATE_ERROR
) {
460 LOG(WARNING
) << "Hiding error message since AP was reselected";
465 ErrorScreen
* UpdateScreen::GetErrorScreen() {
466 return get_screen_observer()->GetErrorScreen();
469 void UpdateScreen::StartUpdateCheck() {
470 NetworkPortalDetector::Get()->RemoveObserver(this);
471 if (state_
== STATE_ERROR
)
473 state_
= STATE_UPDATE
;
474 DBusThreadManager::Get()->GetUpdateEngineClient()->AddObserver(this);
475 VLOG(1) << "Initiate update check";
476 DBusThreadManager::Get()->GetUpdateEngineClient()->RequestUpdateCheck(
477 base::Bind(StartUpdateCallback
, this));
480 void UpdateScreen::ShowErrorMessage() {
481 LOG(WARNING
) << "UpdateScreen::ShowErrorMessage()";
482 state_
= STATE_ERROR
;
483 GetErrorScreen()->SetUIState(ErrorScreen::UI_STATE_UPDATE
);
484 get_screen_observer()->ShowErrorScreen();
487 void UpdateScreen::HideErrorMessage() {
488 LOG(WARNING
) << "UpdateScreen::HideErrorMessage()";
489 get_screen_observer()->HideErrorScreen(this);
492 void UpdateScreen::UpdateErrorMessage(
493 const NetworkState
* network
,
494 const NetworkPortalDetector::CaptivePortalStatus status
) {
496 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE
:
499 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN
:
500 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE
:
501 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_OFFLINE
,
504 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL
:
506 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PORTAL
,
508 if (is_first_portal_notification_
) {
509 is_first_portal_notification_
= false;
510 GetErrorScreen()->FixCaptivePortal();
513 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED
:
514 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PROXY
,
523 } // namespace chromeos