1 // Copyright (c) 2012 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/policy/async_policy_loader.h" 6 7 #include "base/bind.h" 8 #include "chrome/browser/policy/policy_bundle.h" 9 #include "chrome/browser/policy/policy_domain_descriptor.h" 10 #include "content/public/browser/browser_thread.h" 11 12 using base::Time; 13 using base::TimeDelta; 14 using content::BrowserThread; 15 16 namespace policy { 17 18 namespace { 19 20 // Amount of time to wait for the files on disk to settle before trying to load 21 // them. This alleviates the problem of reading partially written files and 22 // makes it possible to batch quasi-simultaneous changes. 23 const int kSettleIntervalSeconds = 5; 24 25 // The time interval for rechecking policy. This is the fallback in case the 26 // implementation never detects changes. 27 const int kReloadIntervalSeconds = 15 * 60; 28 29 } // namespace 30 31 AsyncPolicyLoader::AsyncPolicyLoader() 32 : weak_factory_(this) {} 33 34 AsyncPolicyLoader::~AsyncPolicyLoader() {} 35 36 base::Time AsyncPolicyLoader::LastModificationTime() { 37 return base::Time(); 38 } 39 40 void AsyncPolicyLoader::Reload(bool force) { 41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 42 43 TimeDelta delay; 44 Time now = Time::Now(); 45 // Check if there was a recent modification to the underlying files. 46 if (!force && !IsSafeToReload(now, &delay)) { 47 ScheduleNextReload(delay); 48 return; 49 } 50 51 scoped_ptr<PolicyBundle> bundle(Load()); 52 53 // Check if there was a modification while reading. 54 if (!force && !IsSafeToReload(now, &delay)) { 55 ScheduleNextReload(delay); 56 return; 57 } 58 59 // Filter out mismatching policies. 60 for (DescriptorMap::iterator it = descriptor_map_.begin(); 61 it != descriptor_map_.end(); ++it) { 62 it->second->FilterBundle(bundle.get()); 63 } 64 65 update_callback_.Run(bundle.Pass()); 66 ScheduleNextReload(TimeDelta::FromSeconds(kReloadIntervalSeconds)); 67 } 68 69 void AsyncPolicyLoader::RegisterPolicyDomain( 70 scoped_refptr<const PolicyDomainDescriptor> descriptor) { 71 if (descriptor->domain() != POLICY_DOMAIN_CHROME) { 72 descriptor_map_[descriptor->domain()] = descriptor; 73 Reload(true); 74 } 75 } 76 77 scoped_ptr<PolicyBundle> AsyncPolicyLoader::InitialLoad() { 78 // This is the first load, early during startup. Use this to record the 79 // initial |last_modification_time_|, so that potential changes made before 80 // installing the watches can be detected. 81 last_modification_time_ = LastModificationTime(); 82 return Load(); 83 } 84 85 void AsyncPolicyLoader::Init(const UpdateCallback& update_callback) { 86 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 87 DCHECK(update_callback_.is_null()); 88 DCHECK(!update_callback.is_null()); 89 update_callback_ = update_callback; 90 91 InitOnFile(); 92 93 // There might have been changes to the underlying files since the initial 94 // load and before the watchers have been created. 95 if (LastModificationTime() != last_modification_time_) 96 Reload(false); 97 98 // Start periodic refreshes. 99 ScheduleNextReload(TimeDelta::FromSeconds(kReloadIntervalSeconds)); 100 } 101 102 void AsyncPolicyLoader::ScheduleNextReload(TimeDelta delay) { 103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 104 weak_factory_.InvalidateWeakPtrs(); 105 BrowserThread::PostDelayedTask( 106 BrowserThread::FILE, 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 base::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