xref: /aosp_15_r20/external/libchrome/components/policy/core/common/async_policy_loader.cc (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
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