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