12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/chromeos/system/automatic_reboot_manager.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#include <fcntl.h>
8a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#include <sys/stat.h>
9a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#include <sys/types.h>
10a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <algorithm>
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <string>
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ash/shell.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind_helpers.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/callback.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h"
20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/files/scoped_file.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/location.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/logging.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/memory/ref_counted.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/path_service.h"
25a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#include "base/posix/eintr_wrapper.h"
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_registry_simple.h"
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_service.h"
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/single_thread_task_runner.h"
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/thread_task_runner_handle.h"
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/threading/sequenced_worker_pool.h"
32a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#include "base/threading/thread_restrictions.h"
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/time/tick_clock.h"
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/browser_process.h"
357dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/chromeos/system/automatic_reboot_manager_observer.h"
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/pref_names.h"
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chromeos/chromeos_paths.h"
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chromeos/chromeos_switches.h"
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chromeos/dbus/dbus_thread_manager.h"
416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "components/user_manager/user_manager.h"
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/browser_thread.h"
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/notification_details.h"
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/notification_service.h"
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/notification_source.h"
46c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "ui/wm/core/user_activity_detector.h"
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace chromeos {
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace system {
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
53a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)const int kMinRebootUptimeMs = 60 * 60 * 1000;     // 1 hour.
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kLoginManagerIdleTimeoutMs = 60 * 1000;  // 60 seconds.
55a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)const int kGracePeriodMs = 24 * 60 * 60 * 1000;    // 24 hours.
56a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)const int kOneKilobyte = 1 << 10;                  // 1 kB in bytes.
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::TimeDelta ReadTimeDeltaFromFile(const base::FilePath& path) {
59a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  base::ThreadRestrictions::AssertIOAllowed();
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  base::ScopedFD fd(
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      HANDLE_EINTR(open(path.value().c_str(), O_RDONLY | O_NOFOLLOW)));
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!fd.is_valid())
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return base::TimeDelta();
64a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
65a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  std::string contents;
66a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  char buffer[kOneKilobyte];
67a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  ssize_t length;
68effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  while ((length = HANDLE_EINTR(read(fd.get(), buffer, sizeof(buffer)))) > 0)
69a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    contents.append(buffer, length);
70a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  double seconds;
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!base::StringToDouble(contents.substr(0, contents.find(' ')), &seconds) ||
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      seconds < 0.0) {
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return base::TimeDelta();
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return base::TimeDelta::FromMilliseconds(seconds * 1000.0);
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void GetSystemEventTimes(
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_refptr<base::SingleThreadTaskRunner> reply_task_runner,
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::Callback<void(
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        const AutomaticRebootManager::SystemEventTimes&)> reply) {
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath uptime_file;
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CHECK(PathService::Get(chromeos::FILE_UPTIME, &uptime_file));
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath update_reboot_needed_uptime_file;
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CHECK(PathService::Get(chromeos::FILE_UPDATE_REBOOT_NEEDED_UPTIME,
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         &update_reboot_needed_uptime_file));
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  reply_task_runner->PostTask(FROM_HERE, base::Bind(reply,
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AutomaticRebootManager::SystemEventTimes(
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          ReadTimeDeltaFromFile(uptime_file),
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          ReadTimeDeltaFromFile(update_reboot_needed_uptime_file))));
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SaveUpdateRebootNeededUptime() {
95a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  base::ThreadRestrictions::AssertIOAllowed();
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::TimeDelta kZeroTimeDelta;
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath update_reboot_needed_uptime_file;
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CHECK(PathService::Get(chromeos::FILE_UPDATE_REBOOT_NEEDED_UPTIME,
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         &update_reboot_needed_uptime_file));
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::TimeDelta last_update_reboot_needed_uptime =
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ReadTimeDeltaFromFile(update_reboot_needed_uptime_file);
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (last_update_reboot_needed_uptime != kZeroTimeDelta)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath uptime_file;
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CHECK(PathService::Get(chromeos::FILE_UPTIME, &uptime_file));
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::TimeDelta uptime = ReadTimeDeltaFromFile(uptime_file);
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (uptime == kZeroTimeDelta)
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  base::ScopedFD fd(HANDLE_EINTR(
113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      open(update_reboot_needed_uptime_file.value().c_str(),
114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)           O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW,
115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)           0666)));
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!fd.is_valid())
117a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    return;
118a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string update_reboot_needed_uptime =
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::DoubleToString(uptime.InSecondsF());
121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  base::WriteFileDescriptor(fd.get(), update_reboot_needed_uptime.c_str(),
122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                            update_reboot_needed_uptime.size());
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AutomaticRebootManager::SystemEventTimes::SystemEventTimes()
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : has_boot_time(false),
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      has_update_reboot_needed_time(false) {
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AutomaticRebootManager::SystemEventTimes::SystemEventTimes(
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::TimeDelta& uptime,
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::TimeDelta& update_reboot_needed_uptime)
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : has_boot_time(false),
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      has_update_reboot_needed_time(false) {
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::TimeDelta kZeroTimeDelta;
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (uptime == kZeroTimeDelta)
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  boot_time = base::TimeTicks::Now() - uptime;
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  has_boot_time = true;
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (update_reboot_needed_uptime == kZeroTimeDelta)
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Calculate the time at which an update was applied and a reboot became
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // necessary in base::TimeTicks::Now() ticks.
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  update_reboot_needed_time = boot_time + update_reboot_needed_uptime;
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  has_update_reboot_needed_time = true;
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AutomaticRebootManager::AutomaticRebootManager(
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_ptr<base::TickClock> clock)
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : clock_(clock.Pass()),
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      have_boot_time_(false),
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      have_update_reboot_needed_time_(false),
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      reboot_requested_(false),
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      weak_ptr_factory_(this) {
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  local_state_registrar_.Init(g_browser_process->local_state());
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  local_state_registrar_.Add(prefs::kUptimeLimit,
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             base::Bind(&AutomaticRebootManager::Reschedule,
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        base::Unretained(this)));
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  local_state_registrar_.Add(prefs::kRebootAfterUpdate,
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             base::Bind(&AutomaticRebootManager::Reschedule,
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        base::Unretained(this)));
164868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
165868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      content::NotificationService::AllSources());
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DBusThreadManager* dbus_thread_manager = DBusThreadManager::Get();
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  dbus_thread_manager->GetPowerManagerClient()->AddObserver(this);
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  dbus_thread_manager->GetUpdateEngineClient()->AddObserver(this);
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If no user is logged in, a reboot may be performed whenever the user is
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // idle. Start listening for user activity to determine whether the user is
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // idle or not.
1746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (!user_manager::UserManager::Get()->IsUserLoggedIn()) {
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (ash::Shell::HasInstance())
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ash::Shell::GetInstance()->user_activity_detector()->AddObserver(this);
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    notification_registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_CHANGED,
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        content::NotificationService::AllSources());
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    login_screen_idle_timer_.reset(
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        new base::OneShotTimer<AutomaticRebootManager>);
1817dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    OnUserActivity(NULL);
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // In a regular browser, base::ThreadTaskRunnerHandle::Get() and
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // base::MessageLoopProxy::current() return pointers to the same object.
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // In unit tests, using base::ThreadTaskRunnerHandle::Get() has the advantage
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // that it allows a custom base::SingleThreadTaskRunner to be injected.
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content::BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FROM_HERE,
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&GetSystemEventTimes,
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 base::ThreadTaskRunnerHandle::Get(),
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 base::Bind(&AutomaticRebootManager::Init,
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            weak_ptr_factory_.GetWeakPtr())),
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AutomaticRebootManager::~AutomaticRebootManager() {
198eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  FOR_EACH_OBSERVER(AutomaticRebootManagerObserver,
199eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                    observers_,
200eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                    WillDestroyAutomaticRebootManager());
201eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DBusThreadManager* dbus_thread_manager = DBusThreadManager::Get();
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  dbus_thread_manager->GetPowerManagerClient()->RemoveObserver(this);
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  dbus_thread_manager->GetUpdateEngineClient()->RemoveObserver(this);
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (ash::Shell::HasInstance())
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this);
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
209eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid AutomaticRebootManager::AddObserver(
210eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    AutomaticRebootManagerObserver* observer) {
211eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  observers_.AddObserver(observer);
212eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
213eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
214eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid AutomaticRebootManager::RemoveObserver(
215eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    AutomaticRebootManagerObserver* observer) {
216eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  observers_.RemoveObserver(observer);
217eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
218eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
2190529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochvoid AutomaticRebootManager::SuspendDone(
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::TimeDelta& sleep_duration) {
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  MaybeReboot(true);
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AutomaticRebootManager::UpdateStatusChanged(
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const UpdateEngineClient::Status& status) {
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Ignore repeated notifications that a reboot is necessary. This is important
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // so that only the time of the first notification is taken into account and
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // repeated notifications do not postpone the reboot request and grace period.
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (status.status != UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT ||
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      !have_boot_time_ || have_update_reboot_needed_time_) {
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content::BrowserThread::PostBlockingPoolTask(
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FROM_HERE, base::Bind(&SaveUpdateRebootNeededUptime));
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  update_reboot_needed_time_ = clock_->NowTicks();
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  have_update_reboot_needed_time_ = true;
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Reschedule();
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochvoid AutomaticRebootManager::OnUserActivity(const ui::Event* event) {
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!login_screen_idle_timer_)
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Destroying and re-creating the timer ensures that Start() posts a fresh
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // task with a delay of exactly |kLoginManagerIdleTimeoutMs|, ensuring that
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the timer fires predictably in tests.
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  login_screen_idle_timer_.reset(
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new base::OneShotTimer<AutomaticRebootManager>);
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  login_screen_idle_timer_->Start(
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FROM_HERE,
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::TimeDelta::FromMilliseconds(kLoginManagerIdleTimeoutMs),
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&AutomaticRebootManager::MaybeReboot,
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 base::Unretained(this),
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 false));
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AutomaticRebootManager::Observe(
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int type,
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const content::NotificationSource& source,
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const content::NotificationDetails& details) {
264868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (type == chrome::NOTIFICATION_APP_TERMINATING) {
2656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (user_manager::UserManager::Get()->IsUserLoggedIn()) {
266868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      // The browser is terminating during a session, either because the session
267868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      // is ending or because the browser is being restarted.
268868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      MaybeReboot(true);
269868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
270868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  } else if (type == chrome::NOTIFICATION_LOGIN_USER_CHANGED) {
271868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // A session is starting. Stop listening for user activity as it no longer
272868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // is a relevant criterion.
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (ash::Shell::HasInstance())
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this);
275868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    notification_registrar_.Remove(
276868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        this, chrome::NOTIFICATION_LOGIN_USER_CHANGED,
277868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        content::NotificationService::AllSources());
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    login_screen_idle_timer_.reset();
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    NOTREACHED();
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AutomaticRebootManager::RegisterPrefs(PrefRegistrySimple* registry) {
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registry->RegisterIntegerPref(prefs::kUptimeLimit, 0);
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registry->RegisterBooleanPref(prefs::kRebootAfterUpdate, false);
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AutomaticRebootManager::Init(const SystemEventTimes& system_event_times) {
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::TimeDelta offset = clock_->NowTicks() - base::TimeTicks::Now();
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (system_event_times.has_boot_time) {
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Convert the time at which the device was booted to |clock_| ticks.
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    boot_time_ = system_event_times.boot_time + offset;
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    have_boot_time_ = true;
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (system_event_times.has_update_reboot_needed_time) {
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Convert the time at which a reboot became necessary to |clock_| ticks.
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::TimeTicks update_reboot_needed_time =
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        system_event_times.update_reboot_needed_time + offset;
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    update_reboot_needed_time_ = update_reboot_needed_time;
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    have_update_reboot_needed_time_ = true;
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    UpdateStatusChanged(
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        DBusThreadManager::Get()->GetUpdateEngineClient()->GetLastStatus());
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Reschedule();
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AutomaticRebootManager::Reschedule() {
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Safeguard against reboot loops under error conditions: If the boot time is
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // unavailable because /proc/uptime could not be read, do nothing.
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!have_boot_time_)
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Assume that no reboot has been requested.
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  reboot_requested_ = false;
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::TimeDelta kZeroTimeDelta;
321eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  AutomaticRebootManagerObserver::Reason reboot_reason =
322eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      AutomaticRebootManagerObserver::REBOOT_REASON_UNKNOWN;
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If an uptime limit is set, calculate the time at which it should cause a
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // reboot to be requested.
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::TimeDelta uptime_limit = base::TimeDelta::FromSeconds(
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      local_state_registrar_.prefs()->GetInteger(prefs::kUptimeLimit));
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::TimeTicks reboot_request_time = boot_time_ + uptime_limit;
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool have_reboot_request_time = uptime_limit != kZeroTimeDelta;
330eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (have_reboot_request_time)
331eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    reboot_reason = AutomaticRebootManagerObserver::REBOOT_REASON_PERIODIC;
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If the policy to automatically reboot after an update is enabled and an
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // update has been applied, set the time at which a reboot should be
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // requested to the minimum of its current value and the time when the reboot
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // became necessary.
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (have_update_reboot_needed_time_ &&
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      local_state_registrar_.prefs()->GetBoolean(prefs::kRebootAfterUpdate) &&
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (!have_reboot_request_time ||
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       update_reboot_needed_time_ < reboot_request_time)) {
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    reboot_request_time = update_reboot_needed_time_;
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    have_reboot_request_time = true;
343eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    reboot_reason = AutomaticRebootManagerObserver::REBOOT_REASON_OS_UPDATE;
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If no reboot should be requested, remove any grace period.
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!have_reboot_request_time) {
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    grace_start_timer_.reset();
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    grace_end_timer_.reset();
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Safeguard against reboot loops: Ensure that the uptime after which a reboot
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // is actually requested and the grace period begins is never less than
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // |kMinRebootUptimeMs|.
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::TimeTicks now = clock_->NowTicks();
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::TimeTicks grace_start_time = std::max(reboot_request_time,
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      boot_time_ + base::TimeDelta::FromMilliseconds(kMinRebootUptimeMs));
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Set up a timer for the start of the grace period. If the grace period
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // started in the past, the timer is still used with its delay set to zero.
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!grace_start_timer_)
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    grace_start_timer_.reset(new base::OneShotTimer<AutomaticRebootManager>);
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  grace_start_timer_->Start(FROM_HERE,
3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            std::max(grace_start_time - now, kZeroTimeDelta),
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            base::Bind(&AutomaticRebootManager::RequestReboot,
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                       base::Unretained(this)));
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::TimeTicks grace_end_time = grace_start_time +
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::TimeDelta::FromMilliseconds(kGracePeriodMs);
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Set up a timer for the end of the grace period. If the grace period ended
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // in the past, the timer is still used with its delay set to zero.
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!grace_end_timer_)
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    grace_end_timer_.reset(new base::OneShotTimer<AutomaticRebootManager>);
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  grace_end_timer_->Start(FROM_HERE,
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          std::max(grace_end_time - now, kZeroTimeDelta),
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          base::Bind(&AutomaticRebootManager::Reboot,
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     base::Unretained(this)));
378eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
379eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK_NE(AutomaticRebootManagerObserver::REBOOT_REASON_UNKNOWN,
380eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            reboot_reason);
381eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  FOR_EACH_OBSERVER(AutomaticRebootManagerObserver,
382eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                    observers_,
383eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                    OnRebootScheduled(reboot_reason));
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AutomaticRebootManager::RequestReboot() {
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  reboot_requested_ = true;
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  MaybeReboot(false);
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
391868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void AutomaticRebootManager::MaybeReboot(bool ignore_session) {
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Do not reboot if any of the following applies:
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // * No reboot has been requested.
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // * A user is interacting with the login screen.
395868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // * A session is in progress and |ignore_session| is not set.
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!reboot_requested_ ||
3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (login_screen_idle_timer_ && login_screen_idle_timer_->IsRunning()) ||
3986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      (!ignore_session && user_manager::UserManager::Get()->IsUserLoggedIn())) {
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Reboot();
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AutomaticRebootManager::Reboot() {
406868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // If a non-kiosk-app session is in progress, do not reboot.
4076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (user_manager::UserManager::Get()->IsUserLoggedIn() &&
4086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      !user_manager::UserManager::Get()->IsLoggedInAsKioskApp()) {
409868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return;
410868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
411868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  login_screen_idle_timer_.reset();
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  grace_start_timer_.reset();
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  grace_end_timer_.reset();
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace system
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace chromeos
420