[Chromoting] Refactor the host policy watcher so that policies can easily be added.

This CL renames some classes, but not the source files, to make it easier to
compare the old and new versions. A follow-up CL will rename the files.

BUG=137906


Review URL: https://chromiumcodereview.appspot.com/10804040

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@148029 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/remoting/host/policy_hack/policy_watcher.cc b/remoting/host/policy_hack/policy_watcher.cc
new file mode 100644
index 0000000..23320ea
--- /dev/null
+++ b/remoting/host/policy_hack/policy_watcher.cc
@@ -0,0 +1,163 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Most of this code is copied from:
+//   src/chrome/browser/policy/asynchronous_policy_loader.{h,cc}
+
+#include "remoting/host/policy_hack/policy_watcher.h"
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/location.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/time.h"
+#include "base/values.h"
+
+namespace remoting {
+namespace policy_hack {
+
+namespace {
+// The time interval for rechecking policy. This is our fallback in case the
+// delegate never reports a change to the ReloadObserver.
+const int kFallbackReloadDelayMinutes = 15;
+
+// Gets a boolean from a dictionary, or returns a default value if the boolean
+// couldn't be read.
+bool GetBooleanOrDefault(const base::DictionaryValue* dict, const char* key,
+                         bool default_if_value_missing,
+                         bool default_if_value_not_boolean) {
+  if (!dict->HasKey(key)) {
+    return default_if_value_missing;
+  }
+  base::Value* value;
+  if (dict->Get(key, &value) && value->IsType(base::Value::TYPE_BOOLEAN)) {
+    bool bool_value;
+    CHECK(value->GetAsBoolean(&bool_value));
+    return bool_value;
+  }
+  return default_if_value_not_boolean;
+}
+
+// Copies a boolean from one dictionary to another, using a default value
+// if the boolean couldn't be read from the first dictionary.
+void CopyBooleanOrDefault(base::DictionaryValue* to,
+                          const base::DictionaryValue* from, const char* key,
+                          bool default_if_value_missing,
+                          bool default_if_value_not_boolean) {
+  to->Set(key, base::Value::CreateBooleanValue(
+      GetBooleanOrDefault(from, key, default_if_value_missing,
+                          default_if_value_not_boolean)));
+}
+
+// Copies all policy values from one dictionary to another, using default values
+// when necessary.
+scoped_ptr<base::DictionaryValue> AddDefaultValuesWhenNecessary(
+    const base::DictionaryValue* from) {
+  scoped_ptr<base::DictionaryValue> to(new base::DictionaryValue());
+  CopyBooleanOrDefault(to.get(), from,
+                       PolicyWatcher::kNatPolicyName, true, false);
+  return to.Pass();
+}
+
+}  // namespace
+
+const char PolicyWatcher::kNatPolicyName[] =
+    "RemoteAccessHostFirewallTraversal";
+
+const char* const PolicyWatcher::kBooleanPolicyNames[] =
+    { PolicyWatcher::kNatPolicyName };
+
+const int PolicyWatcher::kBooleanPolicyNamesNum =
+    arraysize(kBooleanPolicyNames);
+
+PolicyWatcher::PolicyWatcher(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : task_runner_(task_runner),
+      old_policies_(new base::DictionaryValue()),
+      ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
+}
+
+PolicyWatcher::~PolicyWatcher() {
+}
+
+void PolicyWatcher::StartWatching(const PolicyCallback& policy_callback) {
+  if (!OnPolicyWatcherThread()) {
+    task_runner_->PostTask(FROM_HERE,
+                           base::Bind(&PolicyWatcher::StartWatching,
+                                      base::Unretained(this),
+                                      policy_callback));
+    return;
+  }
+
+  policy_callback_ = policy_callback;
+  StartWatchingInternal();
+}
+
+void PolicyWatcher::StopWatching(base::WaitableEvent* done) {
+  if (!OnPolicyWatcherThread()) {
+    task_runner_->PostTask(FROM_HERE,
+                           base::Bind(&PolicyWatcher::StopWatching,
+                                      base::Unretained(this), done));
+    return;
+  }
+
+  StopWatchingInternal();
+  weak_factory_.InvalidateWeakPtrs();
+  policy_callback_.Reset();
+
+  done->Signal();
+}
+
+void PolicyWatcher::ScheduleFallbackReloadTask() {
+  DCHECK(OnPolicyWatcherThread());
+  ScheduleReloadTask(
+      base::TimeDelta::FromMinutes(kFallbackReloadDelayMinutes));
+}
+
+void PolicyWatcher::ScheduleReloadTask(const base::TimeDelta& delay) {
+  DCHECK(OnPolicyWatcherThread());
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&PolicyWatcher::Reload, weak_factory_.GetWeakPtr()),
+      delay);
+}
+
+bool PolicyWatcher::OnPolicyWatcherThread() const {
+  return task_runner_->BelongsToCurrentThread();
+}
+
+void PolicyWatcher::UpdatePolicies(
+    const base::DictionaryValue* new_policies_raw) {
+  DCHECK(OnPolicyWatcherThread());
+
+  // Use default values for any missing policies.
+  scoped_ptr<base::DictionaryValue> new_policies =
+      AddDefaultValuesWhenNecessary(new_policies_raw);
+
+  // Find the changed policies.
+  scoped_ptr<base::DictionaryValue> changed_policies(
+      new base::DictionaryValue());
+  base::DictionaryValue::Iterator iter(*new_policies);
+  while (iter.HasNext()) {
+    base::Value* old_policy;
+    if (!(old_policies_->Get(iter.key(), &old_policy) &&
+          old_policy->Equals(&iter.value()))) {
+      changed_policies->Set(iter.key(), iter.value().DeepCopy());
+    }
+    iter.Advance();
+  }
+
+  // Save the new policies.
+  old_policies_.swap(new_policies);
+
+  // Notify our client of the changed policies.
+  if (!changed_policies->empty()) {
+    policy_callback_.Run(changed_policies.Pass());
+  }
+}
+
+}  // namespace policy_hack
+}  // namespace remoting