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_provider.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/location.h"
10#include "base/message_loop/message_loop.h"
11#include "base/message_loop/message_loop_proxy.h"
12#include "base/sequenced_task_runner.h"
13#include "components/policy/core/common/async_policy_loader.h"
14#include "components/policy/core/common/policy_bundle.h"
15#include "components/policy/core/common/schema_registry.h"
16
17namespace policy {
18
19AsyncPolicyProvider::AsyncPolicyProvider(
20    SchemaRegistry* registry,
21    scoped_ptr<AsyncPolicyLoader> loader)
22    : loader_(loader.release()),
23      weak_factory_(this) {
24  // Make an immediate synchronous load on startup.
25  OnLoaderReloaded(loader_->InitialLoad(registry->schema_map()));
26}
27
28AsyncPolicyProvider::~AsyncPolicyProvider() {
29  DCHECK(CalledOnValidThread());
30  // Shutdown() must have been called before.
31  DCHECK(!loader_);
32}
33
34void AsyncPolicyProvider::Init(SchemaRegistry* registry) {
35  DCHECK(CalledOnValidThread());
36  ConfigurationPolicyProvider::Init(registry);
37
38  if (!loader_)
39    return;
40
41  AsyncPolicyLoader::UpdateCallback callback =
42      base::Bind(&AsyncPolicyProvider::LoaderUpdateCallback,
43                 base::MessageLoopProxy::current(),
44                 weak_factory_.GetWeakPtr());
45  bool post = loader_->task_runner()->PostTask(
46      FROM_HERE,
47      base::Bind(&AsyncPolicyLoader::Init,
48                 base::Unretained(loader_),
49                 callback));
50  DCHECK(post) << "AsyncPolicyProvider::Init() called with threads not running";
51}
52
53void AsyncPolicyProvider::Shutdown() {
54  DCHECK(CalledOnValidThread());
55  // Note on the lifetime of |loader_|:
56  // The |loader_| lives on the background thread, and is deleted from here.
57  // This means that posting tasks on the |loader_| to the background thread
58  // from the AsyncPolicyProvider is always safe, since a potential DeleteSoon()
59  // is only posted from here. The |loader_| posts back to the
60  // AsyncPolicyProvider through the |update_callback_|, which has a WeakPtr to
61  // |this|.
62  if (!loader_->task_runner()->DeleteSoon(FROM_HERE, loader_)) {
63    // The background thread doesn't exist; this only happens on unit tests.
64    delete loader_;
65  }
66  loader_ = NULL;
67  ConfigurationPolicyProvider::Shutdown();
68}
69
70void AsyncPolicyProvider::RefreshPolicies() {
71  DCHECK(CalledOnValidThread());
72
73  // Subtle: RefreshPolicies() has a contract that requires the next policy
74  // update notification (triggered from UpdatePolicy()) to reflect any changes
75  // made before this call. So if a caller has modified the policy settings and
76  // invoked RefreshPolicies(), then by the next notification these policies
77  // should already be provided.
78  // However, it's also possible that an asynchronous Reload() is in progress
79  // and just posted OnLoaderReloaded(). Therefore a task is posted to the
80  // background thread before posting the next Reload, to prevent a potential
81  // concurrent Reload() from triggering a notification too early. If another
82  // refresh task has been posted, it is invalidated now.
83  if (!loader_)
84    return;
85  refresh_callback_.Reset(
86      base::Bind(&AsyncPolicyProvider::ReloadAfterRefreshSync,
87                 weak_factory_.GetWeakPtr()));
88  loader_->task_runner()->PostTaskAndReply(
89      FROM_HERE,
90      base::Bind(base::DoNothing),
91      refresh_callback_.callback());
92}
93
94void AsyncPolicyProvider::ReloadAfterRefreshSync() {
95  DCHECK(CalledOnValidThread());
96  // This task can only enter if it was posted from RefreshPolicies(), and it
97  // hasn't been cancelled meanwhile by another call to RefreshPolicies().
98  DCHECK(!refresh_callback_.IsCancelled());
99  // There can't be another refresh callback pending now, since its creation
100  // in RefreshPolicies() would have cancelled the current execution. So it's
101  // safe to cancel the |refresh_callback_| now, so that OnLoaderReloaded()
102  // sees that there is no refresh pending.
103  refresh_callback_.Cancel();
104
105  if (!loader_)
106    return;
107
108  loader_->task_runner()->PostTask(
109      FROM_HERE,
110      base::Bind(&AsyncPolicyLoader::RefreshPolicies,
111                 base::Unretained(loader_),
112                 schema_map()));
113}
114
115void AsyncPolicyProvider::OnLoaderReloaded(scoped_ptr<PolicyBundle> bundle) {
116  DCHECK(CalledOnValidThread());
117  // Only propagate policy updates if there are no pending refreshes, and if
118  // Shutdown() hasn't been called yet.
119  if (refresh_callback_.IsCancelled() && loader_)
120    UpdatePolicy(bundle.Pass());
121}
122
123// static
124void AsyncPolicyProvider::LoaderUpdateCallback(
125    scoped_refptr<base::MessageLoopProxy> loop,
126    base::WeakPtr<AsyncPolicyProvider> weak_this,
127    scoped_ptr<PolicyBundle> bundle) {
128  loop->PostTask(FROM_HERE,
129                 base::Bind(&AsyncPolicyProvider::OnLoaderReloaded,
130                            weak_this,
131                            base::Passed(&bundle)));
132}
133
134}  // namespace policy
135