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.
4
5 #include "chrome/browser/chromeos/login/screens/update_screen.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/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/screens/error_screen.h"
15 #include "chrome/browser/chromeos/login/screens/screen_observer.h"
16 #include "chrome/browser/chromeos/login/screens/update_screen_actor.h"
17 #include "chrome/browser/chromeos/login/startup_utils.h"
18 #include "chrome/browser/chromeos/login/wizard_controller.h"
19 #include "chromeos/dbus/dbus_thread_manager.h"
20 #include "chromeos/network/network_state.h"
21 #include "content/public/browser/browser_thread.h"
22
23 using content::BrowserThread;
24
25 namespace chromeos {
26
27 namespace {
28
29 // Progress bar stages. Each represents progress bar value
30 // at the beginning of each stage.
31 // TODO(nkostylev): Base stage progress values on approximate time.
32 // TODO(nkostylev): Animate progress during each state.
33 const int kBeforeUpdateCheckProgress = 7;
34 const int kBeforeDownloadProgress = 14;
35 const int kBeforeVerifyingProgress = 74;
36 const int kBeforeFinalizingProgress = 81;
37 const int kProgressComplete = 100;
38
39 // Defines what part of update progress does download part takes.
40 const int kDownloadProgressIncrement = 60;
41
42 const char kUpdateDeadlineFile[] = "/tmp/update-check-response-deadline";
43
44 // Minimum timestep between two consecutive measurements for the
45 // download rate.
46 const base::TimeDelta kMinTimeStep = base::TimeDelta::FromSeconds(1);
47
48 // Smooth factor that is used for the average downloading speed
49 // estimation.
50 // avg_speed = smooth_factor * cur_speed + (1.0 - smooth_factor) * avg_speed.
51 const double kDownloadSpeedSmoothFactor = 0.1;
52
53 // Minumum allowed value for the average downloading speed.
54 const double kDownloadAverageSpeedDropBound = 1e-8;
55
56 // An upper bound for possible downloading time left estimations.
57 const double kMaxTimeLeft = 24 * 60 * 60;
58
59 // Invoked from call to RequestUpdateCheck upon completion of the DBus call.
StartUpdateCallback(UpdateScreen * screen,UpdateEngineClient::UpdateCheckResult result)60 void StartUpdateCallback(UpdateScreen* screen,
61 UpdateEngineClient::UpdateCheckResult result) {
62 VLOG(1) << "Callback from RequestUpdateCheck, result " << result;
63 if (UpdateScreen::HasInstance(screen)) {
64 if (result == UpdateEngineClient::UPDATE_RESULT_SUCCESS)
65 screen->SetIgnoreIdleStatus(false);
66 else
67 screen->ExitUpdate(UpdateScreen::REASON_UPDATE_INIT_FAILED);
68 }
69 }
70
71 } // anonymous namespace
72
73 // static
GetInstanceSet()74 UpdateScreen::InstanceSet& UpdateScreen::GetInstanceSet() {
75 CR_DEFINE_STATIC_LOCAL(std::set<UpdateScreen*>, instance_set, ());
76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // not threadsafe.
77 return instance_set;
78 }
79
80 // static
HasInstance(UpdateScreen * inst)81 bool UpdateScreen::HasInstance(UpdateScreen* inst) {
82 InstanceSet& instance_set = GetInstanceSet();
83 InstanceSet::iterator found = instance_set.find(inst);
84 return (found != instance_set.end());
85 }
86
UpdateScreen(ScreenObserver * screen_observer,UpdateScreenActor * actor)87 UpdateScreen::UpdateScreen(
88 ScreenObserver* screen_observer,
89 UpdateScreenActor* actor)
90 : WizardScreen(screen_observer),
91 state_(STATE_IDLE),
92 reboot_check_delay_(0),
93 is_checking_for_update_(true),
94 is_downloading_update_(false),
95 is_ignore_update_deadlines_(false),
96 is_shown_(false),
97 ignore_idle_status_(true),
98 actor_(actor),
99 is_first_detection_notification_(true),
100 is_first_portal_notification_(true),
101 weak_factory_(this) {
102 DCHECK(actor_);
103 if (actor_)
104 actor_->SetDelegate(this);
105 GetInstanceSet().insert(this);
106 }
107
~UpdateScreen()108 UpdateScreen::~UpdateScreen() {
109 DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
110 NetworkPortalDetector::Get()->RemoveObserver(this);
111 GetInstanceSet().erase(this);
112 if (actor_)
113 actor_->SetDelegate(NULL);
114 }
115
UpdateStatusChanged(const UpdateEngineClient::Status & status)116 void UpdateScreen::UpdateStatusChanged(
117 const UpdateEngineClient::Status& status) {
118 if (!actor_)
119 return;
120
121 if (is_checking_for_update_ &&
122 status.status > UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE) {
123 is_checking_for_update_ = false;
124 }
125 if (ignore_idle_status_ && status.status >
126 UpdateEngineClient::UPDATE_STATUS_IDLE) {
127 ignore_idle_status_ = false;
128 }
129
130 switch (status.status) {
131 case UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE:
132 // Do nothing in these cases, we don't want to notify the user of the
133 // check unless there is an update.
134 break;
135 case UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE:
136 MakeSureScreenIsShown();
137 actor_->SetProgress(kBeforeDownloadProgress);
138 actor_->ShowEstimatedTimeLeft(false);
139 if (!HasCriticalUpdate()) {
140 VLOG(1) << "Noncritical update available: " << status.new_version;
141 ExitUpdate(REASON_UPDATE_NON_CRITICAL);
142 } else {
143 VLOG(1) << "Critical update available: " << status.new_version;
144 actor_->SetProgressMessage(
145 UpdateScreenActor::PROGRESS_MESSAGE_UPDATE_AVAILABLE);
146 actor_->ShowProgressMessage(true);
147 actor_->ShowCurtain(false);
148 }
149 break;
150 case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING:
151 {
152 MakeSureScreenIsShown();
153 if (!is_downloading_update_) {
154 // Because update engine doesn't send UPDATE_STATUS_UPDATE_AVAILABLE
155 // we need to is update critical on first downloading notification.
156 is_downloading_update_ = true;
157 download_start_time_ = download_last_time_ = base::Time::Now();
158 download_start_progress_ = status.download_progress;
159 download_last_progress_ = status.download_progress;
160 is_download_average_speed_computed_ = false;
161 download_average_speed_ = 0.0;
162 if (!HasCriticalUpdate()) {
163 VLOG(1) << "Non-critical update available: " << status.new_version;
164 ExitUpdate(REASON_UPDATE_NON_CRITICAL);
165 } else {
166 VLOG(1) << "Critical update available: " << status.new_version;
167 actor_->SetProgressMessage(
168 UpdateScreenActor::PROGRESS_MESSAGE_INSTALLING_UPDATE);
169 actor_->ShowProgressMessage(true);
170 actor_->ShowCurtain(false);
171 }
172 }
173 UpdateDownloadingStats(status);
174 }
175 break;
176 case UpdateEngineClient::UPDATE_STATUS_VERIFYING:
177 MakeSureScreenIsShown();
178 actor_->SetProgress(kBeforeVerifyingProgress);
179 actor_->SetProgressMessage(UpdateScreenActor::PROGRESS_MESSAGE_VERIFYING);
180 actor_->ShowProgressMessage(true);
181 break;
182 case UpdateEngineClient::UPDATE_STATUS_FINALIZING:
183 MakeSureScreenIsShown();
184 actor_->SetProgress(kBeforeFinalizingProgress);
185 actor_->SetProgressMessage(
186 UpdateScreenActor::PROGRESS_MESSAGE_FINALIZING);
187 actor_->ShowProgressMessage(true);
188 break;
189 case UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT:
190 MakeSureScreenIsShown();
191 actor_->SetProgress(kProgressComplete);
192 actor_->ShowEstimatedTimeLeft(false);
193 if (HasCriticalUpdate()) {
194 actor_->ShowCurtain(false);
195 VLOG(1) << "Initiate reboot after update";
196 DBusThreadManager::Get()->GetUpdateEngineClient()->RebootAfterUpdate();
197 reboot_timer_.Start(FROM_HERE,
198 base::TimeDelta::FromSeconds(reboot_check_delay_),
199 this,
200 &UpdateScreen::OnWaitForRebootTimeElapsed);
201 } else {
202 ExitUpdate(REASON_UPDATE_NON_CRITICAL);
203 }
204 break;
205 case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK:
206 VLOG(1) << "Attempting rollback";
207 break;
208 case UpdateEngineClient::UPDATE_STATUS_IDLE:
209 if (ignore_idle_status_) {
210 // It is first IDLE status that is sent before we initiated the check.
211 break;
212 }
213 // else no break
214 case UpdateEngineClient::UPDATE_STATUS_ERROR:
215 case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT:
216 ExitUpdate(REASON_UPDATE_ENDED);
217 break;
218 default:
219 NOTREACHED();
220 break;
221 }
222 }
223
OnPortalDetectionCompleted(const NetworkState * network,const NetworkPortalDetector::CaptivePortalState & state)224 void UpdateScreen::OnPortalDetectionCompleted(
225 const NetworkState* network,
226 const NetworkPortalDetector::CaptivePortalState& state) {
227 LOG(WARNING) << "UpdateScreen::PortalDetectionCompleted(): "
228 << "network=" << (network ? network->path() : "") << ", "
229 << "state.status=" << state.status << ", "
230 << "state.response_code=" << state.response_code;
231
232 // Wait for the sane detection results.
233 if (network &&
234 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN) {
235 return;
236 }
237
238 // Restart portal detection for the first notification about offline state.
239 if ((!network ||
240 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE) &&
241 is_first_detection_notification_) {
242 is_first_detection_notification_ = false;
243 base::MessageLoop::current()->PostTask(
244 FROM_HERE,
245 base::Bind(
246 base::IgnoreResult(&NetworkPortalDetector::StartDetectionIfIdle),
247 base::Unretained(NetworkPortalDetector::Get())));
248 return;
249 }
250 is_first_detection_notification_ = false;
251
252 NetworkPortalDetector::CaptivePortalStatus status = state.status;
253 if (state_ == STATE_ERROR) {
254 // In the case of online state hide error message and proceed to
255 // the update stage. Otherwise, update error message content.
256 if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE)
257 StartUpdateCheck();
258 else
259 UpdateErrorMessage(network, status);
260 } else if (state_ == STATE_FIRST_PORTAL_CHECK) {
261 // In the case of online state immediately proceed to the update
262 // stage. Otherwise, prepare and show error message.
263 if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE) {
264 StartUpdateCheck();
265 } else {
266 UpdateErrorMessage(network, status);
267 ShowErrorMessage();
268 }
269 }
270 }
271
StartNetworkCheck()272 void UpdateScreen::StartNetworkCheck() {
273 // If portal detector is enabled and portal detection before AU is
274 // allowed, initiate network state check. Otherwise, directly
275 // proceed to update.
276 if (!NetworkPortalDetector::Get()->IsEnabled()) {
277 StartUpdateCheck();
278 return;
279 }
280 state_ = STATE_FIRST_PORTAL_CHECK;
281 is_first_detection_notification_ = true;
282 is_first_portal_notification_ = true;
283 NetworkPortalDetector::Get()->AddAndFireObserver(this);
284 }
285
CancelUpdate()286 void UpdateScreen::CancelUpdate() {
287 VLOG(1) << "Forced update cancel";
288 ExitUpdate(REASON_UPDATE_CANCELED);
289 }
290
Show()291 void UpdateScreen::Show() {
292 is_shown_ = true;
293 if (actor_) {
294 actor_->Show();
295 actor_->SetProgress(kBeforeUpdateCheckProgress);
296 }
297 }
298
Hide()299 void UpdateScreen::Hide() {
300 if (actor_)
301 actor_->Hide();
302 is_shown_ = false;
303 }
304
GetName() const305 std::string UpdateScreen::GetName() const {
306 return WizardController::kUpdateScreenName;
307 }
308
PrepareToShow()309 void UpdateScreen::PrepareToShow() {
310 if (actor_)
311 actor_->PrepareToShow();
312 }
313
ExitUpdate(UpdateScreen::ExitReason reason)314 void UpdateScreen::ExitUpdate(UpdateScreen::ExitReason reason) {
315 DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
316 NetworkPortalDetector::Get()->RemoveObserver(this);
317
318 switch (reason) {
319 case REASON_UPDATE_CANCELED:
320 get_screen_observer()->OnExit(ScreenObserver::UPDATE_NOUPDATE);
321 break;
322 case REASON_UPDATE_INIT_FAILED:
323 get_screen_observer()->OnExit(
324 ScreenObserver::UPDATE_ERROR_CHECKING_FOR_UPDATE);
325 break;
326 case REASON_UPDATE_NON_CRITICAL:
327 case REASON_UPDATE_ENDED:
328 {
329 UpdateEngineClient* update_engine_client =
330 DBusThreadManager::Get()->GetUpdateEngineClient();
331 switch (update_engine_client->GetLastStatus().status) {
332 case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK:
333 break;
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.
341 // no break
342 case UpdateEngineClient::UPDATE_STATUS_IDLE:
343 get_screen_observer()->OnExit(ScreenObserver::UPDATE_NOUPDATE);
344 break;
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);
350 break;
351 default:
352 NOTREACHED();
353 }
354 }
355 break;
356 default:
357 NOTREACHED();
358 }
359 }
360
OnWaitForRebootTimeElapsed()361 void UpdateScreen::OnWaitForRebootTimeElapsed() {
362 LOG(ERROR) << "Unable to reboot - asking user for a manual reboot.";
363 MakeSureScreenIsShown();
364 if (actor_)
365 actor_->ShowManualRebootInfo();
366 }
367
MakeSureScreenIsShown()368 void UpdateScreen::MakeSureScreenIsShown() {
369 if (!is_shown_)
370 get_screen_observer()->ShowCurrentScreen();
371 }
372
SetRebootCheckDelay(int seconds)373 void UpdateScreen::SetRebootCheckDelay(int seconds) {
374 if (seconds <= 0)
375 reboot_timer_.Stop();
376 DCHECK(!reboot_timer_.IsRunning());
377 reboot_check_delay_ = seconds;
378 }
379
SetIgnoreIdleStatus(bool ignore_idle_status)380 void UpdateScreen::SetIgnoreIdleStatus(bool ignore_idle_status) {
381 ignore_idle_status_ = ignore_idle_status;
382 }
383
UpdateDownloadingStats(const UpdateEngineClient::Status & status)384 void UpdateScreen::UpdateDownloadingStats(
385 const UpdateEngineClient::Status& status) {
386 if (!actor_)
387 return;
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);
393 double time_delta =
394 (download_current_time - download_last_time_).InSecondsF();
395 double download_rate = status.new_size * progress_delta / time_delta;
396
397 download_last_time_ = download_current_time;
398 download_last_progress_ = status.download_progress;
399
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;
405 }
406 download_average_speed_ =
407 kDownloadSpeedSmoothFactor * download_rate +
408 (1.0 - kDownloadSpeedSmoothFactor) * download_average_speed_;
409 if (download_average_speed_ < kDownloadAverageSpeedDropBound) {
410 time_delta =
411 (download_current_time - download_start_time_).InSecondsF();
412 download_average_speed_ =
413 status.new_size *
414 (status.download_progress - download_start_progress_) /
415 time_delta;
416 }
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);
422
423 actor_->ShowEstimatedTimeLeft(true);
424 actor_->SetEstimatedTimeLeft(
425 base::TimeDelta::FromSeconds(static_cast<int64>(time_left)));
426 }
427
428 int download_progress = static_cast<int>(
429 status.download_progress * kDownloadProgressIncrement);
430 actor_->SetProgress(kBeforeDownloadProgress + download_progress);
431 }
432
HasCriticalUpdate()433 bool UpdateScreen::HasCriticalUpdate() {
434 if (is_ignore_update_deadlines_)
435 return true;
436
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) ||
443 deadline.empty()) {
444 return false;
445 }
446
447 // TODO(dpolukhin): Analyze file content. Now we can just assume that
448 // if the file exists and not empty, there is critical update.
449 return true;
450 }
451
OnActorDestroyed(UpdateScreenActor * actor)452 void UpdateScreen::OnActorDestroyed(UpdateScreenActor* actor) {
453 if (actor_ == actor)
454 actor_ = NULL;
455 }
456
OnConnectToNetworkRequested(const std::string & service_path)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";
461 StartUpdateCheck();
462 }
463 }
464
GetErrorScreen()465 ErrorScreen* UpdateScreen::GetErrorScreen() {
466 return get_screen_observer()->GetErrorScreen();
467 }
468
StartUpdateCheck()469 void UpdateScreen::StartUpdateCheck() {
470 NetworkPortalDetector::Get()->RemoveObserver(this);
471 if (state_ == STATE_ERROR)
472 HideErrorMessage();
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));
478 }
479
ShowErrorMessage()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();
485 }
486
HideErrorMessage()487 void UpdateScreen::HideErrorMessage() {
488 LOG(WARNING) << "UpdateScreen::HideErrorMessage()";
489 get_screen_observer()->HideErrorScreen(this);
490 }
491
UpdateErrorMessage(const NetworkState * network,const NetworkPortalDetector::CaptivePortalStatus status)492 void UpdateScreen::UpdateErrorMessage(
493 const NetworkState* network,
494 const NetworkPortalDetector::CaptivePortalStatus status) {
495 switch (status) {
496 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE:
497 NOTREACHED();
498 break;
499 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN:
500 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE:
501 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_OFFLINE,
502 std::string());
503 break;
504 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL:
505 DCHECK(network);
506 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PORTAL,
507 network->name());
508 if (is_first_portal_notification_) {
509 is_first_portal_notification_ = false;
510 GetErrorScreen()->FixCaptivePortal();
511 }
512 break;
513 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED:
514 GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PROXY,
515 std::string());
516 break;
517 default:
518 NOTREACHED();
519 break;
520 }
521 }
522
523 } // namespace chromeos
524