Merge "Re-add mistakenly removed lock in SurfaceFlinger::removeLayer."
diff --git a/libs/vr/libdisplay/Android.mk b/libs/vr/libdisplay/Android.mk
index 6a9458c..60f73c6 100644
--- a/libs/vr/libdisplay/Android.mk
+++ b/libs/vr/libdisplay/Android.mk
@@ -15,7 +15,6 @@
 LOCAL_PATH := $(call my-dir)
 
 sourceFiles := \
-	native_window.cpp \
 	native_buffer_queue.cpp \
 	display_client.cpp \
 	display_manager_client.cpp \
@@ -72,8 +71,7 @@
 LOCAL_MODULE := libdisplay
 include $(BUILD_STATIC_LIBRARY)
 
-
-testFiles := \
+graphicsAppTestFiles := \
   tests/graphics_app_tests.cpp
 
 include $(CLEAR_VARS)
@@ -81,7 +79,7 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := \
-  $(testFiles) \
+  $(graphicsAppTestFiles) \
 
 LOCAL_C_INCLUDES := \
   $(includeFiles) \
@@ -94,3 +92,25 @@
   $(staticLibraries) \
 
 include $(BUILD_NATIVE_TEST)
+
+dummyNativeWindowTestFiles := \
+  tests/dummy_native_window_tests.cpp \
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dummy_native_window_tests
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+  $(dummyNativeWindowTestFiles) \
+
+LOCAL_C_INCLUDES := \
+  $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := \
+  $(sharedLibraries) \
+
+LOCAL_STATIC_LIBRARIES := \
+  libdisplay \
+  $(staticLibraries) \
+include $(BUILD_NATIVE_TEST)
+
diff --git a/libs/vr/libdisplay/dummy_native_window.cpp b/libs/vr/libdisplay/dummy_native_window.cpp
index 5547f53..4628b8e 100644
--- a/libs/vr/libdisplay/dummy_native_window.cpp
+++ b/libs/vr/libdisplay/dummy_native_window.cpp
@@ -30,6 +30,11 @@
 
 int DummyNativeWindow::Query(const ANativeWindow*, int what, int* value) {
   switch (what) {
+    // This must be 1 in order for eglCreateWindowSurface to not trigger an
+    // error
+    case NATIVE_WINDOW_IS_VALID:
+      *value = 1;
+      return NO_ERROR;
     case NATIVE_WINDOW_WIDTH:
     case NATIVE_WINDOW_HEIGHT:
     case NATIVE_WINDOW_FORMAT:
diff --git a/libs/vr/libdisplay/graphics.cpp b/libs/vr/libdisplay/graphics.cpp
index d0557a9..61f6fea 100644
--- a/libs/vr/libdisplay/graphics.cpp
+++ b/libs/vr/libdisplay/graphics.cpp
@@ -483,25 +483,6 @@
   return 0;
 }
 
-extern "C" int dvrGetDisplaySurfaceInfo(EGLNativeWindowType win, int* width,
-                                        int* height, int* format) {
-  ANativeWindow* nwin = reinterpret_cast<ANativeWindow*>(win);
-  int w, h, f;
-
-  nwin->query(nwin, NATIVE_WINDOW_DEFAULT_WIDTH, &w);
-  nwin->query(nwin, NATIVE_WINDOW_DEFAULT_HEIGHT, &h);
-  nwin->query(nwin, NATIVE_WINDOW_FORMAT, &f);
-
-  if (width)
-    *width = w;
-  if (height)
-    *height = h;
-  if (format)
-    *format = f;
-
-  return 0;
-}
-
 struct DvrGraphicsContext : public android::ANativeObjectBase<
                                 ANativeWindow, DvrGraphicsContext,
                                 android::LightRefBase<DvrGraphicsContext>> {
diff --git a/libs/vr/libdisplay/include/dvr/graphics.h b/libs/vr/libdisplay/include/dvr/graphics.h
index 50d2754..19deec3 100644
--- a/libs/vr/libdisplay/include/dvr/graphics.h
+++ b/libs/vr/libdisplay/include/dvr/graphics.h
@@ -21,11 +21,6 @@
 
 __BEGIN_DECLS
 
-// Create a stereo surface that will be lens-warped by the system.
-EGLNativeWindowType dvrCreateWarpedDisplaySurface(int* display_width,
-                                                  int* display_height);
-EGLNativeWindowType dvrCreateDisplaySurface(void);
-
 // Display surface parameters used to specify display surface options.
 enum {
   DVR_SURFACE_PARAMETER_NONE = 0,
@@ -163,9 +158,16 @@
   float right_fov[4];
 };
 
-// Creates a display surface with the given parameters. The list of parameters
-// is terminated with an entry where key == DVR_SURFACE_PARAMETER_NONE.
-// For example, the parameters array could be built as follows:
+int dvrGetNativeDisplayDimensions(int* native_width, int* native_height);
+
+// Opaque struct that represents a graphics context, the texture swap chain,
+// and surfaces.
+typedef struct DvrGraphicsContext DvrGraphicsContext;
+
+// Create the graphics context. with the given parameters. The list of
+// parameters is terminated with an entry where key ==
+// DVR_SURFACE_PARAMETER_NONE. For example, the parameters array could be built
+// as follows:
 //   int display_width = 0, display_height = 0;
 //   int surface_width = 0, surface_height = 0;
 //   float inter_lens_meters = 0.0f;
@@ -183,38 +185,6 @@
 //       DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov),
 //       DVR_SURFACE_PARAMETER_LIST_END,
 //   };
-EGLNativeWindowType dvrCreateDisplaySurfaceExtended(
-    struct DvrSurfaceParameter* parameters);
-
-int dvrGetNativeDisplayDimensions(int* native_width, int* native_height);
-
-int dvrGetDisplaySurfaceInfo(EGLNativeWindowType win, int* width, int* height,
-                             int* format);
-
-// NOTE: Only call the functions below on windows created with the API above.
-
-// Sets the display surface visible based on the boolean evaluation of
-// |visible|.
-void dvrDisplaySurfaceSetVisible(EGLNativeWindowType window, int visible);
-
-// Sets the application z-order of the display surface. Higher values display on
-// top of lower values.
-void dvrDisplaySurfaceSetZOrder(EGLNativeWindowType window, int z_order);
-
-// Post the next buffer early. This allows the application to race with either
-// the async EDS process or the scanline for applications that are not using
-// system distortion. When this is called, the next buffer in the queue is
-// posted for display. It is up to the application to kick its GPU rendering
-// work in time. If the rendering is incomplete there will be significant,
-// undesirable tearing artifacts.
-// It is not recommended to use this feature with system distortion.
-void dvrDisplayPostEarly(EGLNativeWindowType window);
-
-// Opaque struct that represents a graphics context, the texture swap chain,
-// and surfaces.
-typedef struct DvrGraphicsContext DvrGraphicsContext;
-
-// Create the graphics context.
 int dvrGraphicsContextCreate(struct DvrSurfaceParameter* parameters,
                              DvrGraphicsContext** return_graphics_context);
 
diff --git a/libs/vr/libdisplay/native_window.cpp b/libs/vr/libdisplay/native_window.cpp
deleted file mode 100644
index 24ecd8a..0000000
--- a/libs/vr/libdisplay/native_window.cpp
+++ /dev/null
@@ -1,458 +0,0 @@
-#include <EGL/egl.h>
-
-#include <android/native_window.h>
-#include <cutils/native_handle.h>
-#include <errno.h>
-#include <pthread.h>
-#include <semaphore.h>
-#include <stdarg.h>
-#include <string.h>
-#include <sys/timerfd.h>
-#include <system/window.h>
-#include <time.h>
-#include <ui/ANativeObjectBase.h>
-#include <utils/Errors.h>
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <utils/Trace.h>
-
-#include <log/log.h>
-
-#include <memory>
-#include <mutex>
-
-#include <dvr/graphics.h>
-#include <private/dvr/clock_ns.h>
-#include <private/dvr/display_client.h>
-#include <private/dvr/native_buffer.h>
-#include <private/dvr/native_buffer_queue.h>
-
-namespace {
-
-constexpr int kDefaultDisplaySurfaceUsage =
-    GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
-constexpr int kDefaultDisplaySurfaceFormat = HAL_PIXEL_FORMAT_RGBA_8888;
-constexpr int kWarpedDisplaySurfaceFlags = 0;
-constexpr int kUnwarpedDisplaySurfaceFlags =
-    DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS |
-    DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION |
-    DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC;
-constexpr int kDefaultBufferCount = 4;
-
-}  // anonymous namespace
-
-namespace android {
-namespace dvr {
-
-// NativeWindow is an implementation of ANativeWindow. This class interacts with
-// displayd through the DisplaySurfaceClient and NativeBufferQueue.
-class NativeWindow : public ANativeObjectBase<ANativeWindow, NativeWindow,
-                                              LightRefBase<NativeWindow> > {
- public:
-  explicit NativeWindow(const std::shared_ptr<DisplaySurfaceClient>& surface);
-
-  void SetVisible(bool visible);
-  void SetZOrder(int z_order);
-  void PostEarly();
-
- private:
-  friend class LightRefBase<NativeWindow>;
-
-  void Post(sp<NativeBufferProducer> buffer, int fence_fd);
-
-  static int SetSwapInterval(ANativeWindow* window, int interval);
-  static int DequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
-                           int* fence_fd);
-  static int QueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
-                         int fence_fd);
-  static int CancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
-                          int fence_fd);
-  static int Query(const ANativeWindow* window, int what, int* value);
-  static int Perform(ANativeWindow* window, int operation, ...);
-
-  static int DequeueBuffer_DEPRECATED(ANativeWindow* window,
-                                      ANativeWindowBuffer** buffer);
-  static int CancelBuffer_DEPRECATED(ANativeWindow* window,
-                                     ANativeWindowBuffer* buffer);
-  static int QueueBuffer_DEPRECATED(ANativeWindow* window,
-                                    ANativeWindowBuffer* buffer);
-  static int LockBuffer_DEPRECATED(ANativeWindow* window,
-                                   ANativeWindowBuffer* buffer);
-
-  std::shared_ptr<DisplaySurfaceClient> surface_;
-
-  std::mutex lock_;
-  NativeBufferQueue buffer_queue_;
-  sp<NativeBufferProducer> next_post_buffer_;
-  bool next_buffer_already_posted_;
-
-  NativeWindow(const NativeWindow&) = delete;
-  void operator=(NativeWindow&) = delete;
-};
-
-NativeWindow::NativeWindow(const std::shared_ptr<DisplaySurfaceClient>& surface)
-    : surface_(surface),
-      buffer_queue_(surface, kDefaultBufferCount),
-      next_post_buffer_(nullptr),
-      next_buffer_already_posted_(false) {
-  ANativeWindow::setSwapInterval = SetSwapInterval;
-  ANativeWindow::dequeueBuffer = DequeueBuffer;
-  ANativeWindow::cancelBuffer = CancelBuffer;
-  ANativeWindow::queueBuffer = QueueBuffer;
-  ANativeWindow::query = Query;
-  ANativeWindow::perform = Perform;
-
-  ANativeWindow::dequeueBuffer_DEPRECATED = DequeueBuffer_DEPRECATED;
-  ANativeWindow::cancelBuffer_DEPRECATED = CancelBuffer_DEPRECATED;
-  ANativeWindow::lockBuffer_DEPRECATED = LockBuffer_DEPRECATED;
-  ANativeWindow::queueBuffer_DEPRECATED = QueueBuffer_DEPRECATED;
-}
-
-void NativeWindow::SetVisible(bool visible) { surface_->SetVisible(visible); }
-
-void NativeWindow::SetZOrder(int z_order) { surface_->SetZOrder(z_order); }
-
-void NativeWindow::PostEarly() {
-  ATRACE_NAME("NativeWindow::PostEarly");
-  ALOGI_IF(TRACE, "NativeWindow::PostEarly");
-
-  std::lock_guard<std::mutex> autolock(lock_);
-
-  if (!next_buffer_already_posted_) {
-    next_buffer_already_posted_ = true;
-
-    if (!next_post_buffer_.get()) {
-      next_post_buffer_ = buffer_queue_.Dequeue();
-    }
-    ATRACE_ASYNC_BEGIN("BufferPost", next_post_buffer_->buffer()->id());
-    Post(next_post_buffer_, -1);
-  }
-}
-
-void NativeWindow::Post(sp<NativeBufferProducer> buffer, int fence_fd) {
-  ATRACE_NAME(__PRETTY_FUNCTION__);
-  ALOGI_IF(TRACE, "NativeWindow::Post: buffer_id=%d, fence_fd=%d",
-           buffer->buffer()->id(), fence_fd);
-  ALOGW_IF(!surface_->visible(),
-           "NativeWindow::Post: Posting buffer on invisible surface!!!");
-  buffer->Post(fence_fd, 0);
-}
-
-int NativeWindow::SetSwapInterval(ANativeWindow* window, int interval) {
-  ALOGI_IF(TRACE, "SetSwapInterval: window=%p interval=%d", window, interval);
-  return 0;
-}
-
-int NativeWindow::DequeueBuffer(ANativeWindow* window,
-                                ANativeWindowBuffer** buffer, int* fence_fd) {
-  ATRACE_NAME(__PRETTY_FUNCTION__);
-
-  NativeWindow* self = getSelf(window);
-  std::lock_guard<std::mutex> autolock(self->lock_);
-
-  if (!self->next_post_buffer_.get()) {
-    self->next_post_buffer_ = self->buffer_queue_.Dequeue();
-  }
-  ATRACE_ASYNC_BEGIN("BufferDraw", self->next_post_buffer_->buffer()->id());
-  *fence_fd = self->next_post_buffer_->ClaimReleaseFence().Release();
-  *buffer = self->next_post_buffer_.get();
-
-  ALOGI_IF(TRACE, "NativeWindow::DequeueBuffer: fence_fd=%d", *fence_fd);
-  return 0;
-}
-
-int NativeWindow::QueueBuffer(ANativeWindow* window,
-                              ANativeWindowBuffer* buffer, int fence_fd) {
-  ATRACE_NAME("NativeWindow::QueueBuffer");
-  ALOGI_IF(TRACE, "NativeWindow::QueueBuffer: fence_fd=%d", fence_fd);
-
-  NativeWindow* self = getSelf(window);
-  std::lock_guard<std::mutex> autolock(self->lock_);
-
-  NativeBufferProducer* native_buffer =
-      static_cast<NativeBufferProducer*>(buffer);
-  ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
-  bool do_post = true;
-  if (self->next_buffer_already_posted_) {
-    // Check that the buffer is the one we expect, but handle it if this happens
-    // in production by allowing this buffer to post on top of the previous one.
-    LOG_FATAL_IF(native_buffer != self->next_post_buffer_.get());
-    if (native_buffer == self->next_post_buffer_.get()) {
-      do_post = false;
-      if (fence_fd >= 0)
-        close(fence_fd);
-    }
-  }
-  if (do_post) {
-    ATRACE_ASYNC_BEGIN("BufferPost", native_buffer->buffer()->id());
-    self->Post(native_buffer, fence_fd);
-  }
-  self->next_buffer_already_posted_ = false;
-  self->next_post_buffer_ = nullptr;
-
-  return NO_ERROR;
-}
-
-int NativeWindow::CancelBuffer(ANativeWindow* window,
-                               ANativeWindowBuffer* buffer, int fence_fd) {
-  ATRACE_NAME("NativeWindow::CancelBuffer");
-  ALOGI_IF(TRACE, "NativeWindow::CancelBuffer: fence_fd: %d", fence_fd);
-
-  NativeWindow* self = getSelf(window);
-  std::lock_guard<std::mutex> autolock(self->lock_);
-
-  NativeBufferProducer* native_buffer =
-      static_cast<NativeBufferProducer*>(buffer);
-  ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
-  ATRACE_INT("CancelBuffer", native_buffer->buffer()->id());
-  bool do_enqueue = true;
-  if (self->next_buffer_already_posted_) {
-    // Check that the buffer is the one we expect, but handle it if this happens
-    // in production by returning this buffer to the buffer queue.
-    LOG_FATAL_IF(native_buffer != self->next_post_buffer_.get());
-    if (native_buffer == self->next_post_buffer_.get()) {
-      do_enqueue = false;
-    }
-  }
-  if (do_enqueue) {
-    self->buffer_queue_.Enqueue(native_buffer);
-  }
-  if (fence_fd >= 0)
-    close(fence_fd);
-  self->next_buffer_already_posted_ = false;
-  self->next_post_buffer_ = nullptr;
-
-  return NO_ERROR;
-}
-
-int NativeWindow::Query(const ANativeWindow* window, int what, int* value) {
-  NativeWindow* self = getSelf(const_cast<ANativeWindow*>(window));
-  std::lock_guard<std::mutex> autolock(self->lock_);
-
-  switch (what) {
-    case NATIVE_WINDOW_WIDTH:
-      *value = self->surface_->width();
-      return NO_ERROR;
-    case NATIVE_WINDOW_HEIGHT:
-      *value = self->surface_->height();
-      return NO_ERROR;
-    case NATIVE_WINDOW_FORMAT:
-      *value = self->surface_->format();
-      return NO_ERROR;
-    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
-      *value = 1;
-      return NO_ERROR;
-    case NATIVE_WINDOW_CONCRETE_TYPE:
-      *value = NATIVE_WINDOW_SURFACE;
-      return NO_ERROR;
-    case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
-      *value = 1;
-      return NO_ERROR;
-    case NATIVE_WINDOW_DEFAULT_WIDTH:
-      *value = self->surface_->width();
-      return NO_ERROR;
-    case NATIVE_WINDOW_DEFAULT_HEIGHT:
-      *value = self->surface_->height();
-      return NO_ERROR;
-    case NATIVE_WINDOW_TRANSFORM_HINT:
-      *value = 0;
-      return NO_ERROR;
-  }
-
-  *value = 0;
-  return BAD_VALUE;
-}
-
-int NativeWindow::Perform(ANativeWindow* window, int operation, ...) {
-  NativeWindow* self = getSelf(window);
-  std::lock_guard<std::mutex> autolock(self->lock_);
-
-  va_list args;
-  va_start(args, operation);
-
-  // TODO(eieio): The following operations are not used at this time. They are
-  // included here to help document which operations may be useful and what
-  // parameters they take.
-  switch (operation) {
-    case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: {
-      int w = va_arg(args, int);
-      int h = va_arg(args, int);
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: w=%d h=%d", w, h);
-      return NO_ERROR;
-    }
-
-    case NATIVE_WINDOW_SET_BUFFERS_FORMAT: {
-      int format = va_arg(args, int);
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_FORMAT: format=%d", format);
-      return NO_ERROR;
-    }
-
-    case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: {
-      int transform = va_arg(args, int);
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: transform=%d",
-               transform);
-      return NO_ERROR;
-    }
-
-    case NATIVE_WINDOW_SET_USAGE: {
-      int usage = va_arg(args, int);
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_USAGE: usage=%d", usage);
-      return NO_ERROR;
-    }
-
-    case NATIVE_WINDOW_CONNECT:
-    case NATIVE_WINDOW_DISCONNECT:
-    case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
-    case NATIVE_WINDOW_API_CONNECT:
-    case NATIVE_WINDOW_API_DISCONNECT:
-      // TODO(eieio): we should implement these
-      return NO_ERROR;
-
-    case NATIVE_WINDOW_SET_BUFFER_COUNT: {
-      int buffer_count = va_arg(args, int);
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFER_COUNT: bufferCount=%d",
-               buffer_count);
-      return NO_ERROR;
-    }
-    case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: {
-      android_dataspace_t data_space =
-          static_cast<android_dataspace_t>(va_arg(args, int));
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DATASPACE: dataSpace=%d",
-               data_space);
-      return NO_ERROR;
-    }
-    case NATIVE_WINDOW_SET_SCALING_MODE: {
-      int mode = va_arg(args, int);
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_SCALING_MODE: mode=%d", mode);
-      return NO_ERROR;
-    }
-
-    case NATIVE_WINDOW_LOCK:
-    case NATIVE_WINDOW_UNLOCK_AND_POST:
-    case NATIVE_WINDOW_SET_CROP:
-    case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
-      return INVALID_OPERATION;
-  }
-
-  return NAME_NOT_FOUND;
-}
-
-int NativeWindow::DequeueBuffer_DEPRECATED(ANativeWindow* window,
-                                           ANativeWindowBuffer** buffer) {
-  int fence_fd = -1;
-  int ret = DequeueBuffer(window, buffer, &fence_fd);
-
-  // wait for fence
-  if (ret == NO_ERROR && fence_fd != -1)
-    close(fence_fd);
-
-  return ret;
-}
-
-int NativeWindow::CancelBuffer_DEPRECATED(ANativeWindow* window,
-                                          ANativeWindowBuffer* buffer) {
-  return CancelBuffer(window, buffer, -1);
-}
-
-int NativeWindow::QueueBuffer_DEPRECATED(ANativeWindow* window,
-                                         ANativeWindowBuffer* buffer) {
-  return QueueBuffer(window, buffer, -1);
-}
-
-int NativeWindow::LockBuffer_DEPRECATED(ANativeWindow* /*window*/,
-                                        ANativeWindowBuffer* /*buffer*/) {
-  return NO_ERROR;
-}
-
-}  // namespace dvr
-}  // namespace android
-
-static EGLNativeWindowType CreateDisplaySurface(int* display_width,
-                                                int* display_height, int format,
-                                                int usage, int flags) {
-  auto client = android::dvr::DisplayClient::Create();
-  if (!client) {
-    ALOGE("Failed to create display client!");
-    return nullptr;
-  }
-
-  // TODO(eieio,jbates): Consider passing flags and other parameters to get
-  // metrics based on specific surface requirements.
-  android::dvr::SystemDisplayMetrics metrics;
-  const int ret = client->GetDisplayMetrics(&metrics);
-  if (ret < 0) {
-    ALOGE("Failed to get display metrics: %s", strerror(-ret));
-    return nullptr;
-  }
-
-  int width, height;
-
-  if (flags & DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION) {
-    width = metrics.display_native_width;
-    height = metrics.display_native_height;
-  } else {
-    width = metrics.distorted_width;
-    height = metrics.distorted_height;
-  }
-
-  std::shared_ptr<android::dvr::DisplaySurfaceClient> surface =
-      client->CreateDisplaySurface(width, height, format, usage, flags);
-
-  if (display_width)
-    *display_width = metrics.display_native_width;
-  if (display_height)
-    *display_height = metrics.display_native_height;
-
-  // Set the surface visible by default.
-  // TODO(eieio,jbates): Remove this from here and set visible somewhere closer
-  // to the application to account for situations where the application wants to
-  // create surfaces that will be used later or shouldn't be visible yet.
-  surface->SetVisible(true);
-
-  return new android::dvr::NativeWindow(surface);
-}
-
-std::shared_ptr<android::dvr::DisplaySurfaceClient> CreateDisplaySurfaceClient(
-    struct DvrSurfaceParameter* parameters,
-    /*out*/ android::dvr::SystemDisplayMetrics* metrics);
-
-extern "C" EGLNativeWindowType dvrCreateDisplaySurfaceExtended(
-    struct DvrSurfaceParameter* parameters) {
-  android::dvr::SystemDisplayMetrics metrics;
-  auto surface = CreateDisplaySurfaceClient(parameters, &metrics);
-  if (!surface) {
-    ALOGE("Failed to create display surface client");
-    return nullptr;
-  }
-  return new android::dvr::NativeWindow(surface);
-}
-
-extern "C" EGLNativeWindowType dvrCreateDisplaySurface() {
-  return CreateDisplaySurface(NULL, NULL, kDefaultDisplaySurfaceFormat,
-                              kDefaultDisplaySurfaceUsage,
-                              kUnwarpedDisplaySurfaceFlags);
-}
-
-extern "C" EGLNativeWindowType dvrCreateWarpedDisplaySurface(
-    int* display_width, int* display_height) {
-  return CreateDisplaySurface(
-      display_width, display_height, kDefaultDisplaySurfaceFormat,
-      kDefaultDisplaySurfaceUsage, kWarpedDisplaySurfaceFlags);
-}
-
-extern "C" void dvrDisplaySurfaceSetVisible(EGLNativeWindowType window,
-                                            int visible) {
-  auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window);
-  native_window->SetVisible(visible);
-}
-
-extern "C" void dvrDisplaySurfaceSetZOrder(EGLNativeWindowType window,
-                                           int z_order) {
-  auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window);
-  native_window->SetZOrder(z_order);
-}
-
-extern "C" void dvrDisplayPostEarly(EGLNativeWindowType window) {
-  auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window);
-  native_window->PostEarly();
-}
diff --git a/libs/vr/libdisplay/native_window_test.cpp b/libs/vr/libdisplay/native_window_test.cpp
deleted file mode 100644
index 2f3bc33..0000000
--- a/libs/vr/libdisplay/native_window_test.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#include <iostream>
-#include <memory>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <dvr/graphics.h>
-#include <private/dvr/display_client.h>
-
-#include <cpp_free_mock/cpp_free_mock.h>
-
-// Checks querying the VSync of the device on display surface creation.
-TEST(CreateDisplaySurface, QueryVSyncPeriod) {
-  using ::testing::_;
-
-  const uint64_t kExpectedVSync = 123456;
-
-  // We only care about the expected VSync value
-  android::dvr::DisplayMetrics metrics;
-  metrics.vsync_period_ns = kExpectedVSync;
-
-  uint64_t outPeriod;
-
-  DvrSurfaceParameter display_params[] = {
-      DVR_SURFACE_PARAMETER_IN(WIDTH, 256),
-      DVR_SURFACE_PARAMETER_IN(HEIGHT, 256),
-      DVR_SURFACE_PARAMETER_OUT(VSYNC_PERIOD, &outPeriod),
-      DVR_SURFACE_PARAMETER_LIST_END,
-  };
-
-  // inject the mocking code to the target method
-  auto mocked_function =
-      MOCKER(&android::dvr::DisplayClient::GetDisplayMetrics);
-
-  // instrument the mock function to return our custom metrics
-  EXPECT_CALL(*mocked_function, MOCK_FUNCTION(_, _))
-      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(metrics),
-                                 ::testing::Return(0)));
-
-  ASSERT_NE(nullptr, dvrCreateDisplaySurfaceExtended(display_params));
-
-  EXPECT_EQ(kExpectedVSync, outPeriod);
-}
diff --git a/libs/vr/libdisplay/tests/dummy_native_window_tests.cpp b/libs/vr/libdisplay/tests/dummy_native_window_tests.cpp
new file mode 100644
index 0000000..5f3ff53
--- /dev/null
+++ b/libs/vr/libdisplay/tests/dummy_native_window_tests.cpp
@@ -0,0 +1,64 @@
+#include <private/dvr/dummy_native_window.h>
+#include <gtest/gtest.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+
+class DummyNativeWindowTests : public ::testing::Test {
+ public:
+  EGLDisplay display_;
+  bool initialized_;
+
+  DummyNativeWindowTests()
+      : display_(nullptr)
+      , initialized_(false)
+  {
+  }
+
+  virtual void SetUp() {
+    display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+    ASSERT_NE(nullptr, display_);
+    initialized_ = eglInitialize(display_, nullptr, nullptr);
+
+    ASSERT_TRUE(initialized_);
+  }
+
+  virtual void TearDown() {
+    if (display_ && initialized_) {
+      eglTerminate(display_);
+    }
+  }
+};
+
+// Test that eglCreateWindowSurface works with DummyNativeWindow
+TEST_F(DummyNativeWindowTests, TryCreateEglWindow) {
+  EGLint attribs[] = {
+      EGL_NONE,
+  };
+
+  EGLint num_configs;
+  EGLConfig config;
+  ASSERT_TRUE(eglChooseConfig(display_, attribs, &config, 1, &num_configs));
+
+  std::unique_ptr<android::dvr::DummyNativeWindow> dummy_window(
+      new android::dvr::DummyNativeWindow());
+
+  EGLint context_attribs[] = {
+    EGL_NONE,
+  };
+
+  EGLSurface surface = eglCreateWindowSurface(display_, config,
+                                              dummy_window.get(),
+                                              context_attribs);
+
+  EXPECT_NE(nullptr, surface);
+
+  bool destroyed = eglDestroySurface(display_, surface);
+
+  EXPECT_TRUE(destroyed);
+}
+
diff --git a/libs/vr/libdisplay/tests/graphics_app_tests.cpp b/libs/vr/libdisplay/tests/graphics_app_tests.cpp
index 7ea3952..f51dd8a 100644
--- a/libs/vr/libdisplay/tests/graphics_app_tests.cpp
+++ b/libs/vr/libdisplay/tests/graphics_app_tests.cpp
@@ -1,55 +1,6 @@
 #include <dvr/graphics.h>
 #include <gtest/gtest.h>
 
-TEST(GraphicsAppTests, CreateWarpedDisplaySurfaceParams) {
-  int width = 0, height = 0;
-  EGLNativeWindowType window = dvrCreateWarpedDisplaySurface(&width, &height);
-  EXPECT_GT(width, 0);
-  EXPECT_GT(height, 0);
-  EXPECT_NE(window, nullptr);
-}
-
-TEST(GraphicsAppTests, CreateDisplaySurface) {
-  EGLNativeWindowType window = dvrCreateDisplaySurface();
-  EXPECT_NE(window, nullptr);
-}
-
-TEST(GraphicsAppTests, CreateDisplaySurfaceExtended) {
-  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,
-  };
-
-  EGLNativeWindowType window = dvrCreateDisplaySurfaceExtended(surface_params);
-  EXPECT_NE(window, nullptr);
-  EXPECT_GT(display_width, 0);
-  EXPECT_GT(display_height, 0);
-  EXPECT_GT(surface_width, 0);
-  EXPECT_GT(surface_height, 0);
-  EXPECT_GT(inter_lens_meters, 0);
-  EXPECT_GT(left_fov[0], 0);
-  EXPECT_GT(left_fov[1], 0);
-  EXPECT_GT(left_fov[2], 0);
-  EXPECT_GT(left_fov[3], 0);
-  EXPECT_GT(right_fov[0], 0);
-  EXPECT_GT(right_fov[1], 0);
-  EXPECT_GT(right_fov[2], 0);
-  EXPECT_GT(right_fov[3], 0);
-}
-
 TEST(GraphicsAppTests, GetNativeDisplayDimensions) {
   int width, height;
   dvrGetNativeDisplayDimensions(&width, &height);
@@ -57,17 +8,6 @@
   EXPECT_GT(height, 0);
 }
 
-TEST(GraphicsAppTests, GetDisplaySurfaceInfo) {
-  int ret, width, height, format;
-  EGLNativeWindowType window = dvrCreateDisplaySurface();
-  ASSERT_NE(window, nullptr);
-  ret = dvrGetDisplaySurfaceInfo(window, &width, &height, &format);
-  ASSERT_EQ(0, ret);
-  ASSERT_GT(width, 0);
-  ASSERT_GT(height, 0);
-  ASSERT_NE(0, format);
-}
-
 // TODO(jpoichet) How to check it worked?
 TEST(GraphicsAppTests, GraphicsSurfaceSetVisible) {
   DvrSurfaceParameter surface_params[] = {DVR_SURFACE_PARAMETER_LIST_END};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 33f435e..ca6d941 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -101,33 +101,6 @@
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
 
-// This is the phase offset in nanoseconds of the software vsync event
-// relative to the vsync event reported by HWComposer.  The software vsync
-// event is when SurfaceFlinger and Choreographer-based applications run each
-// frame.
-//
-// This phase offset allows adjustment of the minimum latency from application
-// wake-up (by Choregographer) time to the time at which the resulting window
-// image is displayed.  This value may be either positive (after the HW vsync)
-// or negative (before the HW vsync).  Setting it to 0 will result in a
-// minimum latency of two vsync periods because the app and SurfaceFlinger
-// will run just after the HW vsync.  Setting it to a positive number will
-// result in the minimum latency being:
-//
-//     (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
-//
-// Note that reducing this latency makes it more likely for the applications
-// to not have their window content image ready in time.  When this happens
-// the latency will end up being an additional vsync period, and animations
-// will hiccup.  Therefore, this latency should be tuned somewhat
-// conservatively (or at least with awareness of the trade-off being made).
-static int64_t vsyncPhaseOffsetNs = getInt64<
-        ISurfaceFlingerConfigs,
-        &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000);
-
-// This is the phase offset at which SurfaceFlinger's composition runs.
-static constexpr int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS;
-
 // ---------------------------------------------------------------------------
 
 const String16 sHardwareTest("android.permission.HARDWARE_TEST");
@@ -136,6 +109,8 @@
 const String16 sDump("android.permission.DUMP");
 
 // ---------------------------------------------------------------------------
+int64_t SurfaceFlinger::vsyncPhaseOffsetNs;
+int64_t SurfaceFlinger::sfVsyncPhaseOffsetNs;
 
 SurfaceFlinger::SurfaceFlinger()
     :   BnSurfaceComposer(),
@@ -179,6 +154,13 @@
         ,mEnterVrMode(false)
 #endif
 {
+
+    vsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000);
+
+    sfVsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(1000000);
+
     ALOGI("SurfaceFlinger is starting");
 
     // debugging stuff...
@@ -743,7 +725,7 @@
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
         info.presentationDeadline = hwConfig->getVsyncPeriod() -
-                SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000;
+                sfVsyncPhaseOffsetNs + 1000000;
 
         // All non-virtual displays are currently considered secure.
         info.secure = true;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 6ce799a..e5aa0bb 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -103,6 +103,31 @@
                        private HWComposer::EventHandler
 {
 public:
+
+
+    // This is the phase offset in nanoseconds of the software vsync event
+    // relative to the vsync event reported by HWComposer.  The software vsync
+    // event is when SurfaceFlinger and Choreographer-based applications run each
+    // frame.
+    //
+    // This phase offset allows adjustment of the minimum latency from application
+    // wake-up time (by Choreographer) to the time at which the resulting window
+    // image is displayed.  This value may be either positive (after the HW vsync)
+    // or negative (before the HW vsync). Setting it to 0 will result in a lower
+    // latency bound of two vsync periods because the app and SurfaceFlinger
+    // will run just after the HW vsync.  Setting it to a positive number will
+    // result in the minimum latency being:
+    //
+    //     (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
+    //
+    // Note that reducing this latency makes it more likely for the applications
+    // to not have their window content image ready in time.  When this happens
+    // the latency will end up being an additional vsync period, and animations
+    // will hiccup.  Therefore, this latency should be tuned somewhat
+    // conservatively (or at least with awareness of the trade-off being made).
+    static int64_t vsyncPhaseOffsetNs;
+    static int64_t sfVsyncPhaseOffsetNs;
+
     static char const* getServiceName() ANDROID_API {
         return "SurfaceFlinger";
     }
@@ -657,7 +682,6 @@
     std::atomic<bool> mEnterVrMode;
 #endif
     };
-
 }; // namespace android
 
 #endif // ANDROID_SURFACE_FLINGER_H
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 2fcbdba..b00792b 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -183,7 +183,7 @@
     // we don't need to factor that in here.  Pad a little to avoid
     // weird effects if apps might be requesting times right on the edge.
     nsecs_t extraPadding = 0;
-    if (VSYNC_EVENT_PHASE_OFFSET_NS == 0) {
+    if (SurfaceFlinger::vsyncPhaseOffsetNs == 0) {
         extraPadding = 1000000;        // 1ms (6% of 60Hz)
     }
 
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 9cffd6e..147232c 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -83,6 +83,9 @@
 #include "RenderEngine/RenderEngine.h"
 #include <cutils/compiler.h>
 
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
 #define DISPLAY_COUNT       1
 
 /*
@@ -94,40 +97,19 @@
 EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 
 namespace android {
-
-// This is the phase offset in nanoseconds of the software vsync event
-// relative to the vsync event reported by HWComposer.  The software vsync
-// event is when SurfaceFlinger and Choreographer-based applications run each
-// frame.
-//
-// This phase offset allows adjustment of the minimum latency from application
-// wake-up (by Choregographer) time to the time at which the resulting window
-// image is displayed.  This value may be either positive (after the HW vsync)
-// or negative (before the HW vsync).  Setting it to 0 will result in a
-// minimum latency of two vsync periods because the app and SurfaceFlinger
-// will run just after the HW vsync.  Setting it to a positive number will
-// result in the minimum latency being:
-//
-//     (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
-//
-// Note that reducing this latency makes it more likely for the applications
-// to not have their window content image ready in time.  When this happens
-// the latency will end up being an additional vsync period, and animations
-// will hiccup.  Therefore, this latency should be tuned somewhat
-// conservatively (or at least with awareness of the trade-off being made).
-static const int64_t vsyncPhaseOffsetNs = VSYNC_EVENT_PHASE_OFFSET_NS;
-
-// This is the phase offset at which SurfaceFlinger's composition runs.
-static const int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS;
-
 // ---------------------------------------------------------------------------
 
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
 const String16 sHardwareTest("android.permission.HARDWARE_TEST");
 const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
 const String16 sDump("android.permission.DUMP");
 
 // ---------------------------------------------------------------------------
+int64_t SurfaceFlinger::vsyncPhaseOffsetNs;
+int64_t SurfaceFlinger::sfVsyncPhaseOffsetNs;
 
 SurfaceFlinger::SurfaceFlinger()
     :   BnSurfaceComposer(),
@@ -164,6 +146,12 @@
         mLastSwapTime(0),
         mNumLayers(0)
 {
+    vsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000);
+
+    sfVsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(1000000);
+
     ALOGI("SurfaceFlinger is starting");
 
     char value[PROPERTY_VALUE_MAX];
@@ -711,7 +699,7 @@
         info.xdpi = xdpi;
         info.ydpi = ydpi;
         info.fps = float(1e9 / hwConfig.refresh);
-        info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS;
+        info.appVsyncOffset = vsyncPhaseOffsetNs;
 
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
@@ -726,7 +714,7 @@
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
         info.presentationDeadline =
-                hwConfig.refresh - SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000;
+                hwConfig.refresh - sfVsyncPhaseOffsetNs + 1000000;
 
         // All non-virtual displays are currently considered secure.
         info.secure = true;
diff --git a/services/vr/virtual_touchpad/Android.mk b/services/vr/virtual_touchpad/Android.mk
index b78eb99..88b2dd9 100644
--- a/services/vr/virtual_touchpad/Android.mk
+++ b/services/vr/virtual_touchpad/Android.mk
@@ -9,7 +9,8 @@
   VirtualTouchpadEvdev.cpp
 
 shared_libs := \
-  libbase
+  libbase \
+  libutils
 
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(src)
@@ -24,21 +25,23 @@
 
 # Touchpad unit tests.
 
-test_src_files := \
-  tests/VirtualTouchpad_test.cpp
-
-static_libs := \
+test_static_libs := \
   libbase \
   libcutils \
-  libutils \
   libvirtualtouchpad
 
+test_shared_libs := \
+  libutils
+
+test_src_files := \
+  tests/VirtualTouchpad_test.cpp
+
 $(foreach file,$(test_src_files), \
     $(eval include $(CLEAR_VARS)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
     $(eval LOCAL_C_INCLUDES := $(LOCAL_PATH)/include) \
-    $(eval LOCAL_STATIC_LIBRARIES := $(static_libs)) \
-    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libs)) \
+    $(eval LOCAL_STATIC_LIBRARIES := $(test_static_libs)) \
+    $(eval LOCAL_SHARED_LIBRARIES := $(test_shared_libs)) \
     $(eval LOCAL_CPPFLAGS += -std=c++11) \
     $(eval LOCAL_LDLIBS := -llog) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
@@ -70,7 +73,7 @@
 LOCAL_STATIC_LIBRARIES := $(static_libs)
 LOCAL_SHARED_LIBRARIES := $(shared_libs)
 LOCAL_CPPFLAGS += -std=c++11
-LOCAL_CFLAGS += -DLOG_TAG=\"VrVirtualTouchpad\"
+LOCAL_CFLAGS += -DLOG_TAG=\"VrVirtualTouchpad\" -DSELINUX_ACCESS_CONTROL
 LOCAL_LDLIBS := -llog
 LOCAL_MODULE := virtual_touchpad
 LOCAL_MODULE_TAGS := optional
diff --git a/services/vr/virtual_touchpad/EvdevInjector.cpp b/services/vr/virtual_touchpad/EvdevInjector.cpp
index d8a1dfa..b4c0a52 100644
--- a/services/vr/virtual_touchpad/EvdevInjector.cpp
+++ b/services/vr/virtual_touchpad/EvdevInjector.cpp
@@ -307,5 +307,11 @@
   return 0;
 }
 
+void EvdevInjector::dumpInternal(String8& result) {
+  result.append("[injector]\n");
+  result.appendFormat("state = %d\n", static_cast<int>(state_));
+  result.appendFormat("error = %d\n\n", error_);
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/virtual_touchpad/EvdevInjector.h b/services/vr/virtual_touchpad/EvdevInjector.h
index 1b1c4da..c69dbef 100644
--- a/services/vr/virtual_touchpad/EvdevInjector.h
+++ b/services/vr/virtual_touchpad/EvdevInjector.h
@@ -3,6 +3,7 @@
 
 #include <android-base/unique_fd.h>
 #include <linux/uinput.h>
+#include <utils/String8.h>
 
 #include <cstdint>
 #include <memory>
@@ -99,6 +100,8 @@
   int SendMultiTouchXY(int32_t slot, int32_t id, int32_t x, int32_t y);
   int SendMultiTouchLift(int32_t slot);
 
+  void dumpInternal(String8& result);
+
  protected:
   // Must be called only between construction and ConfigureBegin().
   inline void SetUInputForTesting(UInput* uinput) { uinput_ = uinput; }
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp b/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp
index 175173f..782b19c 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp
@@ -10,17 +10,49 @@
 
 class VirtualTouchpadClientImpl : public VirtualTouchpadClient {
  public:
-  VirtualTouchpadClientImpl(sp<IVirtualTouchpadService> service)
-      : service_(service) {}
-  ~VirtualTouchpadClientImpl() override {}
+  VirtualTouchpadClientImpl() {}
+  ~VirtualTouchpadClientImpl() override {
+    if (service_ != nullptr) {
+      Detach();
+    }
+  }
 
-  status_t Touch(int touchpad,
-                 float x, float y, float pressure) override {
+  status_t Attach() {
+    if (service_ != nullptr) {
+      return ALREADY_EXISTS;
+    }
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == nullptr) {
+      ALOGE("no service manager");
+      return NO_INIT;
+    }
+    sp<IVirtualTouchpadService> service =
+        interface_cast<IVirtualTouchpadService>(
+            sm->getService(IVirtualTouchpadService::SERVICE_NAME()));
+    if (service == nullptr) {
+      ALOGE("failed to get service");
+      return NAME_NOT_FOUND;
+    }
+    service_ = service;
+    return service_->attach().transactionError();
+  }
+
+  status_t Detach() {
+    if (service_ == nullptr) {
+      return NO_INIT;
+    }
+    status_t status = service_->detach().transactionError();
+    service_ = nullptr;
+    return status;
+  }
+
+  status_t Touch(int touchpad, float x, float y, float pressure) override {
     if (service_ == nullptr) {
       return NO_INIT;
     }
     return service_->touch(touchpad, x, y, pressure).transactionError();
   }
+
   status_t ButtonState(int touchpad, int buttons) override {
     if (service_ == nullptr) {
       return NO_INIT;
@@ -28,6 +60,12 @@
     return service_->buttonState(touchpad, buttons).transactionError();
   }
 
+  void dumpInternal(String8& result) override {
+    result.append("[virtual touchpad]\n");
+    result.appendFormat("connected = %s\n\n",
+                        service_ != nullptr ? "true" : "false");
+  }
+
  private:
   sp<IVirtualTouchpadService> service_;
 };
@@ -35,18 +73,7 @@
 }  // anonymous namespace
 
 sp<VirtualTouchpad> VirtualTouchpadClient::Create() {
-  sp<IServiceManager> sm = defaultServiceManager();
-  if (sm == nullptr) {
-    ALOGE("no service manager");
-    return sp<VirtualTouchpad>();
-  }
-  sp<IVirtualTouchpadService> service = interface_cast<IVirtualTouchpadService>(
-      sm->getService(IVirtualTouchpadService::SERVICE_NAME()));
-  if (service == nullptr) {
-    ALOGE("failed to get service");
-    return sp<VirtualTouchpad>();
-  }
-  return new VirtualTouchpadClientImpl(service);
+  return new VirtualTouchpadClientImpl();
 }
 
 }  // namespace dvr
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
index f25a2ad..bc29408 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
@@ -32,15 +32,10 @@
 
 sp<VirtualTouchpad> VirtualTouchpadEvdev::Create() {
   VirtualTouchpadEvdev* const touchpad = new VirtualTouchpadEvdev();
-  const status_t status = touchpad->Initialize();
-  if (status) {
-    ALOGE("initialization failed: %d", status);
-    return sp<VirtualTouchpad>();
-  }
   return sp<VirtualTouchpad>(touchpad);
 }
 
-int VirtualTouchpadEvdev::Initialize() {
+status_t VirtualTouchpadEvdev::Attach() {
   if (!injector_) {
     owned_injector_.reset(new EvdevInjector());
     injector_ = owned_injector_.get();
@@ -56,9 +51,20 @@
   return injector_->GetError();
 }
 
+status_t VirtualTouchpadEvdev::Detach() {
+  injector_->Close();
+  injector_ = nullptr;
+  owned_injector_.reset();
+  last_device_x_ = INT32_MIN;
+  last_device_y_ = INT32_MIN;
+  touches_ = 0;
+  last_motion_event_buttons_ = 0;
+  return OK;
+}
+
 int VirtualTouchpadEvdev::Touch(int touchpad, float x, float y,
                                 float pressure) {
-  (void)touchpad; // TODO(b/35992608) Support multiple virtual touchpad devices.
+  (void)touchpad;  // TODO(b/35992608) Support multiple touchpad devices.
   if ((x < 0.0f) || (x >= 1.0f) || (y < 0.0f) || (y >= 1.0f)) {
     return EINVAL;
   }
@@ -104,7 +110,7 @@
 }
 
 int VirtualTouchpadEvdev::ButtonState(int touchpad, int buttons) {
-  (void)touchpad; // TODO(b/35992608) Support multiple virtual touchpad devices.
+  (void)touchpad;  // TODO(b/35992608) Support multiple touchpad devices.
   const int changes = last_motion_event_buttons_ ^ buttons;
   if (!changes) {
     return 0;
@@ -123,10 +129,26 @@
     injector_->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK)
                                      ? EvdevInjector::KEY_PRESS
                                      : EvdevInjector::KEY_RELEASE);
+    injector_->SendSynReport();
   }
   last_motion_event_buttons_ = buttons;
   return injector_->GetError();
 }
 
+void VirtualTouchpadEvdev::dumpInternal(String8& result) {
+  result.append("[virtual touchpad]\n");
+  if (!injector_) {
+    result.append("injector = none\n");
+    return;
+  }
+  result.appendFormat("injector = %s\n", owned_injector_ ? "normal" : "test");
+  result.appendFormat("touches = %d\n", touches_);
+  result.appendFormat("last_position = (%" PRId32 ", %" PRId32 ")\n",
+                      last_device_x_, last_device_y_);
+  result.appendFormat("last_buttons = 0x%" PRIX32 "\n\n",
+                      last_motion_event_buttons_);
+  injector_->dumpInternal(result);
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
index ec8006b..b158cec 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
+++ b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
@@ -18,15 +18,17 @@
   static sp<VirtualTouchpad> Create();
 
   // VirtualTouchpad implementation:
+  status_t Attach() override;
+  status_t Detach() override;
   status_t Touch(int touchpad, float x, float y, float pressure) override;
   status_t ButtonState(int touchpad, int buttons) override;
+  void dumpInternal(String8& result) override;
 
  protected:
   VirtualTouchpadEvdev() {}
   ~VirtualTouchpadEvdev() override {}
-  status_t Initialize();
 
-  // Must be called only between construction and Initialize().
+  // Must be called only between construction and Attach().
   inline void SetEvdevInjectorForTesting(EvdevInjector* injector) {
     injector_ = injector;
   }
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
index a1f281c..2e2f622 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
@@ -1,22 +1,136 @@
 #include "VirtualTouchpadService.h"
 
+#include <inttypes.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/PermissionCache.h>
 #include <binder/Status.h>
+#include <cutils/log.h>
 #include <linux/input.h>
-#include <log/log.h>
+#include <private/android_filesystem_config.h>
 #include <utils/Errors.h>
 
 namespace android {
 namespace dvr {
 
-binder::Status VirtualTouchpadService::touch(int touchpad,
-                                             float x, float y, float pressure) {
-  const status_t error = touchpad_->Touch(touchpad, x, y, pressure);
-  return error ? binder::Status::fromStatusT(error) : binder::Status::ok();
+namespace {
+const String16 kDumpPermission("android.permission.DUMP");
+const String16 kTouchPermission("android.permission.RESTRICTED_VR_ACCESS");
+}  // anonymous namespace
+
+VirtualTouchpadService::~VirtualTouchpadService() {
+  if (client_pid_) {
+    client_pid_ = 0;
+    touchpad_->Detach();
+  }
+}
+
+binder::Status VirtualTouchpadService::attach() {
+  pid_t pid;
+  if (!CheckTouchPermission(&pid)) {
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+  if (client_pid_ == pid) {
+    // The same client has called attach() twice with no intervening detach().
+    // This indicates a problem with the client, so return an error.
+    // However, since the client is already attached, any touchpad actions
+    // it takes will still work.
+    ALOGE("pid=%ld attached twice", static_cast<long>(pid));
+    return binder::Status::fromStatusT(ALREADY_EXISTS);
+  }
+  if (client_pid_ != 0) {
+    // Attach while another client is attached. This can happen if the client
+    // dies without cleaning up after itself, so move ownership to the current
+    // caller. If two actual clients have connected, the problem will be
+    // reported when the previous client performs any touchpad action.
+    ALOGE("pid=%ld replaces %ld", static_cast<long>(pid),
+          static_cast<long>(client_pid_));
+  }
+  client_pid_ = pid;
+  if (const status_t error = touchpad_->Attach()) {
+    return binder::Status::fromStatusT(error);
+  }
+  return binder::Status::ok();
+}
+
+binder::Status VirtualTouchpadService::detach() {
+  if (!CheckPermissions()) {
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+  client_pid_ = 0;
+  if (const status_t error = touchpad_->Detach()) {
+    return binder::Status::fromStatusT(error);
+  }
+  return binder::Status::ok();
+}
+
+binder::Status VirtualTouchpadService::touch(int touchpad, float x, float y,
+                                             float pressure) {
+  if (!CheckPermissions()) {
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+  if (const status_t error = touchpad_->Touch(touchpad, x, y, pressure)) {
+    return binder::Status::fromStatusT(error);
+  }
+  return binder::Status::ok();
 }
 
 binder::Status VirtualTouchpadService::buttonState(int touchpad, int buttons) {
-  const status_t error = touchpad_->ButtonState(touchpad, buttons);
-  return error ? binder::Status::fromStatusT(error) : binder::Status::ok();
+  if (!CheckPermissions()) {
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+  if (const status_t error = touchpad_->ButtonState(touchpad, buttons)) {
+    return binder::Status::fromStatusT(error);
+  }
+  return binder::Status::ok();
+}
+
+status_t VirtualTouchpadService::dump(
+    int fd, const Vector<String16>& args[[gnu::unused]]) {
+  String8 result;
+  const android::IPCThreadState* ipc = android::IPCThreadState::self();
+  const pid_t pid = ipc->getCallingPid();
+  const uid_t uid = ipc->getCallingUid();
+  if ((uid != AID_SHELL) &&
+      !PermissionCache::checkPermission(kDumpPermission, pid, uid)) {
+    result.appendFormat("Permission denial: can't dump " LOG_TAG
+                        " from pid=%ld, uid=%ld\n",
+                        static_cast<long>(pid), static_cast<long>(uid));
+  } else {
+    result.appendFormat("[service]\nclient_pid = %ld\n\n",
+                        static_cast<long>(client_pid_));
+    touchpad_->dumpInternal(result);
+  }
+  write(fd, result.string(), result.size());
+  return OK;
+}
+
+bool VirtualTouchpadService::CheckPermissions() {
+  pid_t pid;
+  if (!CheckTouchPermission(&pid)) {
+    return false;
+  }
+  if (client_pid_ != pid) {
+    ALOGE("pid=%ld is not owner", static_cast<long>(pid));
+    return false;
+  }
+  return true;
+}
+
+bool VirtualTouchpadService::CheckTouchPermission(pid_t* out_pid) {
+  const android::IPCThreadState* ipc = android::IPCThreadState::self();
+  *out_pid = ipc->getCallingPid();
+#ifdef SELINUX_ACCESS_CONTROL
+  return true;
+#else
+  const uid_t uid = ipc->getCallingUid();
+  const bool permission = PermissionCache::checkPermission(kTouchPermission, *out_pid, uid);
+  if (!permission) {
+    ALOGE("permission denied to pid=%ld uid=%ld", static_cast<long>(*out_pid),
+          static_cast<long>(uid));
+  }
+  return permission;
+#endif
 }
 
 }  // namespace dvr
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.h b/services/vr/virtual_touchpad/VirtualTouchpadService.h
index 9b880b2..194d787 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadService.h
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.h
@@ -14,17 +14,28 @@
 class VirtualTouchpadService : public BnVirtualTouchpadService {
  public:
   VirtualTouchpadService(sp<VirtualTouchpad> touchpad)
-      : touchpad_(touchpad) {}
-  ~VirtualTouchpadService() override {}
+      : touchpad_(touchpad), client_pid_(0) {}
+  ~VirtualTouchpadService() override;
 
  protected:
   // Implements IVirtualTouchpadService.
+  binder::Status attach() override;
+  binder::Status detach() override;
   binder::Status touch(int touchpad, float x, float y, float pressure) override;
   binder::Status buttonState(int touchpad, int buttons) override;
 
+  // Implements BBinder::dump().
+  status_t dump(int fd, const Vector<String16>& args) override;
+
  private:
+  bool CheckPermissions();
+  bool CheckTouchPermission(pid_t* out_pid);
+
   sp<VirtualTouchpad> touchpad_;
 
+  // Only one client at a time can use the virtual touchpad.
+  pid_t client_pid_;
+
   VirtualTouchpadService(const VirtualTouchpadService&) = delete;
   void operator=(const VirtualTouchpadService&) = delete;
 };
diff --git a/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
index 496c5e2..9cfb186 100644
--- a/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
+++ b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
@@ -6,6 +6,16 @@
   const String SERVICE_NAME = "virtual_touchpad";
 
   /**
+   * Initialize the virtual touchpad.
+   */
+  void attach() = 0;
+
+  /**
+   * Shut down the virtual touchpad.
+   */
+  void detach() = 1;
+
+  /**
    * Generate a simulated touch event.
    *
    * @param touchpad Selects touchpad.
@@ -15,7 +25,7 @@
    *
    * Position values in the range [0.0, 1.0) map to the screen.
    */
-  void touch(int touchpad, float x, float y, float pressure);
+  void touch(int touchpad, float x, float y, float pressure) = 2;
 
   /**
    * Generate a simulated touchpad button state event.
@@ -23,5 +33,5 @@
    * @param touchpad Selects touchpad.
    * @param buttons A union of MotionEvent BUTTON_* values.
    */
-  void buttonState(int touchpad, int buttons);
+  void buttonState(int touchpad, int buttons) = 3;
 }
diff --git a/services/vr/virtual_touchpad/include/VirtualTouchpad.h b/services/vr/virtual_touchpad/include/VirtualTouchpad.h
index d24d121..b1ee700 100644
--- a/services/vr/virtual_touchpad/include/VirtualTouchpad.h
+++ b/services/vr/virtual_touchpad/include/VirtualTouchpad.h
@@ -3,6 +3,7 @@
 
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
+#include <utils/String8.h>
 #include <utils/StrongPointer.h>
 
 namespace android {
@@ -21,10 +22,18 @@
   // Implementations should provide this, and hide their constructors.
   // For the user, switching implementations should be as simple as changing
   // the class whose |Create()| is called.
+  // Implementations should be minimial; major resource allocation should
+  // be performed in Attach().
   static sp<VirtualTouchpad> Create() {
     return sp<VirtualTouchpad>();
   }
 
+  // Initialize a virtual touchpad.
+  virtual status_t Attach() = 0;
+
+  // Shut down a virtual touchpad.
+  virtual status_t Detach() = 0;
+
   // Generate a simulated touch event.
   //
   // @param touchpad Touchpad selector index.
@@ -49,6 +58,9 @@
   //
   virtual status_t ButtonState(int touchpad, int buttons) = 0;
 
+  // Report state for 'dumpsys'.
+  virtual void dumpInternal(String8& result) = 0;
+
  protected:
   VirtualTouchpad() {}
   ~VirtualTouchpad() override {}
diff --git a/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h b/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h
index dd9c265..471d9e0 100644
--- a/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h
+++ b/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h
@@ -13,8 +13,11 @@
  public:
   // VirtualTouchpad implementation:
   static sp<VirtualTouchpad> Create();
+  status_t Attach() override;
+  status_t Detach() override;
   status_t Touch(int touchpad, float x, float y, float pressure) override;
   status_t ButtonState(int touchpad, int buttons) override;
+  void dumpInternal(String8& result) override;
 
  protected:
   VirtualTouchpadClient() {}
diff --git a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
index 469a2d0..b11a983 100644
--- a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
+++ b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
@@ -97,7 +97,6 @@
   static sp<VirtualTouchpad> Create(EvdevInjectorForTesting& injector) {
     VirtualTouchpadForTesting* const touchpad = new VirtualTouchpadForTesting();
     touchpad->SetEvdevInjectorForTesting(&injector);
-    touchpad->Initialize();
     return sp<VirtualTouchpad>(touchpad);
   }
 };
@@ -123,6 +122,9 @@
   EvdevInjectorForTesting injector(record);
   sp<VirtualTouchpad> touchpad(VirtualTouchpadForTesting::Create(injector));
 
+  status_t touch_status = touchpad->Attach();
+  EXPECT_EQ(0, touch_status);
+
   // Check some aspects of uinput_user_dev.
   const uinput_user_dev* uidev = injector.GetUiDev();
   for (int i = 0; i < ABS_CNT; ++i) {
@@ -151,6 +153,7 @@
   // From ConfigureKey(BTN_TOUCH):
   expect.IoctlSetInt(UI_SET_EVBIT, EV_KEY);
   expect.IoctlSetInt(UI_SET_KEYBIT, BTN_TOUCH);
+  expect.IoctlSetInt(UI_SET_KEYBIT, BTN_BACK);
   // From ConfigureEnd():
   expect.Write(uidev, sizeof(uinput_user_dev));
   expect.IoctlVoid(UI_DEV_CREATE);
@@ -158,7 +161,7 @@
 
   expect.Reset();
   record.Reset();
-  int touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0, 0, 0);
+  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0, 0, 0);
   EXPECT_EQ(0, touch_status);
   expect.WriteInputEvent(EV_ABS, ABS_MT_SLOT, 0);
   expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
@@ -180,16 +183,22 @@
 
   expect.Reset();
   record.Reset();
-  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 1.0f, 1.0f, 1.0f);
+  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.99f, 0.99f, 0.99f);
   EXPECT_EQ(0, touch_status);
   expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
-  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, width);
-  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, height);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.99f * width);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.99f * height);
   expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
   EXPECT_EQ(expect.GetString(), record.GetString());
 
   expect.Reset();
   record.Reset();
+  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 1.0f, 1.0f, 1.0f);
+  EXPECT_EQ(EINVAL, touch_status);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  expect.Reset();
+  record.Reset();
   touch_status =
       touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, 0.75f, -0.01f);
   EXPECT_EQ(0, touch_status);
@@ -224,6 +233,10 @@
 
   expect.Reset();
   record.Reset();
+  touch_status = touchpad->Detach();
+  EXPECT_EQ(0, touch_status);
+  expect.Close();
+  EXPECT_EQ(expect.GetString(), record.GetString());
 }
 
 TEST_F(VirtualTouchpadTest, Badness) {
@@ -232,11 +245,14 @@
   EvdevInjectorForTesting injector(record);
   sp<VirtualTouchpad> touchpad(VirtualTouchpadForTesting::Create(injector));
 
+  status_t touch_status = touchpad->Attach();
+  EXPECT_EQ(0, touch_status);
+
   // Touch off-screen should return an error,
   // and should not result in any system calls.
   expect.Reset();
   record.Reset();
-  status_t touch_status =
+  touch_status =
       touchpad->Touch(VirtualTouchpad::PRIMARY, -0.25f, 0.75f, 1.0f);
   EXPECT_NE(OK, touch_status);
   touch_status =
@@ -256,6 +272,10 @@
                                        AMOTION_EVENT_BUTTON_FORWARD);
   EXPECT_NE(OK, touch_status);
   EXPECT_EQ(expect.GetString(), record.GetString());
+
+  // Repeated attach is an error.
+  touch_status = touchpad->Attach();
+  EXPECT_NE(0, touch_status);
 }
 
 }  // namespace dvr
diff --git a/services/vr/vr_window_manager/shell_view.cpp b/services/vr/vr_window_manager/shell_view.cpp
index fae1dd9..10588b7 100644
--- a/services/vr/vr_window_manager/shell_view.cpp
+++ b/services/vr/vr_window_manager/shell_view.cpp
@@ -250,8 +250,12 @@
 
   translate_ = Eigen::Translation3f(0, 0, -2.5f);
 
-  if (!InitializeTouch())
-    ALOGE("Failed to initialize virtual touchpad");
+  virtual_touchpad_ = VirtualTouchpadClient::Create();
+  const status_t touchpad_status = virtual_touchpad_->Attach();
+  if (touchpad_status != OK) {
+    ALOGE("Failed to connect to virtual touchpad");
+    return touchpad_status;
+  }
 
   surface_flinger_view_.reset(new SurfaceFlingerView);
   if (!surface_flinger_view_->Initialize(this))
@@ -702,22 +706,10 @@
   controller_mesh_->Draw();
 }
 
-bool ShellView::InitializeTouch() {
-  virtual_touchpad_ = VirtualTouchpadClient::Create();
-  if (!virtual_touchpad_.get()) {
-    ALOGE("Failed to connect to virtual touchpad");
-    return false;
-  }
-  return true;
-}
-
 void ShellView::Touch() {
   if (!virtual_touchpad_.get()) {
     ALOGE("missing virtual touchpad");
-    // Try to reconnect; useful in development.
-    if (!InitializeTouch()) {
-      return;
-    }
+    return;
   }
 
   const android::status_t status = virtual_touchpad_->Touch(
diff --git a/services/vr/vr_window_manager/shell_view.h b/services/vr/vr_window_manager/shell_view.h
index c477669..49456c6 100644
--- a/services/vr/vr_window_manager/shell_view.h
+++ b/services/vr/vr_window_manager/shell_view.h
@@ -58,7 +58,6 @@
              bool test_ime);
   bool IsImeHit(const vec3& view_location, const vec3& view_direction,
                 vec3 *hit_location);
-  bool InitializeTouch();
   void Touch();
   bool OnTouchpadButton(bool down, int button);
 
diff --git a/services/vr/vr_window_manager/vr_window_manager_binder.cpp b/services/vr/vr_window_manager/vr_window_manager_binder.cpp
index c2138b7..bd3f3ee 100644
--- a/services/vr/vr_window_manager/vr_window_manager_binder.cpp
+++ b/services/vr/vr_window_manager/vr_window_manager_binder.cpp
@@ -16,7 +16,8 @@
 
 namespace {
 const String16 kDumpPermission("android.permission.DUMP");
-const String16 kSendMeControllerInputPermission("TODO");  // TODO(kpschoedel)
+const String16 kSendMeControllerInputPermission(
+    "android.permission.RESTRICTED_VR_ACCESS");
 }  // anonymous namespace
 
 constexpr size_t AshmemControllerDataProvider::kRegionLength;
@@ -136,8 +137,8 @@
     int fd, const Vector<String16>& args [[gnu::unused]]) {
   String8 result;
   const android::IPCThreadState* ipc = android::IPCThreadState::self();
-  const int pid = ipc->getCallingPid();
-  const int uid = ipc->getCallingUid();
+  const pid_t pid = ipc->getCallingPid();
+  const uid_t uid = ipc->getCallingUid();
   if ((uid != AID_SHELL) &&
       !PermissionCache::checkPermission(kDumpPermission, pid, uid)) {
     result.appendFormat("Permission denial: can't dump " LOG_TAG