Add DaydreamVR native libraries and services

Upstreaming the main VR system components from master-dreamos-dev
into goog/master.

Bug: None
Test: `m -j32` succeeds. Sailfish boots and basic_vr sample app works
Change-Id: I853015872afc443aecee10411ef2d6b79184d051
diff --git a/libs/vr/libdvrcommon/Android.mk b/libs/vr/libdvrcommon/Android.mk
new file mode 100644
index 0000000..72afab2
--- /dev/null
+++ b/libs/vr/libdvrcommon/Android.mk
@@ -0,0 +1,81 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+	frame_time_history.cpp \
+	revision.cpp \
+	revision_path.cpp \
+	sync_util.cpp \
+
+includeFiles := \
+  $(LOCAL_PATH)/include \
+  external/eigen \
+
+sharedLibraries := \
+	libbase \
+	libcutils \
+	liblog \
+	libutils \
+	libEGL \
+	libGLESv2 \
+	libui \
+	libgui \
+	libhardware
+
+staticLibraries := \
+	libchrome \
+	libpdx_default_transport \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := \
+  $(includeFiles) \
+
+LOCAL_CFLAGS += -DLOG_TAG=\"libdvrcommon\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+  $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_MODULE := libdvrcommon
+include $(BUILD_STATIC_LIBRARY)
+
+testFiles := \
+  tests/numeric_test.cpp \
+  tests/pose_test.cpp \
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdvrcommon_test
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+  $(testFiles) \
+
+LOCAL_C_INCLUDES := \
+  $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := \
+  $(sharedLibraries) \
+
+LOCAL_STATIC_LIBRARIES := \
+  libgmock_main \
+  libgmock \
+  libgtest \
+  $(staticLibraries) \
+
+include $(BUILD_NATIVE_TEST)
+
diff --git a/libs/vr/libdvrcommon/frame_time_history.cpp b/libs/vr/libdvrcommon/frame_time_history.cpp
new file mode 100644
index 0000000..796498c
--- /dev/null
+++ b/libs/vr/libdvrcommon/frame_time_history.cpp
@@ -0,0 +1,37 @@
+#include <private/dvr/frame_time_history.h>
+
+#include <cutils/log.h>
+
+namespace android {
+namespace dvr {
+
+void FrameTimeHistory::AddSample(int64_t frame_time) {
+  if (size_ == frame_times_.size()) {
+    int64_t expired_frame_time = frame_times_[start_];
+    frame_times_[start_] = frame_time;
+    start_ = (start_ + 1) % frame_times_.size();
+    total_frame_time_ -= expired_frame_time;
+  } else {
+    frame_times_[(start_ + size_) % frame_times_.size()] = frame_time;
+    size_++;
+  }
+  total_frame_time_ += frame_time;
+}
+
+int FrameTimeHistory::GetSampleCount() const { return size_; }
+
+int64_t FrameTimeHistory::GetAverage() const {
+  LOG_ALWAYS_FATAL_IF(size_ == 0);
+  return total_frame_time_ / size_;
+}
+
+void FrameTimeHistory::ResetWithSeed(int64_t frame_time_seed) {
+  start_ = 0;
+  size_ = frame_times_.size();
+  for (size_t i = 0; i < size_; ++i)
+    frame_times_[i] = frame_time_seed;
+  total_frame_time_ = frame_time_seed * size_;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrcommon/include/private/dvr/benchmark.h b/libs/vr/libdvrcommon/include/private/dvr/benchmark.h
new file mode 100644
index 0000000..2dbb5f2
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/benchmark.h
@@ -0,0 +1,86 @@
+#ifndef ANDROID_DVR_BENCHMARK_H_
+#define ANDROID_DVR_BENCHMARK_H_
+
+#include <stdio.h>
+#include <time.h>
+
+#include <cutils/trace.h>
+
+#include <private/dvr/clock_ns.h>
+
+// Set benchmark traces, using Android systrace.
+//
+// The simplest one-parameter version of btrace automatically sets the
+// timestamp with the system clock. The other versions can optionally set the
+// timestamp manually, or pass additional data to be written to the log line.
+//
+// Example:
+// Btrace("Start execution");
+// ... code to benchmark ...
+// Btrace("End execution");
+//
+// Use compute_benchmarks.py (currently in dreamos/system/core/applications),
+// with the trace path "Start execution,End execution",
+// to report the elapsed time between the two calls.
+//
+// Btrace will either output to standard atrace, or to a file if specified.
+// The versions BtraceData also allow an int64_t to be included in the trace.
+
+// Btrace without data payload.
+static inline void Btrace(const char* name, int64_t nanoseconds_monotonic);
+static inline void Btrace(const char* name);
+static inline void Btrace(FILE* file, const char* name,
+                          int64_t nanoseconds_monotonic);
+static inline void Btrace(FILE* file, const char* name);
+
+// Btrace with data payload.
+static inline void BtraceData(const char* name, int64_t nanoseconds_monotonic,
+                              int64_t data);
+static inline void BtraceData(const char* name, int64_t data);
+static inline void BtraceData(FILE* file, const char* name,
+                              int64_t nanoseconds_monotonic, int64_t data);
+static inline void BtraceData(FILE* file, const char* name, int64_t data);
+
+static inline void Btrace(const char* name, int64_t nanoseconds_monotonic) {
+  const int kLogMessageLength = 256;
+  char log_message[kLogMessageLength];
+  snprintf(log_message, kLogMessageLength, "#btrace#%s", name);
+  atrace_int64(ATRACE_TAG_WEBVIEW, log_message, nanoseconds_monotonic);
+}
+
+static inline void Btrace(const char* name) {
+  Btrace(name, android::dvr::GetSystemClockNs());
+}
+
+static inline void Btrace(FILE* file, const char* name,
+                          int64_t nanoseconds_monotonic) {
+  fprintf(file, "#btrace#%s|%" PRId64 "\n", name, nanoseconds_monotonic);
+}
+
+static inline void Btrace(FILE* file, const char* name) {
+  Btrace(file, name, android::dvr::GetSystemClockNs());
+}
+
+static inline void BtraceData(const char* name, int64_t nanoseconds_monotonic,
+                              int64_t data) {
+  const int kLogMessageLength = 256;
+  char log_message[kLogMessageLength];
+  snprintf(log_message, kLogMessageLength, "#btrace#%s|%" PRId64, name, data);
+  atrace_int64(ATRACE_TAG_WEBVIEW, log_message, nanoseconds_monotonic);
+}
+
+static inline void BtraceData(const char* name, int64_t data) {
+  BtraceData(name, android::dvr::GetSystemClockNs(), data);
+}
+
+static inline void BtraceData(FILE* file, const char* name,
+                              int64_t nanoseconds_monotonic, int64_t data) {
+  fprintf(file, "#btrace#%s|%" PRId64 "|%" PRId64 "\n", name, data,
+          nanoseconds_monotonic);
+}
+
+static inline void BtraceData(FILE* file, const char* name, int64_t data) {
+  BtraceData(file, name, android::dvr::GetSystemClockNs(), data);
+}
+
+#endif  // ANDROID_DVR_BENCHMARK_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h b/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h
new file mode 100644
index 0000000..8e777ed
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h
@@ -0,0 +1,84 @@
+#ifndef ANDROID_DVR_CLOCK_NS_H_
+#define ANDROID_DVR_CLOCK_NS_H_
+
+#include <stdint.h>
+#include <time.h>
+
+namespace android {
+namespace dvr {
+
+constexpr int64_t kNanosPerSecond = 1000000000ll;
+
+// Returns the standard Dream OS monotonic system time that corresponds with all
+// timestamps found in Dream OS APIs.
+static inline timespec GetSystemClock() {
+  timespec t;
+  clock_gettime(CLOCK_MONOTONIC, &t);
+  return t;
+}
+
+static inline timespec GetSystemClockRaw() {
+  timespec t;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &t);
+  return t;
+}
+
+static inline int64_t GetSystemClockNs() {
+  timespec t = GetSystemClock();
+  int64_t ns = kNanosPerSecond * (int64_t)t.tv_sec + (int64_t)t.tv_nsec;
+  return ns;
+}
+
+static inline int64_t GetSystemClockRawNs() {
+  timespec t = GetSystemClockRaw();
+  int64_t ns = kNanosPerSecond * (int64_t)t.tv_sec + (int64_t)t.tv_nsec;
+  return ns;
+}
+
+static inline double NsToSec(int64_t nanoseconds) {
+  return nanoseconds / static_cast<double>(kNanosPerSecond);
+}
+
+static inline double GetSystemClockSec() { return NsToSec(GetSystemClockNs()); }
+
+static inline double GetSystemClockMs() { return GetSystemClockSec() * 1000.0; }
+
+// Converts a nanosecond timestamp to a timespec. Based on the kernel function
+// of the same name.
+static inline timespec NsToTimespec(int64_t ns) {
+  timespec t;
+  int32_t remainder;
+
+  t.tv_sec = ns / kNanosPerSecond;
+  remainder = ns % kNanosPerSecond;
+  if (remainder < 0) {
+    t.tv_nsec--;
+    remainder += kNanosPerSecond;
+  }
+  t.tv_nsec = remainder;
+
+  return t;
+}
+
+// Timestamp comparison functions that handle wrapping values correctly.
+static inline bool TimestampLT(int64_t a, int64_t b) {
+  return static_cast<int64_t>(static_cast<uint64_t>(a) -
+                              static_cast<uint64_t>(b)) < 0;
+}
+static inline bool TimestampLE(int64_t a, int64_t b) {
+  return static_cast<int64_t>(static_cast<uint64_t>(a) -
+                              static_cast<uint64_t>(b)) <= 0;
+}
+static inline bool TimestampGT(int64_t a, int64_t b) {
+  return static_cast<int64_t>(static_cast<uint64_t>(a) -
+                              static_cast<uint64_t>(b)) > 0;
+}
+static inline bool TimestampGE(int64_t a, int64_t b) {
+  return static_cast<int64_t>(static_cast<uint64_t>(a) -
+                              static_cast<uint64_t>(b)) >= 0;
+}
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_CLOCK_NS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/debug.h b/libs/vr/libdvrcommon/include/private/dvr/debug.h
new file mode 100644
index 0000000..7db681a
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/debug.h
@@ -0,0 +1,37 @@
+#ifndef ANDROID_DVR_DEBUG_H_
+#define ANDROID_DVR_DEBUG_H_
+
+#include <GLES3/gl3.h>
+#include <math.h>
+
+#include <base/logging.h>
+
+#ifndef NDEBUG
+#define CHECK_GL()                          \
+  do {                                      \
+    const GLenum err = glGetError();        \
+    if (err != GL_NO_ERROR) {               \
+      LOG(ERROR) << "OpenGL error " << err; \
+    }                                       \
+  } while (0)
+
+#define CHECK_GL_FBO()                                        \
+  do {                                                        \
+    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); \
+    switch (status) {                                         \
+      case GL_FRAMEBUFFER_COMPLETE:                           \
+        break;                                                \
+      case GL_FRAMEBUFFER_UNSUPPORTED:                        \
+        LOG(ERROR) << "GL_FRAMEBUFFER_UNSUPPORTED";           \
+        break;                                                \
+      default:                                                \
+        LOG(ERROR) << "FBO user error: " << status;           \
+        break;                                                \
+    }                                                         \
+  } while (0)
+#else
+#define CHECK_GL()
+#define CHECK_GL_FBO()
+#endif
+
+#endif  // ANDROID_DVR_DEBUG_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/eigen.h b/libs/vr/libdvrcommon/include/private/dvr/eigen.h
new file mode 100644
index 0000000..defaf58
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/eigen.h
@@ -0,0 +1,57 @@
+#ifndef ANDROID_DVR_EIGEN_H_
+#define ANDROID_DVR_EIGEN_H_
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+namespace Eigen {
+
+// Eigen doesn't take advantage of C++ template typedefs, but we can
+template <class T, int N>
+using Vector = Matrix<T, N, 1>;
+
+template <class T>
+using Vector2 = Vector<T, 2>;
+
+template <class T>
+using Vector3 = Vector<T, 3>;
+
+template <class T>
+using Vector4 = Vector<T, 4>;
+
+template <class T, int N>
+using RowVector = Matrix<T, 1, N>;
+
+template <class T>
+using RowVector2 = RowVector<T, 2>;
+
+template <class T>
+using RowVector3 = RowVector<T, 3>;
+
+template <class T>
+using RowVector4 = RowVector<T, 4>;
+
+// In Eigen, the type you should be using for transformation matrices is the
+// `Transform` class, instead of a raw `Matrix`.
+// The `Projective` option means this will not make any assumptions about the
+// last row of the object, making this suitable for use as general OpenGL
+// projection matrices (which is the most common use-case). The one caveat
+// is that in order to apply this transformation to non-homogeneous vectors
+// (e.g., vec3), you must use the `.linear()` method to get the affine part of
+// the matrix.
+//
+// Example:
+//   mat4 transform;
+//   vec3 position;
+//   vec3 transformed = transform.linear() * position;
+//
+// Note, the use of N-1 is because the parameter passed to Eigen is the ambient
+// dimension of the transformation, not the size of the matrix iself.
+// However graphics programmers sometimes get upset when they see a 3 next
+// to a matrix when they expect a 4, so I'm hoping this will avoid that.
+template <class T, int N>
+using AffineMatrix = Transform<T, N-1, Projective>;
+
+}  // namespace Eigen
+
+#endif  // ANDROID_DVR_EIGEN_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
new file mode 100644
index 0000000..8df741c
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
@@ -0,0 +1,62 @@
+#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
+#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
+
+#include <android-base/unique_fd.h>
+#include <base/logging.h>
+#include <sys/epoll.h>
+
+namespace android {
+namespace dvr {
+
+class EpollFileDescriptor {
+ public:
+  static const int CTL_ADD = EPOLL_CTL_ADD;
+  static const int CTL_MOD = EPOLL_CTL_MOD;
+  static const int CTL_DEL = EPOLL_CTL_DEL;
+
+  EpollFileDescriptor() : fd_(-1) {}
+
+  // Constructs an EpollFileDescriptor from an integer file descriptor and
+  // takes ownership.
+  explicit EpollFileDescriptor(int fd) : fd_(fd) {}
+
+  bool IsValid() const { return fd_.get() >= 0; }
+
+  int Create() {
+    if (IsValid()) {
+      LOG(WARNING) << "epoll fd has already been created.";
+      return -EALREADY;
+    }
+
+    fd_.reset(epoll_create(64));
+
+    if (fd_.get() < 0)
+      return -errno;
+    else
+      return 0;
+  }
+
+  int Control(int op, int target_fd, epoll_event* ev) {
+    if (epoll_ctl(fd_.get(), op, target_fd, ev) < 0)
+      return -errno;
+    else
+      return 0;
+  }
+
+  int Wait(epoll_event* events, int maxevents, int timeout) {
+    int ret = epoll_wait(fd_.get(), events, maxevents, timeout);
+
+    if (ret < 0)
+      return -errno;
+    else
+      return ret;
+  }
+
+ private:
+  base::unique_fd fd_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h b/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h
new file mode 100644
index 0000000..d0ee69c
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h
@@ -0,0 +1,95 @@
+#ifndef ANDROID_DVR_FIELD_OF_VIEW_H_
+#define ANDROID_DVR_FIELD_OF_VIEW_H_
+
+#include <cmath>
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+// Encapsulates a generalized, asymmetric field of view with four half angles.
+// Each half angle denotes the angle between the corresponding frustum plane.
+// Together with a near and far plane, a FieldOfView forms the frustum of an
+// off-axis perspective projection.
+class FieldOfView {
+ public:
+  // The default constructor sets an angle of 0 (in any unit) for all four
+  // half-angles.
+  FieldOfView() : left_(0.0f), right_(0.0f), bottom_(0.0f), top_(0.0f) {}
+
+  // Constructs a FieldOfView from four angles.
+  FieldOfView(float left, float right, float bottom, float top)
+      : left_(left), right_(right), bottom_(bottom), top_(top) {}
+
+  explicit FieldOfView(const float* fov)
+      : FieldOfView(fov[0], fov[1], fov[2], fov[3]) {}
+
+  // Accessors for all four half-angles.
+  float GetLeft() const { return left_; }
+  float GetRight() const { return right_; }
+  float GetBottom() const { return bottom_; }
+  float GetTop() const { return top_; }
+
+  // Setters for all four half-angles.
+  void SetLeft(float left) { left_ = left; }
+  void SetRight(float right) { right_ = right; }
+  void SetBottom(float bottom) { bottom_ = bottom; }
+  void SetTop(float top) { top_ = top; }
+
+  Eigen::AffineMatrix<float, 4> GetProjectionMatrix(float z_near,
+                                                    float z_far) const {
+    float x_left = -std::tan(left_) * z_near;
+    float x_right = std::tan(right_) * z_near;
+    float y_bottom = -std::tan(bottom_) * z_near;
+    float y_top = std::tan(top_) * z_near;
+
+    float zero = 0.0f;
+    if (x_left == x_right || y_bottom == y_top || z_near == z_far ||
+        z_near <= zero || z_far <= zero) {
+      return Eigen::AffineMatrix<float, 4>::Identity();
+    }
+
+    float x = (2 * z_near) / (x_right - x_left);
+    float y = (2 * z_near) / (y_top - y_bottom);
+    float a = (x_right + x_left) / (x_right - x_left);
+    float b = (y_top + y_bottom) / (y_top - y_bottom);
+    float c = (z_near + z_far) / (z_near - z_far);
+    float d = (2 * z_near * z_far) / (z_near - z_far);
+
+    // Note: Eigen matrix initialization syntax is always 'column-major'
+    // even if the storage is row-major. Or in other words, just write the
+    // matrix like you'd see in a math textbook.
+    Eigen::AffineMatrix<float, 4> result;
+    result.matrix() << x,  0,  a,  0,
+                       0,  y,  b,  0,
+                       0,  0,  c,  d,
+                       0,  0, -1,  0;
+    return result;
+  }
+
+  static FieldOfView FromProjectionMatrix(
+      const Eigen::AffineMatrix<float, 4>& m) {
+    // Compute tangents.
+    float tan_vert_fov = 1.0f / m(1, 1);
+    float tan_horz_fov = 1.0f / m(0, 0);
+    float t = (m(1, 2) + 1.0f) * tan_vert_fov;
+    float b = (m(1, 2) - 1.0f) * tan_vert_fov;
+    float l = (m(0, 2) - 1.0f) * tan_horz_fov;
+    float r = (m(0, 2) + 1.0f) * tan_horz_fov;
+
+    return FieldOfView(std::atan(-l), std::atan(r), std::atan(-b),
+                       std::atan(t));
+  }
+
+ private:
+  float left_;
+  float right_;
+  float bottom_;
+  float top_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_FIELD_OF_VIEW_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/frame_time_history.h b/libs/vr/libdvrcommon/include/private/dvr/frame_time_history.h
new file mode 100644
index 0000000..008c636
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/frame_time_history.h
@@ -0,0 +1,33 @@
+#ifndef ANDROID_DVR_FRAME_TIME_HISTORY_H_
+#define ANDROID_DVR_FRAME_TIME_HISTORY_H_
+
+#include <stdint.h>
+
+#include <array>
+
+namespace android {
+namespace dvr {
+
+// Maintains frame time history and provides averaging utility methods.
+class FrameTimeHistory {
+ public:
+  void AddSample(int64_t frame_time);
+  int GetSampleCount() const;
+  int64_t GetAverage() const;
+  float GetAverageFps() const {
+    return 1000000000.0f / static_cast<float>(GetAverage());
+  }
+  void ResetWithSeed(int64_t frame_time_seed);
+
+ private:
+  static constexpr int kFrameTimeHistoryNumSamples = 30;
+  std::array<int64_t, kFrameTimeHistoryNumSamples> frame_times_;
+  int start_ = 0;
+  size_t size_ = 0;
+  int64_t total_frame_time_ = 0;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_FRAME_TIME_HISTORY_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
new file mode 100644
index 0000000..c9f7f8e
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
@@ -0,0 +1,63 @@
+#ifndef ANDROID_DVR_LOG_HELPERS_H_
+#define ANDROID_DVR_LOG_HELPERS_H_
+
+#include <iomanip>
+
+#include <base/logging.h>
+#include <private/dvr/eigen.h>
+#include <private/dvr/field_of_view.h>
+
+namespace android {
+namespace dvr {
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+                                const Eigen::Vector<T, 2>& vec) {
+  return out << "vec2(" << vec.x() << ',' << vec.y() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+                                const Eigen::Vector<T, 3>& vec) {
+  return out << "vec3(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+                                const Eigen::Vector<T, 4>& vec) {
+  return out << "vec4(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ','
+             << vec.w() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+                                const Eigen::AffineMatrix<T, 4>& mat) {
+  out << std::setfill(' ') << std::setprecision(4) << std::fixed << std::showpos;
+  out << "\nmat4[";
+  out << std::setw(10) << mat(0, 0) << " " << std::setw(10) << mat(0, 1) << " "
+      << std::setw(10) << mat(0, 2) << " " << std::setw(10) << mat(0, 3);
+  out << "]\n    [";
+  out << std::setw(10) << mat(1, 0) << " " << std::setw(10) << mat(1, 1) << " "
+      << std::setw(10) << mat(1, 2) << " " << std::setw(10) << mat(1, 3);
+  out << "]\n    [";
+  out << std::setw(10) << mat(2, 0) << " " << std::setw(10) << mat(2, 1) << " "
+      << std::setw(10) << mat(2, 2) << " " << std::setw(10) << mat(2, 3);
+  out << "]\n    [";
+  out << std::setw(10) << mat(3, 0) << " " << std::setw(10) << mat(3, 1) << " "
+      << std::setw(10) << mat(3, 2) << " " << std::setw(10) << mat(3, 3);
+  out << "]\n";
+
+  return out;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const FieldOfView& fov) {
+  return out << "fov(" << (fov.GetLeft() * 180.0f / M_PI) << ','
+             << (fov.GetRight() * 180.0f / M_PI) << ','
+             << (fov.GetBottom() * 180.0f / M_PI) << ','
+             << (fov.GetTop() * 180.0f / M_PI) << ')';
+}
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_LOG_HELPERS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/numeric.h b/libs/vr/libdvrcommon/include/private/dvr/numeric.h
new file mode 100644
index 0000000..584236a
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/numeric.h
@@ -0,0 +1,178 @@
+#ifndef ANDROID_DVR_NUMERIC_H_
+#define ANDROID_DVR_NUMERIC_H_
+
+#include <cmath>
+#include <limits>
+#include <random>
+#include <type_traits>
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+template <typename FT>
+static inline FT ToDeg(FT f) {
+  return f * static_cast<FT>(180.0 / M_PI);
+}
+
+template <typename FT>
+static inline FT ToRad(FT f) {
+  return f * static_cast<FT>(M_PI / 180.0);
+}
+
+// Adjusts `x` to the periodic range `[lo, hi]` (to normalize angle values
+// for example).
+template <typename T>
+T NormalizePeriodicRange(T x, T lo, T hi) {
+  T range_size = hi - lo;
+
+  while (x < lo) {
+    x += range_size;
+  }
+
+  while (x > hi) {
+    x -= range_size;
+  }
+
+  return x;
+}
+
+// Normalizes a measurement in radians.
+// @param x the angle to be normalized
+// @param centre the point around which to normalize the range
+// @return the value of x, normalized to the range [centre - 180, centre + 180]
+template <typename T>
+T NormalizeDegrees(T x, T centre = static_cast<T>(180.0)) {
+  return NormalizePeriodicRange(x, centre - static_cast<T>(180.0),
+                                centre + static_cast<T>(180.0));
+}
+
+// Normalizes a measurement in radians.
+// @param x the angle to be normalized
+// @param centre the point around which to normalize the range
+// @return the value of x, normalized to the range
+//         [centre - M_PI, centre + M_PI]
+// @remark the centre parameter is to make it possible to specify a different
+//         periodic range. This is useful if you are planning on comparing two
+//         angles close to 0 or M_PI, so that one might not accidentally end
+//         up on the other side of the range
+template <typename T>
+T NormalizeRadians(T x, T centre = static_cast<T>(M_PI)) {
+  return NormalizePeriodicRange(x, centre - static_cast<T>(M_PI),
+                                centre + static_cast<T>(M_PI));
+}
+
+static inline vec2i Round(const vec2& v) {
+  return vec2i(roundf(v.x()), roundf(v.y()));
+}
+
+static inline vec2i Scale(const vec2i& v, float scale) {
+  return vec2i(roundf(static_cast<float>(v.x()) * scale),
+               roundf(static_cast<float>(v.y()) * scale));
+}
+
+// Re-maps `x` from `[lba,uba]` to `[lbb,ubb]`.
+template <typename T>
+T ConvertRange(T x, T lba, T uba, T lbb, T ubb) {
+  return (((x - lba) * (ubb - lbb)) / (uba - lba)) + lbb;
+}
+
+template <typename R1, typename R2>
+static inline vec2 MapPoint(const vec2& pt, const R1& from, const R2& to) {
+  vec2 normalized((pt - vec2(from.p1)).array() / vec2(from.GetSize()).array());
+  return (normalized * vec2(to.GetSize())) + vec2(to.p1);
+}
+
+template <typename T>
+inline bool IsZero(const T& v,
+                   const T& tol = std::numeric_limits<T>::epsilon()) {
+  return std::abs(v) <= tol;
+}
+
+template <typename T>
+inline bool IsEqual(const T& a, const T& b,
+                    const T& tol = std::numeric_limits<T>::epsilon()) {
+  return std::abs(b - a) <= tol;
+}
+
+template <typename T>
+T Square(const T& x) {
+  return x * x;
+}
+
+template <typename T>
+T RandomInRange(T lo, T hi,
+                typename
+                std::enable_if<std::is_floating_point<T>::value>::type* = 0) {
+  std::random_device rd;
+  std::mt19937 gen(rd());
+  std::uniform_real_distribution<T> distro(lo, hi);
+  return distro(gen);
+}
+
+template <typename T>
+T RandomInRange(T lo, T hi,
+                typename
+                std::enable_if<std::is_integral<T>::value>::type* = 0) {
+  std::random_device rd;
+  std::mt19937 gen(rd());
+  std::uniform_int_distribution<T> distro(lo, hi);
+  return distro(gen);
+}
+
+template <typename Derived1, typename Derived2>
+Derived1 RandomInRange(
+    const Eigen::MatrixBase<Derived1>& lo,
+    const Eigen::MatrixBase<Derived2>& hi) {
+  using Matrix1_t = Eigen::MatrixBase<Derived1>;
+  using Matrix2_t = Eigen::MatrixBase<Derived2>;
+
+  EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(Matrix1_t, Matrix2_t);
+
+  Derived1 result = Matrix1_t::Zero();
+
+  for (int row = 0; row < result.rows(); ++row) {
+    for (int col = 0; col < result.cols(); ++col) {
+      result(row, col) = RandomInRange(lo(row, col), hi(row, col));
+    }
+  }
+
+  return result;
+}
+
+template <typename T>
+T RandomRange(T x) {
+  return RandomInRange(-x, x);
+}
+
+template <typename T>
+T Clamp(T x, T lo, T hi) {
+  return std::min(std::max(x, lo), hi);
+}
+
+inline mat3 ScaleMatrix(const vec2& scale_xy) {
+  return mat3(Eigen::Scaling(scale_xy[0], scale_xy[1], 1.0f));
+}
+
+inline mat3 TranslationMatrix(const vec2& translation) {
+  return mat3(Eigen::Translation2f(translation));
+}
+
+inline mat4 TranslationMatrix(const vec3& translation) {
+  return mat4(Eigen::Translation3f(translation));
+}
+
+inline vec2 TransformPoint(const mat3& m, const vec2& p) {
+  return m.linear() * p + m.translation();
+}
+
+inline vec2 TransformVector(const mat3& m, const vec2& p) {
+  return m.linear() * p;
+}
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_NUMERIC_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/ortho.h b/libs/vr/libdvrcommon/include/private/dvr/ortho.h
new file mode 100644
index 0000000..fc0bce3
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/ortho.h
@@ -0,0 +1,31 @@
+#ifndef ANDROID_DVR_ORTHO_H_
+#define ANDROID_DVR_ORTHO_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+template <class T>
+Eigen::AffineMatrix<T, 4> OrthoMatrix(T left, T right, T bottom, T top,
+                                      T znear, T zfar) {
+  Eigen::AffineMatrix<T, 4> result;
+  const T t2 = static_cast<T>(2);
+  const T a = t2 / (right - left);
+  const T b = t2 / (top - bottom);
+  const T c = t2 / (zfar - znear);
+  const T xoff = -(right + left) / (right - left);
+  const T yoff = -(top + bottom) / (top - bottom);
+  const T zoff = -(zfar + znear) / (zfar - znear);
+  const T t1 = static_cast<T>(1);
+  result.matrix() << a, 0, 0, xoff,
+            0, b, 0, yoff,
+            0, 0, c, zoff,
+            0, 0, 0, t1;
+  return result;
+}
+
+}  // namespace android
+}  // namespace dvr
+
+#endif  // ANDROID_DVR_ORTHO_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/platform_defines.h b/libs/vr/libdvrcommon/include/private/dvr/platform_defines.h
new file mode 100644
index 0000000..71d4c8c
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/platform_defines.h
@@ -0,0 +1,16 @@
+#ifndef ANDROID_DVR_PLATFORM_DEFINES_H_
+#define ANDROID_DVR_PLATFORM_DEFINES_H_
+
+// Platform-specific macros and defines.
+
+// QCOM's GRALLOC_USAGE_PRIVATE_ALLOC_UBWC usage bit.
+#define GRALLOC_USAGE_QCOM_FRAMEBUFFER_COMPRESSION GRALLOC_USAGE_PRIVATE_1
+
+// QCOM bit to use the ADSP heap. This carveout heap is accessible to Linux,
+// Hexagon DSPs, and the GPU.
+#define GRALLOC_USAGE_PRIVATE_ADSP_HEAP 0x01000000
+
+// Force a gralloc buffer to get the uncached ION option set.
+#define GRALLOC_USAGE_PRIVATE_UNCACHED 0x02000000
+
+#endif  // ANDROID_DVR_PLATFORM_DEFINES_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/pose.h b/libs/vr/libdvrcommon/include/private/dvr/pose.h
new file mode 100644
index 0000000..97944e8
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/pose.h
@@ -0,0 +1,118 @@
+#ifndef ANDROID_DVR_POSE_H_
+#define ANDROID_DVR_POSE_H_
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+// Encapsulates a 3D pose (rotation and position).
+//
+// @tparam T Data type for storing the position coordinate and rotation
+//     quaternion.
+template <typename T>
+class Pose {
+ public:
+  // Creates identity pose.
+  Pose()
+      : rotation_(Eigen::Quaternion<T>::Identity()),
+        position_(Eigen::Vector3<T>::Zero()) {}
+
+  // Initializes a pose with given rotation and position.
+  //
+  // rotation Initial rotation.
+  // position Initial position.
+  Pose(Eigen::Quaternion<T> rotation, Eigen::Vector3<T> position)
+      : rotation_(rotation), position_(position) {}
+
+  void Invert() {
+    rotation_ = rotation_.inverse();
+    position_ = rotation_ * -position_;
+  }
+
+  Pose Inverse() const {
+    Pose result(*this);
+    result.Invert();
+    return result;
+  }
+
+  // Compute the composition of this pose with another, storing the result
+  // in the current object
+  void ComposeInPlace(const Pose& other) {
+    position_ = position_ + rotation_ * other.position_;
+    rotation_ = rotation_ * other.rotation_;
+  }
+
+  // Computes the composition of this pose with another, and returns the result
+  Pose Compose(const Pose& other) const {
+    Pose result(*this);
+    result.ComposeInPlace(other);
+    return result;
+  }
+
+  Eigen::Vector3<T> TransformPoint(const Eigen::Vector3<T>& v) const {
+    return rotation_ * v + position_;
+  }
+
+  Eigen::Vector3<T> Transform(const Eigen::Vector3<T>& v) const {
+    return rotation_ * v;
+  }
+
+  Pose& operator*=(const Pose& other) {
+    ComposeInPlace(other);
+    return *this;
+  }
+
+  Pose operator*(const Pose& other) const { return Compose(other); }
+
+  // Gets the rotation of the 3D pose.
+  Eigen::Quaternion<T> GetRotation() const { return rotation_; }
+
+  // Gets the position of the 3D pose.
+  Eigen::Vector3<T> GetPosition() const { return position_; }
+
+  // Sets the rotation of the 3D pose.
+  void SetRotation(Eigen::Quaternion<T> rotation) { rotation_ = rotation; }
+
+  // Sets the position of the 3D pose.
+  void SetPosition(Eigen::Vector3<T> position) { position_ = position; }
+
+  // Gets a 4x4 matrix representing a transform from the reference space (that
+  // the rotation and position of the pose are relative to) to the object space.
+  Eigen::AffineMatrix<T, 4> GetObjectFromReferenceMatrix() const;
+
+  // Gets a 4x4 matrix representing a transform from the object space to the
+  // reference space (that the rotation and position of the pose are relative
+  // to).
+  Eigen::AffineMatrix<T, 4> GetReferenceFromObjectMatrix() const;
+
+ private:
+  Eigen::Quaternion<T> rotation_;
+  Eigen::Vector3<T> position_;
+};
+
+template <typename T>
+Eigen::AffineMatrix<T, 4> Pose<T>::GetObjectFromReferenceMatrix() const {
+  // The transfrom from the reference is the inverse of the pose.
+  Eigen::AffineMatrix<T, 4> matrix(rotation_.inverse().toRotationMatrix());
+  return matrix.translate(-position_);
+}
+
+template <typename T>
+Eigen::AffineMatrix<T, 4> Pose<T>::GetReferenceFromObjectMatrix() const {
+  // The transfrom to the reference.
+  Eigen::AffineMatrix<T, 4> matrix(rotation_.toRotationMatrix());
+  return matrix.pretranslate(position_);
+}
+
+//------------------------------------------------------------------------------
+// Type-specific typedefs.
+//------------------------------------------------------------------------------
+
+using Posef = Pose<float>;
+using Posed = Pose<double>;
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_POSE_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/range.h b/libs/vr/libdvrcommon/include/private/dvr/range.h
new file mode 100644
index 0000000..1d06c96
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/range.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_DVR_RANGE_H_
+#define ANDROID_DVR_RANGE_H_
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+// TODO(skiazyk): Replace all instances of this with Eigen::AlignedBox
+
+// Container of two points that define a 2D range.
+template <class T, int d>
+struct Range {
+  // Construct an uninitialized Range.
+  Range() {}
+  Range(Eigen::Vector<T, d> p1, Eigen::Vector<T, d> p2) : p1(p1), p2(p2) {}
+
+  static Range<T, d> FromSize(Eigen::Vector<T, d> p1,
+                              Eigen::Vector<T, d> size) {
+    return Range<T, d>(p1, p1 + size);
+  }
+
+  bool operator==(const Range<T, d>& rhs) const {
+    return p1 == rhs.p1 && p2 == rhs.p2;
+  }
+
+  Eigen::Vector<T, d> GetMinPoint() const { return p1; }
+
+  Eigen::Vector<T, d> GetMaxPoint() const { return p2; }
+
+  Eigen::Vector<T, d> GetSize() const { return p2 - p1; }
+
+  Eigen::Vector<T, d> p1;
+  Eigen::Vector<T, d> p2;
+};
+
+typedef Range<int, 2> Range2i;
+typedef Range<float, 2> Range2f;
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_RANGE_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/revision.h b/libs/vr/libdvrcommon/include/private/dvr/revision.h
new file mode 100644
index 0000000..dda0fce
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/revision.h
@@ -0,0 +1,47 @@
+#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_REVISION_H_
+#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_REVISION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// List of DreamOS products
+typedef enum DvrProduct {
+  DVR_PRODUCT_UNKNOWN,
+  DVR_PRODUCT_A00,
+  DVR_PRODUCT_A65R,
+  DVR_PRODUCT_TWILIGHT = DVR_PRODUCT_A65R
+} DvrProduct;
+
+// List of possible revisions.
+typedef enum DvrRevision {
+  DVR_REVISION_UNKNOWN,
+  DVR_REVISION_P1,
+  DVR_REVISION_P2,
+  DVR_REVISION_P3,
+} DvrRevision;
+
+// Query the device's product.
+//
+// @return DvrProduct value, or DvrProductUnknown on error.
+DvrProduct dvr_get_product();
+
+// Query the device's revision.
+//
+// @return DvrRevision value, or DvrRevisionUnknown on error.
+DvrRevision dvr_get_revision();
+
+// Returns the device's board revision string.
+//
+// @return NULL-terminated string such as 'a00-p1'.
+const char* dvr_get_product_revision_str();
+
+// Returns the device's serial number.
+//
+// @return Returns NULL on error, or a NULL-terminated string.
+const char* dvr_get_serial_number();
+
+#ifdef __cplusplus
+}
+#endif  // extern "C"
+#endif  // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_REVISION_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h b/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h
new file mode 100644
index 0000000..44485a7
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h
@@ -0,0 +1,99 @@
+#ifndef ANDROID_DVR_RING_BUFFER_H_
+#define ANDROID_DVR_RING_BUFFER_H_
+
+#include <utility>
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+// A simple ring buffer implementation.
+//
+// A vector works but you either have to keep track of start_ and size_ yourself
+// or erase() from the front which is inefficient.
+//
+// A deque works but the common usage pattern of Append() PopFront() Append()
+// PopFront() looks like it allocates each time size goes from 0 --> 1, which we
+// don't want. This class allocates only once.
+template <typename T>
+class RingBuffer {
+ public:
+  RingBuffer() { Reset(0); }
+
+  explicit RingBuffer(size_t capacity) { Reset(capacity); }
+
+  RingBuffer(const RingBuffer& other) = default;
+  RingBuffer(RingBuffer&& other) = default;
+  RingBuffer& operator=(const RingBuffer& other) = default;
+  RingBuffer& operator=(RingBuffer&& other) = default;
+
+  void Append(const T& val) {
+    if (IsFull())
+      PopFront();
+    Get(size_) = val;
+    size_++;
+  }
+
+  void Append(T&& val) {
+    if (IsFull())
+      PopFront();
+    Get(size_) = std::move(val);
+    size_++;
+  }
+
+  bool IsEmpty() const { return size_ == 0; }
+
+  bool IsFull() const { return size_ == buffer_.size(); }
+
+  size_t GetSize() const { return size_; }
+
+  size_t GetCapacity() const { return buffer_.size(); }
+
+  T& Get(size_t i) { return buffer_[(start_ + i) % buffer_.size()]; }
+
+  const T& Get(size_t i) const {
+    return buffer_[(start_ + i) % buffer_.size()];
+  }
+
+  const T& Back() const { return Get(size_ - 1); }
+
+  T& Back() { return Get(size_ - 1); }
+
+  const T& Front() const { return Get(0); }
+
+  T& Front() { return Get(0); }
+
+  void PopBack() {
+    if (size_ != 0) {
+      Get(size_ - 1) = T();
+      size_--;
+    }
+  }
+
+  void PopFront() {
+    if (size_ != 0) {
+      Get(0) = T();
+      start_ = (start_ + 1) % buffer_.size();
+      size_--;
+    }
+  }
+
+  void Clear() { Reset(GetCapacity()); }
+
+  void Reset(size_t capacity) {
+    start_ = size_ = 0;
+    buffer_.clear();
+    buffer_.resize(capacity);
+  }
+
+ private:
+  // Ideally we'd allocate our own memory and use placement new to instantiate
+  // instances of T instead of using a vector, but the vector is simpler.
+  std::vector<T> buffer_;
+  size_t start_, size_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_RING_BUFFER_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/sync_util.h b/libs/vr/libdvrcommon/include/private/dvr/sync_util.h
new file mode 100644
index 0000000..c6911bc
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/sync_util.h
@@ -0,0 +1,31 @@
+#ifndef ANDROID_DVR_SYNC_UTIL_H_
+#define ANDROID_DVR_SYNC_UTIL_H_
+
+#include <cstdint>
+#include <type_traits>
+
+namespace android {
+namespace dvr {
+
+constexpr size_t kFenceInfoBufferSize = 4096;
+
+// This buffer is eventually mapped to a sync_fence_info_data struct (from
+// sync.h), whose largest member is a uint32_t. We align to 8 bytes to be extra
+// cautious.
+using FenceInfoBuffer = std::aligned_storage<kFenceInfoBufferSize, 8>::type;
+
+// Get fence info. Internally this works just like sync_fence_info(), except the
+// caller supplies a memory buffer instead of allocating memory.
+// On success, returns 0. On error, -1 is returned, and errno is set.
+int GetSyncFenceInfo(int fence_fd, FenceInfoBuffer* buffer);
+
+// Returns the timestamp when the fence was first signaled. buffer is used as
+// described in GetSyncFenceInfo().
+// On success, returns 0. On error, -1 is returned, and errno is set.
+int GetFenceSignaledTimestamp(int fence_fd, FenceInfoBuffer* buffer,
+                              int64_t* timestamp);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SYNC_UTIL_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h b/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h
new file mode 100644
index 0000000..6048652
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h
@@ -0,0 +1,124 @@
+#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_
+#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_
+
+#include <gtest/gtest.h>
+
+#include <cmath>
+
+#include <private/dvr/numeric.h>
+
+namespace android {
+namespace dvr {
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpArrayLikeFloatEq(
+    const char* expectedStr, const char* actualStr, const char* toleranceStr,
+    const A& expected, const B& actual, const T& tolerance) {
+  for (int i = 0; i < N; ++i) {
+    if (!IsEqual(expected[i], actual[i], tolerance)) {
+      return ::testing::AssertionFailure()
+             << "\"" << expectedStr << "\" and \"" << actualStr
+             << "\" differ at element " << i << " by at least " << tolerance
+             << " : "
+             << " Expected \"" << expected[i] << "\", was \"" << actual[i]
+             << "\".";
+    }
+  }
+
+  return ::testing::AssertionSuccess();
+}
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpMatrixLikeFloatEq(
+    const char* expectedStr, const char* actualStr, const char* toleranceStr,
+    const A& expected, const B& actual, const T& tolerance) {
+  for (int r = 0; r < N; ++r) {
+    for (int c = 0; c < N; ++c) {
+      if (!IsEqual(expected(r, c), actual(r, c), tolerance)) {
+        return ::testing::AssertionFailure()
+               << "\"" << expectedStr << "\" and \"" << actualStr
+               << "\" differ at (" << r << "," << c << ")"
+               << " by at least " << tolerance << " : "
+               << " Expected \"" << expected(r, c) << "\", was \""
+               << actual(r, c) << "\".";
+      }
+    }
+  }
+
+  return ::testing::AssertionSuccess();
+}
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpArrayLikeFloatNe(
+    const char* expectedStr, const char* actualStr, const char* toleranceStr,
+    const A& expected, const B& actual, const T& tolerance) {
+  for (int i = 0; i < N; ++i) {
+    if (!IsEqual(expected[i], actual[i], tolerance)) {
+      return ::testing::AssertionSuccess();
+    }
+  }
+
+  ::testing::Message message;
+  message << "Expected \"" << expectedStr
+          << "\" to differ from provided value \"" << actualStr
+          << "\" by at least " << tolerance << ".";
+
+  return ::testing::AssertionFailure(message);
+}
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpMatrixLikeFloatNe(
+    const char* expectedStr, const char* actualStr, const char* toleranceStr,
+    const A& expected, const B& actual, const T& tolerance) {
+  for (int r = 0; r < N; ++r) {
+    for (int c = 0; c < N; ++c) {
+      if (!IsEqual(expected(r, c), actual(r, c), tolerance)) {
+        return ::testing::AssertionSuccess();
+      }
+    }
+  }
+
+  ::testing::Message message;
+  message << "Expected \"" << expectedStr
+          << "\" to differ from provided value \"" << actualStr
+          << "\" by at least " << tolerance << ".";
+
+  return ::testing::AssertionFailure(message);
+}
+
+}  // namespace dvr
+}  // namespace android
+
+#define EXPECT_VEC3_NEAR(expected, actual, tol)                               \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatEq<3>, expected, actual, \
+                      tol)
+
+#define EXPECT_VEC3_NOT_NEAR(expected, actual, tol)                           \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatNe<3>, expected, actual, \
+                      tol)
+
+#define EXPECT_QUAT_NEAR(expected, actual, tol)                                \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatEq<3>, expected.coeffs(), \
+                      actual.coeffs(), tol)
+
+#define EXPECT_QUAT_NOT_NEAR(expected, actual, tol)                            \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatNe<3>, expected.coeffs(), \
+                      actual.coeffs(), tol)
+
+#define EXPECT_MAT4_NEAR(expected, actual, tol)                                \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatEq<4>, expected, actual, \
+                      tol)
+
+#define EXPECT_MAT4_NOT_NEAR(expected, actual, tol)                            \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatNe<4>, expected, actual, \
+                      tol)
+
+#define EXPECT_MAT3_NEAR(expected, actual, tol) \
+  EXPECT_PRED_FORMAT3(android::dvr              \
+                      : CmpMatrixLikeFloatEq<3>, expected, actual, tol)
+
+#define EXPECT_MAT3_NOT_NEAR(expected, actual, tol)                            \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatNe<3>, expected, actual, \
+                      tol)
+
+#endif  // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/types.h b/libs/vr/libdvrcommon/include/private/dvr/types.h
new file mode 100644
index 0000000..1fa54af
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/types.h
@@ -0,0 +1,51 @@
+#ifndef ANDROID_DVR_TYPES_H_
+#define ANDROID_DVR_TYPES_H_
+
+// All basic types used by VR code.
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/field_of_view.h>
+#include <private/dvr/pose.h>
+#include <private/dvr/range.h>
+
+namespace android {
+namespace dvr {
+
+enum RgbColorChannel { kRed, kGreen, kBlue };
+
+// EyeType: 0 for left, 1 for right.
+enum EyeType { kLeftEye = 0, kRightEye = 1 };
+
+// In the context of VR, vector types are used as much as base types.
+
+using vec2f = Eigen::Vector2f;
+using vec2d = Eigen::Vector2d;
+using vec2i = Eigen::Vector2i;
+using vec2 = vec2f;
+
+using vec3f = Eigen::Vector3f;
+using vec3d = Eigen::Vector3d;
+using vec3i = Eigen::Vector3i;
+using vec3 = vec3f;
+
+using vec4f = Eigen::Vector4f;
+using vec4d = Eigen::Vector4d;
+using vec4i = Eigen::Vector4i;
+using vec4 = vec4f;
+
+using mat3f = Eigen::AffineMatrix<float, 3>;
+using mat3d = Eigen::AffineMatrix<double, 3>;
+using mat3 = mat3f;
+
+using mat4f = Eigen::AffineMatrix<float, 4>;
+using mat4d = Eigen::AffineMatrix<double, 4>;
+using mat4 = mat4f;
+
+using quatf = Eigen::Quaternionf;
+using quatd = Eigen::Quaterniond;
+using quat = quatf;
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_TYPES_H_
diff --git a/libs/vr/libdvrcommon/revision.cpp b/libs/vr/libdvrcommon/revision.cpp
new file mode 100644
index 0000000..ae8603f
--- /dev/null
+++ b/libs/vr/libdvrcommon/revision.cpp
@@ -0,0 +1,175 @@
+#include "private/dvr/revision.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <base/logging.h>
+
+#include "revision_path.h"
+
+namespace {
+
+// Allows quicker access to the product revision. If non-zero, then
+// the product revision file has already been processed.
+static bool global_product_revision_processed = false;
+
+static bool global_serial_number_processed = false;
+
+// The product.
+static DvrProduct global_product = DVR_PRODUCT_UNKNOWN;
+
+// The revision.
+static DvrRevision global_revision = DVR_REVISION_UNKNOWN;
+
+// Maximum size of the product revision string.
+constexpr int kProductRevisionStringSize = 32;
+
+// Maximum size of the serial number.
+constexpr int kSerialNumberStringSize = 32;
+
+// The product revision string.
+static char global_product_revision_str[kProductRevisionStringSize + 1] = "";
+
+// The serial number string
+static char global_serial_number[kSerialNumberStringSize + 1] = "";
+
+// Product and revision combinations.
+struct DvrProductRevision {
+  const char* str;
+  DvrProduct product;
+  DvrRevision revision;
+};
+
+// Null-terminated list of all product and revision combinations.
+static constexpr DvrProductRevision kProductRevisions[] = {
+    {"a00-p1", DVR_PRODUCT_A00, DVR_REVISION_P1},
+    {"a00-p2", DVR_PRODUCT_A00, DVR_REVISION_P2},
+    {"a00-p3", DVR_PRODUCT_A00, DVR_REVISION_P3},
+    {"twilight-p1", DVR_PRODUCT_A65R, DVR_REVISION_P1},
+    {"twilight-p2", DVR_PRODUCT_A65R, DVR_REVISION_P2},
+    {NULL, DVR_PRODUCT_UNKNOWN, DVR_REVISION_UNKNOWN}};
+
+// Read the product revision string, and store the global data.
+static void process_product_revision() {
+  int fd;
+  ssize_t read_rc;
+  const DvrProductRevision* product_revision = kProductRevisions;
+
+  // Of course in a multi-threaded environment, for a few microseconds
+  // during process startup, it is possible that this function will be
+  // called and execute fully multiple times. That is why the product
+  // revision string is statically allocated.
+
+  if (global_product_revision_processed)
+    return;
+
+  // Whether there was a failure or not, we don't want to do this again.
+  // Upon failure it's most likely to fail again anyway.
+
+  fd = open(dvr_product_revision_file_path(), O_RDONLY);
+  if (fd < 0) {
+    PLOG(ERROR) << "Could not open '" << dvr_product_revision_file_path()
+                << "' to get product revision";
+    global_product_revision_processed = true;
+    return;
+  }
+
+  read_rc = read(fd, global_product_revision_str, kProductRevisionStringSize);
+  if (read_rc <= 0) {
+    PLOG(ERROR) << "Could not read from '" << dvr_product_revision_file_path()
+                << "'";
+    global_product_revision_processed = true;
+    return;
+  }
+
+  close(fd);
+
+  global_product_revision_str[read_rc] = '\0';
+
+  while (product_revision->str) {
+    if (!strcmp(product_revision->str, global_product_revision_str))
+      break;
+    product_revision++;
+  }
+
+  if (product_revision->str) {
+    global_product = product_revision->product;
+    global_revision = product_revision->revision;
+  } else {
+    LOG(ERROR) << "Unable to match '" << global_product_revision_str
+               << "' to a product/revision.";
+  }
+
+  global_product_revision_processed = true;
+}
+
+}  // anonymous namespace
+
+extern "C" DvrProduct dvr_get_product() {
+  process_product_revision();
+  return global_product;
+}
+
+extern "C" DvrRevision dvr_get_revision() {
+  process_product_revision();
+  return global_revision;
+}
+
+extern "C" const char* dvr_get_product_revision_str() {
+  process_product_revision();
+  return global_product_revision_str;
+}
+
+extern "C" const char* dvr_get_serial_number() {
+  process_product_revision();
+  if (global_product == DVR_PRODUCT_A00) {
+    if (!global_serial_number_processed) {
+#ifdef DVR_HOST
+      global_serial_number_processed = true;
+#else
+      int width = 4;
+      uintptr_t addr = 0x00074138;
+      uintptr_t endaddr = addr + width - 1;
+
+      int fd = open("/dev/mem", O_RDWR | O_SYNC);
+      if (fd < 0) {
+        if (errno == EPERM)
+          global_serial_number_processed = true;
+        fprintf(stderr, "cannot open /dev/mem\n");
+        return global_serial_number;
+      }
+
+      off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
+      size_t mmap_size = endaddr - mmap_start + 1;
+      mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+
+      void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+                          mmap_start);
+
+      if (page == MAP_FAILED) {
+        global_serial_number_processed = true;
+        fprintf(stderr, "cannot mmap region\n");
+        close(fd);
+        return global_serial_number;
+      }
+
+      uint32_t* x =
+          reinterpret_cast<uint32_t*>((((uintptr_t)page) + (addr & 4095)));
+      snprintf(global_serial_number, kSerialNumberStringSize, "%08x", *x);
+      global_serial_number_processed = true;
+
+      munmap(page, mmap_size);
+      close(fd);
+#endif
+    }
+    return global_serial_number;
+  } else {
+    return nullptr;
+  }
+}
diff --git a/libs/vr/libdvrcommon/revision_path.cpp b/libs/vr/libdvrcommon/revision_path.cpp
new file mode 100644
index 0000000..c49f9aa
--- /dev/null
+++ b/libs/vr/libdvrcommon/revision_path.cpp
@@ -0,0 +1,15 @@
+#include "revision_path.h"
+
+namespace {
+
+// The path to the product revision file.
+static const char* kProductRevisionFilePath =
+    "/sys/firmware/devicetree/base/goog,board-revision";
+
+}  // anonymous namespace
+
+// This exists in a separate file so that it can be replaced for
+// testing.
+const char* dvr_product_revision_file_path() {
+  return kProductRevisionFilePath;
+}
diff --git a/libs/vr/libdvrcommon/revision_path.h b/libs/vr/libdvrcommon/revision_path.h
new file mode 100644
index 0000000..afcea46
--- /dev/null
+++ b/libs/vr/libdvrcommon/revision_path.h
@@ -0,0 +1,9 @@
+#ifndef ANDROID_DVR_LIBDVRCOMMON_REVISION_PATH_H_
+#define ANDROID_DVR_LIBDVRCOMMON_REVISION_PATH_H_
+
+// Returns the revision file path.
+// This exists in a separate file so that it can be replaced for
+// testing.
+const char* dvr_product_revision_file_path();
+
+#endif  // ANDROID_DVR_LIBDVRCOMMON_REVISION_PATH_H_
diff --git a/libs/vr/libdvrcommon/sync_util.cpp b/libs/vr/libdvrcommon/sync_util.cpp
new file mode 100644
index 0000000..3637936
--- /dev/null
+++ b/libs/vr/libdvrcommon/sync_util.cpp
@@ -0,0 +1,87 @@
+#include "include/private/dvr/sync_util.h"
+
+#include <errno.h>
+#include <sys/ioctl.h>
+
+// TODO: http://b/33239638 Move GetSyncFenceInfo() into upstream libsync instead
+//   of duplicating functionality and structure definitions from it.
+struct sync_fence_info_data {
+ uint32_t len;
+ char name[32];
+ int32_t status;
+ uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+ uint32_t len;
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint64_t timestamp_ns;
+ uint8_t driver_data[0];
+};
+
+#define SYNC_IOC_MAGIC '>'
+#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
+#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data)
+#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2, struct sync_fence_info_data)
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// This is copied from sync_pt_info() in libsync/sync.c. It's been cleaned up to
+// remove lint warnings.
+sync_pt_info* GetSyncPtInfo(sync_fence_info_data* info, sync_pt_info* itr) {
+  if (itr == nullptr)
+    itr = reinterpret_cast<sync_pt_info*>(info->pt_info);
+  else
+    itr = reinterpret_cast<sync_pt_info*>(reinterpret_cast<uint8_t*>(itr) +
+                                          itr->len);
+
+  if (reinterpret_cast<uint8_t*>(itr) - reinterpret_cast<uint8_t*>(info) >=
+      static_cast<int>(info->len))
+    return nullptr;
+
+  return itr;
+}
+
+}  // namespace
+
+int GetSyncFenceInfo(int fence_fd, FenceInfoBuffer* buffer) {
+  // If the implementation of sync_fence_info() in libsync/sync.c changes, this
+  // function should be changed to match.
+  if (buffer == nullptr) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  sync_fence_info_data* fence_info =
+      reinterpret_cast<sync_fence_info_data*>(buffer);
+  fence_info->len = kFenceInfoBufferSize;
+  return ioctl(fence_fd, SYNC_IOC_FENCE_INFO, fence_info);
+}
+
+int GetFenceSignaledTimestamp(int fence_fd, FenceInfoBuffer* buffer,
+                              int64_t* timestamp) {
+  int result = GetSyncFenceInfo(fence_fd, buffer);
+  if (result < 0)
+    return result;
+
+  sync_fence_info_data* fence_info =
+      reinterpret_cast<sync_fence_info_data*>(buffer);
+  struct sync_pt_info* pt_info = nullptr;
+  while ((pt_info = GetSyncPtInfo(fence_info, pt_info)) != nullptr) {
+    if (pt_info->status == 1) {
+      *timestamp = pt_info->timestamp_ns;
+      return 0;
+    }
+  }
+
+  errno = EAGAIN;
+  return -1;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrcommon/tests/numeric_test.cpp b/libs/vr/libdvrcommon/tests/numeric_test.cpp
new file mode 100644
index 0000000..1ee1447
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/numeric_test.cpp
@@ -0,0 +1,67 @@
+#include <gtest/gtest.h>
+
+#include <private/dvr/numeric.h>
+
+using TestTypes = ::testing::Types<float, double, int>;
+
+using android::dvr::RandomInRange;
+
+template <typename T>
+class NumericTest : public ::testing::TestWithParam<T> {
+ public:
+  using FT = T;
+};
+
+TYPED_TEST_CASE(NumericTest, TestTypes);
+
+TYPED_TEST(NumericTest, RandomInRange) {
+  using FT = typename TestFixture::FT;
+
+  const int kNumTrials = 50;
+  const FT kLowRange = static_cast<FT>(-100);
+  const FT kHighRange = static_cast<FT>(100);
+
+  for (int i = 0; i < kNumTrials; ++i) {
+    FT value = RandomInRange(kLowRange, kHighRange);
+
+    EXPECT_LE(kLowRange, value);
+    EXPECT_GE(kHighRange, value);
+  }
+}
+
+TEST(RandomInRange, TestIntVersion) {
+  // This checks specifically that the function does not always give the lo
+  // value (this was previously a bug)
+
+  const int kNumTrials = 50;
+  const int kLowRange = -100;
+  const int kHighRange = 100;
+
+  for (int i = 0; i < kNumTrials; ++i) {
+    int value = RandomInRange(kLowRange, kHighRange);
+
+    if (value != kLowRange) {
+      SUCCEED();
+      return;
+    }
+  }
+
+  FAIL() << "Did not produce a value other than the range minimum for "
+         << "integers.";
+}
+
+TEST(RandomInRange, TestVectorVersion) {
+  Eigen::Vector3d lo(-3.0, -4.0, -5.0);
+  Eigen::Vector3d hi(5.0, 4.0, 3.0);
+
+  const int kNumTrials = 50;
+
+  for (int i = 0; i < kNumTrials; ++i) {
+    Eigen::Vector3d result = RandomInRange(lo, hi);
+
+    for (int j = 0; j < 3; ++j) {
+      EXPECT_LE(lo[j], result[j]);
+      EXPECT_GE(hi[j], result[j]);
+    }
+  }
+}
diff --git a/libs/vr/libdvrcommon/tests/pose_test.cpp b/libs/vr/libdvrcommon/tests/pose_test.cpp
new file mode 100644
index 0000000..aa1896d
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/pose_test.cpp
@@ -0,0 +1,154 @@
+#include <gtest/gtest.h>
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/pose.h>
+#include <private/dvr/test/test_macros.h>
+
+using PoseTypes = ::testing::Types<float, double>;
+
+template <class T>
+class PoseTest : public ::testing::TestWithParam<T> {
+ public:
+  using FT = T;
+  using Pose_t = android::dvr::Pose<FT>;
+  using quat_t = Eigen::Quaternion<FT>;
+  using vec3_t = Eigen::Vector3<FT>;
+  using mat4_t = Eigen::AffineMatrix<FT, 4>;
+};
+
+TYPED_TEST_CASE(PoseTest, PoseTypes);
+
+// Check that the two matrix methods are inverses of each other
+TYPED_TEST(PoseTest, SelfInverse) {
+  using quat_t = typename TestFixture::quat_t;
+  using vec3_t = typename TestFixture::vec3_t;
+  using Pose_t = typename TestFixture::Pose_t;
+  using mat4_t = typename TestFixture::mat4_t;
+  using FT = typename TestFixture::FT;
+
+  const auto tolerance = FT(0.0001);
+
+  const quat_t initial_rotation(Eigen::AngleAxis<FT>(
+      FT(M_PI / 3.0), vec3_t(FT(3.0), FT(4.0), FT(5.0)).normalized()));
+  const vec3_t initial_position = vec3_t(FT(2.0), FT(10.0), FT(-4.0));
+  const Pose_t initial_pose(initial_rotation, initial_position);
+
+  auto result_pose = initial_pose.GetReferenceFromObjectMatrix() *
+                     initial_pose.GetObjectFromReferenceMatrix();
+
+  EXPECT_MAT4_NEAR(result_pose, mat4_t::Identity(), tolerance);
+}
+
+TYPED_TEST(PoseTest, TransformPoint) {
+  using quat_t = typename TestFixture::quat_t;
+  using vec3_t = typename TestFixture::vec3_t;
+  using Pose_t = typename TestFixture::Pose_t;
+  using FT = typename TestFixture::FT;
+
+  const auto tolerance = FT(0.0001);
+
+  const quat_t pose_rotation(
+      Eigen::AngleAxis<FT>(FT(M_PI / 2.0), vec3_t(FT(0.0), FT(0.0), FT(1.0))));
+  const auto pose_position = vec3_t(FT(1.0), FT(1.0), FT(2.0));
+
+  const Pose_t test_pose(pose_rotation, pose_position);
+
+  for (int axis = 0; axis < 3; ++axis) {
+    vec3_t start_position = vec3_t::Zero();
+    start_position[axis] = FT(1.0);
+    const vec3_t expected_transformed =
+        (pose_rotation * start_position) + pose_position;
+    const vec3_t actual_transformed = test_pose.TransformPoint(start_position);
+    EXPECT_VEC3_NEAR(expected_transformed, actual_transformed, tolerance);
+  }
+}
+
+TYPED_TEST(PoseTest, TransformVector) {
+  using quat_t = typename TestFixture::quat_t;
+  using vec3_t = typename TestFixture::vec3_t;
+  using Pose_t = typename TestFixture::Pose_t;
+  using FT = typename TestFixture::FT;
+
+  const auto tolerance = FT(0.0001);
+
+  const quat_t pose_rotation(Eigen::AngleAxis<FT>(
+      FT(M_PI / 6.0), vec3_t(FT(3.0), FT(4.0), FT(5.0)).normalized()));
+
+  const auto pose_position = vec3_t(FT(500.0), FT(-500.0), FT(300.0));
+
+  const Pose_t test_pose(pose_rotation, pose_position);
+
+  for (int axis = 0; axis < 3; ++axis) {
+    vec3_t start_position = vec3_t::Zero();
+    start_position[axis] = FT(1.0);
+    const vec3_t expected_rotated = pose_rotation * start_position;
+    const vec3_t actual_rotated = test_pose.Transform(start_position);
+    EXPECT_VEC3_NEAR(expected_rotated, actual_rotated, tolerance);
+  }
+}
+
+TYPED_TEST(PoseTest, Composition) {
+  using quat_t = typename TestFixture::quat_t;
+  using Pose_t = typename TestFixture::Pose_t;
+  using vec3_t = typename TestFixture::vec3_t;
+  using FT = typename TestFixture::FT;
+
+  const auto tolerance = FT(0.0001);
+
+  const quat_t first_rotation(
+      Eigen::AngleAxis<FT>(FT(M_PI / 2.0), vec3_t(FT(0.0), FT(0.0), FT(1.0))));
+  const auto first_offset = vec3_t(FT(-3.0), FT(2.0), FT(-1.0));
+  const quat_t second_rotation(Eigen::AngleAxis<FT>(
+      FT(M_PI / 3.0), vec3_t(FT(1.0), FT(-1.0), FT(0.0)).normalized()));
+  const auto second_offset = vec3_t(FT(6.0), FT(-7.0), FT(-8.0));
+
+  const Pose_t first_pose(first_rotation, first_offset);
+  const Pose_t second_pose(second_rotation, second_offset);
+
+  const auto combined_pose(second_pose.Compose(first_pose));
+
+  for (int axis = 0; axis < 3; ++axis) {
+    vec3_t start_position = vec3_t::Zero();
+    start_position[axis] = FT(1.0);
+    const vec3_t expected_transformed =
+        second_pose.TransformPoint(first_pose.TransformPoint(start_position));
+    const vec3_t actual_transformed =
+        combined_pose.TransformPoint(start_position);
+    EXPECT_VEC3_NEAR(expected_transformed, actual_transformed, tolerance);
+  }
+}
+
+TYPED_TEST(PoseTest, Inverse) {
+  using quat_t = typename TestFixture::quat_t;
+  using vec3_t = typename TestFixture::vec3_t;
+  using Pose_t = typename TestFixture::Pose_t;
+  using FT = typename TestFixture::FT;
+
+  const auto tolerance = FT(0.0001);
+
+  const quat_t pose_rotation(Eigen::AngleAxis<FT>(
+      FT(M_PI / 2.0), vec3_t(FT(4.0), FT(-2.0), FT(-1.0)).normalized()));
+  const auto pose_position = vec3_t(FT(-1.0), FT(2.0), FT(-4.0));
+
+  Pose_t pose(pose_rotation, pose_position);
+  const Pose_t pose_inverse = pose.Inverse();
+
+  for (int axis = 0; axis < 3; ++axis) {
+    vec3_t start_position = vec3_t::Zero();
+    start_position[axis] = FT(1.0);
+    const vec3_t transformed = pose.Transform(start_position);
+    const vec3_t inverted = pose_inverse.Transform(transformed);
+    EXPECT_VEC3_NEAR(start_position, inverted, tolerance);
+  }
+
+  Pose_t nullified_pose[2] = {
+      pose.Compose(pose_inverse), pose_inverse.Compose(pose),
+  };
+
+  for (int i = 0; i < 2; ++i) {
+    EXPECT_QUAT_NEAR(quat_t::Identity(), nullified_pose[i].GetRotation(),
+                     tolerance);
+    EXPECT_VEC3_NEAR(vec3_t::Zero(), nullified_pose[i].GetPosition(),
+                     tolerance);
+  }
+}
diff --git a/libs/vr/libdvrcommon/tests/revision_app_tests.cpp b/libs/vr/libdvrcommon/tests/revision_app_tests.cpp
new file mode 100644
index 0000000..772481b
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/revision_app_tests.cpp
@@ -0,0 +1,34 @@
+#include <dvr/test/app_test.h>
+#include <gtest/gtest.h>
+#include <private/dvr/revision.h>
+
+// Making sure this information is not available
+// inside the sandbox
+
+namespace {
+
+TEST(RevisionTests, GetProduct) {
+  ASSERT_EQ(DVR_PRODUCT_UNKNOWN, dvr_get_product());
+}
+
+TEST(RevisionTests, GetRevision) {
+  ASSERT_EQ(DVR_REVISION_UNKNOWN, dvr_get_revision());
+}
+
+TEST(RevisionTests, GetRevisionStr) {
+  ASSERT_STREQ("", dvr_get_product_revision_str());
+}
+
+TEST(RevisionTests, GetSerialNo) {
+  ASSERT_EQ(nullptr, dvr_get_serial_number());
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  dreamos::test::AppTestBegin();
+  ::testing::InitGoogleTest(&argc, argv);
+  int result = RUN_ALL_TESTS();
+  dreamos::test::AppTestEnd(result);
+  return result;
+}
diff --git a/libs/vr/libdvrcommon/tests/revision_tests.cpp b/libs/vr/libdvrcommon/tests/revision_tests.cpp
new file mode 100644
index 0000000..9abf480
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/revision_tests.cpp
@@ -0,0 +1,27 @@
+#include <gtest/gtest.h>
+#include <private/dvr/revision.h>
+
+namespace {
+
+TEST(RevisionTests, GetProduct) {
+  ASSERT_NE(DVR_PRODUCT_UNKNOWN, dvr_get_product());
+}
+
+TEST(RevisionTests, GetRevision) {
+  ASSERT_NE(DVR_REVISION_UNKNOWN, dvr_get_revision());
+}
+
+TEST(RevisionTests, GetRevisionStr) {
+  ASSERT_NE(nullptr, dvr_get_product_revision_str());
+}
+
+TEST(RevisionTests, GetSerialNo) {
+  ASSERT_NE(nullptr, dvr_get_serial_number());
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}