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