buffet: Add state change queue

When device state properties are updated, compile the change
notifications in StateChangeQueue object so they can be pulled
by Cloud Server adaptor and batch-update the device state on
the server.

When StateManager receives property updates, it notifies the
StateChangeQueue object of the changes via StateChangeQueueInterface.
The changes are kept in the queue along with the their time stamps
until they are pulled by Cloud Server adaper (using StateChangeQueue
method). At this point, the adapter would notify the server of
recorded device state changes and the StateChangeQueue is cleared,
ready to record new state updates.

BUG=chromium:415364
TEST=FEATURES=test emerge-link buffet

Change-Id: Ie99e2ada39aaf0164e08699d65153abfc5235a2f
Reviewed-on: https://chromium-review.googlesource.com/226014
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/states/state_change_queue.cc b/buffet/states/state_change_queue.cc
new file mode 100644
index 0000000..a288c0f
--- /dev/null
+++ b/buffet/states/state_change_queue.cc
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <base/logging.h>
+
+#include "buffet/states/state_change_queue.h"
+
+namespace buffet {
+
+StateChangeQueue::StateChangeQueue(size_t max_queue_size)
+    : max_queue_size_(max_queue_size) {
+  CHECK_GT(max_queue_size_, 0) << "Max queue size must not be zero";
+}
+
+bool StateChangeQueue::NotifyPropertiesUpdated(const StateChange& change) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  state_changes_.push_back(change);
+  while (state_changes_.size() > max_queue_size_) {
+    // Queue is full.
+    // Merge the two oldest records into one. The merge strategy is:
+    //  - Move non-existent properties from element [old] to [new].
+    //  - If both [old] and [new] specify the same property,
+    //    keep the value of [new].
+    //  - Keep the timestamp of [new].
+    auto element_old = state_changes_.begin();
+    auto element_new = std::next(element_old);
+    // This will skip elements that exist in both [old] and [new].
+    element_new->property_set.insert(element_old->property_set.begin(),
+                                     element_old->property_set.end());
+    state_changes_.erase(element_old);
+  }
+  return true;
+}
+
+std::vector<StateChange> StateChangeQueue::GetAndClearRecordedStateChanges() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // Return the accumulated state changes and clear the current queue.
+  return std::move(state_changes_);
+}
+
+}  // namespace buffet