Home | History | Annotate | Download | only in policy
      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