Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / login / screens / update_screen.cc
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/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"
24
25 using content::BrowserThread;
26 using pairing_chromeos::HostPairingController;
27
28 namespace chromeos {
29
30 namespace {
31
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;
41
42 // Defines what part of update progress does download part takes.
43 const int kDownloadProgressIncrement = 60;
44
45 const char kUpdateDeadlineFile[] = "/tmp/update-check-response-deadline";
46
47 // Minimum timestep between two consecutive measurements for the
48 // download rate.
49 const base::TimeDelta kMinTimeStep = base::TimeDelta::FromSeconds(1);
50
51 // Smooth factor that is used for the average downloading speed
52 // estimation.
53 // avg_speed = smooth_factor * cur_speed + (1.0 - smooth_factor) * avg_speed.
54 const double kDownloadSpeedSmoothFactor = 0.1;
55
56 // Minumum allowed value for the average downloading speed.
57 const double kDownloadAverageSpeedDropBound = 1e-8;
58
59 // An upper bound for possible downloading time left estimations.
60 const double kMaxTimeLeft = 24 * 60 * 60;
61
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);
69     else
70       screen->ExitUpdate(UpdateScreen::REASON_UPDATE_INIT_FAILED);
71   }
72 }
73
74 }  // anonymous namespace
75
76 // static
77 UpdateScreen::InstanceSet& UpdateScreen::GetInstanceSet() {
78   CR_DEFINE_STATIC_LOCAL(std::set<UpdateScreen*>, instance_set, ());
79   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));  // not threadsafe.
80   return instance_set;
81 }
82
83 // static
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());
88 }
89
90 // static
91 UpdateScreen* UpdateScreen::Get(ScreenManager* manager) {
92   return static_cast<UpdateScreen*>(
93       manager->GetScreen(WizardController::kUpdateScreenName));
94 }
95
96 UpdateScreen::UpdateScreen(BaseScreenDelegate* base_screen_delegate,
97                            UpdateScreenActor* actor,
98                            HostPairingController* remora_controller)
99     : BaseScreen(base_screen_delegate),
100       state_(STATE_IDLE),
101       reboot_check_delay_(0),
102       is_checking_for_update_(true),
103       is_downloading_update_(false),
104       is_ignore_update_deadlines_(false),
105       is_shown_(false),
106       ignore_idle_status_(true),
107       actor_(actor),
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) {
113   DCHECK(actor_);
114   if (actor_)
115     actor_->SetDelegate(this);
116   GetInstanceSet().insert(this);
117 }
118
119 UpdateScreen::~UpdateScreen() {
120   DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
121   NetworkPortalDetector::Get()->RemoveObserver(this);
122   GetInstanceSet().erase(this);
123   if (actor_)
124     actor_->SetDelegate(NULL);
125 }
126
127 void UpdateScreen::UpdateStatusChanged(
128     const UpdateEngineClient::Status& status) {
129   if (!actor_)
130     return;
131
132   if (is_checking_for_update_ &&
133       status.status > UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE) {
134     is_checking_for_update_ = false;
135   }
136   if (ignore_idle_status_ && status.status >
137       UpdateEngineClient::UPDATE_STATUS_IDLE) {
138     ignore_idle_status_ = false;
139   }
140
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);
147       break;
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);
155       } else {
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);
161       }
162       break;
163     case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING:
164       {
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);
178           } else {
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);
184           }
185         }
186         UpdateDownloadingStats(status);
187       }
188       break;
189     case UpdateEngineClient::UPDATE_STATUS_VERIFYING:
190       MakeSureScreenIsShown();
191       actor_->SetProgress(kBeforeVerifyingProgress);
192       actor_->SetProgressMessage(UpdateScreenActor::PROGRESS_MESSAGE_VERIFYING);
193       actor_->ShowProgressMessage(true);
194       break;
195     case UpdateEngineClient::UPDATE_STATUS_FINALIZING:
196       MakeSureScreenIsShown();
197       actor_->SetProgress(kBeforeFinalizingProgress);
198       actor_->SetProgressMessage(
199           UpdateScreenActor::PROGRESS_MESSAGE_FINALIZING);
200       actor_->ShowProgressMessage(true);
201       break;
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_),
214                             this,
215                             &UpdateScreen::OnWaitForRebootTimeElapsed);
216       } else {
217         ExitUpdate(REASON_UPDATE_NON_CRITICAL);
218       }
219       break;
220     case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK:
221       VLOG(1) << "Attempting rollback";
222       break;
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.
226         break;
227       }
228       // else no break
229     case UpdateEngineClient::UPDATE_STATUS_ERROR:
230     case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT:
231       ExitUpdate(REASON_UPDATE_ENDED);
232       break;
233     default:
234       NOTREACHED();
235       break;
236   }
237 }
238
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;
246
247   // Wait for the sane detection results.
248   if (network &&
249       state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN) {
250     return;
251   }
252
253   // Restart portal detection for the first notification about offline state.
254   if ((!network ||
255        state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE) &&
256       is_first_detection_notification_) {
257     is_first_detection_notification_ = false;
258     base::MessageLoop::current()->PostTask(
259         FROM_HERE,
260         base::Bind(
261             base::IgnoreResult(&NetworkPortalDetector::StartDetectionIfIdle),
262             base::Unretained(NetworkPortalDetector::Get())));
263     return;
264   }
265   is_first_detection_notification_ = false;
266
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)
272       StartUpdateCheck();
273     else
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) {
279       StartUpdateCheck();
280     } else {
281       UpdateErrorMessage(network, status);
282       ShowErrorMessage();
283     }
284   }
285 }
286
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()) {
292     StartUpdateCheck();
293     return;
294   }
295   state_ = STATE_FIRST_PORTAL_CHECK;
296   is_first_detection_notification_ = true;
297   is_first_portal_notification_ = true;
298   NetworkPortalDetector::Get()->AddAndFireObserver(this);
299 }
300
301 void UpdateScreen::CancelUpdate() {
302   VLOG(1) << "Forced update cancel";
303   ExitUpdate(REASON_UPDATE_CANCELED);
304 }
305
306 void UpdateScreen::Show() {
307   is_shown_ = true;
308   histogram_helper_->OnScreenShow();
309   if (actor_) {
310     actor_->Show();
311     actor_->SetProgress(kBeforeUpdateCheckProgress);
312   }
313 }
314
315 void UpdateScreen::Hide() {
316   if (actor_)
317     actor_->Hide();
318   is_shown_ = false;
319 }
320
321 std::string UpdateScreen::GetName() const {
322   return WizardController::kUpdateScreenName;
323 }
324
325 void UpdateScreen::PrepareToShow() {
326   if (actor_)
327     actor_->PrepareToShow();
328 }
329
330 void UpdateScreen::ExitUpdate(UpdateScreen::ExitReason reason) {
331   DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
332   NetworkPortalDetector::Get()->RemoveObserver(this);
333   SetHostPairingControllerStatus(HostPairingController::UPDATE_STATUS_UPDATED);
334
335
336   switch (reason) {
337     case REASON_UPDATE_CANCELED:
338       get_base_screen_delegate()->OnExit(BaseScreenDelegate::UPDATE_NOUPDATE);
339       break;
340     case REASON_UPDATE_INIT_FAILED:
341       get_base_screen_delegate()->OnExit(
342           BaseScreenDelegate::UPDATE_ERROR_CHECKING_FOR_UPDATE);
343       break;
344     case REASON_UPDATE_NON_CRITICAL:
345     case REASON_UPDATE_ENDED:
346       {
347         UpdateEngineClient* update_engine_client =
348             DBusThreadManager::Get()->GetUpdateEngineClient();
349         switch (update_engine_client->GetLastStatus().status) {
350           case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK:
351             break;
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.
359             // no break
360           case UpdateEngineClient::UPDATE_STATUS_IDLE:
361             get_base_screen_delegate()->OnExit(
362                 BaseScreenDelegate::UPDATE_NOUPDATE);
363             break;
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);
370             break;
371           default:
372             NOTREACHED();
373         }
374       }
375       break;
376     default:
377       NOTREACHED();
378   }
379 }
380
381 void UpdateScreen::OnWaitForRebootTimeElapsed() {
382   LOG(ERROR) << "Unable to reboot - asking user for a manual reboot.";
383   MakeSureScreenIsShown();
384   if (actor_)
385     actor_->ShowManualRebootInfo();
386 }
387
388 void UpdateScreen::MakeSureScreenIsShown() {
389   if (!is_shown_)
390     get_base_screen_delegate()->ShowCurrentScreen();
391 }
392
393 void UpdateScreen::SetRebootCheckDelay(int seconds) {
394   if (seconds <= 0)
395     reboot_timer_.Stop();
396   DCHECK(!reboot_timer_.IsRunning());
397   reboot_check_delay_ = seconds;
398 }
399
400 void UpdateScreen::SetIgnoreIdleStatus(bool ignore_idle_status) {
401   ignore_idle_status_ = ignore_idle_status;
402 }
403
404 void UpdateScreen::UpdateDownloadingStats(
405     const UpdateEngineClient::Status& status) {
406   if (!actor_)
407     return;
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);
413     double time_delta =
414         (download_current_time - download_last_time_).InSecondsF();
415     double download_rate = status.new_size * progress_delta / time_delta;
416
417     download_last_time_ = download_current_time;
418     download_last_progress_ = status.download_progress;
419
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;
425     }
426     download_average_speed_ =
427         kDownloadSpeedSmoothFactor * download_rate +
428         (1.0 - kDownloadSpeedSmoothFactor) * download_average_speed_;
429     if (download_average_speed_ < kDownloadAverageSpeedDropBound) {
430       time_delta =
431           (download_current_time - download_start_time_).InSecondsF();
432       download_average_speed_ =
433           status.new_size *
434           (status.download_progress - download_start_progress_) /
435           time_delta;
436     }
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);
442
443     actor_->ShowEstimatedTimeLeft(true);
444     actor_->SetEstimatedTimeLeft(
445         base::TimeDelta::FromSeconds(static_cast<int64>(time_left)));
446   }
447
448   int download_progress = static_cast<int>(
449       status.download_progress * kDownloadProgressIncrement);
450   actor_->SetProgress(kBeforeDownloadProgress + download_progress);
451 }
452
453 bool UpdateScreen::HasCriticalUpdate() {
454   if (is_ignore_update_deadlines_)
455     return true;
456
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) ||
463       deadline.empty()) {
464     return false;
465   }
466
467   // TODO(dpolukhin): Analyze file content. Now we can just assume that
468   // if the file exists and not empty, there is critical update.
469   return true;
470 }
471
472 void UpdateScreen::OnActorDestroyed(UpdateScreenActor* actor) {
473   if (actor_ == actor)
474     actor_ = NULL;
475 }
476
477 void UpdateScreen::OnConnectToNetworkRequested() {
478   if (state_ == STATE_ERROR) {
479     LOG(WARNING) << "Hiding error message since AP was reselected";
480     StartUpdateCheck();
481   }
482 }
483
484 ErrorScreen* UpdateScreen::GetErrorScreen() {
485   return get_base_screen_delegate()->GetErrorScreen();
486 }
487
488 void UpdateScreen::StartUpdateCheck() {
489   NetworkPortalDetector::Get()->RemoveObserver(this);
490   if (state_ == STATE_ERROR)
491     HideErrorMessage();
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));
497 }
498
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());
505 }
506
507 void UpdateScreen::HideErrorMessage() {
508   LOG(WARNING) << "UpdateScreen::HideErrorMessage()";
509   get_base_screen_delegate()->HideErrorScreen(this);
510   histogram_helper_->OnErrorHide();
511 }
512
513 void UpdateScreen::UpdateErrorMessage(
514     const NetworkState* network,
515     const NetworkPortalDetector::CaptivePortalStatus status) {
516   switch (status) {
517     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE:
518       NOTREACHED();
519       break;
520     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN:
521     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE:
522       GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_OFFLINE,
523                                       std::string());
524       break;
525     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL:
526       DCHECK(network);
527       GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PORTAL,
528                                       network->name());
529       if (is_first_portal_notification_) {
530         is_first_portal_notification_ = false;
531         GetErrorScreen()->FixCaptivePortal();
532       }
533       break;
534     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED:
535       GetErrorScreen()->SetErrorState(ErrorScreen::ERROR_STATE_PROXY,
536                                       std::string());
537       break;
538     default:
539       NOTREACHED();
540       break;
541   }
542 }
543
544 void UpdateScreen::SetHostPairingControllerStatus(
545     HostPairingController::UpdateStatus update_status) {
546   if (remora_controller_) {
547     remora_controller_->OnUpdateStatusChanged(update_status);
548   }
549 }
550
551 }  // namespace chromeos