automatic_reboot_manager.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright (c) 2013 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/system/automatic_reboot_manager.h"
6
7#include <algorithm>
8#include <string>
9
10#include "ash/shell.h"
11#include "ash/wm/user_activity_detector.h"
12#include "base/bind.h"
13#include "base/bind_helpers.h"
14#include "base/callback.h"
15#include "base/file_util.h"
16#include "base/files/file_path.h"
17#include "base/location.h"
18#include "base/logging.h"
19#include "base/memory/ref_counted.h"
20#include "base/path_service.h"
21#include "base/prefs/pref_registry_simple.h"
22#include "base/prefs/pref_service.h"
23#include "base/single_thread_task_runner.h"
24#include "base/strings/string_number_conversions.h"
25#include "base/thread_task_runner_handle.h"
26#include "base/threading/sequenced_worker_pool.h"
27#include "base/time/tick_clock.h"
28#include "chrome/browser/browser_process.h"
29#include "chrome/browser/chromeos/login/user_manager.h"
30#include "chrome/common/chrome_notification_types.h"
31#include "chrome/common/chrome_paths.h"
32#include "chrome/common/pref_names.h"
33#include "chromeos/dbus/dbus_thread_manager.h"
34#include "content/public/browser/browser_thread.h"
35#include "content/public/browser/notification_details.h"
36#include "content/public/browser/notification_service.h"
37#include "content/public/browser/notification_source.h"
38
39namespace chromeos {
40namespace system {
41
42namespace {
43
44const int kMinRebootUptimeMs = 60 * 60 * 1000;  // 1 hour.
45const int kLoginManagerIdleTimeoutMs = 60 * 1000;  // 60 seconds.
46const int kGracePeriodMs = 24 * 60 * 60 * 1000;  // 24 hours.
47
48base::TimeDelta ReadTimeDeltaFromFile(const base::FilePath& path) {
49  std::string contents;
50  if (!file_util::ReadFileToString(path, &contents))
51    return base::TimeDelta();
52  double seconds;
53  if (!base::StringToDouble(contents.substr(0, contents.find(' ')), &seconds) ||
54      seconds < 0.0) {
55    return base::TimeDelta();
56  }
57  return base::TimeDelta::FromMilliseconds(seconds * 1000.0);
58}
59
60void GetSystemEventTimes(
61    scoped_refptr<base::SingleThreadTaskRunner> reply_task_runner,
62    base::Callback<void(
63        const AutomaticRebootManager::SystemEventTimes&)> reply) {
64  base::FilePath uptime_file;
65  CHECK(PathService::Get(chrome::FILE_UPTIME, &uptime_file));
66  base::FilePath update_reboot_needed_uptime_file;
67  CHECK(PathService::Get(chrome::FILE_UPDATE_REBOOT_NEEDED_UPTIME,
68                         &update_reboot_needed_uptime_file));
69  reply_task_runner->PostTask(FROM_HERE, base::Bind(reply,
70      AutomaticRebootManager::SystemEventTimes(
71          ReadTimeDeltaFromFile(uptime_file),
72          ReadTimeDeltaFromFile(update_reboot_needed_uptime_file))));
73}
74
75void SaveUpdateRebootNeededUptime() {
76  const base::TimeDelta kZeroTimeDelta;
77
78  base::FilePath update_reboot_needed_uptime_file;
79  CHECK(PathService::Get(chrome::FILE_UPDATE_REBOOT_NEEDED_UPTIME,
80                         &update_reboot_needed_uptime_file));
81  const base::TimeDelta last_update_reboot_needed_uptime =
82      ReadTimeDeltaFromFile(update_reboot_needed_uptime_file);
83  if (last_update_reboot_needed_uptime != kZeroTimeDelta)
84    return;
85
86  base::FilePath uptime_file;
87  CHECK(PathService::Get(chrome::FILE_UPTIME, &uptime_file));
88  const base::TimeDelta uptime = ReadTimeDeltaFromFile(uptime_file);
89  if (uptime == kZeroTimeDelta)
90    return;
91
92  std::string update_reboot_needed_uptime =
93      base::DoubleToString(uptime.InSecondsF());
94  file_util::WriteFile(update_reboot_needed_uptime_file,
95                       update_reboot_needed_uptime.c_str(),
96                       update_reboot_needed_uptime.size());
97}
98
99}  // namespace
100
101AutomaticRebootManager::SystemEventTimes::SystemEventTimes()
102    : has_boot_time(false),
103      has_update_reboot_needed_time(false) {
104}
105
106AutomaticRebootManager::SystemEventTimes::SystemEventTimes(
107    const base::TimeDelta& uptime,
108    const base::TimeDelta& update_reboot_needed_uptime)
109    : has_boot_time(false),
110      has_update_reboot_needed_time(false) {
111  const base::TimeDelta kZeroTimeDelta;
112  if (uptime == kZeroTimeDelta)
113    return;
114  boot_time = base::TimeTicks::Now() - uptime;
115  has_boot_time = true;
116  if (update_reboot_needed_uptime == kZeroTimeDelta)
117    return;
118  // Calculate the time at which an update was applied and a reboot became
119  // necessary in base::TimeTicks::Now() ticks.
120  update_reboot_needed_time = boot_time + update_reboot_needed_uptime;
121  has_update_reboot_needed_time = true;
122}
123
124AutomaticRebootManager::AutomaticRebootManager(
125    scoped_ptr<base::TickClock> clock)
126    : clock_(clock.Pass()),
127      have_boot_time_(false),
128      have_update_reboot_needed_time_(false),
129      reboot_requested_(false),
130      weak_ptr_factory_(this) {
131  local_state_registrar_.Init(g_browser_process->local_state());
132  local_state_registrar_.Add(prefs::kUptimeLimit,
133                             base::Bind(&AutomaticRebootManager::Reschedule,
134                                        base::Unretained(this)));
135  local_state_registrar_.Add(prefs::kRebootAfterUpdate,
136                             base::Bind(&AutomaticRebootManager::Reschedule,
137                                        base::Unretained(this)));
138
139  DBusThreadManager* dbus_thread_manager = DBusThreadManager::Get();
140  dbus_thread_manager->GetPowerManagerClient()->AddObserver(this);
141  dbus_thread_manager->GetUpdateEngineClient()->AddObserver(this);
142
143  // If no user is logged in, a reboot may be performed whenever the user is
144  // idle. Start listening for user activity to determine whether the user is
145  // idle or not.
146  if (!UserManager::Get()->IsUserLoggedIn()) {
147    if (ash::Shell::HasInstance())
148      ash::Shell::GetInstance()->user_activity_detector()->AddObserver(this);
149    notification_registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_CHANGED,
150        content::NotificationService::AllSources());
151    login_screen_idle_timer_.reset(
152        new base::OneShotTimer<AutomaticRebootManager>);
153    OnUserActivity();
154  }
155
156  // In a regular browser, base::ThreadTaskRunnerHandle::Get() and
157  // base::MessageLoopProxy::current() return pointers to the same object.
158  // In unit tests, using base::ThreadTaskRunnerHandle::Get() has the advantage
159  // that it allows a custom base::SingleThreadTaskRunner to be injected.
160  content::BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
161      FROM_HERE,
162      base::Bind(&GetSystemEventTimes,
163                 base::ThreadTaskRunnerHandle::Get(),
164                 base::Bind(&AutomaticRebootManager::Init,
165                            weak_ptr_factory_.GetWeakPtr())),
166      base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
167}
168
169AutomaticRebootManager::~AutomaticRebootManager() {
170  DBusThreadManager* dbus_thread_manager = DBusThreadManager::Get();
171  dbus_thread_manager->GetPowerManagerClient()->RemoveObserver(this);
172  dbus_thread_manager->GetUpdateEngineClient()->RemoveObserver(this);
173  if (ash::Shell::HasInstance())
174    ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this);
175}
176
177void AutomaticRebootManager::SystemResumed(
178    const base::TimeDelta& sleep_duration) {
179  MaybeReboot(true);
180}
181
182void AutomaticRebootManager::UpdateStatusChanged(
183    const UpdateEngineClient::Status& status) {
184  // Ignore repeated notifications that a reboot is necessary. This is important
185  // so that only the time of the first notification is taken into account and
186  // repeated notifications do not postpone the reboot request and grace period.
187  if (status.status != UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT ||
188      !have_boot_time_ || have_update_reboot_needed_time_) {
189    return;
190  }
191
192  content::BrowserThread::PostBlockingPoolTask(
193      FROM_HERE, base::Bind(&SaveUpdateRebootNeededUptime));
194
195  update_reboot_needed_time_ = clock_->NowTicks();
196  have_update_reboot_needed_time_ = true;
197
198  Reschedule();
199}
200
201void AutomaticRebootManager::OnUserActivity() {
202  if (!login_screen_idle_timer_)
203    return;
204
205  // Destroying and re-creating the timer ensures that Start() posts a fresh
206  // task with a delay of exactly |kLoginManagerIdleTimeoutMs|, ensuring that
207  // the timer fires predictably in tests.
208  login_screen_idle_timer_.reset(
209      new base::OneShotTimer<AutomaticRebootManager>);
210  login_screen_idle_timer_->Start(
211      FROM_HERE,
212      base::TimeDelta::FromMilliseconds(kLoginManagerIdleTimeoutMs),
213      base::Bind(&AutomaticRebootManager::MaybeReboot,
214                 base::Unretained(this),
215                 false));
216}
217
218void AutomaticRebootManager::Observe(
219    int type,
220    const content::NotificationSource& source,
221    const content::NotificationDetails& details) {
222  // Reboots are always inhibited while a user is logged in and the device is
223  // not sleeping. Stop listening for user activity as this is no longer a
224  // relevant criterion.
225  if (type == chrome::NOTIFICATION_LOGIN_USER_CHANGED) {
226    if (ash::Shell::HasInstance())
227      ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this);
228    notification_registrar_.RemoveAll();
229    login_screen_idle_timer_.reset();
230  } else {
231    NOTREACHED();
232  }
233}
234
235// static
236void AutomaticRebootManager::RegisterPrefs(PrefRegistrySimple* registry) {
237  registry->RegisterIntegerPref(prefs::kUptimeLimit, 0);
238  registry->RegisterBooleanPref(prefs::kRebootAfterUpdate, false);
239}
240
241void AutomaticRebootManager::Init(const SystemEventTimes& system_event_times) {
242  const base::TimeDelta offset = clock_->NowTicks() - base::TimeTicks::Now();
243  if (system_event_times.has_boot_time) {
244    // Convert the time at which the device was booted to |clock_| ticks.
245    boot_time_ = system_event_times.boot_time + offset;
246    have_boot_time_ = true;
247  }
248  if (system_event_times.has_update_reboot_needed_time) {
249    // Convert the time at which a reboot became necessary to |clock_| ticks.
250    const base::TimeTicks update_reboot_needed_time =
251        system_event_times.update_reboot_needed_time + offset;
252    update_reboot_needed_time_ = update_reboot_needed_time;
253    have_update_reboot_needed_time_ = true;
254  } else {
255    UpdateStatusChanged(
256        DBusThreadManager::Get()->GetUpdateEngineClient()->GetLastStatus());
257  }
258
259  Reschedule();
260}
261
262void AutomaticRebootManager::Reschedule() {
263  // Safeguard against reboot loops under error conditions: If the boot time is
264  // unavailable because /proc/uptime could not be read, do nothing.
265  if (!have_boot_time_)
266    return;
267
268  // Assume that no reboot has been requested.
269  reboot_requested_ = false;
270
271  const base::TimeDelta kZeroTimeDelta;
272
273  // If an uptime limit is set, calculate the time at which it should cause a
274  // reboot to be requested.
275  const base::TimeDelta uptime_limit = base::TimeDelta::FromSeconds(
276      local_state_registrar_.prefs()->GetInteger(prefs::kUptimeLimit));
277  base::TimeTicks reboot_request_time = boot_time_ + uptime_limit;
278  bool have_reboot_request_time = uptime_limit != kZeroTimeDelta;
279
280  // If the policy to automatically reboot after an update is enabled and an
281  // update has been applied, set the time at which a reboot should be
282  // requested to the minimum of its current value and the time when the reboot
283  // became necessary.
284  if (have_update_reboot_needed_time_ &&
285      local_state_registrar_.prefs()->GetBoolean(prefs::kRebootAfterUpdate) &&
286      (!have_reboot_request_time ||
287       update_reboot_needed_time_ < reboot_request_time)) {
288    reboot_request_time = update_reboot_needed_time_;
289    have_reboot_request_time = true;
290  }
291
292  // If no reboot should be requested, remove any grace period.
293  if (!have_reboot_request_time) {
294    grace_start_timer_.reset();
295    grace_end_timer_.reset();
296    return;
297  }
298
299  // Safeguard against reboot loops: Ensure that the uptime after which a reboot
300  // is actually requested and the grace period begins is never less than
301  // |kMinRebootUptimeMs|.
302  const base::TimeTicks now = clock_->NowTicks();
303  const base::TimeTicks grace_start_time = std::max(reboot_request_time,
304      boot_time_ + base::TimeDelta::FromMilliseconds(kMinRebootUptimeMs));
305  // Set up a timer for the start of the grace period. If the grace period
306  // started in the past, the timer is still used with its delay set to zero.
307  if (!grace_start_timer_)
308    grace_start_timer_.reset(new base::OneShotTimer<AutomaticRebootManager>);
309  grace_start_timer_->Start(FROM_HERE,
310                            std::max(grace_start_time - now, kZeroTimeDelta),
311                            base::Bind(&AutomaticRebootManager::RequestReboot,
312                                       base::Unretained(this)));
313
314  const base::TimeTicks grace_end_time = grace_start_time +
315      base::TimeDelta::FromMilliseconds(kGracePeriodMs);
316  // Set up a timer for the end of the grace period. If the grace period ended
317  // in the past, the timer is still used with its delay set to zero.
318  if (!grace_end_timer_)
319    grace_end_timer_.reset(new base::OneShotTimer<AutomaticRebootManager>);
320  grace_end_timer_->Start(FROM_HERE,
321                          std::max(grace_end_time - now, kZeroTimeDelta),
322                          base::Bind(&AutomaticRebootManager::Reboot,
323                                     base::Unretained(this)));
324}
325
326void AutomaticRebootManager::RequestReboot() {
327  reboot_requested_ = true;
328  MaybeReboot(false);
329}
330
331void AutomaticRebootManager::MaybeReboot(bool ignore_logged_in_user) {
332  // Do not reboot if any of the following applies:
333  // * No reboot has been requested.
334  // * A user is interacting with the login screen.
335  // * A user is logged in and |ignore_logged_in_user| is not set.
336  if (!reboot_requested_ ||
337      (login_screen_idle_timer_ && login_screen_idle_timer_->IsRunning()) ||
338      (!ignore_logged_in_user && UserManager::Get()->IsUserLoggedIn())) {
339    return;
340  }
341
342  Reboot();
343}
344
345void AutomaticRebootManager::Reboot() {
346  login_screen_idle_timer_.reset();
347  grace_start_timer_.reset();
348  grace_end_timer_.reset();
349  DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
350}
351
352}  // namespace system
353}  // namespace chromeos
354