• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 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 "components/policy/core/common/async_policy_loader.h"
6 
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/sequenced_task_runner.h"
10 #include "components/policy/core/common/policy_bundle.h"
11 
12 using base::Time;
13 using base::TimeDelta;
14 
15 namespace policy {
16 
17 namespace {
18 
19 // Amount of time to wait for the files on disk to settle before trying to load
20 // them. This alleviates the problem of reading partially written files and
21 // makes it possible to batch quasi-simultaneous changes.
22 const int kSettleIntervalSeconds = 5;
23 
24 // The time interval for rechecking policy. This is the fallback in case the
25 // implementation never detects changes.
26 const int kReloadIntervalSeconds = 15 * 60;
27 
28 }  // namespace
29 
AsyncPolicyLoader(scoped_refptr<base::SequencedTaskRunner> task_runner)30 AsyncPolicyLoader::AsyncPolicyLoader(
31     scoped_refptr<base::SequencedTaskRunner> task_runner)
32     : task_runner_(task_runner),
33       weak_factory_(this) {}
34 
~AsyncPolicyLoader()35 AsyncPolicyLoader::~AsyncPolicyLoader() {}
36 
LastModificationTime()37 Time AsyncPolicyLoader::LastModificationTime() {
38   return Time();
39 }
40 
Reload(bool force)41 void AsyncPolicyLoader::Reload(bool force) {
42   DCHECK(task_runner_->RunsTasksOnCurrentThread());
43 
44   TimeDelta delay;
45   Time now = Time::Now();
46   // Check if there was a recent modification to the underlying files.
47   if (!force && !IsSafeToReload(now, &delay)) {
48     ScheduleNextReload(delay);
49     return;
50   }
51 
52   scoped_ptr<PolicyBundle> bundle(Load());
53 
54   // Check if there was a modification while reading.
55   if (!force && !IsSafeToReload(now, &delay)) {
56     ScheduleNextReload(delay);
57     return;
58   }
59 
60   // Filter out mismatching policies.
61   schema_map_->FilterBundle(bundle.get());
62 
63   update_callback_.Run(bundle.Pass());
64   ScheduleNextReload(TimeDelta::FromSeconds(kReloadIntervalSeconds));
65 }
66 
InitialLoad(const scoped_refptr<SchemaMap> & schema_map)67 scoped_ptr<PolicyBundle> AsyncPolicyLoader::InitialLoad(
68     const scoped_refptr<SchemaMap>& schema_map) {
69   // This is the first load, early during startup. Use this to record the
70   // initial |last_modification_time_|, so that potential changes made before
71   // installing the watches can be detected.
72   last_modification_time_ = LastModificationTime();
73   schema_map_ = schema_map;
74   scoped_ptr<PolicyBundle> bundle(Load());
75   // Filter out mismatching policies.
76   schema_map_->FilterBundle(bundle.get());
77   return bundle.Pass();
78 }
79 
Init(const UpdateCallback & update_callback)80 void AsyncPolicyLoader::Init(const UpdateCallback& update_callback) {
81   DCHECK(task_runner_->RunsTasksOnCurrentThread());
82   DCHECK(update_callback_.is_null());
83   DCHECK(!update_callback.is_null());
84   update_callback_ = update_callback;
85 
86   InitOnBackgroundThread();
87 
88   // There might have been changes to the underlying files since the initial
89   // load and before the watchers have been created.
90   if (LastModificationTime() != last_modification_time_)
91     Reload(false);
92 
93   // Start periodic refreshes.
94   ScheduleNextReload(TimeDelta::FromSeconds(kReloadIntervalSeconds));
95 }
96 
RefreshPolicies(scoped_refptr<SchemaMap> schema_map)97 void AsyncPolicyLoader::RefreshPolicies(scoped_refptr<SchemaMap> schema_map) {
98   DCHECK(task_runner_->RunsTasksOnCurrentThread());
99   schema_map_ = schema_map;
100   Reload(true);
101 }
102 
ScheduleNextReload(TimeDelta delay)103 void AsyncPolicyLoader::ScheduleNextReload(TimeDelta delay) {
104   DCHECK(task_runner_->RunsTasksOnCurrentThread());
105   weak_factory_.InvalidateWeakPtrs();
106   task_runner_->PostDelayedTask(FROM_HERE,
107                                 base::Bind(&AsyncPolicyLoader::Reload,
108                                            weak_factory_.GetWeakPtr(),
109                                            false /* force */),
110                                 delay);
111 }
112 
IsSafeToReload(const Time & now,TimeDelta * delay)113 bool AsyncPolicyLoader::IsSafeToReload(const Time& now, TimeDelta* delay) {
114   Time last_modification = LastModificationTime();
115   if (last_modification.is_null())
116     return true;
117 
118   // If there was a change since the last recorded modification, wait some more.
119   const TimeDelta kSettleInterval(
120       TimeDelta::FromSeconds(kSettleIntervalSeconds));
121   if (last_modification != last_modification_time_) {
122     last_modification_time_ = last_modification;
123     last_modification_clock_ = now;
124     *delay = kSettleInterval;
125     return false;
126   }
127 
128   // Check whether the settle interval has elapsed.
129   const TimeDelta age = now - last_modification_clock_;
130   if (age < kSettleInterval) {
131     *delay = kSettleInterval - age;
132     return false;
133   }
134 
135   return true;
136 }
137 
138 }  // namespace policy
139