Add timestamp filter interpreter

This CL lets the gesture library use time deltas from MSC_TIMESTAMP, if
these values are provided by the touchpad firmware.

MSC_TIMESTAMP will overflow after 36 minutes, but more importantly, it
is reset to 0 after 1 second of inactivity.  It should also have more
accurate time deltas than CLOCK_MONOTONIC.

This CL modifies timestamps using the more accurate time deltas from
MSC_TIMESTAMP, but reports event times to Chrome browser using
CLOCK_MONOTONIC as a base.

BUG=b:65041115
TEST=Tested manually on lux and eve, using CL:938751, CL:817979 and CL:742488.
CQ-DEPEND=CL:938751

Change-Id: I9bdcd52ca34f556106a9d6c7697d03b3df0c8f10
Reviewed-on: https://chromium-review.googlesource.com/938851
Commit-Ready: Sean O'Brien <seobrien@chromium.org>
Tested-by: Sean O'Brien <seobrien@chromium.org>
Reviewed-by: Andrew de los Reyes <adlr@chromium.org>
diff --git a/Makefile b/Makefile
index f41864b..856887a 100644
--- a/Makefile
+++ b/Makefile
@@ -37,6 +37,7 @@
 	$(OBJDIR)/string_util.o \
 	$(OBJDIR)/stuck_button_inhibitor_filter_interpreter.o \
 	$(OBJDIR)/t5r2_correcting_filter_interpreter.o \
+	$(OBJDIR)/timestamp_filter_interpreter.o \
 	$(OBJDIR)/trace_marker.o \
 	$(OBJDIR)/tracer.o \
 	$(OBJDIR)/trend_classifying_filter_interpreter.o \
@@ -72,6 +73,7 @@
 	$(OBJDIR)/split_correcting_filter_interpreter_unittest.o \
 	$(OBJDIR)/stuck_button_inhibitor_filter_interpreter_unittest.o \
 	$(OBJDIR)/t5r2_correcting_filter_interpreter_unittest.o \
+	$(OBJDIR)/timestamp_filter_interpreter_unittest.o \
 	$(OBJDIR)/trace_marker_unittest.o \
 	$(OBJDIR)/tracer_unittest.o \
 	$(OBJDIR)/unittest_util.o \
diff --git a/include/timestamp_filter_interpreter.h b/include/timestamp_filter_interpreter.h
new file mode 100644
index 0000000..d4fbf25
--- /dev/null
+++ b/include/timestamp_filter_interpreter.h
@@ -0,0 +1,50 @@
+// Copyright 2017 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 "gestures/include/filter_interpreter.h"
+#include "gestures/include/gestures.h"
+#include "gestures/include/tracer.h"
+
+#ifndef GESTURES_TIMESTAMP_FILTER_INTERPRETER_H_
+#define GESTURES_TIMESTAMP_FILTER_INTERPRETER_H_
+
+// This class fixes up the timestamp of the hardware state.
+// Before this filter is applied, there are two possibilities:
+//   1) hwstate->timestamp == CLOCK_MONOTONIC &&
+//      hwstate->msc_timestamp == 0.0
+//        - No changes are needed in this case
+//   2) hwstate->timestamp == CLOCK_MONOTONIC &&
+//      hwstate->msc_timestamp == MSC_TIMESTAMP
+//        - MSC_TIMESTAMP will be more accurate than CLOCK_MONOTONIC, so we want
+//          to use it for time deltas in the gesture library.  However,
+//          MSC_TIMESTAMP will reset to 0.0 if there are no touch events for at
+//          least 1 second. So whenever MSC_TIMESTAMP resets, we record the
+//          offset between CLOCK_MONOTONIC and MSC_TIMESTAMP and add this offset
+//          to subsequent events.
+// After this filter is applied:
+//   - hwstate->timestamp uses CLOCK_MONOTONIC as the time base, possibly with
+//     fine tuning provided by MSC_TIMESTAMP.
+//   - hwstate->msc_timestamp should not be used.
+
+namespace gestures {
+
+class TimestampFilterInterpreter : public FilterInterpreter {
+ public:
+  // Takes ownership of |next|:
+  explicit TimestampFilterInterpreter(Interpreter* next, Tracer* tracer);
+  virtual ~TimestampFilterInterpreter() {}
+
+ protected:
+  virtual void SyncInterpretImpl(HardwareState* hwstate, stime_t* timeout);
+
+ private:
+  stime_t prev_msc_timestamp_;
+
+  // Difference between msc_timestamp and timestamp as of last timestamp reset.
+  stime_t msc_timestamp_offset_;
+};
+
+}  // namespace gestures
+
+#endif  // GESTURES_TIMESTAMP_FILTER_INTERPRETER_H_
diff --git a/src/gestures.cc b/src/gestures.cc
index 88db25f..b126d54 100644
--- a/src/gestures.cc
+++ b/src/gestures.cc
@@ -33,6 +33,7 @@
 #include "gestures/include/string_util.h"
 #include "gestures/include/stuck_button_inhibitor_filter_interpreter.h"
 #include "gestures/include/t5r2_correcting_filter_interpreter.h"
+#include "gestures/include/timestamp_filter_interpreter.h"
 #include "gestures/include/trace_marker.h"
 #include "gestures/include/tracer.h"
 #include "gestures/include/trend_classifying_filter_interpreter.h"
@@ -552,6 +553,7 @@
                                                 tracer_.get());
   temp = new NonLinearityFilterInterpreter(prop_reg_.get(), temp,
                                            tracer_.get());
+  temp = new TimestampFilterInterpreter(temp, tracer_.get());
   temp = loggingFilter_ = new LoggingFilterInterpreter(prop_reg_.get(), temp,
                                                        tracer_.get());
   interpreter_.reset(temp);
@@ -577,6 +579,7 @@
                                       GESTURES_DEVCLASS_TOUCHPAD);
   temp = new FingerMergeFilterInterpreter(prop_reg_.get(), temp, tracer_.get());
   temp = new StuckButtonInhibitorFilterInterpreter(temp, tracer_.get());
+  temp = new TimestampFilterInterpreter(temp, tracer_.get());
   temp = loggingFilter_ = new LoggingFilterInterpreter(prop_reg_.get(), temp,
                                                        tracer_.get());
   interpreter_.reset(temp);
diff --git a/src/timestamp_filter_interpreter.cc b/src/timestamp_filter_interpreter.cc
new file mode 100644
index 0000000..046f9da
--- /dev/null
+++ b/src/timestamp_filter_interpreter.cc
@@ -0,0 +1,35 @@
+// Copyright 2017 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 "gestures/include/timestamp_filter_interpreter.h"
+
+#include "gestures/include/logging.h"
+#include "gestures/include/tracer.h"
+
+namespace gestures {
+
+TimestampFilterInterpreter::TimestampFilterInterpreter(
+    Interpreter* next, Tracer* tracer)
+    : FilterInterpreter(NULL, next, tracer, false),
+      prev_msc_timestamp_(-1.0),
+      msc_timestamp_offset_(-1.0) {
+  InitName();
+}
+
+void TimestampFilterInterpreter::SyncInterpretImpl(
+    HardwareState* hwstate, stime_t* timeout) {
+
+  // Check if this is the first event or there has been a jump backwards.
+  if (prev_msc_timestamp_ < 0.0 ||
+      hwstate->msc_timestamp == 0.0 ||
+      hwstate->msc_timestamp < prev_msc_timestamp_) {
+    msc_timestamp_offset_ = hwstate->timestamp - hwstate->msc_timestamp;
+  }
+  prev_msc_timestamp_ = hwstate->msc_timestamp;
+  hwstate->timestamp = hwstate->msc_timestamp + msc_timestamp_offset_;
+  hwstate->msc_timestamp = 0.0;
+  next_->SyncInterpret(hwstate, timeout);
+}
+
+}  // namespace gestures
diff --git a/src/timestamp_filter_interpreter_unittest.cc b/src/timestamp_filter_interpreter_unittest.cc
new file mode 100644
index 0000000..9c02f3e
--- /dev/null
+++ b/src/timestamp_filter_interpreter_unittest.cc
@@ -0,0 +1,89 @@
+// Copyright 2017 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 <gtest/gtest.h>
+
+#include "gestures/include/gestures.h"
+#include "gestures/include/timestamp_filter_interpreter.h"
+#include "gestures/include/unittest_util.h"
+
+namespace gestures {
+
+class TimestampFilterInterpreterTest : public ::testing::Test {};
+
+class TimestampFilterInterpreterTestInterpreter : public Interpreter {
+ public:
+  TimestampFilterInterpreterTestInterpreter()
+      : Interpreter(NULL, NULL, false) {}
+};
+
+TEST(TimestampFilterInterpreterTest, SimpleTest) {
+  TimestampFilterInterpreterTestInterpreter* base_interpreter =
+      new TimestampFilterInterpreterTestInterpreter;
+  TimestampFilterInterpreter interpreter(base_interpreter, NULL);
+  TestInterpreterWrapper wrapper(&interpreter);
+
+  HardwareState hs[] = {
+    { 1.000, 0, 1, 1, NULL, 0, 0, 0, 0, 0.000 },
+    { 1.010, 0, 1, 1, NULL, 0, 0, 0, 0, 0.012 },
+    { 1.020, 0, 1, 1, NULL, 0, 0, 0, 0, 0.018 },
+    { 1.030, 0, 1, 1, NULL, 0, 0, 0, 0, 0.031 }
+  };
+
+  stime_t expected_timestamps[] = { 1.000, 1.012, 1.018, 1.031 };
+
+  for (size_t i = 0; i < arraysize(hs); i++) {
+    wrapper.SyncInterpret(&hs[i], NULL);
+    EXPECT_EQ(hs[i].timestamp, expected_timestamps[i]);
+  }
+}
+
+TEST(TimestampFilterInterpreterTest, NoMscTimestampTest) {
+  TimestampFilterInterpreterTestInterpreter* base_interpreter =
+      new TimestampFilterInterpreterTestInterpreter;
+  TimestampFilterInterpreter interpreter(base_interpreter, NULL);
+  TestInterpreterWrapper wrapper(&interpreter);
+
+  HardwareState hs[] = {
+    { 1.000, 0, 1, 1, NULL, 0, 0, 0, 0, 0.000 },
+    { 1.010, 0, 1, 1, NULL, 0, 0, 0, 0, 0.000 },
+    { 1.020, 0, 1, 1, NULL, 0, 0, 0, 0, 0.000 },
+    { 1.030, 0, 1, 1, NULL, 0, 0, 0, 0, 0.000 }
+  };
+
+  for (size_t i = 0; i < arraysize(hs); i++) {
+    stime_t expected_timestamp = hs[i].timestamp;
+    wrapper.SyncInterpret(&hs[i], NULL);
+    EXPECT_EQ(hs[i].timestamp, expected_timestamp);
+  }
+}
+
+TEST(TimestampFilterInterpreterTest, MscTimestampResetTest) {
+  TimestampFilterInterpreterTestInterpreter* base_interpreter =
+      new TimestampFilterInterpreterTestInterpreter;
+  TimestampFilterInterpreter interpreter(base_interpreter, NULL);
+  TestInterpreterWrapper wrapper(&interpreter);
+
+  HardwareState hs[] = {
+    { 1.000, 0, 1, 1, NULL, 0, 0, 0, 0, 0.000 },
+    { 1.010, 0, 1, 1, NULL, 0, 0, 0, 0, 0.012 },
+    { 1.020, 0, 1, 1, NULL, 0, 0, 0, 0, 0.018 },
+    { 1.030, 0, 1, 1, NULL, 0, 0, 0, 0, 0.031 },
+    { 3.000, 0, 1, 1, NULL, 0, 0, 0, 0, 0.000 },  //msc_timestamp reset to 0
+    { 3.010, 0, 1, 1, NULL, 0, 0, 0, 0, 0.008 },
+    { 3.020, 0, 1, 1, NULL, 0, 0, 0, 0, 0.020 },
+    { 3.030, 0, 1, 1, NULL, 0, 0, 0, 0, 0.035 }
+  };
+
+  stime_t expected_timestamps[] = {
+    1.000, 1.012, 1.018, 1.031,
+    3.000, 3.008, 3.020, 3.035
+  };
+
+  for (size_t i = 0; i < arraysize(hs); i++) {
+    wrapper.SyncInterpret(&hs[i], NULL);
+    EXPECT_EQ(hs[i].timestamp, expected_timestamps[i]);
+  }
+}
+}  // namespace gestures