Force vrhwc/wm to report the native resolution

Due to some Yak-shaving, this also involved a partial conversion of our
libraries to soong.

Notes:
* I also changed the name of libsensor to libvrsensor to avoid a naming
  clash with an existing library
* The remaining libraries will be soongified in a separate CL

Bug: 36139334
Test: Build and run vr apps.
Change-Id: Ib39687bd01e3d2e1c30adc54e18f362a85954ab9
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
new file mode 100644
index 0000000..376630e
--- /dev/null
+++ b/libs/vr/libvrsensor/Android.bp
@@ -0,0 +1,71 @@
+// Copyright (C) 2015 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.
+
+sourceFiles = [
+    "pose_client.cpp",
+    "sensor_client.cpp",
+]
+
+includeFiles = [
+    "include",
+]
+
+staticLibraries = [
+    "libbufferhub",
+    "libdvrcommon",
+    "libpdx_default_transport",
+]
+
+sharedLibraries = [
+    "libbase",
+    "libcutils",
+    "libhardware",
+    "liblog",
+    "libutils",
+]
+
+cc_library {
+    srcs: sourceFiles,
+    export_include_dirs: includeFiles,
+    static_libs: staticLibraries,
+    shared_libs: sharedLibraries,
+    name: "libvrsensor",
+}
+
+testFiles = ["tests/sensor_app_tests.cpp"]
+
+cc_test {
+    name: "sensor_app_tests",
+    tags: ["optional"],
+
+    srcs: testFiles,
+
+    shared_libs: [
+        "libEGL",
+        "libGLESv1_CM",
+        "libGLESv2",
+        "libvulkan",
+        "libsync",
+    ] + sharedLibraries,
+
+    static_libs: [
+        "libgmock_main",
+        "libgmock",
+        "libdisplay",
+        "libeds",
+        "libvrsensor",
+        "libdvrgraphics",
+    ] + staticLibraries,
+
+}
diff --git a/libs/vr/libvrsensor/include/CPPLINT.cfg b/libs/vr/libvrsensor/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libvrsensor/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h
new file mode 100644
index 0000000..ed75f84
--- /dev/null
+++ b/libs/vr/libvrsensor/include/dvr/pose_client.h
@@ -0,0 +1,205 @@
+#ifndef ANDROID_DVR_POSE_CLIENT_H_
+#define ANDROID_DVR_POSE_CLIENT_H_
+
+#ifdef __ARM_NEON
+#include <arm_neon.h>
+#else
+#ifndef __FLOAT32X4T_86
+#define __FLOAT32X4T_86
+typedef float float32x4_t __attribute__ ((__vector_size__ (16)));
+typedef struct float32x4x4_t { float32x4_t val[4]; };
+#endif
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DvrPose DvrPose;
+
+// Represents the current state provided by the pose service, containing a
+// rotation and translation.
+typedef struct __attribute__((packed, aligned(8))) DvrPoseState {
+  // A quaternion representing the rotation of the HMD in Start Space.
+  struct __attribute__((packed)) {
+    float x, y, z, w;
+  } head_from_start_rotation;
+  // The position of the HMD in Start Space.
+  struct __attribute__((packed)) {
+    float x, y, z;
+  } head_from_start_translation;
+  // Time in nanoseconds for the current pose.
+  uint64_t timestamp_ns;
+  // The rotational velocity of the HMD.
+  struct __attribute__((packed)) {
+    float x, y, z;
+  } sensor_from_start_rotation_velocity;
+} DvrPoseState;
+
+enum {
+  DVR_POSE_FLAG_VALID = (1UL << 0),       // This pose is valid.
+  DVR_POSE_FLAG_HEAD = (1UL << 1),        // This pose is the head.
+  DVR_POSE_FLAG_CONTROLLER = (1UL << 2),  // This pose is a controller.
+};
+
+// Represents an estimated pose, accessed asynchronously through a shared ring
+// buffer. No assumptions should be made about the data in padding space.
+// The size of this struct is 128 bytes.
+typedef struct __attribute__((packed, aligned(16))) DvrPoseAsync {
+  // Left eye head-from-start orientation quaternion x,y,z,w.
+  float32x4_t orientation;
+  // Left eye head-from-start translation x,y,z,pad in meters.
+  float32x4_t translation;
+  // Right eye head-from-start orientation quaternion x,y,z,w.
+  float32x4_t right_orientation;
+  // Right eye head-from-start translation x,y,z,pad in meters.
+  float32x4_t right_translation;
+  // Start-space angular velocity x,y,z,pad in radians per second.
+  float32x4_t angular_velocity;
+  // Start-space positional velocity x,y,z,pad in meters per second.
+  float32x4_t velocity;
+  // Timestamp of when this pose is predicted for, typically halfway through
+  // scanout.
+  int64_t timestamp_ns;
+  // Bitmask of DVR_POSE_FLAG_* constants that apply to this pose.
+  //
+  // If DVR_POSE_FLAG_VALID is not set, the pose is indeterminate.
+  uint64_t flags;
+  // Reserved padding to 128 bytes.
+  uint8_t pad[16];
+} DvrPoseAsync;
+
+// Returned by the async pose ring buffer access API.
+typedef struct DvrPoseRingBufferInfo {
+  // Read-only pointer to the pose ring buffer. The current pose is in this
+  // buffer at element buffer[current_frame & (buffer_size - 1)]. The next
+  // frame's forecasted pose is at element
+  // ((current_frame + 1) & (buffer_size - 1)). And so on. The poses are
+  // predicted for when 50% of the corresponding frame's pixel data is visible
+  // to the user.
+  // The last value returned by dvrPresent is the count for the next frame,
+  // which is the earliest that the application could display something if they
+  // were to render promptly. (TODO(jbates) move this comment to dvrPresent).
+  volatile const DvrPoseAsync* buffer;
+  // Minimum number of accurate forecasted poses including the current frame's
+  // pose. This is the number of poses that are udpated by the pose service.
+  // If the application reads past this count, they will get a stale prediction
+  // from a previous frame. Guaranteed to be at least 2.
+  uint32_t min_future_count;
+  // Number of elements in buffer. At least 8 and greater than min_future_count.
+  // Guaranteed to be a power of two. The total size of the buffer in bytes is:
+  //   total_count * sizeof(DvrPoseAsync)
+  uint32_t total_count;
+} DvrPoseRingBufferInfo;
+
+typedef enum DvrPoseMode {
+  DVR_POSE_MODE_6DOF = 0,
+  DVR_POSE_MODE_3DOF,
+  DVR_POSE_MODE_MOCK_FROZEN,
+  DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW,
+  DVR_POSE_MODE_MOCK_HEAD_TURN_FAST,
+  DVR_POSE_MODE_MOCK_ROTATE_SLOW,
+  DVR_POSE_MODE_MOCK_ROTATE_MEDIUM,
+  DVR_POSE_MODE_MOCK_ROTATE_FAST,
+  DVR_POSE_MODE_MOCK_CIRCLE_STRAFE,
+
+  // Always last.
+  DVR_POSE_MODE_COUNT,
+} DvrPoseMode;
+
+typedef enum DvrControllerId {
+  DVR_CONTROLLER_0 = 0,
+  DVR_CONTROLLER_1 = 1,
+} DvrControllerId;
+
+// Creates a new pose client.
+//
+// @return Pointer to the created pose client, nullptr on failure.
+DvrPose* dvrPoseCreate();
+
+// Destroys a pose client.
+//
+// @param client Pointer to the pose client to be destroyed.
+void dvrPoseDestroy(DvrPose* client);
+
+// Gets the pose for the given vsync count.
+//
+// @param client Pointer to the pose client.
+// @param vsync_count Vsync that this pose should be forward-predicted to.
+//     Typically this is the count returned by dvrGetNextVsyncCount.
+// @param out_pose Struct to store pose state.
+// @return Zero on success, negative error code on failure.
+int dvrPoseGet(DvrPose* client, uint32_t vsync_count, DvrPoseAsync* out_pose);
+
+// Gets the current vsync count.
+uint32_t dvrPoseGetVsyncCount(DvrPose* client);
+
+// Gets the pose for the given controller at the given vsync count.
+//
+// @param client Pointer to the pose client.
+// @param controller_id The controller id.
+// @param vsync_count Vsync that this pose should be forward-predicted to.
+//     Typically this is the count returned by dvrGetNextVsyncCount.
+// @param out_pose Struct to store pose state.
+// @return Zero on success, negative error code on failure.
+int dvrPoseGetController(DvrPose* client, int32_t controller_id,
+                         uint32_t vsync_count, DvrPoseAsync* out_pose);
+
+// Enables/disables logging for the controller fusion.
+//
+// @param client Pointer to the pose client.
+// @param enable True starts logging, False stops.
+// @return Zero on success, negative error code on failure.
+int dvrPoseLogController(DvrPose* client, bool enable);
+
+// DEPRECATED
+// Polls current pose state.
+//
+// @param client Pointer to the pose client.
+// @param state Struct to store polled state.
+// @return Zero on success, negative error code on failure.
+int dvrPosePoll(DvrPose* client, DvrPoseState* state);
+
+// Freezes the pose to the provided state.
+//
+// Future poll operations will return this state until a different state is
+// frozen or dvrPoseSetMode() is called with a different mode. The timestamp is
+// not frozen.
+//
+// @param client Pointer to the pose client.
+// @param frozen_state State pose to be frozen to.
+// @return Zero on success, negative error code on failure.
+int dvrPoseFreeze(DvrPose* client, const DvrPoseState* frozen_state);
+
+// Sets the pose service mode.
+//
+// @param mode The requested pose mode.
+// @return Zero on success, negative error code on failure.
+int dvrPoseSetMode(DvrPose* client, DvrPoseMode mode);
+
+// Gets the pose service mode.
+//
+// @param mode Return value for the current pose mode.
+// @return Zero on success, negative error code on failure.
+int dvrPoseGetMode(DvrPose* client, DvrPoseMode* mode);
+
+// Get access to the shared memory pose ring buffer.
+// A future pose at vsync <current> + <offset> is accessed at index:
+//   index = (<current> + <offset>) % out_buffer_size
+// Where <current> was the last value returned by dvrPresent and
+// <offset> is less than or equal to |out_min_future_count|.
+// |out_buffer| will be set to a pointer to the buffer.
+// |out_fd| will be set to the gralloc buffer file descriptor, which is
+//   required for binding this buffer for GPU use.
+// Returns 0 on success.
+int dvrPoseGetRingBuffer(DvrPose* client, DvrPoseRingBufferInfo* out_info);
+
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_POSE_CLIENT_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
new file mode 100644
index 0000000..0616d46
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
@@ -0,0 +1,28 @@
+#ifndef ANDROID_DVR_POSE_IPC_H_
+#define ANDROID_DVR_POSE_IPC_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DVR_POSE_SERVICE_BASE "system/vr/pose"
+#define DVR_POSE_SERVICE_CLIENT (DVR_POSE_SERVICE_BASE "/client")
+
+enum {
+  DVR_POSE_POLL = 0,
+  DVR_POSE_FREEZE,
+  DVR_POSE_SET_MODE,
+  DVR_POSE_GET_RING_BUFFER,
+  DVR_POSE_NOTIFY_VSYNC,
+  DVR_POSE_GET_MODE,
+  DVR_POSE_GET_CONTROLLER_RING_BUFFER,
+  DVR_POSE_LOG_CONTROLLER,
+};
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_POSE_IPC_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
new file mode 100644
index 0000000..66c4c7c
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
+#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
+
+#include <stdint.h>
+
+#include <dvr/pose_client.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/sensor_constants.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Sensord head pose ring buffer.
+typedef struct __attribute__((packed, aligned(16))) DvrPoseRingBuffer {
+  // Ring buffer always at the beginning of the structure, as consumers may
+  // not have access to this parent structure definition.
+  DvrPoseAsync ring[kPoseAsyncBufferTotalCount];
+  // Current vsync_count (where sensord is writing poses from).
+  uint32_t vsync_count;
+} DvrPoseMetadata;
+
+// Called by displayd to give vsync count info to the pose service.
+// |display_timestamp| Display timestamp is in the middle of scanout.
+// |display_period_ns| Nanos between vsyncs.
+// |right_eye_photon_offset_ns| Nanos to shift the prediction timestamp for
+//    the right eye head pose (relative to the left eye prediction).
+int privateDvrPoseNotifyVsync(DvrPose* client, uint32_t vsync_count,
+                              int64_t display_timestamp,
+                              int64_t display_period_ns,
+                              int64_t right_eye_photon_offset_ns);
+
+// Get file descriptor for access to the shared memory pose buffer. This can be
+// used with GL extensions that support shared memory buffer objects. The caller
+// takes ownership of the returned fd and must close it or pass on ownership.
+int privateDvrPoseGetRingBufferFd(DvrPose* client,
+                                  android::pdx::LocalHandle* fd);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h b/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h
new file mode 100644
index 0000000..b2ebd95
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_DVR_SENSOR_IPC_H_
+#define ANDROID_DVR_SENSOR_IPC_H_
+
+#define DVR_SENSOR_SERVICE_BASE "system/vr/sensors"
+
+#define DVR_SENSOR_SERVICE_CLIENT (DVR_SENSOR_SERVICE_BASE "/client")
+
+/*
+ * Endpoint ops
+ */
+enum {
+  DVR_SENSOR_START = 0,
+  DVR_SENSOR_STOP,
+  DVR_SENSOR_POLL,
+};
+
+#endif  // ANDROID_DVR_SENSOR_IPC_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor_client.h b/libs/vr/libvrsensor/include/private/dvr/sensor_client.h
new file mode 100644
index 0000000..15a9b8f
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/sensor_client.h
@@ -0,0 +1,37 @@
+#ifndef ANDROID_DVR_SENSOR_CLIENT_H_
+#define ANDROID_DVR_SENSOR_CLIENT_H_
+
+#include <hardware/sensors.h>
+#include <pdx/client.h>
+#include <poll.h>
+
+namespace android {
+namespace dvr {
+
+// SensorClient is a remote interface to the sensor service in sensord.
+class SensorClient : public pdx::ClientBase<SensorClient> {
+ public:
+  ~SensorClient();
+
+  int StartSensor();
+  int StopSensor();
+  int Poll(sensors_event_t* events, int max_count);
+
+ private:
+  friend BASE;
+
+  // Set up a channel associated with the sensor of the indicated type.
+  // NOTE(segal): If our hardware ends up with multiple sensors of the same
+  // type, we'll have to change this.
+  explicit SensorClient(int sensor_type);
+
+  int sensor_type_;
+
+  SensorClient(const SensorClient&);
+  SensorClient& operator=(const SensorClient&);
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSOR_CLIENT_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h b/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h
new file mode 100644
index 0000000..8fa87b3
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h
@@ -0,0 +1,23 @@
+#ifndef ANDROID_DVR_SENSOR_CONSTANTS_H_
+#define ANDROID_DVR_SENSOR_CONSTANTS_H_
+
+namespace android {
+namespace dvr {
+
+// Number of elements in the async pose buffer.
+// Must be power of two.
+// Macro so that shader code can easily include this value.
+#define kPoseAsyncBufferTotalCount 8
+
+// Mask for accessing the current ring buffer array element:
+// index = vsync_count & kPoseAsyncBufferIndexMask
+constexpr uint32_t kPoseAsyncBufferIndexMask = kPoseAsyncBufferTotalCount - 1;
+
+// Number of pose frames including the current frame that are kept updated with
+// pose forecast data. The other poses are left their last known estimates.
+constexpr uint32_t kPoseAsyncBufferMinFutureCount = 4;
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSOR_CONSTANTS_H_
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
new file mode 100644
index 0000000..9eae3aa
--- /dev/null
+++ b/libs/vr/libvrsensor/pose_client.cpp
@@ -0,0 +1,338 @@
+#define LOG_TAG "PoseClient"
+#include <dvr/pose_client.h>
+
+#include <stdint.h>
+
+#include <log/log.h>
+#include <pdx/client.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/pose-ipc.h>
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/sensor_constants.h>
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Status;
+using android::pdx::Transaction;
+
+#define arraysize(x) (static_cast<int32_t>(std::extent<decltype(x)>::value))
+
+namespace android {
+namespace dvr {
+
+// PoseClient is a remote interface to the pose service in sensord.
+class PoseClient : public pdx::ClientBase<PoseClient> {
+ public:
+  ~PoseClient() override {}
+
+  // Casts C handle into an instance of this class.
+  static PoseClient* FromC(DvrPose* client) {
+    return reinterpret_cast<PoseClient*>(client);
+  }
+
+  // Polls the pose service for the current state and stores it in *state.
+  // Returns zero on success, a negative error code otherwise.
+  int Poll(DvrPoseState* state) {
+    Transaction trans{*this};
+    Status<int> status =
+        trans.Send<int>(DVR_POSE_POLL, nullptr, 0, state, sizeof(*state));
+    ALOGE_IF(!status, "Pose poll() failed because: %s\n",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  int GetPose(uint32_t vsync_count, DvrPoseAsync* out_pose) {
+    if (!mapped_pose_buffer_) {
+      int ret = GetRingBuffer(nullptr);
+      if (ret < 0)
+        return ret;
+    }
+    *out_pose =
+        mapped_pose_buffer_->ring[vsync_count & kPoseAsyncBufferIndexMask];
+    return 0;
+  }
+
+  uint32_t GetVsyncCount() {
+    if (!mapped_pose_buffer_) {
+      int ret = GetRingBuffer(nullptr);
+      if (ret < 0)
+        return 0;
+    }
+    return mapped_pose_buffer_->vsync_count;
+  }
+
+  int GetControllerPose(int32_t controller_id, uint32_t vsync_count,
+                        DvrPoseAsync* out_pose) {
+    if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
+      return -EINVAL;
+    }
+    if (!controllers_[controller_id].mapped_pose_buffer) {
+      int ret = GetControllerRingBuffer(controller_id);
+      if (ret < 0)
+        return ret;
+    }
+    *out_pose =
+        controllers_[controller_id]
+            .mapped_pose_buffer[vsync_count & kPoseAsyncBufferIndexMask];
+    return 0;
+  }
+
+  int LogController(bool enable) {
+    Transaction trans{*this};
+    Status<int> status = trans.Send<int>(DVR_POSE_LOG_CONTROLLER, &enable,
+                                         sizeof(enable), nullptr, 0);
+    ALOGE_IF(!status, "Pose LogController() failed because: %s",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  // Freezes the pose to the provided state. Future poll operations will return
+  // this state until a different state is frozen or SetMode() is called with a
+  // different mode.
+  // Returns zero on success, a negative error code otherwise.
+  int Freeze(const DvrPoseState& frozen_state) {
+    Transaction trans{*this};
+    Status<int> status = trans.Send<int>(DVR_POSE_FREEZE, &frozen_state,
+                                         sizeof(frozen_state), nullptr, 0);
+    ALOGE_IF(!status, "Pose Freeze() failed because: %s\n",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  // Sets the data mode for the pose service.
+  int SetMode(DvrPoseMode mode) {
+    Transaction trans{*this};
+    Status<int> status =
+        trans.Send<int>(DVR_POSE_SET_MODE, &mode, sizeof(mode), nullptr, 0);
+    ALOGE_IF(!status, "Pose SetPoseMode() failed because: %s",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  // Gets the data mode for the pose service.
+  int GetMode(DvrPoseMode* out_mode) {
+    int mode;
+    Transaction trans{*this};
+    Status<int> status =
+        trans.Send<int>(DVR_POSE_GET_MODE, nullptr, 0, &mode, sizeof(mode));
+    ALOGE_IF(!status, "Pose GetPoseMode() failed because: %s",
+             status.GetErrorMessage().c_str());
+    if (status)
+      *out_mode = DvrPoseMode(mode);
+    return ReturnStatusOrError(status);
+  }
+
+  int GetRingBuffer(DvrPoseRingBufferInfo* out_info) {
+    if (pose_buffer_.get()) {
+      if (out_info) {
+        GetPoseRingBufferInfo(out_info);
+      }
+      return 0;
+    }
+
+    Transaction trans{*this};
+    Status<LocalChannelHandle> status =
+        trans.Send<LocalChannelHandle>(DVR_POSE_GET_RING_BUFFER);
+    if (!status) {
+      ALOGE("Pose GetRingBuffer() failed because: %s",
+            status.GetErrorMessage().c_str());
+      return -status.error();
+    }
+
+    auto buffer = BufferConsumer::Import(status.take());
+    if (!buffer) {
+      ALOGE("Pose failed to import ring buffer");
+      return -EIO;
+    }
+    void* addr = nullptr;
+    int ret = buffer->GetBlobReadOnlyPointer(sizeof(DvrPoseRingBuffer), &addr);
+    if (ret < 0 || !addr) {
+      ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
+      return -EIO;
+    }
+    pose_buffer_.swap(buffer);
+    mapped_pose_buffer_ = static_cast<const DvrPoseRingBuffer*>(addr);
+    ALOGI("Mapped pose data translation %f,%f,%f quat %f,%f,%f,%f",
+          mapped_pose_buffer_->ring[0].translation[0],
+          mapped_pose_buffer_->ring[0].translation[1],
+          mapped_pose_buffer_->ring[0].translation[2],
+          mapped_pose_buffer_->ring[0].orientation[0],
+          mapped_pose_buffer_->ring[0].orientation[1],
+          mapped_pose_buffer_->ring[0].orientation[2],
+          mapped_pose_buffer_->ring[0].orientation[3]);
+    if (out_info) {
+      GetPoseRingBufferInfo(out_info);
+    }
+    return 0;
+  }
+
+  int GetControllerRingBuffer(int32_t controller_id) {
+    if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
+      return -EINVAL;
+    }
+    ControllerClientState& client_state = controllers_[controller_id];
+    if (client_state.pose_buffer.get()) {
+      return 0;
+    }
+
+    Transaction trans{*this};
+    Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
+        DVR_POSE_GET_CONTROLLER_RING_BUFFER, &controller_id,
+        sizeof(controller_id), nullptr, 0);
+    if (!status) {
+      return -status.error();
+    }
+
+    auto buffer = BufferConsumer::Import(status.take());
+    if (!buffer) {
+      ALOGE("Pose failed to import ring buffer");
+      return -EIO;
+    }
+    constexpr size_t size = kPoseAsyncBufferTotalCount * sizeof(DvrPoseAsync);
+    void* addr = nullptr;
+    int ret = buffer->GetBlobReadOnlyPointer(size, &addr);
+    if (ret < 0 || !addr) {
+      ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
+      return -EIO;
+    }
+    client_state.pose_buffer.swap(buffer);
+    client_state.mapped_pose_buffer = static_cast<const DvrPoseAsync*>(addr);
+    ALOGI(
+        "Mapped controller %d pose data translation %f,%f,%f quat %f,%f,%f,%f",
+        controller_id, client_state.mapped_pose_buffer[0].translation[0],
+        client_state.mapped_pose_buffer[0].translation[1],
+        client_state.mapped_pose_buffer[0].translation[2],
+        client_state.mapped_pose_buffer[0].orientation[0],
+        client_state.mapped_pose_buffer[0].orientation[1],
+        client_state.mapped_pose_buffer[0].orientation[2],
+        client_state.mapped_pose_buffer[0].orientation[3]);
+    return 0;
+  }
+
+  int NotifyVsync(uint32_t vsync_count, int64_t display_timestamp,
+                  int64_t display_period_ns,
+                  int64_t right_eye_photon_offset_ns) {
+    const struct iovec data[] = {
+        {.iov_base = &vsync_count, .iov_len = sizeof(vsync_count)},
+        {.iov_base = &display_timestamp, .iov_len = sizeof(display_timestamp)},
+        {.iov_base = &display_period_ns, .iov_len = sizeof(display_period_ns)},
+        {.iov_base = &right_eye_photon_offset_ns,
+         .iov_len = sizeof(right_eye_photon_offset_ns)},
+    };
+    Transaction trans{*this};
+    Status<int> status =
+        trans.SendVector<int>(DVR_POSE_NOTIFY_VSYNC, data, nullptr);
+    ALOGE_IF(!status, "Pose NotifyVsync() failed because: %s\n",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  int GetRingBufferFd(LocalHandle* fd) {
+    int ret = GetRingBuffer(nullptr);
+    if (ret < 0)
+      return ret;
+    *fd = pose_buffer_->GetBlobFd();
+    return 0;
+  }
+
+ private:
+  friend BASE;
+
+  // Set up a channel to the pose service.
+  PoseClient()
+      : BASE(pdx::default_transport::ClientChannelFactory::Create(
+            DVR_POSE_SERVICE_CLIENT)) {
+    // TODO(eieio): Cache the pose and make timeout 0 so that the API doesn't
+    // block while waiting for the pose service to come back up.
+    EnableAutoReconnect(kInfiniteTimeout);
+  }
+
+  PoseClient(const PoseClient&) = delete;
+  PoseClient& operator=(const PoseClient&) = delete;
+
+  void GetPoseRingBufferInfo(DvrPoseRingBufferInfo* out_info) const {
+    out_info->min_future_count = kPoseAsyncBufferMinFutureCount;
+    out_info->total_count = kPoseAsyncBufferTotalCount;
+    out_info->buffer = mapped_pose_buffer_->ring;
+  }
+
+  std::unique_ptr<BufferConsumer> pose_buffer_;
+  const DvrPoseRingBuffer* mapped_pose_buffer_ = nullptr;
+
+  struct ControllerClientState {
+    std::unique_ptr<BufferConsumer> pose_buffer;
+    const DvrPoseAsync* mapped_pose_buffer = nullptr;
+  };
+  ControllerClientState controllers_[2];
+};
+
+}  // namespace dvr
+}  // namespace android
+
+using android::dvr::PoseClient;
+
+struct DvrPose {};
+
+extern "C" {
+
+DvrPose* dvrPoseCreate() {
+  PoseClient* client = PoseClient::Create().release();
+  return reinterpret_cast<DvrPose*>(client);
+}
+
+void dvrPoseDestroy(DvrPose* client) { delete PoseClient::FromC(client); }
+
+int dvrPoseGet(DvrPose* client, uint32_t vsync_count, DvrPoseAsync* out_pose) {
+  return PoseClient::FromC(client)->GetPose(vsync_count, out_pose);
+}
+
+uint32_t dvrPoseGetVsyncCount(DvrPose* client) {
+  return PoseClient::FromC(client)->GetVsyncCount();
+}
+
+int dvrPoseGetController(DvrPose* client, int32_t controller_id,
+                         uint32_t vsync_count, DvrPoseAsync* out_pose) {
+  return PoseClient::FromC(client)->GetControllerPose(controller_id,
+                                                      vsync_count, out_pose);
+}
+
+int dvrPoseLogController(DvrPose* client, bool enable) {
+  return PoseClient::FromC(client)->LogController(enable);
+}
+
+int dvrPosePoll(DvrPose* client, DvrPoseState* state) {
+  return PoseClient::FromC(client)->Poll(state);
+}
+
+int dvrPoseFreeze(DvrPose* client, const DvrPoseState* frozen_state) {
+  return PoseClient::FromC(client)->Freeze(*frozen_state);
+}
+
+int dvrPoseSetMode(DvrPose* client, DvrPoseMode mode) {
+  return PoseClient::FromC(client)->SetMode(mode);
+}
+
+int dvrPoseGetMode(DvrPose* client, DvrPoseMode* mode) {
+  return PoseClient::FromC(client)->GetMode(mode);
+}
+
+int dvrPoseGetRingBuffer(DvrPose* client, DvrPoseRingBufferInfo* out_info) {
+  return PoseClient::FromC(client)->GetRingBuffer(out_info);
+}
+
+int privateDvrPoseNotifyVsync(DvrPose* client, uint32_t vsync_count,
+                              int64_t display_timestamp,
+                              int64_t display_period_ns,
+                              int64_t right_eye_photon_offset_ns) {
+  return PoseClient::FromC(client)->NotifyVsync(vsync_count, display_timestamp,
+                                                display_period_ns,
+                                                right_eye_photon_offset_ns);
+}
+
+int privateDvrPoseGetRingBufferFd(DvrPose* client, LocalHandle* fd) {
+  return PoseClient::FromC(client)->GetRingBufferFd(fd);
+}
+
+}  // extern "C"
diff --git a/libs/vr/libvrsensor/sensor_client.cpp b/libs/vr/libvrsensor/sensor_client.cpp
new file mode 100644
index 0000000..04e88cc
--- /dev/null
+++ b/libs/vr/libvrsensor/sensor_client.cpp
@@ -0,0 +1,79 @@
+#define LOG_TAG "SensorClient"
+#include <private/dvr/sensor_client.h>
+
+#include <log/log.h>
+#include <poll.h>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/sensor-ipc.h>
+
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+
+SensorClient::SensorClient(int sensor_type)
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+          DVR_SENSOR_SERVICE_CLIENT)),
+      sensor_type_(sensor_type) {}
+
+SensorClient::~SensorClient() {}
+
+int SensorClient::StartSensor() {
+  Transaction trans{*this};
+  auto status = trans.Send<int>(DVR_SENSOR_START, &sensor_type_,
+                                sizeof(sensor_type_), nullptr, 0);
+  ALOGE_IF(!status, "startSensor() failed because: %s\n",
+           status.GetErrorMessage().c_str());
+  return ReturnStatusOrError(status);
+}
+
+int SensorClient::StopSensor() {
+  Transaction trans{*this};
+  auto status = trans.Send<int>(DVR_SENSOR_STOP);
+  ALOGE_IF(!status, "stopSensor() failed because: %s\n",
+           status.GetErrorMessage().c_str());
+  return ReturnStatusOrError(status);
+}
+
+int SensorClient::Poll(sensors_event_t* events, int max_events) {
+  int num_events = 0;
+  struct iovec rvec[] = {
+      {.iov_base = &num_events, .iov_len = sizeof(int)},
+      {.iov_base = events, .iov_len = max_events * sizeof(sensors_event_t)},
+  };
+  Transaction trans{*this};
+  auto status = trans.SendVector<int>(DVR_SENSOR_POLL, nullptr, rvec);
+  ALOGE_IF(!status, "Sensor poll() failed because: %s\n",
+           status.GetErrorMessage().c_str());
+  return !status ? -status.error() : num_events;
+}
+
+}  // namespace dvr
+}  // namespace android
+
+// Entrypoints to simplify using the library when programmatically dynamicly
+// loading it.
+// Allows us to call this library without linking it, as, for instance,
+// when compiling GVR in Google3.
+// NOTE(segal): It's kind of a hack.
+
+extern "C" uint64_t dvrStartSensor(int type) {
+  android::dvr::SensorClient* service =
+      android::dvr::SensorClient::Create(type).release();
+  service->StartSensor();
+  return (uint64_t)service;
+}
+
+extern "C" void dvrStopSensor(uint64_t service) {
+  android::dvr::SensorClient* iss =
+      reinterpret_cast<android::dvr::SensorClient*>(service);
+  iss->StopSensor();
+  delete iss;
+}
+
+extern "C" int dvrPollSensor(uint64_t service, int max_count,
+                             sensors_event_t* events) {
+  return reinterpret_cast<android::dvr::SensorClient*>(service)->Poll(
+      events, max_count);
+}
diff --git a/libs/vr/libvrsensor/tests/sensor_app_tests.cpp b/libs/vr/libvrsensor/tests/sensor_app_tests.cpp
new file mode 100644
index 0000000..64c9864
--- /dev/null
+++ b/libs/vr/libvrsensor/tests/sensor_app_tests.cpp
@@ -0,0 +1,132 @@
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <math.h>
+#include <inttypes.h>
+
+#include <dvr/graphics.h>
+#include <dvr/pose_client.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <private/dvr/types.h>
+
+using android::dvr::vec4;
+
+namespace {
+
+vec4 ToVec4(float32x4_t rhs) { return vec4(rhs[0], rhs[1], rhs[2], rhs[3]); }
+
+}
+
+DvrGraphicsContext* CreateContext() {
+  DvrGraphicsContext* context = nullptr;
+  int display_width = 0, display_height = 0;
+  int surface_width = 0, surface_height = 0;
+  float inter_lens_meters = 0.0f;
+  float left_fov[4] = {0.0f};
+  float right_fov[4] = {0.0f};
+  int disable_warp = 0;
+  DvrSurfaceParameter surface_params[] = {
+      DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, disable_warp),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_WIDTH, &display_width),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_HEIGHT, &display_height),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &surface_width),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &surface_height),
+      DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &inter_lens_meters),
+      DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, left_fov),
+      DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov),
+      DVR_SURFACE_PARAMETER_LIST_END,
+  };
+  dvrGraphicsContextCreate(surface_params, &context);
+  return context;
+}
+
+TEST(SensorAppTests, GetPose) {
+  DvrGraphicsContext* context = CreateContext();
+  ASSERT_NE(nullptr, context);
+  DvrPose* client = dvrPoseCreate();
+  ASSERT_NE(nullptr, client);
+
+  DvrPoseAsync last_pose;
+  uint32_t last_vsync_count = 0;
+  for (int i = 0; i < 10; ++i) {
+    DvrFrameSchedule schedule;
+    dvrGraphicsWaitNextFrame(context, 0, &schedule);
+    DvrPoseAsync pose;
+    int ret = dvrPoseGet(client, schedule.vsync_count, &pose);
+    ASSERT_EQ(0, ret);
+
+    // Check for unit-length quaternion to verify valid pose.
+    vec4 quaternion = ToVec4(pose.orientation);
+    float length = quaternion.norm();
+    EXPECT_GT(0.001, fabs(1.0f - length));
+
+    // Check for different data each frame, but skip first few to allow
+    // startup anomalies.
+    if (i > 0) {
+      if (last_vsync_count == schedule.vsync_count)
+        ALOGE("vsync did not increment: %u", schedule.vsync_count);
+      if (pose.timestamp_ns == last_pose.timestamp_ns)
+        ALOGE("timestamp did not change: %" PRIu64, pose.timestamp_ns);
+      // TODO(jbates) figure out why the bots are not passing this check.
+      // EXPECT_NE(last_vsync_count, schedule.vsync_count);
+      // EXPECT_NE(pose.timestamp_ns, last_pose.timestamp_ns);
+    }
+    last_pose = pose;
+    last_vsync_count = schedule.vsync_count;
+    dvrBeginRenderFrame(context);
+    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    dvrPresent(context);
+  }
+
+  dvrPoseDestroy(client);
+  dvrGraphicsContextDestroy(context);
+}
+
+TEST(SensorAppTests, PoseRingBuffer) {
+  DvrGraphicsContext* context = CreateContext();
+  ASSERT_NE(nullptr, context);
+  DvrPose* client = dvrPoseCreate();
+  ASSERT_NE(nullptr, client);
+
+  DvrPoseRingBufferInfo info;
+  int ret = dvrPoseGetRingBuffer(client, &info);
+  ASSERT_EQ(0, ret);
+  ASSERT_NE(nullptr, info.buffer);
+  EXPECT_LE(2u, info.min_future_count);
+  EXPECT_LE(8u, info.total_count);
+
+  DvrPoseAsync last_pose;
+  uint32_t last_vsync_count = 0;
+  for (int i = 0; i < 10; ++i) {
+    DvrFrameSchedule schedule;
+    dvrGraphicsWaitNextFrame(context, 0, &schedule);
+    DvrPoseAsync pose;
+    ret = dvrPoseGet(client, schedule.vsync_count, &pose);
+    ASSERT_EQ(0, ret);
+
+    // Check for unit-length quaternion to verify valid pose.
+    vec4 quaternion = ToVec4(pose.orientation);
+    float length = quaternion.norm();
+    EXPECT_GT(0.001, fabs(1.0f - length));
+
+    // Check for different data each frame, but skip first few to allow
+    // startup anomalies.
+    if (i > 0) {
+      if (last_vsync_count == schedule.vsync_count)
+        ALOGE("vsync did not increment: %u", schedule.vsync_count);
+      if (pose.timestamp_ns == last_pose.timestamp_ns)
+        ALOGE("timestamp did not change: %" PRIu64, pose.timestamp_ns);
+      // TODO(jbates) figure out why the bots are not passing this check.
+      // EXPECT_NE(last_vsync_count, schedule.vsync_count);
+      // EXPECT_NE(pose.timestamp_ns, last_pose.timestamp_ns);
+    }
+    last_pose = pose;
+    last_vsync_count = schedule.vsync_count;
+    dvrBeginRenderFrame(context);
+    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    dvrPresent(context);
+  }
+
+  dvrPoseDestroy(client);
+  dvrGraphicsContextDestroy(context);
+}