Merge "Add android.hardware.vulkan.compute=0 feature file"
diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp
index 69ed416..225de20 100644
--- a/cmds/atrace/Android.bp
+++ b/cmds/atrace/Android.bp
@@ -16,6 +16,9 @@
         "libz",
         "libbase",
     ],
+    static_libs: [
+        "libpdx_default_transport",
+    ],
 
     init_rc: ["atrace.rc"],
 }
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 4be0432..ce0caed 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -18,6 +18,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <ftw.h>
 #include <getopt.h>
 #include <inttypes.h>
 #include <signal.h>
@@ -41,6 +42,7 @@
 #include <hidl/ServiceManagement.h>
 #include <cutils/properties.h>
 
+#include <pdx/default_transport/service_utility.h>
 #include <utils/String8.h>
 #include <utils/Timers.h>
 #include <utils/Tokenizer.h>
@@ -48,6 +50,7 @@
 #include <android-base/file.h>
 
 using namespace android;
+using pdx::default_transport::ServiceUtility;
 
 using std::string;
 #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
@@ -569,6 +572,46 @@
     }
 }
 
+// Sends the sysprop_change message to the service at fpath, so it re-reads its
+// system properties. Returns 0 on success or a negated errno code on failure.
+static int pokeOnePDXService(const char *fpath, const struct stat * /*sb*/,
+                             int typeflag, struct FTW * /*ftwbuf*/)
+{
+    const bool kIgnoreErrors = true;
+
+    if (typeflag == FTW_F) {
+        int error;
+        auto utility = ServiceUtility::Create(fpath, &error);
+        if (!utility) {
+            if (error != -ECONNREFUSED) {
+                ALOGE("pokeOnePDXService: Failed to open %s, %s.", fpath,
+                      strerror(-error));
+            }
+            return kIgnoreErrors ? 0 : error;
+        }
+
+        auto status = utility->ReloadSystemProperties();
+        if (!status) {
+            ALOGE("pokeOnePDXService: Failed to send sysprop change to %s, "
+                  "error %d, %s.", fpath, status.error(),
+                  status.GetErrorMessage().c_str());
+            return kIgnoreErrors ? 0 : -status.error();
+        }
+    }
+
+    return 0;
+}
+
+// Pokes all the PDX processes in the system to get them to re-read
+// their system properties. Returns true on success, false on failure.
+static bool pokePDXServices()
+{
+    const int kMaxDepth = 16;
+    const int result = nftw(ServiceUtility::GetRootEndpointPath().c_str(),
+                            pokeOnePDXService, kMaxDepth, FTW_PHYS);
+    return result == 0 ? true : false;
+}
+
 // Set the trace tags that userland tracing uses, and poke the running
 // processes to pick up the new value.
 static bool setTagsProperty(uint64_t tags)
@@ -812,6 +855,7 @@
     ok &= setAppCmdlineProperty(&packageList[0]);
     ok &= pokeBinderServices();
     pokeHalServices();
+    ok &= pokePDXServices();
 
     // Disable all the sysfs enables.  This is done as a separate loop from
     // the enables to allow the same enable to exist in multiple categories.
@@ -849,6 +893,7 @@
     setTagsProperty(0);
     clearAppProperties();
     pokeBinderServices();
+    pokePDXServices();
 
     // Set the options back to their defaults.
     setTraceOverwriteEnable(true);
diff --git a/include/audiomanager/IPlayer.h b/include/audiomanager/IPlayer.h
index efcac74..94afae5 100644
--- a/include/audiomanager/IPlayer.h
+++ b/include/audiomanager/IPlayer.h
@@ -41,6 +41,10 @@
 
     virtual void setVolume(float vol) = 0;
 
+    virtual void setPan(float pan) = 0;
+
+    virtual void setStartDelayMs(int delayMs) = 0;
+
 };
 
 // ----------------------------------------------------------------------------
diff --git a/libs/vr/libeds/distortion_renderer.cpp b/libs/vr/libeds/distortion_renderer.cpp
index 59029d8..13090ca 100644
--- a/libs/vr/libeds/distortion_renderer.cpp
+++ b/libs/vr/libeds/distortion_renderer.cpp
@@ -308,7 +308,7 @@
   if (blend_with_previous_layer) {
     // Check for unsupported shader combinations:
     LOG_ALWAYS_FATAL_IF(num_layers != 1);
-    LOG_ALWAYS_FATAL_IF(!use_alpha_vignette);
+    LOG_ALWAYS_FATAL_IF(use_alpha_vignette);
     if (kUseFramebufferReadback)
       frag_builder += "#define BLEND_WITH_PREVIOUS_LAYER\n";
   }
diff --git a/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr.so b/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr.so
index 1d0ba50..bfd5956 100644
--- a/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr.so
+++ b/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr_audio.so b/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr_audio.so
index 905ca64..c3012b1 100644
--- a/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr_audio.so
+++ b/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr_audio.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr.so b/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr.so
index d62f7ca..6608c25 100644
--- a/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr.so
+++ b/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr_audio.so b/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr_audio.so
index e342f6a..b1d7690 100644
--- a/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr_audio.so
+++ b/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr_audio.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr.so b/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr.so
index 8092138..f7f7786 100644
--- a/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr.so
+++ b/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr_audio.so b/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr_audio.so
index 3fe5b2c..97aec40 100644
--- a/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr_audio.so
+++ b/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr_audio.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr.so b/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr.so
index 3bcf60e..2e2dbc1 100644
--- a/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr.so
+++ b/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr_audio.so b/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr_audio.so
index 2f2d834..cd8d0e0 100644
--- a/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr_audio.so
+++ b/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr_audio.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/common_library.aar b/libs/vr/libgvr/prebuilt/lib/common_library.aar
index 13147fe..9c1fbd0 100644
--- a/libs/vr/libgvr/prebuilt/lib/common_library.aar
+++ b/libs/vr/libgvr/prebuilt/lib/common_library.aar
Binary files differ
diff --git a/libs/vr/libgvr/shim_gvr.cpp b/libs/vr/libgvr/shim_gvr.cpp
index fa8a655..5eb6e3d 100644
--- a/libs/vr/libgvr/shim_gvr.cpp
+++ b/libs/vr/libgvr/shim_gvr.cpp
@@ -512,7 +512,8 @@
 }
 
 bool gvr_is_feature_supported(const gvr_context* /*gvr*/, int32_t feature) {
-  return feature == GVR_FEATURE_ASYNC_REPROJECTION;
+  return feature == GVR_FEATURE_ASYNC_REPROJECTION ||
+      feature == GVR_FEATURE_HEAD_POSE_6DOF;
 }
 
 /////////////////////////////////////////////////////////////////////////////
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index e0b592e..cc08209 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -211,11 +211,15 @@
     layer->Initialize(hwc2_hidl_.get(), &native_display_metrics_);
   }
 
+#if ENABLE_BACKLIGHT_BRIGHTNESS
+  // TODO(hendrikw): This isn't required at the moment. It's possible that there
+  //                 is another method to access this when needed.
   // Open the backlight brightness control sysfs node.
   backlight_brightness_fd_ = LocalHandle(kBacklightBrightnessSysFile, O_RDWR);
   ALOGW_IF(!backlight_brightness_fd_,
            "HardwareComposer: Failed to open backlight brightness control: %s",
            strerror(errno));
+#endif // ENABLE_BACKLIGHT_BRIGHTNESS
 
   // Open the vsync event node for the primary display.
   // TODO(eieio): Move this into a platform-specific class.
diff --git a/services/audiomanager/IPlayer.cpp b/services/audiomanager/IPlayer.cpp
index 3b0b4e9..47edc4b 100644
--- a/services/audiomanager/IPlayer.cpp
+++ b/services/audiomanager/IPlayer.cpp
@@ -33,6 +33,8 @@
     PAUSE      = IBinder::FIRST_CALL_TRANSACTION + 1,
     STOP       = IBinder::FIRST_CALL_TRANSACTION + 2,
     SET_VOLUME = IBinder::FIRST_CALL_TRANSACTION + 3,
+    SET_PAN    = IBinder::FIRST_CALL_TRANSACTION + 4,
+    SET_START_DELAY_MS = IBinder::FIRST_CALL_TRANSACTION + 5,
 };
 
 class BpPlayer : public BpInterface<IPlayer>
@@ -71,6 +73,21 @@
         data.writeFloat(vol);
         remote()->transact(SET_VOLUME, data, &reply);
     }
+
+    virtual void setPan(float pan)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        data.writeFloat(pan);
+        remote()->transact(SET_PAN, data, &reply);
+    }
+
+    virtual void setStartDelayMs(int32_t delayMs) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        data.writeInt32(delayMs);
+        remote()->transact(SET_START_DELAY_MS, data, &reply);
+    }
 };
 
 IMPLEMENT_META_INTERFACE(Player, "android.media.IPlayer");
@@ -100,7 +117,17 @@
             CHECK_INTERFACE(IPlayer, data, reply);
             setVolume(data.readFloat());
             return NO_ERROR;
-        }
+        } break;
+        case SET_PAN: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            setPan(data.readFloat());
+            return NO_ERROR;
+        } break;
+        case SET_START_DELAY_MS: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            setStartDelayMs(data.readInt32());
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
index 37de7a2..2b603cc 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -1298,6 +1298,7 @@
         auto& hwc1Layer = mHwc1RequestedContents->hwLayers[layer->getHwc1Id()];
         hwc1Layer.releaseFenceFd = -1;
         hwc1Layer.acquireFenceFd = -1;
+        ALOGV("Applying states for layer %" PRIu64 " ", layer->getId());
         layer->applyState(hwc1Layer, applyAllState);
     }
 
@@ -2009,7 +2010,6 @@
     mZ(0),
     mReleaseFence(),
     mHwc1Id(0),
-    mHasUnsupportedDataspace(false),
     mHasUnsupportedPlaneAlpha(false) {}
 
 bool HWC2On1Adapter::SortLayersByZ::operator()(
@@ -2070,9 +2070,8 @@
     return Error::None;
 }
 
-Error HWC2On1Adapter::Layer::setDataspace(android_dataspace_t dataspace)
+Error HWC2On1Adapter::Layer::setDataspace(android_dataspace_t)
 {
-    mHasUnsupportedDataspace = (dataspace != HAL_DATASPACE_UNKNOWN);
     return Error::None;
 }
 
@@ -2318,8 +2317,13 @@
     // HWC1 never supports color transforms or dataspaces and only sometimes
     // supports plane alpha (depending on the version). These require us to drop
     // some or all layers to client composition.
-    if (mHasUnsupportedDataspace || mHasUnsupportedPlaneAlpha ||
-            mDisplay.hasColorTransform() || mHasUnsupportedBackgroundColor) {
+    ALOGV("applyCompositionType");
+    ALOGV("mHasUnsupportedPlaneAlpha = %d", mHasUnsupportedPlaneAlpha);
+    ALOGV("mDisplay.hasColorTransform() = %d", mDisplay.hasColorTransform());
+    ALOGV("mHasUnsupportedBackgroundColor = %d", mHasUnsupportedBackgroundColor);
+
+    if (mHasUnsupportedPlaneAlpha || mDisplay.hasColorTransform() ||
+        mHasUnsupportedBackgroundColor) {
         hwc1Layer.compositionType = HWC_FRAMEBUFFER;
         hwc1Layer.flags = HWC_SKIP_LAYER;
         return;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
index 9abdc38..df33ec3 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
@@ -605,7 +605,6 @@
             DeferredFence mReleaseFence;
 
             size_t mHwc1Id;
-            bool mHasUnsupportedDataspace;
             bool mHasUnsupportedPlaneAlpha;
             bool mHasUnsupportedBackgroundColor;
     };
diff --git a/services/vr/vr_window_manager/application.cpp b/services/vr/vr_window_manager/application.cpp
index b4568b8..62db639 100644
--- a/services/vr/vr_window_manager/application.cpp
+++ b/services/vr/vr_window_manager/application.cpp
@@ -161,8 +161,10 @@
         }
         break;
       case MainThreadTask::EnteringVrMode:
-        if (!initialized_)
-          AllocateResources();
+        if (!initialized_) {
+          if (AllocateResources())
+            ALOGE("Failed to allocate resources");
+        }
         break;
       case MainThreadTask::ExitingVrMode:
         if (initialized_)
diff --git a/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp b/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
index 53c7d8e..f83fa86 100644
--- a/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
+++ b/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
@@ -142,36 +142,48 @@
   }
 }
 
-std::vector<ComposerView::ComposerLayer> HwcDisplay::GetFrame() {
-  // Increment the time the fence is signalled every time we get the
-  // presentation frame. This ensures that calling ReleaseFrame() only affects
-  // the current frame.
-  fence_time_++;
-
+Error HwcDisplay::GetFrame(
+    std::vector<ComposerView::ComposerLayer>* out_frames) {
   bool queued_client_target = false;
   std::vector<ComposerView::ComposerLayer> frame;
   for (const auto& layer : layers_) {
     if (layer.composition_type == IComposerClient::Composition::CLIENT) {
-      if (!queued_client_target) {
-        ComposerView::ComposerLayer client_target_layer = {
-            .buffer = buffer_,
-            .fence = fence_.get() ? fence_ : new Fence(-1),
-            .display_frame = {0, 0, static_cast<int32_t>(buffer_->getWidth()),
-              static_cast<int32_t>(buffer_->getHeight())},
-            .crop = {0.0f, 0.0f, static_cast<float>(buffer_->getWidth()),
-              static_cast<float>(buffer_->getHeight())},
-            .blend_mode = IComposerClient::BlendMode::NONE,
-        };
+      if (queued_client_target)
+        continue;
 
-        frame.push_back(client_target_layer);
-        queued_client_target = true;
+      if (!buffer_.get()) {
+        ALOGE("Client composition requested but no client target buffer");
+        return Error::BAD_LAYER;
       }
+
+      ComposerView::ComposerLayer client_target_layer = {
+          .buffer = buffer_,
+          .fence = fence_.get() ? fence_ : new Fence(-1),
+          .display_frame = {0, 0, static_cast<int32_t>(buffer_->getWidth()),
+            static_cast<int32_t>(buffer_->getHeight())},
+          .crop = {0.0f, 0.0f, static_cast<float>(buffer_->getWidth()),
+            static_cast<float>(buffer_->getHeight())},
+          .blend_mode = IComposerClient::BlendMode::NONE,
+      };
+
+      frame.push_back(client_target_layer);
+      queued_client_target = true;
     } else {
+      if (!layer.info.buffer.get() || !layer.info.fence.get()) {
+        ALOGE("Layer requested without valid buffer");
+        return Error::BAD_LAYER;
+      }
+
       frame.push_back(layer.info);
     }
   }
 
-  return frame;
+  // Increment the time the fence is signalled every time we get the
+  // presentation frame. This ensures that calling ReleaseFrame() only affects
+  // the current frame.
+  fence_time_++;
+  out_frames->swap(frame);
+  return Error::NONE;
 }
 
 void HwcDisplay::GetReleaseFences(int* present_fence,
@@ -392,7 +404,8 @@
   base::unique_fd fence(releaseFence);
   if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
 
-  return Error::NONE;
+  ALOGE("Virtual display support not implemented");
+  return Error::UNSUPPORTED;
 }
 
 Error VrHwc::validateDisplay(
@@ -423,7 +436,10 @@
   std::vector<ComposerView::ComposerLayer> frame;
   {
     std::lock_guard<std::mutex> guard(mutex_);
-    frame = display_.GetFrame();
+    Error status = display_.GetFrame(&frame);
+    if (status != Error::NONE)
+      return status;
+
     display_.GetReleaseFences(outPresentFence, outLayers, outReleaseFences);
   }
 
diff --git a/services/vr/vr_window_manager/composer/impl/vr_hwc.h b/services/vr/vr_window_manager/composer/impl/vr_hwc.h
index 1de056a..6b9487b 100644
--- a/services/vr/vr_window_manager/composer/impl/vr_hwc.h
+++ b/services/vr/vr_window_manager/composer/impl/vr_hwc.h
@@ -115,7 +115,7 @@
       std::vector<Layer>* layer_ids,
       std::vector<IComposerClient::Composition>* composition);
 
-  std::vector<ComposerView::ComposerLayer> GetFrame();
+  Error GetFrame(std::vector<ComposerView::ComposerLayer>* out_frame);
 
   void GetReleaseFences(int* present_fence, std::vector<Layer>* layer_ids,
                         std::vector<int>* fences);
diff --git a/services/vr/vr_window_manager/hwc_callback.cpp b/services/vr/vr_window_manager/hwc_callback.cpp
index b2edc20..5045790 100644
--- a/services/vr/vr_window_manager/hwc_callback.cpp
+++ b/services/vr/vr_window_manager/hwc_callback.cpp
@@ -79,8 +79,7 @@
   }
 
   std::lock_guard<std::mutex> guard(mutex_);
-  if (client_)
-    client_->OnFrame(std::make_unique<Frame>(std::move(hwc_frame)));
+  client_->OnFrame(std::make_unique<Frame>(std::move(hwc_frame)));
 
   return Void();
 }
diff --git a/services/vr/vr_window_manager/render_thread.cpp b/services/vr/vr_window_manager/render_thread.cpp
index 5f777e3..b67a051 100644
--- a/services/vr/vr_window_manager/render_thread.cpp
+++ b/services/vr/vr_window_manager/render_thread.cpp
@@ -75,7 +75,6 @@
   jobject android_context = env->NewLocalRef(android_context_global_ref_);
 
   int init_result = shell_view_.Initialize(env, android_context, class_loader);
-  init_result += shell_view_.AllocateResources();
   init_result_promise->set_value(init_result);
   if (init_result == 0) {
     while (!quit_)
diff --git a/services/vr/vr_window_manager/shell_view.cpp b/services/vr/vr_window_manager/shell_view.cpp
index ca49db7..11680af 100644
--- a/services/vr/vr_window_manager/shell_view.cpp
+++ b/services/vr/vr_window_manager/shell_view.cpp
@@ -15,6 +15,8 @@
 
 namespace {
 
+constexpr float kLayerScaleFactor = 4.0f;
+
 constexpr unsigned int kVRAppLayerCount = 2;
 
 constexpr unsigned int kMaximumPendingFrames = 8;
@@ -105,6 +107,9 @@
   else
     xscale = ar;
 
+  xscale *= kLayerScaleFactor;
+  yscale *= kLayerScaleFactor;
+
   return mat4(Eigen::Scaling<float>(xscale, yscale, 1.0));
 }
 
@@ -126,7 +131,7 @@
   m(3, 0) = 0.0f; m(3, 1) = 0.0f; m(3, 2) = 0.0f; m(3, 3) = 1.0f;
   // clang-format on
 
-  return m;
+  return m * Eigen::AngleAxisf(M_PI * 0.5f, vec3::UnitZ());
 }
 
 // Helper function that applies the crop transform to the texture layer and
@@ -194,16 +199,22 @@
 }
 
 // Determine if ths frame should be shown or hidden.
-bool CalculateVisibilityFromLayerConfig(const HwcCallback::Frame& frame,
-                                        uint32_t vr_app) {
+ViewMode CalculateVisibilityFromLayerConfig(const HwcCallback::Frame& frame,
+                                            uint32_t vr_app) {
   auto& layers = frame.layers();
 
   // We assume the first two layers are the VR app.
   if (layers.size() < kVRAppLayerCount)
-    return false;
+    return ViewMode::Hidden;
 
-  if (vr_app != layers[0].appid || layers[0].appid == 0)
-    return false;
+  if (vr_app != layers[0].appid || layers[0].appid == 0 ||
+      layers[1].appid != layers[0].appid) {
+    if (layers[1].appid != layers[0].appid && layers[0].appid) {
+      // This might be a 2D app.
+      return ViewMode::App;
+    }
+    return ViewMode::Hidden;
+  }
 
   // If a non-VR-app, non-skipped layer appears, show.
   size_t index = kVRAppLayerCount;
@@ -219,11 +230,12 @@
   // If any non-skipped layers exist now then we show, otherwise hide.
   for (size_t i = index; i < layers.size(); i++) {
     if (!layers[i].should_skip_layer())
-      return true;
+      return ViewMode::VR;
   }
-  return false;
+  return ViewMode::Hidden;
 }
 
+
 }  // namespace
 
 ShellView::ShellView() {
@@ -245,6 +257,10 @@
   if (!InitializeTouch())
     ALOGE("Failed to initialize virtual touchpad");
 
+  surface_flinger_view_.reset(new SurfaceFlingerView);
+  if (!surface_flinger_view_->Initialize(this))
+    return 1;
+
   return 0;
 }
 
@@ -262,10 +278,6 @@
   if (!program_ || !overlay_program_ || !controller_program_)
     return 1;
 
-  surface_flinger_view_.reset(new SurfaceFlingerView);
-  if (!surface_flinger_view_->Initialize(this))
-    return 1;
-
   reticle_.reset(new Reticle());
   if (!reticle_->Initialize())
     return 1;
@@ -299,34 +311,45 @@
                  : MainThreadTask::ExitingVrMode);
 }
 
+void ShellView::AdvanceFrame() {
+  if (!pending_frames_.empty()) {
+    // Check if we should advance the frame.
+    auto& frame = pending_frames_.front();
+    if (frame.visibility == ViewMode::Hidden ||
+        frame.frame->Finish() == HwcCallback::FrameStatus::kFinished) {
+      current_frame_ = std::move(frame);
+      pending_frames_.pop_front();
+
+      for(int i = 0; i < skipped_frame_count_ + 1; i++)
+        surface_flinger_view_->ReleaseFrame();
+      skipped_frame_count_ = 0;
+    }
+  }
+}
+
 void ShellView::OnDrawFrame() {
   textures_.clear();
   has_ime_ = false;
 
   {
     std::unique_lock<std::mutex> l(pending_frame_mutex_);
-    if (!pending_frames_.empty()) {
-      // Check if we should advance the frame.
-      auto& frame = pending_frames_.front();
-      if (!frame.visibility ||
-          frame.frame->Finish() == HwcCallback::FrameStatus::kFinished) {
-        current_frame_ = std::move(frame);
-        pending_frames_.pop_front();
-      }
-    }
+    AdvanceFrame();
   }
 
-  if (!debug_mode_ && current_frame_.visibility != is_visible_) {
-    SetVisibility(current_frame_.visibility);
+  bool visible = current_frame_.visibility != ViewMode::Hidden;
+
+  if (!debug_mode_ && visible != is_visible_) {
+    SetVisibility(current_frame_.visibility != ViewMode::Hidden);
   }
 
-  if (!current_frame_.visibility)
+  if (!debug_mode_ && !visible)
     return;
 
   ime_texture_ = TextureLayer();
 
   surface_flinger_view_->GetTextures(*current_frame_.frame.get(), &textures_,
-                                     &ime_texture_, debug_mode_);
+                                     &ime_texture_, debug_mode_,
+                                     current_frame_.visibility == ViewMode::VR);
   has_ime_ = ime_texture_.texture != nullptr;
 }
 
@@ -370,33 +393,35 @@
 }
 
 void ShellView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame) {
-  if (!frame || frame->layers().empty())
-    return;
+  ViewMode visibility =
+      CalculateVisibilityFromLayerConfig(*frame.get(), current_vr_app_);
 
-  bool visibility = debug_mode_ || CalculateVisibilityFromLayerConfig(
-                                       *frame.get(), current_vr_app_);
+  if (visibility == ViewMode::Hidden && debug_mode_)
+    visibility = ViewMode::VR;
   current_vr_app_ = frame->layers().front().appid;
 
-  // If we are not showing the frame there's no need to keep anything around.
-  if (!visibility) {
-    // Hidden, no change so drop it completely
-    if (!current_frame_.visibility)
-      return;
-
-    frame.reset(nullptr);
-  }
-
   std::unique_lock<std::mutex> l(pending_frame_mutex_);
 
   pending_frames_.emplace_back(std::move(frame), visibility);
 
-  if (pending_frames_.size() > kMaximumPendingFrames)
+  if (pending_frames_.size() > kMaximumPendingFrames) {
+    skipped_frame_count_++;
     pending_frames_.pop_front();
+  }
+
+  if (visibility == ViewMode::Hidden &&
+      current_frame_.visibility == ViewMode::Hidden) {
+    // Consume all frames while hidden.
+    while (!pending_frames_.empty())
+      AdvanceFrame();
+  }
 
   // If we are showing ourselves the main thread is not processing anything,
   // so give it a kick.
-  if (visibility && !current_frame_.visibility)
+  if (visibility != ViewMode::Hidden && current_frame_.visibility == ViewMode::Hidden) {
+    QueueTask(MainThreadTask::EnteringVrMode);
     QueueTask(MainThreadTask::Show);
+  }
 }
 
 bool ShellView::IsHit(const vec3& view_location, const vec3& view_direction,
diff --git a/services/vr/vr_window_manager/shell_view.h b/services/vr/vr_window_manager/shell_view.h
index 589902e..ba46e6d 100644
--- a/services/vr/vr_window_manager/shell_view.h
+++ b/services/vr/vr_window_manager/shell_view.h
@@ -14,6 +14,12 @@
 namespace android {
 namespace dvr {
 
+enum class ViewMode {
+  Hidden,
+  VR,
+  App,
+};
+
 class ShellView : public Application, public HwcCallback::Client {
  public:
   ShellView();
@@ -57,6 +63,8 @@
 
   bool OnClick(bool down);
 
+  void AdvanceFrame();
+
   // HwcCallback::Client:
   void OnFrame(std::unique_ptr<HwcCallback::Frame> frame) override;
 
@@ -64,6 +72,9 @@
   std::unique_ptr<ShaderProgram> overlay_program_;
   std::unique_ptr<ShaderProgram> controller_program_;
 
+  // This starts at -1 so we don't call ReleaseFrame for the first frame.
+  int skipped_frame_count_ = -1;
+
   uint32_t current_vr_app_;
 
   // Used to center the scene when the shell becomes visible.
@@ -92,7 +103,7 @@
 
   struct PendingFrame {
     PendingFrame() = default;
-    PendingFrame(std::unique_ptr<HwcCallback::Frame>&& frame, bool visibility)
+    PendingFrame(std::unique_ptr<HwcCallback::Frame>&& frame, ViewMode visibility)
         : frame(std::move(frame)), visibility(visibility) {}
     PendingFrame(PendingFrame&& r)
         : frame(std::move(r.frame)), visibility(r.visibility) {}
@@ -103,7 +114,7 @@
     }
 
     std::unique_ptr<HwcCallback::Frame> frame;
-    bool visibility = false;
+    ViewMode visibility = ViewMode::Hidden;
   };
   std::deque<PendingFrame> pending_frames_;
   std::mutex pending_frame_mutex_;
diff --git a/services/vr/vr_window_manager/surface_flinger_view.cpp b/services/vr/vr_window_manager/surface_flinger_view.cpp
index d38fcc0..b15d262 100644
--- a/services/vr/vr_window_manager/surface_flinger_view.cpp
+++ b/services/vr/vr_window_manager/surface_flinger_view.cpp
@@ -38,13 +38,13 @@
 bool SurfaceFlingerView::GetTextures(const HwcCallback::Frame& frame,
                                      std::vector<TextureLayer>* texture_layers,
                                      TextureLayer* ime_layer,
-                                     bool debug) const {
+                                     bool debug, bool skip_first_layer) const {
   auto& layers = frame.layers();
   texture_layers->clear();
 
   size_t start = 0;
   // Skip the second layer if it is from the VR app.
-  if (!debug) {
+  if (!debug && skip_first_layer) {
     start = 1;
     if (layers[0].appid && layers[0].appid == layers[1].appid)
       start = 2;
@@ -75,5 +75,9 @@
   return true;
 }
 
+void SurfaceFlingerView::ReleaseFrame() {
+  composer_service_->releaseFrame();
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/vr_window_manager/surface_flinger_view.h b/services/vr/vr_window_manager/surface_flinger_view.h
index e079cdb..2e36ec1 100644
--- a/services/vr/vr_window_manager/surface_flinger_view.h
+++ b/services/vr/vr_window_manager/surface_flinger_view.h
@@ -33,7 +33,10 @@
 
   bool GetTextures(const HwcCallback::Frame& layers,
                    std::vector<TextureLayer>* texture_layers,
-                   TextureLayer* ime_layer, bool debug) const;
+                   TextureLayer* ime_layer, bool debug,
+                   bool skip_first_layer) const;
+
+  void ReleaseFrame();
 
  private:
   sp<IVrComposerView> composer_service_;
diff --git a/services/vr/vr_window_manager/vr_window_manager.cpp b/services/vr/vr_window_manager/vr_window_manager.cpp
index 736a14f..8d9ad79 100644
--- a/services/vr/vr_window_manager/vr_window_manager.cpp
+++ b/services/vr/vr_window_manager/vr_window_manager.cpp
@@ -11,11 +11,6 @@
     return 1;
   }
 
-  if (app.AllocateResources()) {
-    ALOGE("Failed to allocate resources");
-    return 1;
-  }
-
   while (true)
     app.DrawFrame();