Update broken references to image assets
[chromium-blink-merge.git] / chrome / browser / chromeos / login / screens / update_screen.cc
blobaf84e47eff7b7c39fd08f729972ce9e8e3037394
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/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/network_error.h"
19 #include "chrome/browser/chromeos/login/screens/update_view.h"
20 #include "chrome/browser/chromeos/login/startup_utils.h"
21 #include "chrome/browser/chromeos/login/wizard_controller.h"
22 #include "chrome/grit/chromium_strings.h"
23 #include "chrome/grit/generated_resources.h"
24 #include "chromeos/dbus/dbus_thread_manager.h"
25 #include "chromeos/network/network_state.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "ui/base/l10n/l10n_util.h"
29 using content::BrowserThread;
30 using pairing_chromeos::HostPairingController;
32 namespace chromeos {
34 namespace {
36 // If reboot didn't happen, ask user to reboot device manually.
37 const int kWaitForRebootTimeSec = 3;
39 // Progress bar stages. Each represents progress bar value
40 // at the beginning of each stage.
41 // TODO(nkostylev): Base stage progress values on approximate time.
42 // TODO(nkostylev): Animate progress during each state.
43 const int kBeforeUpdateCheckProgress = 7;
44 const int kBeforeDownloadProgress = 14;
45 const int kBeforeVerifyingProgress = 74;
46 const int kBeforeFinalizingProgress = 81;
47 const int kProgressComplete = 100;
49 // Defines what part of update progress does download part takes.
50 const int kDownloadProgressIncrement = 60;
52 const char kUpdateDeadlineFile[] = "/tmp/update-check-response-deadline";
54 // Minimum timestep between two consecutive measurements for the
55 // download rate.
56 const base::TimeDelta kMinTimeStep = base::TimeDelta::FromSeconds(1);
58 // Smooth factor that is used for the average downloading speed
59 // estimation.
60 // avg_speed = smooth_factor * cur_speed + (1.0 - smooth_factor) * avg_speed.
61 const double kDownloadSpeedSmoothFactor = 0.1;
63 // Minumum allowed value for the average downloading speed.
64 const double kDownloadAverageSpeedDropBound = 1e-8;
66 // An upper bound for possible downloading time left estimations.
67 const double kMaxTimeLeft = 24 * 60 * 60;
69 // Delay before showing error message if captive portal is detected.
70 // We wait for this delay to let captive portal to perform redirect and show
71 // its login page before error message appears.
72 const int kDelayErrorMessageSec = 10;
74 // Invoked from call to RequestUpdateCheck upon completion of the DBus call.
75 void StartUpdateCallback(UpdateScreen* screen,
76 UpdateEngineClient::UpdateCheckResult result) {
77 VLOG(1) << "Callback from RequestUpdateCheck, result " << result;
78 if (UpdateScreen::HasInstance(screen)) {
79 if (result == UpdateEngineClient::UPDATE_RESULT_SUCCESS)
80 screen->SetIgnoreIdleStatus(false);
81 else
82 screen->ExitUpdate(UpdateScreen::REASON_UPDATE_INIT_FAILED);
86 } // anonymous namespace
88 // static
89 UpdateScreen::InstanceSet& UpdateScreen::GetInstanceSet() {
90 CR_DEFINE_STATIC_LOCAL(std::set<UpdateScreen*>, instance_set, ());
91 DCHECK_CURRENTLY_ON(BrowserThread::UI); // not threadsafe.
92 return instance_set;
95 // static
96 bool UpdateScreen::HasInstance(UpdateScreen* inst) {
97 InstanceSet& instance_set = GetInstanceSet();
98 InstanceSet::iterator found = instance_set.find(inst);
99 return (found != instance_set.end());
102 // static
103 UpdateScreen* UpdateScreen::Get(ScreenManager* manager) {
104 return static_cast<UpdateScreen*>(
105 manager->GetScreen(WizardController::kUpdateScreenName));
108 UpdateScreen::UpdateScreen(BaseScreenDelegate* base_screen_delegate,
109 UpdateView* view,
110 HostPairingController* remora_controller)
111 : UpdateModel(base_screen_delegate),
112 state_(STATE_IDLE),
113 reboot_check_delay_(kWaitForRebootTimeSec),
114 is_checking_for_update_(true),
115 is_downloading_update_(false),
116 is_ignore_update_deadlines_(false),
117 is_shown_(false),
118 ignore_idle_status_(true),
119 view_(view),
120 remora_controller_(remora_controller),
121 is_first_detection_notification_(true),
122 is_first_portal_notification_(true),
123 histogram_helper_(new ErrorScreensHistogramHelper("Update")),
124 weak_factory_(this) {
125 if (view_)
126 view_->Bind(*this);
128 GetInstanceSet().insert(this);
131 UpdateScreen::~UpdateScreen() {
132 if (view_)
133 view_->Unbind();
135 DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
136 NetworkPortalDetector::Get()->RemoveObserver(this);
137 GetInstanceSet().erase(this);
140 void UpdateScreen::UpdateStatusChanged(
141 const UpdateEngineClient::Status& status) {
142 if (is_checking_for_update_ &&
143 status.status > UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE) {
144 is_checking_for_update_ = false;
146 if (ignore_idle_status_ && status.status >
147 UpdateEngineClient::UPDATE_STATUS_IDLE) {
148 ignore_idle_status_ = false;
151 switch (status.status) {
152 case UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE:
153 // Do nothing in these cases, we don't want to notify the user of the
154 // check unless there is an update.
155 SetHostPairingControllerStatus(
156 HostPairingController::UPDATE_STATUS_UPDATING);
157 break;
158 case UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE:
159 MakeSureScreenIsShown();
160 GetContextEditor()
161 .SetInteger(kContextKeyProgress, kBeforeDownloadProgress)
162 .SetBoolean(kContextKeyShowEstimatedTimeLeft, false);
163 if (!HasCriticalUpdate()) {
164 VLOG(1) << "Noncritical update available: " << status.new_version;
165 ExitUpdate(REASON_UPDATE_NON_CRITICAL);
166 } else {
167 VLOG(1) << "Critical update available: " << status.new_version;
168 GetContextEditor()
169 .SetString(kContextKeyProgressMessage,
170 l10n_util::GetStringUTF16(IDS_UPDATE_AVAILABLE))
171 .SetBoolean(kContextKeyShowProgressMessage, true)
172 .SetBoolean(kContextKeyShowCurtain, false);
174 break;
175 case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING:
177 MakeSureScreenIsShown();
178 if (!is_downloading_update_) {
179 // Because update engine doesn't send UPDATE_STATUS_UPDATE_AVAILABLE
180 // we need to is update critical on first downloading notification.
181 is_downloading_update_ = true;
182 download_start_time_ = download_last_time_ = base::Time::Now();
183 download_start_progress_ = status.download_progress;
184 download_last_progress_ = status.download_progress;
185 is_download_average_speed_computed_ = false;
186 download_average_speed_ = 0.0;
187 if (!HasCriticalUpdate()) {
188 VLOG(1) << "Non-critical update available: " << status.new_version;
189 ExitUpdate(REASON_UPDATE_NON_CRITICAL);
190 } else {
191 VLOG(1) << "Critical update available: " << status.new_version;
192 GetContextEditor()
193 .SetString(kContextKeyProgressMessage,
194 l10n_util::GetStringUTF16(IDS_INSTALLING_UPDATE))
195 .SetBoolean(kContextKeyShowProgressMessage, true)
196 .SetBoolean(kContextKeyShowCurtain, false);
199 UpdateDownloadingStats(status);
201 break;
202 case UpdateEngineClient::UPDATE_STATUS_VERIFYING:
203 MakeSureScreenIsShown();
204 GetContextEditor()
205 .SetInteger(kContextKeyProgress, kBeforeVerifyingProgress)
206 .SetString(kContextKeyProgressMessage,
207 l10n_util::GetStringUTF16(IDS_UPDATE_VERIFYING))
208 .SetBoolean(kContextKeyShowProgressMessage, true);
209 break;
210 case UpdateEngineClient::UPDATE_STATUS_FINALIZING:
211 MakeSureScreenIsShown();
212 GetContextEditor()
213 .SetInteger(kContextKeyProgress, kBeforeFinalizingProgress)
214 .SetString(kContextKeyProgressMessage,
215 l10n_util::GetStringUTF16(IDS_UPDATE_FINALIZING))
216 .SetBoolean(kContextKeyShowProgressMessage, true);
217 break;
218 case UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT:
219 MakeSureScreenIsShown();
220 GetContextEditor()
221 .SetInteger(kContextKeyProgress, kProgressComplete)
222 .SetBoolean(kContextKeyShowEstimatedTimeLeft, false);
223 if (HasCriticalUpdate()) {
224 GetContextEditor().SetBoolean(kContextKeyShowCurtain, false);
225 VLOG(1) << "Initiate reboot after update";
226 SetHostPairingControllerStatus(
227 HostPairingController::UPDATE_STATUS_REBOOTING);
228 DBusThreadManager::Get()->GetUpdateEngineClient()->RebootAfterUpdate();
229 reboot_timer_.Start(FROM_HERE,
230 base::TimeDelta::FromSeconds(reboot_check_delay_),
231 this,
232 &UpdateScreen::OnWaitForRebootTimeElapsed);
233 } else {
234 ExitUpdate(REASON_UPDATE_NON_CRITICAL);
236 break;
237 case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK:
238 VLOG(1) << "Attempting rollback";
239 break;
240 case UpdateEngineClient::UPDATE_STATUS_IDLE:
241 if (ignore_idle_status_) {
242 // It is first IDLE status that is sent before we initiated the check.
243 break;
245 // else no break
246 case UpdateEngineClient::UPDATE_STATUS_ERROR:
247 case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT:
248 ExitUpdate(REASON_UPDATE_ENDED);
249 break;
250 default:
251 NOTREACHED();
252 break;
256 void UpdateScreen::OnPortalDetectionCompleted(
257 const NetworkState* network,
258 const NetworkPortalDetector::CaptivePortalState& state) {
259 LOG(WARNING) << "UpdateScreen::OnPortalDetectionCompleted(): "
260 << "network=" << (network ? network->path() : "") << ", "
261 << "state.status=" << state.status << ", "
262 << "state.response_code=" << state.response_code;
264 // Wait for the sane detection results.
265 if (network &&
266 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN) {
267 return;
270 // Restart portal detection for the first notification about offline state.
271 if ((!network ||
272 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE) &&
273 is_first_detection_notification_) {
274 is_first_detection_notification_ = false;
275 base::MessageLoop::current()->PostTask(
276 FROM_HERE,
277 base::Bind(
278 base::IgnoreResult(&NetworkPortalDetector::StartDetectionIfIdle),
279 base::Unretained(NetworkPortalDetector::Get())));
280 return;
282 is_first_detection_notification_ = false;
284 NetworkPortalDetector::CaptivePortalStatus status = state.status;
285 if (state_ == STATE_ERROR) {
286 // In the case of online state hide error message and proceed to
287 // the update stage. Otherwise, update error message content.
288 if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE)
289 StartUpdateCheck();
290 else
291 UpdateErrorMessage(network, status);
292 } else if (state_ == STATE_FIRST_PORTAL_CHECK) {
293 // In the case of online state immediately proceed to the update
294 // stage. Otherwise, prepare and show error message.
295 if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE) {
296 StartUpdateCheck();
297 } else {
298 UpdateErrorMessage(network, status);
300 if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL)
301 DelayErrorMessage();
302 else
303 ShowErrorMessage();
308 void UpdateScreen::StartNetworkCheck() {
309 // If portal detector is enabled and portal detection before AU is
310 // allowed, initiate network state check. Otherwise, directly
311 // proceed to update.
312 if (!NetworkPortalDetector::Get()->IsEnabled()) {
313 StartUpdateCheck();
314 return;
316 state_ = STATE_FIRST_PORTAL_CHECK;
317 is_first_detection_notification_ = true;
318 is_first_portal_notification_ = true;
319 NetworkPortalDetector::Get()->AddAndFireObserver(this);
322 void UpdateScreen::PrepareToShow() {
323 if (!view_)
324 return;
326 view_->PrepareToShow();
329 void UpdateScreen::Show() {
330 is_shown_ = true;
331 histogram_helper_->OnScreenShow();
333 #if !defined(OFFICIAL_BUILD)
334 GetContextEditor().SetBoolean(kContextKeyCancelUpdateShortcutEnabled, true);
335 #endif
336 GetContextEditor().SetInteger(kContextKeyProgress,
337 kBeforeUpdateCheckProgress);
339 if (view_)
340 view_->Show();
343 void UpdateScreen::Hide() {
344 if (view_)
345 view_->Hide();
346 is_shown_ = false;
349 void UpdateScreen::Initialize(::login::ScreenContext* context) {
350 UpdateModel::Initialize(context);
353 void UpdateScreen::OnViewDestroyed(UpdateView* view) {
354 if (view_ == view)
355 view_ = nullptr;
358 void UpdateScreen::OnUserAction(const std::string& action_id) {
359 #if !defined(OFFICIAL_BUILD)
360 if (action_id == kUserActionCancelUpdateShortcut)
361 CancelUpdate();
362 else
363 #endif
364 BaseScreen::OnUserAction(action_id);
367 void UpdateScreen::OnContextKeyUpdated(
368 const ::login::ScreenContext::KeyType& key) {
369 UpdateModel::OnContextKeyUpdated(key);
372 void UpdateScreen::ExitUpdate(UpdateScreen::ExitReason reason) {
373 DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
374 NetworkPortalDetector::Get()->RemoveObserver(this);
375 SetHostPairingControllerStatus(HostPairingController::UPDATE_STATUS_UPDATED);
378 switch (reason) {
379 case REASON_UPDATE_CANCELED:
380 Finish(BaseScreenDelegate::UPDATE_NOUPDATE);
381 break;
382 case REASON_UPDATE_INIT_FAILED:
383 Finish(BaseScreenDelegate::UPDATE_ERROR_CHECKING_FOR_UPDATE);
384 break;
385 case REASON_UPDATE_NON_CRITICAL:
386 case REASON_UPDATE_ENDED:
388 UpdateEngineClient* update_engine_client =
389 DBusThreadManager::Get()->GetUpdateEngineClient();
390 switch (update_engine_client->GetLastStatus().status) {
391 case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK:
392 break;
393 case UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE:
394 case UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT:
395 case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING:
396 case UpdateEngineClient::UPDATE_STATUS_FINALIZING:
397 case UpdateEngineClient::UPDATE_STATUS_VERIFYING:
398 DCHECK(!HasCriticalUpdate());
399 // Noncritical update, just exit screen as if there is no update.
400 // no break
401 case UpdateEngineClient::UPDATE_STATUS_IDLE:
402 Finish(BaseScreenDelegate::UPDATE_NOUPDATE);
403 break;
404 case UpdateEngineClient::UPDATE_STATUS_ERROR:
405 case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT:
406 Finish(is_checking_for_update_
407 ? BaseScreenDelegate::UPDATE_ERROR_CHECKING_FOR_UPDATE
408 : BaseScreenDelegate::UPDATE_ERROR_UPDATING);
409 break;
410 default:
411 NOTREACHED();
414 break;
415 default:
416 NOTREACHED();
420 void UpdateScreen::OnWaitForRebootTimeElapsed() {
421 LOG(ERROR) << "Unable to reboot - asking user for a manual reboot.";
422 MakeSureScreenIsShown();
423 GetContextEditor().SetString(kContextKeyUpdateMessage,
424 l10n_util::GetStringUTF16(IDS_UPDATE_COMPLETED));
427 void UpdateScreen::MakeSureScreenIsShown() {
428 if (!is_shown_)
429 get_base_screen_delegate()->ShowCurrentScreen();
432 void UpdateScreen::SetIgnoreIdleStatus(bool ignore_idle_status) {
433 ignore_idle_status_ = ignore_idle_status;
436 void UpdateScreen::CancelUpdate() {
437 VLOG(1) << "Forced update cancel";
438 ExitUpdate(REASON_UPDATE_CANCELED);
441 void UpdateScreen::UpdateDownloadingStats(
442 const UpdateEngineClient::Status& status) {
443 base::Time download_current_time = base::Time::Now();
444 if (download_current_time >= download_last_time_ + kMinTimeStep) {
445 // Estimate downloading rate.
446 double progress_delta =
447 std::max(status.download_progress - download_last_progress_, 0.0);
448 double time_delta =
449 (download_current_time - download_last_time_).InSecondsF();
450 double download_rate = status.new_size * progress_delta / time_delta;
452 download_last_time_ = download_current_time;
453 download_last_progress_ = status.download_progress;
455 // Estimate time left.
456 double progress_left = std::max(1.0 - status.download_progress, 0.0);
457 if (!is_download_average_speed_computed_) {
458 download_average_speed_ = download_rate;
459 is_download_average_speed_computed_ = true;
461 download_average_speed_ =
462 kDownloadSpeedSmoothFactor * download_rate +
463 (1.0 - kDownloadSpeedSmoothFactor) * download_average_speed_;
464 if (download_average_speed_ < kDownloadAverageSpeedDropBound) {
465 time_delta =
466 (download_current_time - download_start_time_).InSecondsF();
467 download_average_speed_ =
468 status.new_size *
469 (status.download_progress - download_start_progress_) /
470 time_delta;
472 double work_left = progress_left * status.new_size;
473 double time_left = work_left / download_average_speed_;
474 // |time_left| may be large enough or even +infinity. So we must
475 // |bound possible estimations.
476 time_left = std::min(time_left, kMaxTimeLeft);
478 GetContextEditor()
479 .SetBoolean(kContextKeyShowEstimatedTimeLeft, true)
480 .SetInteger(kContextKeyEstimatedTimeLeftSec,
481 static_cast<int>(time_left));
484 int download_progress = static_cast<int>(
485 status.download_progress * kDownloadProgressIncrement);
486 GetContextEditor().SetInteger(kContextKeyProgress,
487 kBeforeDownloadProgress + download_progress);
490 bool UpdateScreen::HasCriticalUpdate() {
491 if (is_ignore_update_deadlines_)
492 return true;
494 std::string deadline;
495 // Checking for update flag file causes us to do blocking IO on UI thread.
496 // Temporarily allow it until we fix http://crosbug.com/11106
497 base::ThreadRestrictions::ScopedAllowIO allow_io;
498 base::FilePath update_deadline_file_path(kUpdateDeadlineFile);
499 if (!base::ReadFileToString(update_deadline_file_path, &deadline) ||
500 deadline.empty()) {
501 return false;
504 // TODO(dpolukhin): Analyze file content. Now we can just assume that
505 // if the file exists and not empty, there is critical update.
506 return true;
509 ErrorScreen* UpdateScreen::GetErrorScreen() {
510 return get_base_screen_delegate()->GetErrorScreen();
513 void UpdateScreen::StartUpdateCheck() {
514 error_message_timer_.Stop();
515 GetErrorScreen()->HideCaptivePortal();
517 NetworkPortalDetector::Get()->RemoveObserver(this);
518 connect_request_subscription_.reset();
519 if (state_ == STATE_ERROR)
520 HideErrorMessage();
521 state_ = STATE_UPDATE;
522 DBusThreadManager::Get()->GetUpdateEngineClient()->AddObserver(this);
523 VLOG(1) << "Initiate update check";
524 DBusThreadManager::Get()->GetUpdateEngineClient()->RequestUpdateCheck(
525 base::Bind(StartUpdateCallback, this));
528 void UpdateScreen::ShowErrorMessage() {
529 LOG(WARNING) << "UpdateScreen::ShowErrorMessage()";
531 error_message_timer_.Stop();
533 state_ = STATE_ERROR;
534 connect_request_subscription_ =
535 GetErrorScreen()->RegisterConnectRequestCallback(base::Bind(
536 &UpdateScreen::OnConnectRequested, base::Unretained(this)));
537 GetErrorScreen()->SetUIState(NetworkError::UI_STATE_UPDATE);
538 get_base_screen_delegate()->ShowErrorScreen();
539 histogram_helper_->OnErrorShow(GetErrorScreen()->GetErrorState());
542 void UpdateScreen::HideErrorMessage() {
543 LOG(WARNING) << "UpdateScreen::HideErrorMessage()";
544 get_base_screen_delegate()->HideErrorScreen(this);
545 histogram_helper_->OnErrorHide();
548 void UpdateScreen::UpdateErrorMessage(
549 const NetworkState* network,
550 const NetworkPortalDetector::CaptivePortalStatus status) {
551 switch (status) {
552 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE:
553 NOTREACHED();
554 break;
555 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN:
556 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE:
557 GetErrorScreen()->SetErrorState(NetworkError::ERROR_STATE_OFFLINE,
558 std::string());
559 break;
560 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL:
561 DCHECK(network);
562 GetErrorScreen()->SetErrorState(NetworkError::ERROR_STATE_PORTAL,
563 network->name());
564 if (is_first_portal_notification_) {
565 is_first_portal_notification_ = false;
566 GetErrorScreen()->FixCaptivePortal();
568 break;
569 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED:
570 GetErrorScreen()->SetErrorState(NetworkError::ERROR_STATE_PROXY,
571 std::string());
572 break;
573 default:
574 NOTREACHED();
575 break;
579 void UpdateScreen::SetHostPairingControllerStatus(
580 HostPairingController::UpdateStatus update_status) {
581 if (remora_controller_) {
582 remora_controller_->OnUpdateStatusChanged(update_status);
586 void UpdateScreen::DelayErrorMessage() {
587 if (error_message_timer_.IsRunning())
588 return;
590 state_ = STATE_ERROR;
591 error_message_timer_.Start(
592 FROM_HERE, base::TimeDelta::FromSeconds(kDelayErrorMessageSec), this,
593 &UpdateScreen::ShowErrorMessage);
596 base::OneShotTimer<UpdateScreen>&
597 UpdateScreen::GetErrorMessageTimerForTesting() {
598 return error_message_timer_;
601 void UpdateScreen::OnConnectRequested() {
602 if (state_ == STATE_ERROR) {
603 LOG(WARNING) << "Hiding error message since AP was reselected";
604 StartUpdateCheck();
608 } // namespace chromeos