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