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 30 AsyncPolicyLoader::AsyncPolicyLoader( 31 scoped_refptr<base::SequencedTaskRunner> task_runner) 32 : task_runner_(task_runner), 33 weak_factory_(this) {} 34 35 AsyncPolicyLoader::~AsyncPolicyLoader() {} 36 37 Time AsyncPolicyLoader::LastModificationTime() { 38 return Time(); 39 } 40 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 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 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 97 void AsyncPolicyLoader::RefreshPolicies(scoped_refptr<SchemaMap> schema_map) { 98 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 99 schema_map_ = schema_map; 100 Reload(true); 101 } 102 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 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