Merge "vulkan: Implement V7 of ANDROID_native_buffer in nulldrv"
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index a5ac8a4..0d5652f 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -529,7 +529,7 @@
 static void open_profile_files(uid_t uid, const std::string& pkgname,
             /*out*/ std::vector<unique_fd>* profiles_fd, /*out*/ unique_fd* reference_profile_fd) {
     // Open the reference profile in read-write mode as profman might need to save the merge.
-    reference_profile_fd->reset(open_reference_profile(uid, pkgname, /*read_write*/ true));
+    *reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ true);
     if (reference_profile_fd->get() < 0) {
         // We can't access the reference profile file.
         return;
diff --git a/include/binder/HalToken.h b/include/binder/HalToken.h
deleted file mode 100644
index ce97c78..0000000
--- a/include/binder/HalToken.h
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HALTOKEN_H
-#define ANDROID_HALTOKEN_H
-
-#include <binder/Parcel.h>
-#include <hidl/HidlSupport.h>
-
-/**
- * Hybrid Interfaces
- * =================
- *
- * A hybrid interface is a binder interface that
- * 1. is implemented both traditionally and as a wrapper around a hidl
- *    interface, and allows querying whether the underlying instance comes from
- *    a hidl interface or not; and
- * 2. allows efficient calls to a hidl interface (if the underlying instance
- *    comes from a hidl interface) by automatically creating the wrapper in the
- *    process that calls it.
- *
- * Terminology:
- * - `HalToken`: The type for a "token" of a hidl interface. This is defined to
- *   be compatible with `ITokenManager.hal`.
- * - `HInterface`: The base type for a hidl interface. Currently, it is defined
- *   as `::android::hidl::base::V1_0::IBase`.
- * - `HALINTERFACE`: The hidl interface that will be sent through binders.
- * - `INTERFACE`: The binder interface that will be the wrapper of
- *   `HALINTERFACE`. `INTERFACE` is supposed to be somewhat similar to
- *   `HALINTERFACE`.
- *
- * To demonstrate how this is done, here is an example. Suppose `INTERFACE` is
- * `IFoo` and `HALINTERFACE` is `HFoo`. The required steps are:
- * 1. Use DECLARE_HYBRID_META_INTERFACE instead of DECLARE_META_INTERFACE in the
- *    definition of `IFoo`. The usage is
- *        DECLARE_HYBRID_META_INTERFACE(IFoo, HFoo)
- *    inside the body of `IFoo`.
- * 2. Create a converter class that derives from
- *    `H2BConverter<HFoo, IFoo, BnFoo>`. Let us call this `H2BFoo`.
- * 3. Add the following constructor in `H2BFoo` that call the corresponding
- *    constructors in `H2BConverter`:
- *        H2BFoo(const sp<HalInterface>& base) : CBase(base) {}
- *    Note: `CBase = H2BConverter<HFoo, IFoo, BnFoo>` and `HalInterface = HFoo`
- *    are member typedefs of `H2BConverter<HFoo, IFoo, BnFoo>`, so the above
- *    line can be copied into `H2BFoo`.
- * 4. Implement `IFoo` in `H2BFoo` on top of `HFoo`. `H2BConverter` provides a
- *    protected `mBase` of type `sp<HFoo>` that can be used to access the `HFoo`
- *    instance. (There is also a public function named `getHalInterface()` that
- *    returns `mBase`.)
- * 5. Create a hardware proxy class that derives from
- *    `HpInterface<BpFoo, H2BFoo>`. Name this class `HpFoo`. (This name cannot
- *    deviate. See step 8 below.)
- * 6. Add the following constructor to `HpFoo`:
- *        HpFoo(const sp<IBinder>& base): PBase(base) {}
- *    Note: `PBase` a member typedef of `HpInterface<BpFoo, H2BFoo>` that is
- *    equal to `HpInterface<BpFoo, H2BFoo>` itself, so the above line can be
- *    copied verbatim into `HpFoo`.
- * 7. Delegate all functions in `HpFoo` that come from `IFoo` except
- *    `getHalInterface` to the protected member `mBase`,
- *    which is defined in `HpInterface<BpFoo, H2BFoo>` (hence in `HpFoo`) with
- *    type `IFoo`. (There is also a public function named `getBaseInterface()`
- *    that returns `mBase`.)
- * 8. Replace the existing `IMPLEMENT_META_INTERFACE` for INTERFACE by
- *    `IMPLEMENT_HYBRID_META_INTERFACE`. Note that this macro relies on the
- *    exact naming of `HpFoo`, where `Foo` comes from the interface name `IFoo`.
- *    An example usage is
- *        IMPLEMENT_HYBRID_META_INTERFACE(IFoo, HFoo, "example.interface.foo");
- *
- * `GETTOKEN` Template Argument
- * ============================
- *
- * Following the instructions above, `H2BConverter` and `HpInterface` would use
- * `transact()` to send over tokens, with `code` (the first argument of
- * `transact()`) equal to a 4-byte value of '_GTK'. If this value clashes with
- * other values already in use in the `Bp` class, it can be changed by supplying
- * the last optional template argument to `H2BConverter` and `HpInterface`.
- *
- */
-
-namespace android {
-
-typedef uint64_t HalToken;
-typedef ::android::hidl::base::V1_0::IBase HInterface;
-
-sp<HInterface> retrieveHalInterface(const HalToken& token);
-bool createHalToken(const sp<HInterface>& interface, HalToken* token);
-bool deleteHalToken(const HalToken& token);
-
-template <
-        typename HINTERFACE,
-        typename INTERFACE,
-        typename BNINTERFACE,
-        uint32_t GETTOKEN = '_GTK'>
-class H2BConverter : public BNINTERFACE {
-public:
-    typedef H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE, GETTOKEN> CBase; // Converter Base
-    typedef INTERFACE BaseInterface;
-    typedef HINTERFACE HalInterface;
-    static constexpr uint32_t GET_HAL_TOKEN = GETTOKEN;
-
-    H2BConverter(const sp<HalInterface>& base) : mBase(base) {}
-    virtual status_t onTransact(uint32_t code,
-            const Parcel& data, Parcel* reply, uint32_t flags = 0);
-    sp<HalInterface> getHalInterface() override { return mBase; }
-    HalInterface* getBaseInterface() { return mBase.get(); }
-
-protected:
-    sp<HalInterface> mBase;
-};
-
-template <
-        typename BPINTERFACE,
-        typename CONVERTER,
-        uint32_t GETTOKEN = '_GTK'>
-class HpInterface : public CONVERTER::BaseInterface {
-public:
-    typedef HpInterface<BPINTERFACE, CONVERTER, GETTOKEN> PBase; // Proxy Base
-    typedef typename CONVERTER::BaseInterface BaseInterface;
-    typedef typename CONVERTER::HalInterface HalInterface;
-    static constexpr uint32_t GET_HAL_TOKEN = GETTOKEN;
-
-    explicit HpInterface(const sp<IBinder>& impl);
-    sp<HalInterface> getHalInterface() override { return mHal; }
-    BaseInterface* getBaseInterface() { return mBase.get(); }
-
-protected:
-    sp<IBinder> mImpl;
-    sp<BaseInterface> mBase;
-    sp<HalInterface> mHal;
-    IBinder* onAsBinder() override { return mImpl.get(); }
-};
-
-// ----------------------------------------------------------------------
-
-#define DECLARE_HYBRID_META_INTERFACE(INTERFACE, HAL)                   \
-    static const ::android::String16 descriptor;                        \
-    static ::android::sp<I##INTERFACE> asInterface(                     \
-            const ::android::sp<::android::IBinder>& obj);              \
-    virtual const ::android::String16& getInterfaceDescriptor() const;  \
-    I##INTERFACE();                                                     \
-    virtual ~I##INTERFACE();                                            \
-    virtual sp<HAL> getHalInterface();                                  \
-
-
-#define IMPLEMENT_HYBRID_META_INTERFACE(INTERFACE, HAL, NAME)           \
-    const ::android::String16 I##INTERFACE::descriptor(NAME);           \
-    const ::android::String16&                                          \
-            I##INTERFACE::getInterfaceDescriptor() const {              \
-        return I##INTERFACE::descriptor;                                \
-    }                                                                   \
-    ::android::sp<I##INTERFACE> I##INTERFACE::asInterface(              \
-            const ::android::sp<::android::IBinder>& obj)               \
-    {                                                                   \
-        ::android::sp<I##INTERFACE> intr;                               \
-        if (obj != NULL) {                                              \
-            intr = static_cast<I##INTERFACE*>(                          \
-                obj->queryLocalInterface(                               \
-                        I##INTERFACE::descriptor).get());               \
-            if (intr == NULL) {                                         \
-                intr = new Hp##INTERFACE(obj);                          \
-            }                                                           \
-        }                                                               \
-        return intr;                                                    \
-    }                                                                   \
-    I##INTERFACE::I##INTERFACE() { }                                    \
-    I##INTERFACE::~I##INTERFACE() { }                                   \
-    sp<HAL> I##INTERFACE::getHalInterface() { return nullptr; }         \
-
-// ----------------------------------------------------------------------
-
-template <
-        typename HINTERFACE,
-        typename INTERFACE,
-        typename BNINTERFACE,
-        uint32_t GETTOKEN>
-status_t H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE, GETTOKEN>::
-        onTransact(
-        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-    if (code == GET_HAL_TOKEN) {
-        HalToken token;
-        bool result;
-        result = createHalToken(mBase, &token);
-        if (!result) {
-            ALOGE("H2BConverter: Failed to create HAL token.");
-        }
-        reply->writeBool(result);
-        reply->writeUint64(token);
-        return NO_ERROR;
-    }
-    return BNINTERFACE::onTransact(code, data, reply, flags);
-}
-
-template <typename BPINTERFACE, typename CONVERTER, uint32_t GETTOKEN>
-HpInterface<BPINTERFACE, CONVERTER, GETTOKEN>::HpInterface(
-        const sp<IBinder>& impl) : mImpl(impl) {
-    Parcel data, reply;
-    data.writeInterfaceToken(BaseInterface::getInterfaceDescriptor());
-    if (impl->transact(GET_HAL_TOKEN, data, &reply) == NO_ERROR) {
-        bool tokenCreated = reply.readBool();
-        HalToken token = reply.readUint64();
-        if (!tokenCreated) {
-            ALOGE("HpInterface: Sender failed to create HAL token.");
-            mBase = new BPINTERFACE(impl);
-        } else {
-            sp<HInterface> hInterface = retrieveHalInterface(token);
-            deleteHalToken(token);
-            if (hInterface != nullptr) {
-                mHal = static_cast<HalInterface*>(hInterface.get());
-                mBase = new CONVERTER(mHal);
-            } else {
-                ALOGE("HpInterface: Cannot retrieve HAL interface from token.");
-                mBase = new BPINTERFACE(impl);
-            }
-        }
-    } else {
-        mBase = new BPINTERFACE(impl);
-    }
-}
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_HALTOKEN_H
diff --git a/include/gui/BufferQueueDefs.h b/include/gui/BufferQueueDefs.h
index 83e9580..ffafb49 100644
--- a/include/gui/BufferQueueDefs.h
+++ b/include/gui/BufferQueueDefs.h
@@ -18,16 +18,12 @@
 #define ANDROID_GUI_BUFFERQUEUECOREDEFS_H
 
 #include <gui/BufferSlot.h>
+#include <ui/BufferQueueDefs.h>
 
 namespace android {
     class BufferQueueCore;
 
     namespace BufferQueueDefs {
-        // BufferQueue will keep track of at most this value of buffers.
-        // Attempts at runtime to increase the number of buffers past this
-        // will fail.
-        enum { NUM_BUFFER_SLOTS = 64 };
-
         typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
     } // namespace BufferQueueDefs
 } // namespace android
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 258cd2f..5810335 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -32,12 +32,17 @@
 
 #include <gui/FrameTimestamps.h>
 
+#include <hidl/HybridInterface.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
 class IProducerListener;
 class NativeHandle;
 class Surface;
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+        HGraphicBufferProducer;
 
 /*
  * This class defines the Binder IPC interface for the producer side of
@@ -56,7 +61,7 @@
 class IGraphicBufferProducer : public IInterface
 {
 public:
-    DECLARE_META_INTERFACE(GraphicBufferProducer)
+    DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer)
 
     enum {
         // A flag returned by dequeueBuffer when the client needs to call
diff --git a/include/gui/bufferqueue/1.0/B2HProducerListener.h b/include/gui/bufferqueue/1.0/B2HProducerListener.h
new file mode 100644
index 0000000..fa6c2d9
--- /dev/null
+++ b/include/gui/bufferqueue/1.0/B2HProducerListener.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/IBinder.h>
+#include <gui/IProducerListener.h>
+
+#include <android/hidl/base/1.0/IBase.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener
+        HProducerListener;
+
+typedef ::android::IProducerListener
+        BProducerListener;
+
+struct B2HProducerListener : public HProducerListener {
+    sp<BProducerListener> mBase;
+    B2HProducerListener(sp<BProducerListener> const& base);
+    Return<void> onBufferReleased() override;
+    Return<bool> needsReleaseNotify() override;
+};
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H
+
diff --git a/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h b/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
new file mode 100644
index 0000000..93c452a
--- /dev/null
+++ b/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/Binder.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
+
+#include <hidl/HybridInterface.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+using ::android::hardware::media::V1_0::AnwBuffer;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+        HGraphicBufferProducer;
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener
+        HProducerListener;
+
+typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
+using ::android::BnGraphicBufferProducer;
+using ::android::IProducerListener;
+
+struct H2BGraphicBufferProducer : public ::android::H2BConverter<
+        HGraphicBufferProducer,
+        BGraphicBufferProducer,
+        BnGraphicBufferProducer> {
+    H2BGraphicBufferProducer(sp<HGraphicBufferProducer> const& base) : CBase(base) {}
+
+    status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override;
+    status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override;
+    status_t setAsyncMode(bool async) override;
+    status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w,
+            uint32_t h, ::android::PixelFormat format, uint32_t usage,
+            FrameEventHistoryDelta* outTimestamps) override;
+    status_t detachBuffer(int slot) override;
+    status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence)
+            override;
+    status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer)
+            override;
+    status_t queueBuffer(int slot,
+            const QueueBufferInput& input,
+            QueueBufferOutput* output) override;
+    status_t cancelBuffer(int slot, const sp<Fence>& fence) override;
+    int query(int what, int* value) override;
+    status_t connect(const sp<IProducerListener>& listener, int api,
+            bool producerControlledByApp, QueueBufferOutput* output) override;
+    status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api)
+            override;
+    status_t setSidebandStream(const sp<NativeHandle>& stream) override;
+    void allocateBuffers(uint32_t width, uint32_t height,
+            ::android::PixelFormat format, uint32_t usage) override;
+    status_t allowAllocation(bool allow) override;
+    status_t setGenerationNumber(uint32_t generationNumber) override;
+    String8 getConsumerName() const override;
+    status_t setSharedBufferMode(bool sharedBufferMode) override;
+    status_t setAutoRefresh(bool autoRefresh) override;
+    status_t setDequeueTimeout(nsecs_t timeout) override;
+    status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+          sp<Fence>* outFence, float outTransformMatrix[16]) override;
+    void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override;
+    status_t getUniqueId(uint64_t* outId) const override;
+};
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace bufferqueue
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H
diff --git a/include/ui/BufferQueueDefs.h b/include/ui/BufferQueueDefs.h
new file mode 100644
index 0000000..56de181
--- /dev/null
+++ b/include/ui/BufferQueueDefs.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_BUFFERQUEUEDEFS_H
+#define ANDROID_UI_BUFFERQUEUEDEFS_H
+
+namespace android {
+    namespace BufferQueueDefs {
+        // BufferQueue will keep track of at most this value of buffers.
+        // Attempts at runtime to increase the number of buffers past this
+        // will fail.
+        static constexpr int NUM_BUFFER_SLOTS = 64;
+    } // namespace BufferQueueDefs
+} // namespace android
+
+#endif
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 2cb44eb..93b8684 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -34,7 +34,6 @@
         "IResultReceiver.cpp",
         "IServiceManager.cpp",
         "IShellCallback.cpp",
-        "HalToken.cpp",
         "MemoryBase.cpp",
         "MemoryDealer.cpp",
         "MemoryHeapBase.cpp",
@@ -66,8 +65,6 @@
         "liblog",
         "libcutils",
         "libutils",
-        "libhidlbase",
-        "android.hidl.token@1.0",
     ],
     export_shared_lib_headers: [
         "libbase",
diff --git a/libs/binder/HalToken.cpp b/libs/binder/HalToken.cpp
deleted file mode 100644
index 6e71a52..0000000
--- a/libs/binder/HalToken.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "HalToken"
-
-#include <utils/Log.h>
-#include <binder/HalToken.h>
-
-#include <android/hidl/token/1.0/ITokenManager.h>
-
-namespace android {
-
-using ::android::hidl::token::V1_0::ITokenManager;
-
-sp<ITokenManager> gTokenManager = nullptr;
-
-ITokenManager* getTokenManager() {
-    if (gTokenManager != nullptr) {
-        return gTokenManager.get();
-    }
-    gTokenManager = ITokenManager::getService();
-    if (gTokenManager == nullptr) {
-        ALOGE("Cannot retrieve TokenManager.");
-    }
-    return gTokenManager.get();
-}
-
-sp<HInterface> retrieveHalInterface(const HalToken& token) {
-    auto transaction = getTokenManager()->get(token);
-    if (!transaction.isOk()) {
-        ALOGE("getHalInterface: Cannot obtain interface from token.");
-        return nullptr;
-    }
-    return static_cast<sp<HInterface> >(transaction);
-}
-
-bool createHalToken(const sp<HInterface>& interface, HalToken* token) {
-    auto transaction = getTokenManager()->createToken(interface);
-    if (!transaction.isOk()) {
-        ALOGE("createHalToken: Cannot create token from interface.");
-        return false;
-    }
-    *token = static_cast<HalToken>(transaction);
-    return true;
-}
-
-bool deleteHalToken(const HalToken& token) {
-    auto transaction = getTokenManager()->unregister(token);
-    if (!transaction.isOk()) {
-        ALOGE("deleteHalToken: Cannot unregister hal token.");
-        return false;
-    }
-    return true;
-}
-
-}; // namespace android
-
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index a1b4abc..5f5fb91 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -41,6 +41,15 @@
         // We are aware of the risks inherent in comparing floats for equality
         "-Wno-float-equal",
 
+        // Pure abstract classes trigger this warning
+        "-Wno-weak-vtables",
+
+        // Allow four-character integer literals
+	"-Wno-four-char-constants",
+
+        // Allow documentation warnings
+        "-Wno-documentation",
+
         "-DDEBUG_ONLY_CODE=0",
     ],
 
@@ -88,6 +97,8 @@
         "SurfaceComposerClient.cpp",
         "SyncFeatures.cpp",
         "view/Surface.cpp",
+        "bufferqueue/1.0/B2HProducerListener.cpp",
+        "bufferqueue/1.0/H2BGraphicBufferProducer.cpp"
     ],
 
     shared_libs: [
@@ -100,9 +111,18 @@
         "libutils",
         "libnativewindow",
         "liblog",
+        "libhidlbase",
+        "android.hidl.base@1.0",
+        "android.hidl.token@1.0-utils",
+        "android.hardware.graphics.bufferqueue@1.0",
     ],
 
-    export_shared_lib_headers: ["libbinder", "libui"],
+    export_shared_lib_headers: [
+        "libbinder",
+        "libui",
+        "android.hidl.token@1.0-utils",
+        "android.hardware.graphics.bufferqueue@1.0",
+    ],
 }
 
 subdirs = ["tests"]
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index abdf649..74117c8 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -30,9 +30,14 @@
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/IProducerListener.h>
 
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
+using ::android::hardware::graphics::bufferqueue::V1_0::utils::
+        H2BGraphicBufferProducer;
+
 enum {
     REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
     DEQUEUE_BUFFER,
@@ -485,7 +490,123 @@
 // translation unit (see clang warning -Wweak-vtables)
 BpGraphicBufferProducer::~BpGraphicBufferProducer() {}
 
-IMPLEMENT_META_INTERFACE(GraphicBufferProducer, "android.gui.IGraphicBufferProducer");
+class HpGraphicBufferProducer : public HpInterface<
+        BpGraphicBufferProducer, H2BGraphicBufferProducer> {
+public:
+    HpGraphicBufferProducer(const sp<IBinder>& base) : PBase(base) {}
+
+    status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override {
+        return mBase->requestBuffer(slot, buf);
+    }
+
+    status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override {
+        return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+    }
+
+    status_t setAsyncMode(bool async) override {
+        return mBase->setAsyncMode(async);
+    }
+
+    status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
+            PixelFormat format, uint32_t usage,
+            FrameEventHistoryDelta* outTimestamps) override {
+        return mBase->dequeueBuffer(
+                slot, fence, w, h, format, usage, outTimestamps);
+    }
+
+    status_t detachBuffer(int slot) override {
+        return mBase->detachBuffer(slot);
+    }
+
+    status_t detachNextBuffer(
+            sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override {
+        return mBase->detachNextBuffer(outBuffer, outFence);
+    }
+
+    status_t attachBuffer(
+            int* outSlot, const sp<GraphicBuffer>& buffer) override {
+        return mBase->attachBuffer(outSlot, buffer);
+    }
+
+    status_t queueBuffer(
+            int slot,
+            const QueueBufferInput& input,
+            QueueBufferOutput* output) override {
+        return mBase->queueBuffer(slot, input, output);
+    }
+
+    status_t cancelBuffer(int slot, const sp<Fence>& fence) override {
+        return mBase->cancelBuffer(slot, fence);
+    }
+
+    int query(int what, int* value) override {
+        return mBase->query(what, value);
+    }
+
+    status_t connect(
+            const sp<IProducerListener>& listener,
+            int api, bool producerControlledByApp,
+            QueueBufferOutput* output) override {
+        return mBase->connect(listener, api, producerControlledByApp, output);
+    }
+
+    status_t disconnect(
+            int api, DisconnectMode mode = DisconnectMode::Api) override {
+        return mBase->disconnect(api, mode);
+    }
+
+    status_t setSidebandStream(const sp<NativeHandle>& stream) override {
+        return mBase->setSidebandStream(stream);
+    }
+
+    void allocateBuffers(uint32_t width, uint32_t height,
+            PixelFormat format, uint32_t usage) override {
+        return mBase->allocateBuffers(width, height, format, usage);
+    }
+
+    status_t allowAllocation(bool allow) override {
+        return mBase->allowAllocation(allow);
+    }
+
+    status_t setGenerationNumber(uint32_t generationNumber) override {
+        return mBase->setGenerationNumber(generationNumber);
+    }
+
+    String8 getConsumerName() const override {
+        return mBase->getConsumerName();
+    }
+
+    status_t setSharedBufferMode(bool sharedBufferMode) override {
+        return mBase->setSharedBufferMode(sharedBufferMode);
+    }
+
+    status_t setAutoRefresh(bool autoRefresh) override {
+        return mBase->setAutoRefresh(autoRefresh);
+    }
+
+    status_t setDequeueTimeout(nsecs_t timeout) override {
+        return mBase->setDequeueTimeout(timeout);
+    }
+
+    status_t getLastQueuedBuffer(
+            sp<GraphicBuffer>* outBuffer,
+            sp<Fence>* outFence,
+            float outTransformMatrix[16]) override {
+        return mBase->getLastQueuedBuffer(
+                outBuffer, outFence, outTransformMatrix);
+    }
+
+    void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override {
+        return mBase->getFrameTimestamps(outDelta);
+    }
+
+    status_t getUniqueId(uint64_t* outId) const override {
+        return mBase->getUniqueId(outId);
+    }
+};
+
+IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer,
+        "android.gui.IGraphicBufferProducer");
 
 // ----------------------------------------------------------------------
 
diff --git a/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp b/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp
new file mode 100644
index 0000000..a5f28cd
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/bufferqueue/1.0/B2HProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+// B2HProducerListener
+B2HProducerListener::B2HProducerListener(
+        sp<BProducerListener> const& base):
+    mBase(base) {
+}
+
+Return<void> B2HProducerListener::onBufferReleased() {
+    mBase->onBufferReleased();
+    return Void();
+}
+
+Return<bool> B2HProducerListener::needsReleaseNotify() {
+    return mBase->needsReleaseNotify();
+}
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace bufferqueue
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
new file mode 100644
index 0000000..eafd296
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -0,0 +1,1234 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "H2BGraphicBufferProducer"
+
+#include <android-base/logging.h>
+
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <gui/bufferqueue/1.0/B2HProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using Status = HGraphicBufferProducer::Status;
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+typedef ::android::hardware::media::V1_0::Rect HRect;
+typedef ::android::hardware::media::V1_0::Region HRegion;
+
+// Conversion functions
+
+// native_handle_t helper functions.
+
+/**
+ * \brief Take an fd and create a native handle containing only the given fd.
+ * The created handle will need to be deleted manually with
+ * `native_handle_delete()`.
+ *
+ * \param[in] fd The source file descriptor (of type `int`).
+ * \return The create `native_handle_t*` that contains the given \p fd. If the
+ * supplied \p fd is negative, the created native handle will contain no file
+ * descriptors.
+ *
+ * If the native handle cannot be created, the return value will be
+ * `nullptr`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+inline native_handle_t* native_handle_create_from_fd(int fd) {
+    if (fd < 0) {
+        return native_handle_create(0, 0);
+    }
+    native_handle_t* nh = native_handle_create(1, 0);
+    if (nh == nullptr) {
+        return nullptr;
+    }
+    nh->data[0] = fd;
+    return nh;
+}
+
+/**
+ * \brief Extract a file descriptor from a native handle.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \param[in] index The index of the file descriptor in \p nh to read from. This
+ * input has the default value of `0`.
+ * \return The `index`-th file descriptor in \p nh. If \p nh does not have
+ * enough file descriptors, the returned value will be `-1`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+inline int native_handle_read_fd(native_handle_t const* nh, int index = 0) {
+    return ((nh == nullptr) || (nh->numFds == 0) ||
+            (nh->numFds <= index) || (index < 0)) ?
+            -1 : nh->data[index];
+}
+
+/**
+ * \brief Convert `Return<Status>` to `status_t`. This is for legacy binder
+ * calls.
+ *
+ * \param[in] t The source `Return<Status>`.
+ * \return The corresponding `status_t`.
+ *
+ * This function first check if \p t has a transport error. If it does, then the
+ * return value is the transport error code. Otherwise, the return value is
+ * converted from `Status` contained inside \p t.
+ *
+ * Note:
+ * - This `Status` is omx-specific. It is defined in `types.hal`.
+ * - The name of this function is not `convert`.
+ */
+// convert: Return<Status> -> status_t
+inline status_t toStatusT(Return<Status> const& t) {
+    return t.isOk() ? static_cast<status_t>(static_cast<Status>(t)) : UNKNOWN_ERROR;
+}
+
+/**
+ * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `status_t`.
+ */
+// convert: Return<void> -> status_t
+inline status_t toStatusT(Return<void> const& t) {
+    return t.isOk() ? OK : UNKNOWN_ERROR;
+}
+
+/**
+ * \brief Wrap `GraphicBuffer` in `AnwBuffer`.
+ *
+ * \param[out] t The wrapper of type `AnwBuffer`.
+ * \param[in] l The source `GraphicBuffer`.
+ */
+// wrap: GraphicBuffer -> AnwBuffer
+inline void wrapAs(AnwBuffer* t, GraphicBuffer const& l) {
+    t->attr.width = l.getWidth();
+    t->attr.height = l.getHeight();
+    t->attr.stride = l.getStride();
+    t->attr.format = static_cast<PixelFormat>(l.getPixelFormat());
+    t->attr.layerCount = l.getLayerCount();
+    t->attr.usage = l.getUsage();
+    t->attr.id = l.getId();
+    t->attr.generationNumber = l.getGenerationNumber();
+    t->nativeHandle = hidl_handle(l.handle);
+}
+
+/**
+ * \brief Convert `AnwBuffer` to `GraphicBuffer`.
+ *
+ * \param[out] l The destination `GraphicBuffer`.
+ * \param[in] t The source `AnwBuffer`.
+ *
+ * This function will duplicate all file descriptors in \p t.
+ */
+// convert: AnwBuffer -> GraphicBuffer
+// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten
+inline bool convertTo(GraphicBuffer* l, AnwBuffer const& t) {
+    native_handle_t* handle = t.nativeHandle == nullptr ?
+            nullptr : native_handle_clone(t.nativeHandle);
+
+    size_t const numInts = 12 +
+            static_cast<size_t>(handle ? handle->numInts : 0);
+    int32_t* ints = new int32_t[numInts];
+
+    size_t numFds = static_cast<size_t>(handle ? handle->numFds : 0);
+    int* fds = new int[numFds];
+
+    ints[0] = 'GBFR';
+    ints[1] = static_cast<int32_t>(t.attr.width);
+    ints[2] = static_cast<int32_t>(t.attr.height);
+    ints[3] = static_cast<int32_t>(t.attr.stride);
+    ints[4] = static_cast<int32_t>(t.attr.format);
+    ints[5] = static_cast<int32_t>(t.attr.layerCount);
+    ints[6] = static_cast<int32_t>(t.attr.usage);
+    ints[7] = static_cast<int32_t>(t.attr.id >> 32);
+    ints[8] = static_cast<int32_t>(t.attr.id & 0xFFFFFFFF);
+    ints[9] = static_cast<int32_t>(t.attr.generationNumber);
+    ints[10] = 0;
+    ints[11] = 0;
+    if (handle) {
+        ints[10] = static_cast<int32_t>(handle->numFds);
+        ints[11] = static_cast<int32_t>(handle->numInts);
+        int* intsStart = handle->data + handle->numFds;
+        std::copy(handle->data, intsStart, fds);
+        std::copy(intsStart, intsStart + handle->numInts, &ints[12]);
+    }
+
+    void const* constBuffer = static_cast<void const*>(ints);
+    size_t size = numInts * sizeof(int32_t);
+    int const* constFds = static_cast<int const*>(fds);
+    status_t status = l->unflatten(constBuffer, size, constFds, numFds);
+
+    delete [] fds;
+    delete [] ints;
+    native_handle_delete(handle);
+    return status == NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/ui/Fence.cpp
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return The required size of the flat buffer.
+ *
+ * The current version of this function always returns 4, which is the number of
+ * bytes required to store the number of file descriptors contained in the fd
+ * part of the flat buffer.
+ */
+inline size_t getFenceFlattenedSize(hidl_handle const& /* fence */) {
+    return 4;
+};
+
+/**
+ * \brief Return the number of file descriptors contained in a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return `0` if \p fence does not contain a valid file descriptor, or `1`
+ * otherwise.
+ */
+inline size_t getFenceFdCount(hidl_handle const& fence) {
+    return native_handle_read_fd(fence) == -1 ? 0 : 1;
+}
+
+/**
+ * \brief Unflatten `Fence` to `hidl_handle`.
+ *
+ * \param[out] fence The destination `hidl_handle`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will point to a newly created
+ * native handle, which needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline status_t unflattenFence(hidl_handle* fence, native_handle_t** nh,
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+    if (size < 4) {
+        return NO_MEMORY;
+    }
+
+    uint32_t numFdsInHandle;
+    FlattenableUtils::read(buffer, size, numFdsInHandle);
+
+    if (numFdsInHandle > 1) {
+        return BAD_VALUE;
+    }
+
+    if (numFds < numFdsInHandle) {
+        return NO_MEMORY;
+    }
+
+    if (numFdsInHandle) {
+        *nh = native_handle_create_from_fd(*fds);
+        if (*nh == nullptr) {
+            return NO_MEMORY;
+        }
+        *fence = *nh;
+        ++fds;
+        --numFds;
+    } else {
+        *nh = nullptr;
+        *fence = hidl_handle();
+    }
+
+    return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `hidl_handle` as `Fence`.
+ *
+ * \param[in] fence The source `hidl_handle`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+inline status_t flattenFence(hidl_handle const& fence,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+    if (size < getFenceFlattenedSize(fence) ||
+            numFds < getFenceFdCount(fence)) {
+        return NO_MEMORY;
+    }
+    // Cast to uint32_t since the size of a size_t can vary between 32- and
+    // 64-bit processes
+    FlattenableUtils::write(buffer, size,
+            static_cast<uint32_t>(getFenceFdCount(fence)));
+    int fd = native_handle_read_fd(fence);
+    if (fd != -1) {
+        *fds = fd;
+        ++fds;
+        --numFds;
+    }
+    return NO_ERROR;
+}
+
+/**
+ * \brief Wrap `Fence` in `hidl_handle`.
+ *
+ * \param[out] t The wrapper of type `hidl_handle`.
+ * \param[out] nh The native handle pointed to by \p t.
+ * \param[in] l The source `Fence`.
+ *
+ * On success, \p nh will hold a newly created native handle, which must be
+ * deleted manually with `native_handle_delete()` afterwards.
+ */
+// wrap: Fence -> hidl_handle
+inline bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) {
+    size_t const baseSize = l.getFlattenedSize();
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = l.getFdCount();
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = static_cast<int*>(baseFds.get());
+    size_t numFds = baseNumFds;
+    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (unflattenFence(t, nh, constBuffer, size, constFds, numFds)
+            != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * \brief Convert `hidl_handle` to `Fence`.
+ *
+ * \param[out] l The destination `Fence`. `l` must not have been used
+ * (`l->isValid()` must return `false`) before this function is called.
+ * \param[in] t The source `hidl_handle`.
+ *
+ * If \p t contains a valid file descriptor, it will be duplicated.
+ */
+// convert: hidl_handle -> Fence
+inline bool convertTo(Fence* l, hidl_handle const& t) {
+    int fd = native_handle_read_fd(t);
+    if (fd != -1) {
+        fd = dup(fd);
+        if (fd == -1) {
+            return false;
+        }
+    }
+    native_handle_t* nh = native_handle_create_from_fd(fd);
+    if (nh == nullptr) {
+        if (fd != -1) {
+            close(fd);
+        }
+        return false;
+    }
+
+    size_t const baseSize = getFenceFlattenedSize(t);
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        native_handle_delete(nh);
+        return false;
+    }
+
+    size_t const baseNumFds = getFenceFdCount(t);
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        native_handle_delete(nh);
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = static_cast<int*>(baseFds.get());
+    size_t numFds = baseNumFds;
+    if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) {
+        native_handle_delete(nh);
+        return false;
+    }
+    native_handle_delete(nh);
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+// Ref: frameworks/native/libs/ui/Region.cpp
+
+/**
+ * \brief Unflatten `HRegion`.
+ *
+ * \param[out] t The destination `HRegion`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+inline status_t unflatten(HRegion* t, void const*& buffer, size_t& size) {
+    if (size < sizeof(uint32_t)) {
+        return NO_MEMORY;
+    }
+
+    uint32_t numRects = 0;
+    FlattenableUtils::read(buffer, size, numRects);
+    if (size < numRects * sizeof(HRect)) {
+        return NO_MEMORY;
+    }
+    if (numRects > (UINT32_MAX / sizeof(HRect))) {
+        return NO_MEMORY;
+    }
+
+    t->resize(numRects);
+    for (size_t r = 0; r < numRects; ++r) {
+        ::android::Rect rect(::android::Rect::EMPTY_RECT);
+        status_t status = rect.unflatten(buffer, size);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        FlattenableUtils::advance(buffer, size, sizeof(rect));
+        (*t)[r] = HRect{
+                static_cast<int32_t>(rect.left),
+                static_cast<int32_t>(rect.top),
+                static_cast<int32_t>(rect.right),
+                static_cast<int32_t>(rect.bottom)};
+    }
+    return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp:
+//      IGraphicBufferProducer::QueueBufferInput
+
+/**
+ * \brief Return a lower bound on the size of the buffer required to flatten
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+        HGraphicBufferProducer::QueueBufferInput const& /* t */) {
+    return sizeof(int64_t) + // timestamp
+            sizeof(int) + // isAutoTimestamp
+            sizeof(android_dataspace) + // dataSpace
+            sizeof(::android::Rect) + // crop
+            sizeof(int) + // scalingMode
+            sizeof(uint32_t) + // transform
+            sizeof(uint32_t) + // stickyTransform
+            sizeof(bool); // getFrameTimestamps
+}
+
+/**
+ * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline status_t unflatten(
+        HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh,
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+    if (size < minFlattenedSize(*t)) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, t->timestamp);
+    int lIsAutoTimestamp;
+    FlattenableUtils::read(buffer, size, lIsAutoTimestamp);
+    t->isAutoTimestamp = static_cast<int32_t>(lIsAutoTimestamp);
+    android_dataspace_t lDataSpace;
+    FlattenableUtils::read(buffer, size, lDataSpace);
+    t->dataSpace = static_cast<Dataspace>(lDataSpace);
+    ::android::Rect lCrop;
+    FlattenableUtils::read(buffer, size, lCrop);
+    t->crop = HRect{
+            static_cast<int32_t>(lCrop.left),
+            static_cast<int32_t>(lCrop.top),
+            static_cast<int32_t>(lCrop.right),
+            static_cast<int32_t>(lCrop.bottom)};
+    int lScalingMode;
+    FlattenableUtils::read(buffer, size, lScalingMode);
+    t->scalingMode = static_cast<int32_t>(lScalingMode);
+    FlattenableUtils::read(buffer, size, t->transform);
+    FlattenableUtils::read(buffer, size, t->stickyTransform);
+    FlattenableUtils::read(buffer, size, t->getFrameTimestamps);
+
+    status_t status = unflattenFence(&(t->fence), nh,
+            buffer, size, fds, numFds);
+    if (status != NO_ERROR) {
+        return status;
+    }
+    return unflatten(&(t->surfaceDamage), buffer, size);
+}
+
+/**
+ * \brief Wrap `IGraphicBufferProducer::QueueBufferInput` in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in] l The source `IGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If the return value is `true` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline bool wrapAs(
+        HGraphicBufferProducer::QueueBufferInput* t,
+        native_handle_t** nh,
+        BGraphicBufferProducer::QueueBufferInput const& l) {
+
+    size_t const baseSize = l.getFlattenedSize();
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = l.getFdCount();
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = baseFds.get();
+    size_t numFds = baseNumFds;
+    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+        HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+    constexpr size_t min = sizeof(t.state);
+    switch (t.state) {
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+            return min;
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+            return min + getFenceFlattenedSize(t.fence);
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+            return min + sizeof(
+                    ::android::FenceTime::Snapshot::signalTime);
+    }
+    return 0;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The number of file descriptors contained in \p snapshot.
+ */
+inline size_t getFdCount(
+        HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+    return t.state ==
+            HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ?
+            getFenceFdCount(t.fence) : 0;
+}
+
+/**
+ * \brief Flatten `FenceTimeSnapshot`.
+ *
+ * \param[in] t The source `FenceTimeSnapshot`.
+ * \param[out] nh The cloned native handle, if necessary.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence` if `t.state ==
+ * FENCE`, in which case \p nh will be returned.
+ */
+inline status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t,
+        native_handle_t** nh,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+    if (size < getFlattenedSize(t)) {
+        return NO_MEMORY;
+    }
+
+    *nh = nullptr;
+    switch (t.state) {
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+            FlattenableUtils::write(buffer, size,
+                    ::android::FenceTime::Snapshot::State::EMPTY);
+            return NO_ERROR;
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+            FlattenableUtils::write(buffer, size,
+                    ::android::FenceTime::Snapshot::State::FENCE);
+            *nh = t.fence.getNativeHandle() == nullptr ?
+                    nullptr : native_handle_clone(t.fence);
+            return flattenFence(hidl_handle(*nh), buffer, size, fds, numFds);
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+            FlattenableUtils::write(buffer, size,
+                    ::android::FenceTime::Snapshot::State::SIGNAL_TIME);
+            FlattenableUtils::write(buffer, size, t.signalTimeNs);
+            return NO_ERROR;
+    }
+    return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta
+
+/**
+ * \brief Return a lower bound on the size of the non-fd buffer required to
+ * flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+        HGraphicBufferProducer::FrameEventsDelta const& /* t */) {
+    return sizeof(uint64_t) + // mFrameNumber
+            sizeof(uint8_t) + // mIndex
+            sizeof(uint8_t) + // mAddPostCompositeCalled
+            sizeof(uint8_t) + // mAddRetireCalled
+            sizeof(uint8_t) + // mAddReleaseCalled
+            sizeof(nsecs_t) + // mPostedTime
+            sizeof(nsecs_t) + // mRequestedPresentTime
+            sizeof(nsecs_t) + // mLatchTime
+            sizeof(nsecs_t) + // mFirstRefreshStartTime
+            sizeof(nsecs_t); // mLastRefreshStartTime
+}
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+        HGraphicBufferProducer::FrameEventsDelta const& t) {
+    return minFlattenedSize(t) +
+            getFlattenedSize(t.gpuCompositionDoneFence) +
+            getFlattenedSize(t.displayPresentFence) +
+            getFlattenedSize(t.displayRetireFence) +
+            getFlattenedSize(t.releaseFence);
+};
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+inline size_t getFdCount(
+        HGraphicBufferProducer::FrameEventsDelta const& t) {
+    return getFdCount(t.gpuCompositionDoneFence) +
+            getFdCount(t.displayPresentFence) +
+            getFdCount(t.displayRetireFence) +
+            getFdCount(t.releaseFence);
+};
+
+/**
+ * \brief Flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The source `FrameEventsDelta`.
+ * \param[out] nh The array of native handles that are cloned.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * On success, this function will duplicate file descriptors contained in \p t.
+ * The cloned native handles will be stored in \p nh. These native handles will
+ * need to be closed by the caller.
+ */
+// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp:
+//      FrameEventsDelta::flatten
+inline status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
+        std::vector<native_handle_t*>* nh,
+        void*& buffer, size_t& size, int*& fds, size_t numFds) {
+    // Check that t.index is within a valid range.
+    if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
+            || t.index > std::numeric_limits<uint8_t>::max()) {
+        return BAD_VALUE;
+    }
+
+    FlattenableUtils::write(buffer, size, t.frameNumber);
+
+    // These are static_cast to uint8_t for alignment.
+    FlattenableUtils::write(buffer, size, static_cast<uint8_t>(t.index));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(t.addPostCompositeCalled));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(t.addRetireCalled));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(t.addReleaseCalled));
+
+    FlattenableUtils::write(buffer, size, t.postedTimeNs);
+    FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs);
+    FlattenableUtils::write(buffer, size, t.latchTimeNs);
+    FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs);
+    FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs);
+    FlattenableUtils::write(buffer, size, t.dequeueReadyTime);
+
+    // Fences
+    HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4];
+    tSnapshot[0] = &t.gpuCompositionDoneFence;
+    tSnapshot[1] = &t.displayPresentFence;
+    tSnapshot[2] = &t.displayRetireFence;
+    tSnapshot[3] = &t.releaseFence;
+    nh->resize(4);
+    for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) {
+        status_t status = flatten(
+                *(tSnapshot[snapshotIndex]),
+                &((*nh)[snapshotIndex]),
+                buffer, size, fds, numFds);
+        if (status != NO_ERROR) {
+            while (snapshotIndex > 0) {
+                --snapshotIndex;
+                native_handle_close((*nh)[snapshotIndex]);
+                native_handle_delete((*nh)[snapshotIndex]);
+                (*nh)[snapshotIndex] = nullptr;
+            }
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+    size_t size = 4 + // mDeltas.size()
+            sizeof(t.compositorTiming);
+    for (size_t i = 0; i < t.deltas.size(); ++i) {
+        size += getFlattenedSize(t.deltas[i]);
+    }
+    return size;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+inline size_t getFdCount(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+    size_t numFds = 0;
+    for (size_t i = 0; i < t.deltas.size(); ++i) {
+        numFds += getFdCount(t.deltas[i]);
+    }
+    return numFds;
+}
+
+/**
+ * \brief Flatten `FrameEventHistoryDelta`.
+ *
+ * \param[in] t The source `FrameEventHistoryDelta`.
+ * \param[out] nh The array of arrays of cloned native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * On success, this function will duplicate file descriptors contained in \p t.
+ * The cloned native handles will be stored in \p nh. Before making the call, \p
+ * nh should have enough space to store `n` pointers to arrays of native
+ * handles, where `n` is the length of `t.deltas`, and each `nh[i]` should have
+ * enough space to store `4` native handles.
+ */
+inline status_t flatten(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t,
+        std::vector<std::vector<native_handle_t*> >* nh,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+    if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+        return BAD_VALUE;
+    }
+    if (size < getFlattenedSize(t)) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, t.compositorTiming);
+
+    FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.deltas.size()));
+    nh->resize(t.deltas.size());
+    for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) {
+        status_t status = flatten(
+                t.deltas[deltaIndex], &((*nh)[deltaIndex]),
+                buffer, size, fds, numFds);
+        if (status != NO_ERROR) {
+            while (deltaIndex > 0) {
+                --deltaIndex;
+                for (size_t snapshotIndex = 0;
+                        snapshotIndex < 4; ++snapshotIndex) {
+                    native_handle_close((*nh)[deltaIndex][snapshotIndex]);
+                    native_handle_delete((*nh)[deltaIndex][snapshotIndex]);
+                    (*nh)[deltaIndex][snapshotIndex] = nullptr;
+                }
+            }
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to
+ * `::android::FrameEventHistoryDelta`.
+ *
+ * \param[out] l The destination `::android::FrameEventHistoryDelta`.
+ * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+inline bool convertTo(
+        ::android::FrameEventHistoryDelta* l,
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+
+    size_t const baseSize = getFlattenedSize(t);
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = getFdCount(t);
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = static_cast<int*>(baseFds.get());
+    size_t numFds = baseNumFds;
+    std::vector<std::vector<native_handle_t*> > nhAA;
+    if (flatten(t, &nhAA, buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+        for (auto nhA : nhAA) {
+            for (auto nh : nhA) {
+                if (nh != nullptr) {
+                    native_handle_close(nh);
+                    native_handle_delete(nh);
+                }
+            }
+        }
+        return false;
+    }
+
+    for (auto nhA : nhAA) {
+        for (auto nh : nhA) {
+            if (nh != nullptr) {
+                native_handle_delete(nh);
+            }
+        }
+    }
+    return true;
+}
+
+// Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp:
+//      IGraphicBufferProducer::QueueBufferOutput
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to
+ * `IGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] l The destination `IGraphicBufferProducer::QueueBufferOutput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+// convert: HGraphicBufferProducer::QueueBufferOutput ->
+// IGraphicBufferProducer::QueueBufferOutput
+inline bool convertTo(
+        BGraphicBufferProducer::QueueBufferOutput* l,
+        HGraphicBufferProducer::QueueBufferOutput const& t) {
+    if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) {
+        return false;
+    }
+    l->width = t.width;
+    l->height = t.height;
+    l->transformHint = t.transformHint;
+    l->numPendingBuffers = t.numPendingBuffers;
+    l->nextFrameNumber = t.nextFrameNumber;
+    l->bufferReplaced = t.bufferReplaced;
+    return true;
+}
+
+/**
+ * \brief Convert `IGraphicBufferProducer::DisconnectMode` to
+ * `HGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `IGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `HGraphicBufferProducer::DisconnectMode`.
+ */
+inline HGraphicBufferProducer::DisconnectMode toHDisconnectMode(
+        BGraphicBufferProducer::DisconnectMode l) {
+    switch (l) {
+        case BGraphicBufferProducer::DisconnectMode::Api:
+            return HGraphicBufferProducer::DisconnectMode::API;
+        case BGraphicBufferProducer::DisconnectMode::AllLocal:
+            return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL;
+    }
+    return HGraphicBufferProducer::DisconnectMode::API;
+}
+
+// H2BGraphicBufferProducer
+
+status_t H2BGraphicBufferProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+    *buf = new GraphicBuffer();
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->requestBuffer(
+            static_cast<int32_t>(slot),
+            [&fnStatus, &buf] (Status status, AnwBuffer const& buffer) {
+                fnStatus = toStatusT(status);
+                if (!convertTo(buf->get(), buffer)) {
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::setMaxDequeuedBufferCount(
+        int maxDequeuedBuffers) {
+    return toStatusT(mBase->setMaxDequeuedBufferCount(
+            static_cast<int32_t>(maxDequeuedBuffers)));
+}
+
+status_t H2BGraphicBufferProducer::setAsyncMode(bool async) {
+    return toStatusT(mBase->setAsyncMode(async));
+}
+
+status_t H2BGraphicBufferProducer::dequeueBuffer(
+        int* slot, sp<Fence>* fence,
+        uint32_t w, uint32_t h, ::android::PixelFormat format,
+        uint32_t usage, FrameEventHistoryDelta* outTimestamps) {
+    *fence = new Fence();
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->dequeueBuffer(
+            w, h, static_cast<PixelFormat>(format), usage,
+            outTimestamps != nullptr,
+            [&fnStatus, slot, fence, outTimestamps] (
+                    Status status,
+                    int32_t tSlot,
+                    hidl_handle const& tFence,
+                    HGraphicBufferProducer::FrameEventHistoryDelta const& tTs) {
+                fnStatus = toStatusT(status);
+                *slot = tSlot;
+                if (!convertTo(fence->get(), tFence)) {
+                    ALOGE("H2BGraphicBufferProducer::dequeueBuffer - "
+                            "Invalid output fence");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+                if (outTimestamps && !convertTo(outTimestamps, tTs)) {
+                    ALOGE("H2BGraphicBufferProducer::dequeueBuffer - "
+                            "Invalid output timestamps");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::detachBuffer(int slot) {
+    return toStatusT(mBase->detachBuffer(static_cast<int>(slot)));
+}
+
+status_t H2BGraphicBufferProducer::detachNextBuffer(
+        sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+    *outBuffer = new GraphicBuffer();
+    *outFence = new Fence();
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->detachNextBuffer(
+            [&fnStatus, outBuffer, outFence] (
+                    Status status,
+                    AnwBuffer const& tBuffer,
+                    hidl_handle const& tFence) {
+                fnStatus = toStatusT(status);
+                if (!convertTo(outFence->get(), tFence)) {
+                    ALOGE("H2BGraphicBufferProducer::detachNextBuffer - "
+                            "Invalid output fence");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+                if (!convertTo(outBuffer->get(), tBuffer)) {
+                    ALOGE("H2BGraphicBufferProducer::detachNextBuffer - "
+                            "Invalid output buffer");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::attachBuffer(
+        int* outSlot, const sp<GraphicBuffer>& buffer) {
+    AnwBuffer tBuffer;
+    wrapAs(&tBuffer, *buffer);
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->attachBuffer(tBuffer,
+            [&fnStatus, outSlot] (Status status, int32_t slot) {
+                fnStatus = toStatusT(status);
+                *outSlot = slot;
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::queueBuffer(
+        int slot,
+        const QueueBufferInput& input,
+        QueueBufferOutput* output) {
+    HGraphicBufferProducer::QueueBufferInput tInput;
+    native_handle_t* nh;
+    if (!wrapAs(&tInput, &nh, input)) {
+        ALOGE("H2BGraphicBufferProducer::queueBuffer - "
+                "Invalid input");
+        return BAD_VALUE;
+    }
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->queueBuffer(slot, tInput,
+            [&fnStatus, output] (
+                    Status status,
+                    HGraphicBufferProducer::QueueBufferOutput const& tOutput) {
+                fnStatus = toStatusT(status);
+                if (!convertTo(output, tOutput)) {
+                    ALOGE("H2BGraphicBufferProducer::queueBuffer - "
+                            "Invalid output");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    native_handle_delete(nh);
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
+    hidl_handle tFence;
+    native_handle_t* nh = nullptr;
+    if ((fence == nullptr) || !wrapAs(&tFence, &nh, *fence)) {
+        ALOGE("H2BGraphicBufferProducer::cancelBuffer - "
+                "Invalid input fence");
+        return BAD_VALUE;
+    }
+
+    status_t status = toStatusT(mBase->cancelBuffer(
+            static_cast<int32_t>(slot), tFence));
+    native_handle_delete(nh);
+    return status;
+}
+
+int H2BGraphicBufferProducer::query(int what, int* value) {
+    int result;
+    status_t transStatus = toStatusT(mBase->query(
+            static_cast<int32_t>(what),
+            [&result, value] (int32_t tResult, int32_t tValue) {
+                result = static_cast<int>(tResult);
+                *value = static_cast<int>(tValue);
+            }));
+    return transStatus == NO_ERROR ? result : static_cast<int>(transStatus);
+}
+
+status_t H2BGraphicBufferProducer::connect(
+        const sp<IProducerListener>& listener, int api,
+        bool producerControlledByApp, QueueBufferOutput* output) {
+    sp<HProducerListener> tListener = listener == nullptr ?
+            nullptr : new B2HProducerListener(listener);
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->connect(
+            tListener, static_cast<int32_t>(api), producerControlledByApp,
+            [&fnStatus, output] (
+                    Status status,
+                    HGraphicBufferProducer::QueueBufferOutput const& tOutput) {
+                fnStatus = toStatusT(status);
+                if (!convertTo(output, tOutput)) {
+                    ALOGE("H2BGraphicBufferProducer::connect - "
+                            "Invalid output");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::disconnect(int api, DisconnectMode mode) {
+    return toStatusT(mBase->disconnect(
+            static_cast<int32_t>(api), toHDisconnectMode(mode)));
+}
+
+status_t H2BGraphicBufferProducer::setSidebandStream(
+        const sp<NativeHandle>& stream) {
+    return toStatusT(mBase->setSidebandStream(stream->handle()));
+}
+
+void H2BGraphicBufferProducer::allocateBuffers(uint32_t width, uint32_t height,
+        ::android::PixelFormat format, uint32_t usage) {
+    mBase->allocateBuffers(
+            width, height, static_cast<PixelFormat>(format), usage);
+}
+
+status_t H2BGraphicBufferProducer::allowAllocation(bool allow) {
+    return toStatusT(mBase->allowAllocation(allow));
+}
+
+status_t H2BGraphicBufferProducer::setGenerationNumber(uint32_t generationNumber) {
+    return toStatusT(mBase->setGenerationNumber(generationNumber));
+}
+
+String8 H2BGraphicBufferProducer::getConsumerName() const {
+    String8 lName;
+    mBase->getConsumerName([&lName] (hidl_string const& name) {
+                lName = name.c_str();
+            });
+    return lName;
+}
+
+status_t H2BGraphicBufferProducer::setSharedBufferMode(bool sharedBufferMode) {
+    return toStatusT(mBase->setSharedBufferMode(sharedBufferMode));
+}
+
+status_t H2BGraphicBufferProducer::setAutoRefresh(bool autoRefresh) {
+    return toStatusT(mBase->setAutoRefresh(autoRefresh));
+}
+
+status_t H2BGraphicBufferProducer::setDequeueTimeout(nsecs_t timeout) {
+    return toStatusT(mBase->setDequeueTimeout(static_cast<int64_t>(timeout)));
+}
+
+status_t H2BGraphicBufferProducer::getLastQueuedBuffer(
+        sp<GraphicBuffer>* outBuffer,
+        sp<Fence>* outFence,
+        float outTransformMatrix[16]) {
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->getLastQueuedBuffer(
+            [&fnStatus, outBuffer, outFence, &outTransformMatrix] (
+                    Status status,
+                    AnwBuffer const& buffer,
+                    hidl_handle const& fence,
+                    hidl_array<float, 16> const& transformMatrix) {
+                fnStatus = toStatusT(status);
+                *outBuffer = new GraphicBuffer();
+                if (!convertTo(outBuffer->get(), buffer)) {
+                    ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
+                            "Invalid output buffer");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+                *outFence = new Fence();
+                if (!convertTo(outFence->get(), fence)) {
+                    ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
+                            "Invalid output fence");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+                std::copy(transformMatrix.data(),
+                        transformMatrix.data() + 16,
+                        outTransformMatrix);
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+void H2BGraphicBufferProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+    mBase->getFrameTimestamps([outDelta] (
+            HGraphicBufferProducer::FrameEventHistoryDelta const& tDelta) {
+                convertTo(outDelta, tDelta);
+            });
+}
+
+status_t H2BGraphicBufferProducer::getUniqueId(uint64_t* outId) const {
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->getUniqueId(
+            [&fnStatus, outId] (Status status, uint64_t id) {
+                fnStatus = toStatusT(status);
+                *outId = id;
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace bufferqueue
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
index 3ca8bd1..ef1e45f 100644
--- a/libs/math/include/math/half.h
+++ b/libs/math/include/math/half.h
@@ -21,6 +21,7 @@
 #include <limits>
 #include <type_traits>
 
+#ifndef LIKELY
 #ifdef __cplusplus
 #   define LIKELY( exp )    (__builtin_expect( !!(exp), true ))
 #   define UNLIKELY( exp )  (__builtin_expect( !!(exp), false ))
@@ -28,6 +29,7 @@
 #   define LIKELY( exp )    (__builtin_expect( !!(exp), 1 ))
 #   define UNLIKELY( exp )  (__builtin_expect( !!(exp), 0 ))
 #endif
+#endif
 
 #if __cplusplus >= 201402L
 #define CONSTEXPR constexpr
diff --git a/libs/vr/libbufferhubqueue/Android.mk b/libs/vr/libbufferhubqueue/Android.mk
index 3ed7ff2..d53f06a 100644
--- a/libs/vr/libbufferhubqueue/Android.mk
+++ b/libs/vr/libbufferhubqueue/Android.mk
@@ -36,10 +36,12 @@
 	liblog \
 	libui \
 	libutils \
+        libgui \
 
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(sourceFiles)
 LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"libbufferhubqueue\"
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
 LOCAL_STATIC_LIBRARIES := $(staticLibraries)
 LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index 0576b21..bad9503 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -1,5 +1,7 @@
 #include "include/private/dvr/buffer_hub_queue_client.h"
 
+//#define LOG_NDEBUG 0
+
 #include <inttypes.h>
 #include <log/log.h>
 #include <sys/epoll.h>
@@ -24,6 +26,7 @@
       meta_size_(meta_size),
       meta_buffer_tmp_(meta_size ? new uint8_t[meta_size] : nullptr),
       buffers_(BufferHubQueue::kMaxQueueCapacity),
+      epollhup_pending_(BufferHubQueue::kMaxQueueCapacity, false),
       available_buffers_(BufferHubQueue::kMaxQueueCapacity),
       capacity_(0) {
   Initialize();
@@ -36,6 +39,7 @@
       meta_size_(meta_size),
       meta_buffer_tmp_(meta_size ? new uint8_t[meta_size] : nullptr),
       buffers_(BufferHubQueue::kMaxQueueCapacity),
+      epollhup_pending_(BufferHubQueue::kMaxQueueCapacity, false),
       available_buffers_(BufferHubQueue::kMaxQueueCapacity),
       capacity_(0) {
   Initialize();
@@ -101,31 +105,12 @@
 
       ALOGD("New BufferHubQueue event %d: index=%" PRId64, i, index);
 
-      if (is_buffer_event_index(index) && (events[i].events & EPOLLIN)) {
-        auto buffer = buffers_[index];
-        ret = OnBufferReady(buffer);
-        if (ret < 0) {
-          ALOGE("Failed to set buffer ready: %s", strerror(-ret));
-          continue;
-        }
-        Enqueue(buffer, index);
-      } else if (is_buffer_event_index(index) &&
-                 (events[i].events & EPOLLHUP)) {
-        // This maybe caused by producer replacing an exising buffer slot.
-        // Currently the epoll FD is cleaned up when the replacement consumer
-        // client is imported.
-        ALOGW("Receives EPOLLHUP at slot: %" PRId64, index);
-      } else if (is_queue_event_index(index) && (events[i].events & EPOLLIN)) {
-        // Note that after buffer imports, if |count()| still returns 0, epoll
-        // wait will be tried again to acquire the newly imported buffer.
-        ret = OnBufferAllocated();
-        if (ret < 0) {
-          ALOGE("Failed to import buffer: %s", strerror(-ret));
-          continue;
-        }
+      if (is_buffer_event_index(index)) {
+        HandleBufferEvent(static_cast<size_t>(index), events[i]);
+      } else if (is_queue_event_index(index)) {
+        HandleQueueEvent(events[i]);
       } else {
-        ALOGW("Unknown event %d: u64=%" PRId64 ": events=%" PRIu32, i, index,
-              events[i].events);
+        ALOGW("Unknown event index: %" PRId64, index);
       }
     }
   }
@@ -133,6 +118,68 @@
   return true;
 }
 
+void BufferHubQueue::HandleBufferEvent(size_t slot, const epoll_event& event) {
+  auto buffer = buffers_[slot];
+  if (!buffer) {
+    ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot);
+    return;
+  }
+
+  auto status = buffer->GetEventMask(event.events);
+  if (!status) {
+    ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s",
+          status.GetErrorMessage().c_str());
+    return;
+  }
+
+  int events = status.get();
+  if (events & EPOLLIN) {
+    int ret = OnBufferReady(buffer);
+    if (ret < 0) {
+      ALOGE("Failed to set buffer ready: %s", strerror(-ret));
+      return;
+    }
+    Enqueue(buffer, slot);
+  } else if (events & EPOLLHUP) {
+    // This might be caused by producer replacing an existing buffer slot, or
+    // when BufferHubQueue is shutting down. For the first case, currently the
+    // epoll FD is cleaned up when the replacement consumer client is imported,
+    // we shouldn't detach again if |epollhub_pending_[slot]| is set.
+    ALOGW(
+        "Receives EPOLLHUP at slot: %zu, buffer event fd: %d, EPOLLHUP "
+        "pending: %d",
+        slot, buffer->event_fd(), epollhup_pending_[slot]);
+    if (epollhup_pending_[slot]) {
+      epollhup_pending_[slot] = false;
+    } else {
+      DetachBuffer(slot);
+    }
+  } else {
+    ALOGW("Unknown event, slot=%zu, epoll events=%d", slot, events);
+  }
+}
+
+void BufferHubQueue::HandleQueueEvent(const epoll_event& event) {
+  auto status = GetEventMask(event.events);
+  if (!status) {
+    ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s",
+          status.GetErrorMessage().c_str());
+    return;
+  }
+
+  int events = status.get();
+  if (events & EPOLLIN) {
+    // Note that after buffer imports, if |count()| still returns 0, epoll
+    // wait will be tried again to acquire the newly imported buffer.
+    int ret = OnBufferAllocated();
+    if (ret < 0) {
+      ALOGE("Failed to import buffer: %s", strerror(-ret));
+    }
+  } else {
+    ALOGW("Unknown epoll events=%d", events);
+  }
+}
+
 int BufferHubQueue::AddBuffer(const std::shared_ptr<BufferHubBuffer>& buf,
                               size_t slot) {
   if (is_full()) {
@@ -146,8 +193,9 @@
   if (buffers_[slot] != nullptr) {
     // Replace the buffer if the slot is preoccupied. This could happen when the
     // producer side replaced the slot with a newly allocated buffer. Detach the
-    // buffer and set up with the new one.
+    // buffer before setting up with the new one.
     DetachBuffer(slot);
+    epollhup_pending_[slot] = true;
   }
 
   epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u64 = slot}};
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp
index 1ea3994..02bca09 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp
@@ -1,5 +1,7 @@
 #include "include/private/dvr/buffer_hub_queue_consumer.h"
 
+//#define LOG_NDEBUG 0
+
 namespace android {
 namespace dvr {
 
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
index a108042..b013c85 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
@@ -1,5 +1,8 @@
 #include "include/private/dvr/buffer_hub_queue_core.h"
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "BufferHubQueueCore"
+
 #include <log/log.h>
 
 namespace android {
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
index 752e8c4..7ddf49b 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
@@ -1,5 +1,7 @@
 #include "include/private/dvr/buffer_hub_queue_producer.h"
 
+//#define LOG_NDEBUG 0
+
 #include <inttypes.h>
 #include <log/log.h>
 
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
index 83e77d4..1f2830a 100644
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -49,6 +49,14 @@
     return buffers_[slot];
   }
 
+  Status<int> GetEventMask(int events) {
+    if (auto* client_channel = GetChannel()) {
+      return client_channel->GetEventMask(events);
+    } else {
+      return pdx::ErrorStatus(EINVAL);
+    }
+  }
+
   // Enqueue a buffer marks buffer to be available (|Gain|'ed for producer
   // and |Acquire|'ed for consumer. This is only used for internal bookkeeping.
   void Enqueue(std::shared_ptr<BufferHubBuffer> buf, size_t slot);
@@ -87,6 +95,9 @@
 
   // Wait for buffers to be released and re-add them to the queue.
   bool WaitForBuffers(int timeout);
+  void HandleBufferEvent(size_t slot, const epoll_event& event);
+  void HandleQueueEvent(const epoll_event& event);
+
   virtual int OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) = 0;
 
   // Called when a buffer is allocated remotely.
@@ -160,6 +171,30 @@
   // |buffers_| tracks all |BufferHubBuffer|s created by this |BufferHubQueue|.
   std::vector<std::shared_ptr<BufferHubBuffer>> buffers_;
 
+  // |epollhup_pending_| tracks whether a slot of |buffers_| get detached before
+  // its corresponding EPOLLHUP event got handled. This could happen as the
+  // following sequence:
+  // 1. Producer queue's client side allocates a new buffer (at slot 1).
+  // 2. Producer queue's client side replaces an existing buffer (at slot 0).
+  //    This is implemented by first detaching the buffer and then allocating a
+  //    new buffer.
+  // 3. During the same epoll_wait, Consumer queue's client side gets EPOLLIN
+  //    event on the queue which indicates a new buffer is avaiable and the
+  //    EPOLLHUP event for slot 0. Consumer handles these two events in order.
+  // 4. Consumer client calls BufferHubRPC::ConsumerQueueImportBuffers and both
+  //    slot 0 and (the new) slot 1 buffer will be imported. During the import
+  //    of the buffer at slot 1, consuemr client detaches the old buffer so that
+  //    the new buffer can be registered. At the same time
+  //    |epollhup_pending_[slot]| is marked to indicate that buffer at this slot
+  //    was detached prior to EPOLLHUP event.
+  // 5. Consumer client continues to handle the EPOLLHUP. Since
+  //    |epollhup_pending_[slot]| is marked as true, it can safely ignore the
+  //    event without detaching the newly allocated buffer at slot 1.
+  //
+  // In normal situations where the previously described sequence doesn't
+  // happen, an EPOLLHUP event should trigger a regular buffer detach.
+  std::vector<bool> epollhup_pending_;
+
   // |available_buffers_| uses |dvr::RingBuffer| to implementation queue
   // sematics. When |Dequeue|, we pop the front element from
   // |available_buffers_|, and  that buffer's reference count will decrease by
@@ -225,7 +260,7 @@
   // Returns Zero on success and negative error code when buffer allocation
   // fails.
   int AllocateBuffer(int width, int height, int format, int usage,
-                     size_t buffer_count, size_t* out_slot);
+                     size_t slice_count, size_t* out_slot);
 
   // Add a producer buffer to populate the queue. Once added, a producer buffer
   // is available to use (i.e. in |Gain|'ed mode).
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
index 8fd3627..fe18619 100644
--- a/libs/vr/libdisplay/display_manager_client.cpp
+++ b/libs/vr/libdisplay/display_manager_client.cpp
@@ -98,14 +98,9 @@
 int dvrDisplayManagerClientGetSurfaceBuffers(
     DvrDisplayManagerClient* client, int surface_id,
     DvrDisplayManagerClientSurfaceBuffers** surface_buffers) {
-  std::vector<std::unique_ptr<android::dvr::BufferConsumer>> buffer_list;
-  int ret = client->client->GetSurfaceBuffers(surface_id, &buffer_list);
-  if (ret < 0)
-    return ret;
-
-  *surface_buffers =
-      new DvrDisplayManagerClientSurfaceBuffers(std::move(buffer_list));
-  return ret;
+  // TODO(jwcai, hendrikw) Remove this after we replacing
+  // dvrDisplayManagerClientGetSurfaceBuffers is dvr_api.
+  return -1;
 }
 
 void dvrDisplayManagerClientSurfaceBuffersDestroy(
diff --git a/libs/vr/libdisplay/display_manager_client_impl.cpp b/libs/vr/libdisplay/display_manager_client_impl.cpp
index 82198b9..3fbd1e0 100644
--- a/libs/vr/libdisplay/display_manager_client_impl.cpp
+++ b/libs/vr/libdisplay/display_manager_client_impl.cpp
@@ -31,27 +31,5 @@
   return 0;
 }
 
-int DisplayManagerClient::GetSurfaceBuffers(
-    int surface_id, std::vector<std::unique_ptr<BufferConsumer>>* consumers) {
-  auto status =
-      InvokeRemoteMethod<DisplayManagerRPC::GetSurfaceBuffers>(surface_id);
-  if (!status) {
-    ALOGE(
-        "DisplayManagerClient::GetSurfaceBuffers: Failed to get buffers for "
-        "surface_id=%d: %s",
-        surface_id, status.GetErrorMessage().c_str());
-    return -status.error();
-  }
-
-  std::vector<std::unique_ptr<BufferConsumer>> consumer_buffers;
-  std::vector<LocalChannelHandle> channel_handles = status.take();
-  for (auto&& handle : channel_handles) {
-    consumer_buffers.push_back(BufferConsumer::Import(std::move(handle)));
-  }
-
-  *consumers = std::move(consumer_buffers);
-  return 0;
-}
-
 }  // namespace dvr
 }  // namespace android
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
index 8ba9175..f515b8f 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
@@ -62,10 +62,8 @@
 bool dvrDisplayManagerClientSurfaceListGetClientIsVisible(
     DvrDisplayManagerClientSurfaceList* surface_list, size_t index);
 
-// Populates |surface_buffers| with the list of buffers for |surface_id|.
-// |surface_id| should be a valid ID from the list of surfaces.
-//
-// @return Returns 0 on success. Otherwise it returns a negative error value.
+// TODO(jwcai, hendrikw) Remove this after we replacing
+// dvrDisplayManagerClientGetSurfaceBuffers is dvr_api.
 int dvrDisplayManagerClientGetSurfaceBuffers(
     DvrDisplayManagerClient* client, int surface_id,
     DvrDisplayManagerClientSurfaceBuffers** surface_buffers);
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h
index 4ecd8d4..0897126 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h
@@ -17,9 +17,6 @@
 
   int GetSurfaceList(std::vector<DisplaySurfaceInfo>* surface_list);
 
-  int GetSurfaceBuffers(
-      int surface_id, std::vector<std::unique_ptr<BufferConsumer>>* consumers);
-
   using Client::event_fd;
   using Client::GetChannel;
 
diff --git a/libs/vr/libdisplay/include/private/dvr/display_rpc.h b/libs/vr/libdisplay/include/private/dvr/display_rpc.h
index 1c1a5e0..d37aed7 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_rpc.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_rpc.h
@@ -258,7 +258,6 @@
   // Op codes.
   enum {
     kOpGetSurfaceList = 0,
-    kOpGetSurfaceBuffers,
     kOpUpdateSurfaces,
   };
 
@@ -269,8 +268,6 @@
   // Methods.
   PDX_REMOTE_METHOD(GetSurfaceList, kOpGetSurfaceList,
                     std::vector<DisplaySurfaceInfo>(Void));
-  PDX_REMOTE_METHOD(GetSurfaceBuffers, kOpGetSurfaceBuffers,
-                    std::vector<LocalChannelHandle>(int surface_id));
   PDX_REMOTE_METHOD(
       UpdateSurfaces, kOpUpdateSurfaces,
       int(const std::map<int, DisplaySurfaceAttributes>& updates));
diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp
index 6730ba8..6df1642 100644
--- a/libs/vr/libvrflinger/display_manager_service.cpp
+++ b/libs/vr/libvrflinger/display_manager_service.cpp
@@ -77,11 +77,6 @@
           *this, &DisplayManagerService::OnGetSurfaceList, message);
       return 0;
 
-    case DisplayManagerRPC::GetSurfaceBuffers::Opcode:
-      DispatchRemoteMethod<DisplayManagerRPC::GetSurfaceBuffers>(
-          *this, &DisplayManagerService::OnGetSurfaceBuffers, message);
-      return 0;
-
     case DisplayManagerRPC::UpdateSurfaces::Opcode:
       DispatchRemoteMethod<DisplayManagerRPC::UpdateSurfaces>(
           *this, &DisplayManagerService::OnUpdateSurfaces, message);
@@ -128,26 +123,6 @@
   return items;
 }
 
-std::vector<LocalChannelHandle> DisplayManagerService::OnGetSurfaceBuffers(
-    pdx::Message& message, int surface_id) {
-  std::shared_ptr<DisplaySurface> surface =
-      display_service_->GetDisplaySurface(surface_id);
-  if (!surface)
-    REPLY_ERROR_RETURN(message, ENOENT, {});
-
-  std::vector<LocalChannelHandle> consumers;
-  int ret = surface->GetConsumers(&consumers);
-  if (ret < 0) {
-    ALOGE(
-        "DisplayManagerService::OnGetDisplaySurfaceBuffers: Failed to get "
-        "consumers for surface %d: %s",
-        surface_id, strerror(-ret));
-    REPLY_ERROR_RETURN(message, -ret, {});
-  }
-
-  return consumers;
-}
-
 int DisplayManagerService::OnUpdateSurfaces(
     pdx::Message& /*message*/,
     const std::map<int, DisplaySurfaceAttributes>& updates) {
diff --git a/libs/vr/libvrflinger/display_manager_service.h b/libs/vr/libvrflinger/display_manager_service.h
index 46401fa..3f83c7d 100644
--- a/libs/vr/libvrflinger/display_manager_service.h
+++ b/libs/vr/libvrflinger/display_manager_service.h
@@ -51,8 +51,6 @@
       const std::shared_ptr<DisplayService>& display_service);
 
   std::vector<DisplaySurfaceInfo> OnGetSurfaceList(pdx::Message& message);
-  std::vector<pdx::LocalChannelHandle> OnGetSurfaceBuffers(
-      pdx::Message& message, int surface_id);
   int OnUpdateSurfaces(pdx::Message& message,
                        const std::map<int, DisplaySurfaceAttributes>& updates);
 
diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp
index 7c821bf..d427ea6 100644
--- a/libs/vr/libvrflinger/display_surface.cpp
+++ b/libs/vr/libvrflinger/display_surface.cpp
@@ -84,22 +84,6 @@
   client_blur_behind_ = blur_behind;
 }
 
-size_t DisplaySurface::GetBufferCount() const {
-  std::lock_guard<std::mutex> autolock(lock_);
-  return buffers_.size();
-}
-
-std::vector<std::shared_ptr<BufferConsumer>> DisplaySurface::GetBuffers() {
-  std::lock_guard<std::mutex> autolock(lock_);
-  std::vector<std::shared_ptr<BufferConsumer>> return_vector(buffers_.size());
-
-  for (const auto pair : buffers_) {
-    return_vector.push_back(pair.second);
-  }
-
-  return return_vector;
-}
-
 AcquiredBuffer DisplaySurface::AcquireNewestAvailableBuffer(
     AcquiredBuffer* skipped_buffer) {
   std::lock_guard<std::mutex> autolock(lock_);
diff --git a/libs/vr/libvrflinger/display_surface.h b/libs/vr/libvrflinger/display_surface.h
index b7bcd97..fa34057 100644
--- a/libs/vr/libvrflinger/display_surface.h
+++ b/libs/vr/libvrflinger/display_surface.h
@@ -65,9 +65,6 @@
     return buffer_id_to_index_[buffer_id];
   }
 
-  size_t GetBufferCount() const;
-  std::vector<std::shared_ptr<BufferConsumer>> GetBuffers();
-
   // Gets a new set of consumers for all of the surface's buffers. These
   // consumers are independent from the consumers maintained internally to the
   // surface and may be passed to other processes over IPC.
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 740ead3..4c4cd4e 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -670,12 +670,42 @@
 #endif
 #endif
 
-#ifndef EGL_KHR_pixel_format_float
-#define EGL_KHR_pixel_format_float 1
-#define EGL_COLOR_COMPONENT_TYPE_EXT 0x3339  // eglChooseConfig and eglGetConfigAttrib attribute
-#define EGL_COLOR_COMPONENT_TYPE_FIXED_EXT 0x333A  // Attribute value for COLOR_COMPONENT_TYPE
-#define EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT 0x333B  // Attribute value for COLOR_COMPONENT_TYPE
-#endif
+#ifndef EGL_EXT_gl_colorspace_bt2020_linear
+#define EGL_EXT_gl_colorspace_bt2020_linear 1
+#define EGL_GL_COLORSPACE_BT2020_LINEAR_EXT 0x333F
+#endif /* EGL_EXT_gl_colorspace_bt2020_linear */
+
+#ifndef EGL_EXT_gl_colorspace_bt2020_pq
+#define EGL_EXT_gl_colorspace_bt2020_pq 1
+#define EGL_GL_COLORSPACE_BT2020_PQ_EXT   0x3340
+#endif /* EGL_EXT_gl_colorspace_bt2020_pq */
+
+#ifndef EGL_EXT_gl_colorspace_scrgb_linear
+#define EGL_EXT_gl_colorspace_scrgb_linear 1
+#define EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT 0x3350
+#endif /* EGL_EXT_gl_colorspace_scrgb_linear */
+
+#ifndef EGL_EXT_pixel_format_float
+#define EGL_EXT_pixel_format_float 1
+#define EGL_COLOR_COMPONENT_TYPE_EXT      0x3339
+#define EGL_COLOR_COMPONENT_TYPE_FIXED_EXT 0x333A
+#define EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT 0x333B
+#endif /* EGL_EXT_pixel_format_float */
+
+#ifndef EGL_EXT_surface_SMPTE2086_metadata
+#define EGL_EXT_surface_SMPTE2086_metadata 1
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT 0x3341
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT 0x3342
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT 0x3343
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT 0x3344
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT 0x3345
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT 0x3346
+#define EGL_SMPTE2086_WHITE_POINT_X_EXT   0x3347
+#define EGL_SMPTE2086_WHITE_POINT_Y_EXT   0x3348
+#define EGL_SMPTE2086_MAX_LUMINANCE_EXT   0x3349
+#define EGL_SMPTE2086_MIN_LUMINANCE_EXT   0x334A
+#define EGL_METADATA_SCALING_EXT          50000
+#endif /* EGL_EXT_surface_SMPTE2086_metadata */
 
 #ifdef __cplusplus
 }
diff --git a/opengl/tests/lib/Android.mk b/opengl/tests/lib/Android.mk
index 0f1c925..ea94bc1 100644
--- a/opengl/tests/lib/Android.mk
+++ b/opengl/tests/lib/Android.mk
@@ -24,6 +24,7 @@
 
 LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -Wall -Wextra -Werror
 
+LOCAL_SHARED_LIBRARIES += libgui
 LOCAL_STATIC_LIBRARIES := libarect
 
 include $(BUILD_STATIC_LIBRARY)
diff --git a/opengl/tools/glgen2/registry/egl.xml b/opengl/tools/glgen2/registry/egl.xml
index 518c369..af13395 100755
--- a/opengl/tools/glgen2/registry/egl.xml
+++ b/opengl/tools/glgen2/registry/egl.xml
@@ -723,6 +723,23 @@
         <enum value="0x3339" name="EGL_COLOR_COMPONENT_TYPE_EXT"/>
         <enum value="0x333A" name="EGL_COLOR_COMPONENT_TYPE_FIXED_EXT"/>
         <enum value="0x333B" name="EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT"/>
+            <unused start="0x333C" end="0x333E"/>
+        <enum value="0x333F" name="EGL_GL_COLORSPACE_BT2020_LINEAR_EXT"/>
+        <enum value="0x3340" name="EGL_GL_COLORSPACE_BT2020_PQ_EXT"/>
+        <enum value="0x3341" name="EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT"/>
+        <enum value="0x3342" name="EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT"/>
+        <enum value="0x3343" name="EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT"/>
+        <enum value="0x3344" name="EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT"/>
+        <enum value="0x3345" name="EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT"/>
+        <enum value="0x3346" name="EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT"/>
+        <enum value="0x3347" name="EGL_SMPTE2086_WHITE_POINT_X_EXT"/>
+        <enum value="0x3348" name="EGL_SMPTE2086_WHITE_POINT_Y_EXT"/>
+        <enum value="0x3349" name="EGL_SMPTE2086_MAX_LUMINANCE_EXT"/>
+        <enum value="0x334A" name="EGL_SMPTE2086_MIN_LUMINANCE_EXT"/>
+        <enum value="50000"  name="EGL_METADATA_SCALING_EXT"/>
+            <unused start="0x334B" end="0x334F"/>
+        <enum value="0x3350" name="EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT"/>
+            <unused start="0x3351" end="0x339F"/>
     </enums>
 
     <enums namespace="EGL" start="0x33A0" end="0x33AF" vendor="ANGLE" comment="Reserved for Shannon Woods (Bug 13175)">
@@ -765,8 +782,8 @@
 
 <!-- Reservable for future use. To generate a new range, allocate multiples
      of 16 starting at the lowest available point in this block. -->
-    <enums namespace="EGL" start="0x3420" end="0x3FFF" vendor="KHR">
-            <unused start="0x3420" end="0x3FFF" comment="Reserved for future use"/>
+    <enums namespace="EGL" start="0x3470" end="0x3FFF" vendor="KHR">
+            <unused start="0x3470" end="0x3FFF" comment="Reserved for future use"/>
     </enums>
 
     <enums namespace="EGL" start="0x8F70" end="0x8F7F" vendor="HI" comment="For Mark Callow, Khronos bug 4055. Shared with GL.">
@@ -1859,6 +1876,21 @@
                 <command name="eglQueryDisplayAttribEXT"/>
             </require>
         </extension>
+        <extension name="EGL_EXT_gl_colorspace_bt2020_linear" supported="egl">
+            <require>
+                <enum name="EGL_GL_COLORSPACE_BT2020_LINEAR_EXT"/>
+            </require>
+        </extension>
+        <extension name="EGL_EXT_gl_colorspace_bt2020_pq" supported="egl">
+            <require>
+                <enum name="EGL_GL_COLORSPACE_BT2020_PQ_EXT"/>
+            </require>
+        </extension>
+        <extension name="EGL_EXT_gl_colorspace_scrgb_linear" supported="egl">
+            <require>
+                <enum name="EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT"/>
+            </require>
+        </extension>
         <extension name="EGL_EXT_image_dma_buf_import" supported="egl">
             <require>
                 <enum name="EGL_LINUX_DMA_BUF_EXT"/>
@@ -1955,6 +1987,22 @@
                 <command name="eglStreamConsumerOutputEXT"/>
             </require>
         </extension>
+        <extension name="EGL_EXT_surface_SMPTE2086_metadata" supported="egl">
+            <require>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT"/>
+                <enum name="EGL_SMPTE2086_WHITE_POINT_X_EXT"/>
+                <enum name="EGL_SMPTE2086_WHITE_POINT_Y_EXT"/>
+                <enum name="EGL_SMPTE2086_MAX_LUMINANCE_EXT"/>
+                <enum name="EGL_SMPTE2086_MIN_LUMINANCE_EXT"/>
+                <enum name="EGL_METADATA_SCALING_EXT"/>
+            </require>
+        </extension>
+
         <extension name="EGL_EXT_swap_buffers_with_damage" supported="egl">
             <require>
                 <command name="eglSwapBuffersWithDamageEXT"/>
diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h
index 54d815b..75e8989 100644
--- a/services/sensorservice/SensorRegistrationInfo.h
+++ b/services/sensorservice/SensorRegistrationInfo.h
@@ -17,29 +17,76 @@
 #ifndef ANDROID_SENSOR_REGISTRATION_INFO_H
 #define ANDROID_SENSOR_REGISTRATION_INFO_H
 
+#include "SensorServiceUtils.h"
+#include <utils/Thread.h>
+#include <iomanip>
+#include <sstream>
+
 namespace android {
 
 class SensorService;
 
-struct SensorService::SensorRegistrationInfo {
-    int32_t mSensorHandle;
-    String8 mPackageName;
-    bool mActivated;
-    int32_t mSamplingRateUs;
-    int32_t mMaxReportLatencyUs;
-    int32_t mHour, mMin, mSec;
-
+class SensorService::SensorRegistrationInfo : public SensorServiceUtil::Dumpable {
+public:
     SensorRegistrationInfo() : mPackageName() {
         mSensorHandle = mSamplingRateUs = mMaxReportLatencyUs = INT32_MIN;
-        mHour = mMin = mSec = INT32_MIN;
+        mHour = mMin = mSec = INT8_MIN;
         mActivated = false;
     }
 
-    static bool isSentinel(const SensorRegistrationInfo& info) {
-       return (info.mHour == INT32_MIN &&
-               info.mMin == INT32_MIN &&
-               info.mSec == INT32_MIN);
+    SensorRegistrationInfo(int32_t handle, const String8 &packageName,
+                           int32_t samplingRateNs, int32_t maxReportLatencyNs, bool activate) {
+        mSensorHandle = handle;
+        mPackageName = packageName;
+
+        mSamplingRateUs = static_cast<int32_t>(samplingRateNs/1000);
+        mMaxReportLatencyUs = static_cast<int32_t>(maxReportLatencyNs/1000);
+        mActivated = activate;
+
+        IPCThreadState *thread = IPCThreadState::self();
+        mPid = (thread != nullptr) ? thread->getCallingPid() : -1;
+        mUid = (thread != nullptr) ? thread->getCallingUid() : -1;
+
+        time_t rawtime = time(NULL);
+        struct tm * timeinfo = localtime(&rawtime);
+        mHour = static_cast<int8_t>(timeinfo->tm_hour);
+        mMin = static_cast<int8_t>(timeinfo->tm_min);
+        mSec = static_cast<int8_t>(timeinfo->tm_sec);
     }
+
+    static bool isSentinel(const SensorRegistrationInfo& info) {
+       return (info.mHour == INT8_MIN &&
+               info.mMin == INT8_MIN &&
+               info.mSec == INT8_MIN);
+    }
+
+    // Dumpable interface
+    virtual std::string dump() const override {
+        std::ostringstream ss;
+        ss << std::setfill('0') << std::setw(2) << static_cast<int>(mHour) << ":"
+           << std::setw(2) << static_cast<int>(mMin) << ":"
+           << std::setw(2) << static_cast<int>(mSec)
+           << (mActivated ? " +" : " -")
+           << " 0x" << std::hex << std::setw(8) << mSensorHandle << std::dec
+           << std::setfill(' ') << " pid=" << std::setw(5) << mPid
+           << " uid=" << std::setw(5) << mUid << " package=" << mPackageName;
+        if (mActivated) {
+           ss  << " samplingPeriod=" << mSamplingRateUs << "us"
+               << " batchingPeriod=" << mMaxReportLatencyUs << "us";
+        };
+        return ss.str();
+    }
+
+private:
+    int32_t mSensorHandle;
+    String8 mPackageName;
+    pid_t   mPid;
+    uid_t   mUid;
+    int32_t mSamplingRateUs;
+    int32_t mMaxReportLatencyUs;
+    bool mActivated;
+    int8_t mHour, mMin, mSec;
+
 };
 
 } // namespace android;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index c11df13..8fc4921 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -480,17 +480,7 @@
                         SENSOR_REGISTRATIONS_BUF_SIZE;
                     continue;
                 }
-                if (reg_info.mActivated) {
-                   result.appendFormat("%02d:%02d:%02d activated handle=0x%08x "
-                           "samplingRate=%dus maxReportLatency=%dus package=%s\n",
-                           reg_info.mHour, reg_info.mMin, reg_info.mSec, reg_info.mSensorHandle,
-                           reg_info.mSamplingRateUs, reg_info.mMaxReportLatencyUs,
-                           reg_info.mPackageName.string());
-                } else {
-                   result.appendFormat("%02d:%02d:%02d de-activated handle=0x%08x package=%s\n",
-                           reg_info.mHour, reg_info.mMin, reg_info.mSec,
-                           reg_info.mSensorHandle, reg_info.mPackageName.string());
-                }
+                result.appendFormat("%s\n", reg_info.dump().c_str());
                 currentIndex = (currentIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) %
                         SENSOR_REGISTRATIONS_BUF_SIZE;
             } while(startIndex != currentIndex);
@@ -1220,18 +1210,10 @@
 
     if (err == NO_ERROR) {
         connection->updateLooperRegistration(mLooper);
-        SensorRegistrationInfo &reg_info =
-            mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex);
-        reg_info.mSensorHandle = handle;
-        reg_info.mSamplingRateUs = samplingPeriodNs/1000;
-        reg_info.mMaxReportLatencyUs = maxBatchReportLatencyNs/1000;
-        reg_info.mActivated = true;
-        reg_info.mPackageName = connection->getPackageName();
-        time_t rawtime = time(NULL);
-        struct tm * timeinfo = localtime(&rawtime);
-        reg_info.mHour = timeinfo->tm_hour;
-        reg_info.mMin = timeinfo->tm_min;
-        reg_info.mSec = timeinfo->tm_sec;
+
+        mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =
+                SensorRegistrationInfo(handle, connection->getPackageName(),
+                                       samplingPeriodNs, maxBatchReportLatencyNs, true);
         mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
     }
 
@@ -1254,16 +1236,8 @@
 
     }
     if (err == NO_ERROR) {
-        SensorRegistrationInfo &reg_info =
-            mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex);
-        reg_info.mActivated = false;
-        reg_info.mPackageName= connection->getPackageName();
-        reg_info.mSensorHandle = handle;
-        time_t rawtime = time(NULL);
-        struct tm * timeinfo = localtime(&rawtime);
-        reg_info.mHour = timeinfo->tm_hour;
-        reg_info.mMin = timeinfo->tm_min;
-        reg_info.mSec = timeinfo->tm_sec;
+        mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =
+                SensorRegistrationInfo(handle, connection->getPackageName(), 0, 0, false);
         mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
     }
     return err;
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 5583dad..d0be121 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -91,7 +91,7 @@
     // nested class/struct for internal use
     class SensorRecord;
     class SensorEventAckReceiver;
-    struct SensorRegistrationInfo;
+    class SensorRegistrationInfo;
 
     enum Mode {
        // The regular operating mode where any application can register/unregister/call flush on
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 5ea3445..ad5955a 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -166,6 +166,7 @@
     libbinder \
     libutils \
     libui \
+    libgui \
     libdl
 
 LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e3a3ff4..b93de7e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3230,9 +3230,6 @@
     result.append(" [sf");
     result.appendFormat(" HAS_CONTEXT_PRIORITY=%d", useContextPriority);
 
-#ifdef NEVER_DEFAULT_TO_ASYNC_MODE
-    result.append(" NEVER_DEFAULT_TO_ASYNC_MODE");
-#endif
     if (isLayerTripleBufferingDisabled())
         result.append(" DISABLE_TRIPLE_BUFFERING");
     result.append("]");
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 2932fc9..c567e61 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -3002,9 +3002,6 @@
     result.append(" [sf");
     result.appendFormat(" HAS_CONTEXT_PRIORITY=%d", useContextPriority);
 
-#ifdef NEVER_DEFAULT_TO_ASYNC_MODE
-    result.append(" NEVER_DEFAULT_TO_ASYNC_MODE");
-#endif
     if (isLayerTripleBufferingDisabled())
         result.append(" DISABLE_TRIPLE_BUFFERING");
     result.append("]");
diff --git a/services/surfaceflinger/tests/hwc2/Android.mk b/services/surfaceflinger/tests/hwc2/Android.mk
index b8c2133..203ced5 100644
--- a/services/surfaceflinger/tests/hwc2/Android.mk
+++ b/services/surfaceflinger/tests/hwc2/Android.mk
@@ -40,7 +40,8 @@
 LOCAL_STATIC_LIBRARIES := \
     libbase \
     libadf \
-    libadfhwc
+    libadfhwc \
+    libmath
 LOCAL_SRC_FILES := \
     Hwc2Test.cpp \
     Hwc2TestProperties.cpp \
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
index 5f90c7a..1d3a1d3 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
@@ -23,7 +23,7 @@
 #include <gui/BufferItemConsumer.h>
 
 #include <ui/GraphicBuffer.h>
-#include <ui/vec4.h>
+#include <math/vec4.h>
 
 #include <GLES3/gl3.h>
 
diff --git a/services/vr/bufferhubd/consumer_queue_channel.cpp b/services/vr/bufferhubd/consumer_queue_channel.cpp
index 39d6bc8..ae87acd 100644
--- a/services/vr/bufferhubd/consumer_queue_channel.cpp
+++ b/services/vr/bufferhubd/consumer_queue_channel.cpp
@@ -92,29 +92,39 @@
     size_t producer_slot = pending_buffer_slots_.front().second;
     pending_buffer_slots_.pop();
 
-    // It's possible that the producer channel has expired.
+    // It's possible that the producer channel has expired. When this occurs,
+    // ignore the producer channel.
     if (producer_channel == nullptr) {
-      ALOGE(
+      ALOGW(
           "ConsumerQueueChannel::OnConsumerQueueImportBuffers: producer "
           "channel has already been expired.");
-      REPLY_ERROR_RETURN(message, ENOENT, {});
+      continue;
     }
 
     RemoteChannelHandle consumer_handle(
         producer_channel->CreateConsumer(message));
 
-    // All buffer imports should succeed together.
+    // If no buffers are imported successfully, clear available and return an
+    // error. Otherwise, return all consumer handles already imported
+    // successfully, but keep available bits on, so that the client can retry
+    // importing remaining consumer buffers.
     if (!consumer_handle.valid()) {
       ALOGE(
           "ConsumerQueueChannel::OnConsumerQueueImportBuffers: imported "
           "consumer handle is invalid.");
-      REPLY_ERROR_RETURN(message, EIO, {});
+      if (buffer_handles.empty()) {
+        ClearAvailable();
+        REPLY_ERROR_RETURN(message, EIO, {});
+      } else {
+        return buffer_handles;
+      }
     }
 
     // Move consumer_handle into buffer_handles.
     buffer_handles.emplace_back(std::move(consumer_handle), producer_slot);
   }
 
+  ClearAvailable();
   return buffer_handles;
 }
 
diff --git a/services/vr/vr_window_manager/Android.mk b/services/vr/vr_window_manager/Android.mk
index ddb58e9..59ef63c 100644
--- a/services/vr/vr_window_manager/Android.mk
+++ b/services/vr/vr_window_manager/Android.mk
@@ -17,6 +17,7 @@
 native_src := \
   application.cpp \
   controller_mesh.cpp \
+  display_view.cpp \
   elbow_model.cpp \
   hwc_callback.cpp \
   reticle.cpp \
diff --git a/services/vr/vr_window_manager/application.cpp b/services/vr/vr_window_manager/application.cpp
index dba797f..eb9f407 100644
--- a/services/vr/vr_window_manager/application.cpp
+++ b/services/vr/vr_window_manager/application.cpp
@@ -224,6 +224,8 @@
     DrawEye(kRightEye, fov_[kRightEye].GetProjectionMatrix(0.1f, 500.0f),
             eye_from_head_[kRightEye], head_matrix);
 
+    OnEndFrame();
+
     dvrPresent(graphics_context_);
   }
 }
diff --git a/services/vr/vr_window_manager/application.h b/services/vr/vr_window_manager/application.h
index 2c60e0a..6215561 100644
--- a/services/vr/vr_window_manager/application.h
+++ b/services/vr/vr_window_manager/application.h
@@ -57,6 +57,7 @@
   virtual void OnDrawFrame() = 0;
   virtual void DrawEye(EyeType eye, const mat4& perspective,
                        const mat4& eye_matrix, const mat4& head_matrix) = 0;
+  virtual void OnEndFrame() = 0;
 
   void SetVisibility(bool visible);
   virtual void OnVisibilityChanged(bool visible);
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 bd801f0..8b50c01 100644
--- a/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
+++ b/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
@@ -102,7 +102,8 @@
 
 HwcLayer* HwcDisplay::GetLayer(Layer id) {
   for (size_t i = 0; i < layers_.size(); ++i)
-    if (layers_[i].info.id == id) return &layers_[i];
+    if (layers_[i].info.id == id)
+      return &layers_[i];
 
   return nullptr;
 }
@@ -219,7 +220,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // VrHwcClient
 
-VrHwc::VrHwc() {}
+VrHwc::VrHwc() { displays_[kDefaultDisplayId].reset(new HwcDisplay()); }
 
 VrHwc::~VrHwc() {}
 
@@ -231,7 +232,6 @@
 }
 
 void VrHwc::enableCallback(bool enable) {
-  std::lock_guard<std::mutex> guard(mutex_);
   if (enable && client_ != nullptr) {
     client_.promote()->onHotplug(kDefaultDisplayId,
                                  IComposerCallback::Connection::CONNECTED);
@@ -247,31 +247,43 @@
   return Error::NONE;
 }
 
-Error VrHwc::destroyVirtualDisplay(Display display) { return Error::NONE; }
+Error VrHwc::destroyVirtualDisplay(Display display) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (display == kDefaultDisplayId || displays_.erase(display) == 0)
+    return Error::BAD_DISPLAY;
+  ComposerView::Frame frame;
+  frame.display_id = display;
+  frame.removed = true;
+  if (observer_)
+    observer_->OnNewFrame(frame);
+  return Error::NONE;
+}
 
 Error VrHwc::createLayer(Display display, Layer* outLayer) {
-  if (display != kDefaultDisplayId) {
-    return Error::BAD_DISPLAY;
-  }
-
   std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* layer = display_.CreateLayer();
+  HwcLayer* layer = display_ptr->CreateLayer();
   *outLayer = layer->info.id;
   return Error::NONE;
 }
 
 Error VrHwc::destroyLayer(Display display, Layer layer) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
   std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr) {
+    return Error::BAD_DISPLAY;
+  }
 
-  return display_.DestroyLayer(layer) ? Error::NONE : Error::BAD_LAYER;
+  return display_ptr->DestroyLayer(layer) ? Error::NONE : Error::BAD_LAYER;
 }
 
 Error VrHwc::getActiveConfig(Display display, Config* outConfig) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   *outConfig = kDefaultConfigId;
   return Error::NONE;
 }
@@ -291,10 +303,9 @@
 Error VrHwc::getDisplayAttribute(Display display, Config config,
                                  IComposerClient::Attribute attribute,
                                  int32_t* outValue) {
-  if (display != kDefaultDisplayId) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
     return Error::BAD_DISPLAY;
-  }
-
   if (config != kDefaultConfigId) {
     return Error::BAD_CONFIG;
   }
@@ -321,10 +332,9 @@
 }
 
 Error VrHwc::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) {
-  if (display != kDefaultDisplayId) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
     return Error::BAD_DISPLAY;
-  }
-
   std::vector<Config> configs(1, kDefaultConfigId);
   *outConfigs = hidl_vec<Config>(configs);
   return Error::NONE;
@@ -337,7 +347,9 @@
 
 Error VrHwc::getDisplayType(Display display,
                             IComposerClient::DisplayType* outType) {
-  if (display != kDefaultDisplayId) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr) {
     *outType = IComposerClient::DisplayType::INVALID;
     return Error::BAD_DISPLAY;
   }
@@ -348,10 +360,10 @@
 
 Error VrHwc::getDozeSupport(Display display, bool* outSupport) {
   *outSupport = false;
-  if (display == kDefaultDisplayId)
-    return Error::NONE;
-  else
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
     return Error::BAD_DISPLAY;
+  return Error::NONE;
 }
 
 Error VrHwc::getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes,
@@ -365,35 +377,41 @@
 }
 
 Error VrHwc::setActiveConfig(Display display, Config config) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
-  if (config != kDefaultConfigId) return Error::BAD_CONFIG;
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
+  if (config != kDefaultConfigId)
+    return Error::BAD_CONFIG;
 
   return Error::NONE;
 }
 
 Error VrHwc::setColorMode(Display display, ColorMode mode) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setPowerMode(Display display, IComposerClient::PowerMode mode) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setColorTransform(Display display, const float* matrix,
                                int32_t hint) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
@@ -401,13 +419,15 @@
                              int32_t acquireFence, int32_t dataspace,
                              const std::vector<hwc_rect_t>& damage) {
   base::unique_fd fence(acquireFence);
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
-  if (target == nullptr) return Error::NONE;
-
   std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  if (!display_.SetClientTarget(target, std::move(fence)))
+  if (target == nullptr)
+    return Error::NONE;
+
+  if (!display_ptr->SetClientTarget(target, std::move(fence)))
     return Error::BAD_PARAMETER;
 
   return Error::NONE;
@@ -416,7 +436,10 @@
 Error VrHwc::setOutputBuffer(Display display, buffer_handle_t buffer,
                              int32_t releaseFence) {
   base::unique_fd fence(releaseFence);
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
   ALOGE("Virtual display support not implemented");
   return Error::UNSUPPORTED;
@@ -427,13 +450,13 @@
     std::vector<IComposerClient::Composition>* outCompositionTypes,
     uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
     std::vector<uint32_t>* outRequestMasks) {
-  if (display != kDefaultDisplayId) {
-    return Error::BAD_DISPLAY;
-  }
-
   std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  display_.GetChangedCompositionTypes(outChangedLayers, outCompositionTypes);
+  display_ptr->GetChangedCompositionTypes(outChangedLayers,
+                                          outCompositionTypes);
   return Error::NONE;
 }
 
@@ -446,18 +469,20 @@
   outLayers->clear();
   outReleaseFences->clear();
 
-  if (display != kDefaultDisplayId) {
-    return Error::BAD_DISPLAY;
-  }
-
-  std::vector<ComposerView::ComposerLayer> frame;
-  std::vector<Layer> last_frame_layers;
   std::lock_guard<std::mutex> guard(mutex_);
-  Error status = display_.GetFrame(&frame);
+  auto display_ptr = FindDisplay(display);
+
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  ComposerView::Frame frame;
+  std::vector<Layer> last_frame_layers;
+  Error status = display_ptr->GetFrame(&frame.layers);
+  frame.display_id = display;
   if (status != Error::NONE)
     return status;
 
-  last_frame_layers = display_.UpdateLastFrameAndGetLastFrameLayers();
+  last_frame_layers = display_ptr->UpdateLastFrameAndGetLastFrameLayers();
 
   base::unique_fd fence;
   if (observer_)
@@ -476,18 +501,23 @@
 
 Error VrHwc::setLayerCursorPosition(Display display, Layer layer, int32_t x,
                                     int32_t y) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerBuffer(Display display, Layer layer,
                             buffer_handle_t buffer, int32_t acquireFence) {
   base::unique_fd fence(acquireFence);
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->info.buffer = GetBufferFromHandle(buffer);
   hwc_layer->info.fence = new Fence(fence.release());
@@ -497,16 +527,21 @@
 
 Error VrHwc::setLayerSurfaceDamage(Display display, Layer layer,
                                    const std::vector<hwc_rect_t>& damage) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerBlendMode(Display display, Layer layer, int32_t mode) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->info.blend_mode =
       static_cast<ComposerView::ComposerLayer::BlendMode>(mode);
@@ -516,17 +551,22 @@
 
 Error VrHwc::setLayerColor(Display display, Layer layer,
                            IComposerClient::Color color) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerCompositionType(Display display, Layer layer,
                                      int32_t type) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->composition_type = static_cast<HwcLayer::Composition>(type);
 
@@ -535,17 +575,22 @@
 
 Error VrHwc::setLayerDataspace(Display display, Layer layer,
                                int32_t dataspace) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerDisplayFrame(Display display, Layer layer,
                                   const hwc_rect_t& frame) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->info.display_frame =
       {frame.left, frame.top, frame.right, frame.bottom};
@@ -554,10 +599,14 @@
 }
 
 Error VrHwc::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->info.alpha = alpha;
 
@@ -566,17 +615,22 @@
 
 Error VrHwc::setLayerSidebandStream(Display display, Layer layer,
                                     buffer_handle_t stream) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerSourceCrop(Display display, Layer layer,
                                 const hwc_frect_t& crop) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->info.crop = {crop.left, crop.top, crop.right, crop.bottom};
 
@@ -585,23 +639,29 @@
 
 Error VrHwc::setLayerTransform(Display display, Layer layer,
                                int32_t transform) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerVisibleRegion(Display display, Layer layer,
                                    const std::vector<hwc_rect_t>& visible) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerZOrder(Display display, Layer layer, uint32_t z) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->z_order = z;
 
@@ -610,10 +670,14 @@
 
 Error VrHwc::setLayerInfo(Display display, Layer layer, uint32_t type,
                           uint32_t appId) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->info.type = type;
   hwc_layer->info.app_id = appId;
@@ -665,6 +729,11 @@
     observer_ = nullptr;
 }
 
+HwcDisplay* VrHwc::FindDisplay(Display display) {
+  auto iter = displays_.find(display);
+  return iter == displays_.end() ? nullptr : iter->second.get();
+}
+
 ComposerView* GetComposerViewFromIComposer(
     hardware::graphics::composer::V2_1::IComposer* composer) {
   return static_cast<VrHwc*>(composer);
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 df09687..b869d3e 100644
--- a/services/vr/vr_window_manager/composer/impl/vr_hwc.h
+++ b/services/vr/vr_window_manager/composer/impl/vr_hwc.h
@@ -24,6 +24,7 @@
 #include <utils/StrongPointer.h>
 
 #include <mutex>
+#include <unordered_map>
 
 using namespace android::hardware::graphics::common::V1_0;
 using namespace android::hardware::graphics::composer::V2_1;
@@ -67,7 +68,14 @@
     uint32_t app_id;
   };
 
-  using Frame = std::vector<ComposerLayer>;
+  struct Frame {
+    Display display_id;
+    // This is set to true to notify the upper layer that the display is
+    // being removed, or left false in the case of a normal frame. The upper
+    // layer tracks display IDs and will handle new ones showing up.
+    bool removed = false;
+    std::vector<ComposerLayer> layers;
+  };
 
   class Observer {
    public:
@@ -231,13 +239,15 @@
   void UnregisterObserver(Observer* observer) override;
 
  private:
+  HwcDisplay* FindDisplay(Display display);
+
   wp<VrComposerClient> client_;
   sp<IComposerCallback> callbacks_;
 
   // Guard access to internal state from binder threads.
   std::mutex mutex_;
 
-  HwcDisplay display_;
+  std::unordered_map<Display, std::unique_ptr<HwcDisplay>> displays_;
 
   Observer* observer_ = nullptr;
 
diff --git a/services/vr/vr_window_manager/display_view.cpp b/services/vr/vr_window_manager/display_view.cpp
new file mode 100644
index 0000000..5f1e73e
--- /dev/null
+++ b/services/vr/vr_window_manager/display_view.cpp
@@ -0,0 +1,432 @@
+#include "display_view.h"
+
+#include "texture.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+constexpr float kLayerScaleFactor = 3.0f;
+constexpr unsigned int kVRAppLayerCount = 2;
+constexpr unsigned int kMaximumPendingFrames = 8;
+
+// clang-format off
+const GLfloat kVertices[] = {
+  -1, -1, 0,
+   1, -1, 0,
+  -1, 1, 0,
+   1, 1, 0,
+};
+
+const GLfloat kTextureVertices[] = {
+  0, 1,
+  1, 1,
+  0, 0,
+  1, 0,
+};
+// clang-format on
+
+// Returns true if the given point is inside the given rect.
+bool IsInside(const vec2& pt, const vec2& tl, const vec2& br) {
+  return pt.x() >= tl.x() && pt.x() <= br.x() && pt.y() >= tl.y() &&
+         pt.y() <= br.y();
+}
+
+mat4 GetScalingMatrix(float width, float height) {
+  float xscale = 1, yscale = 1;
+  float ar = width / height;
+  if (ar > 1)
+    yscale = 1.0 / ar;
+  else
+    xscale = ar;
+
+  xscale *= kLayerScaleFactor;
+  yscale *= kLayerScaleFactor;
+
+  return mat4(Eigen::Scaling<float>(xscale, yscale, 1.0));
+}
+
+// Helper function that applies the crop transform to the texture layer and
+// positions (and scales) the texture layer in the appropriate location in the
+// display space.
+mat4 GetLayerTransform(const TextureLayer& texture_layer, float display_width,
+                       float display_height) {
+  // Map from vertex coordinates to [0, 1] coordinates:
+  //  1) Flip y since in vertex coordinates (-1, -1) is at the bottom left and
+  //     in texture coordinates (0, 0) is at the top left.
+  //  2) Translate by (1, 1) to map vertex coordinates to [0, 2] on x and y.
+  //  3) Scale by 1 / 2 to map coordinates to [0, 1] on x  and y.
+  mat4 unit_space(Eigen::AlignedScaling3f(0.5f, 0.5f, 1.0f) *
+                  Eigen::Translation3f(1.0f, 1.0f, 0.0f) *
+                  Eigen::AlignedScaling3f(1.0f, -1.0f, 1.0f));
+
+  mat4 texture_space(Eigen::AlignedScaling3f(
+      texture_layer.texture->width(), texture_layer.texture->height(), 1.0f));
+
+  // 1) Translate the layer to crop the left and top edge.
+  // 2) Scale the layer such that the cropped right and bottom edges map outside
+  //    the exture region.
+  float crop_width = texture_layer.crop.right - texture_layer.crop.left;
+  float crop_height = texture_layer.crop.bottom - texture_layer.crop.top;
+  mat4 texture_crop(Eigen::AlignedScaling3f(
+                        texture_layer.texture->width() / crop_width,
+                        texture_layer.texture->height() / crop_height, 1.0f) *
+                    Eigen::Translation3f(-texture_layer.crop.left,
+                                         -texture_layer.crop.top, 0.0f));
+
+  mat4 display_space(
+      Eigen::AlignedScaling3f(display_width, display_height, 1.0f));
+
+  // 1) Scale the texture to fit the display frame.
+  // 2) Translate the texture in the display frame location.
+  float display_frame_width =
+      texture_layer.display_frame.right - texture_layer.display_frame.left;
+  float display_frame_height =
+      texture_layer.display_frame.bottom - texture_layer.display_frame.top;
+  mat4 display_frame(
+      Eigen::Translation3f(texture_layer.display_frame.left,
+                           texture_layer.display_frame.top, 0.0f) *
+      Eigen::AlignedScaling3f(display_frame_width / display_width,
+                              display_frame_height / display_height, 1.0f));
+
+  mat4 layer_transform = unit_space.inverse() * display_space.inverse() *
+                         display_frame * display_space *
+                         texture_space.inverse() * texture_crop *
+                         texture_space * unit_space;
+  return layer_transform;
+}
+
+// Determine if ths frame should be shown or hidden.
+ViewMode CalculateVisibilityFromLayerConfig(const HwcCallback::Frame& frame,
+                                            uint32_t vr_app) {
+  auto& layers = frame.layers();
+
+  // TODO(achaulk): Figure out how to identify the current VR app for 2D app
+  // detection.
+
+  size_t index;
+  // Skip all layers that we don't know about.
+  for (index = 0; index < layers.size(); index++) {
+    if (layers[index].type != 0xFFFFFFFF && layers[index].type != 0)
+      break;
+  }
+
+  if (index == layers.size())
+    return ViewMode::Hidden;
+
+  if (layers[index].type != 1) {
+    // We don't have a VR app layer? Abort.
+    return ViewMode::Hidden;
+  }
+
+  // This is the VR app, ignore it.
+  index++;
+
+  // Now, find a dim layer if it exists.
+  // If it does, ignore any layers behind it for visibility determination.
+  for (size_t i = index; i < layers.size(); i++) {
+    if (layers[i].appid == HwcCallback::HwcLayer::kSurfaceFlingerLayer) {
+      index = i + 1;
+    }
+  }
+
+  // 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 ViewMode::VR;
+  }
+  return ViewMode::Hidden;
+}
+
+}  // namespace
+
+DisplayView::DisplayView(uint32_t id, int touchpad_id)
+    : id_(id), touchpad_id_(touchpad_id) {
+  translate_ = Eigen::Translation3f(0, 0, -2.5f);
+  ime_translate_ = mat4(Eigen::Translation3f(0.0f, -0.5f, 0.25f));
+  ime_top_left_ = vec2(0, 0);
+  ime_size_ = vec2(0, 0);
+}
+
+DisplayView::~DisplayView() {}
+
+void DisplayView::Recenter(const mat4& initial) {
+  initial_head_matrix_ = initial;
+}
+
+void DisplayView::SetPrograms(ShaderProgram* program,
+                              ShaderProgram* overlay_program) {
+  program_ = program;
+  overlay_program_ = overlay_program;
+}
+
+void DisplayView::DrawEye(EyeType /* eye */, const mat4& perspective,
+                          const mat4& eye_matrix, const mat4& head_matrix,
+                          const vec2& size, float fade_value) {
+  size_ = size;
+  scale_ = GetScalingMatrix(size_.x(), size_.y());
+
+  DrawOverlays(perspective, eye_matrix, head_matrix, fade_value);
+}
+
+void DisplayView::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();
+    }
+  }
+}
+
+void DisplayView::OnDrawFrame(SurfaceFlingerView* surface_flinger_view,
+                              bool debug_mode) {
+  textures_.clear();
+  has_ime_ = false;
+
+  if (!visible())
+    return;
+
+  surface_flinger_view->GetTextures(*current_frame_.frame.get(), &textures_,
+                                    &ime_texture_, debug_mode,
+                                    current_frame_.visibility == ViewMode::VR);
+  has_ime_ = ime_texture_.texture != nullptr;
+}
+
+base::unique_fd DisplayView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame,
+                                     bool debug_mode, bool* showing) {
+  ViewMode visibility =
+      CalculateVisibilityFromLayerConfig(*frame.get(), current_vr_app_);
+
+  if (visibility == ViewMode::Hidden && debug_mode)
+    visibility = ViewMode::VR;
+
+  if (frame->layers().empty())
+    current_vr_app_ = 0;
+  else
+    current_vr_app_ = frame->layers().front().appid;
+
+  pending_frames_.emplace_back(std::move(frame), visibility);
+
+  if (pending_frames_.size() > kMaximumPendingFrames) {
+    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 != ViewMode::Hidden &&
+      current_frame_.visibility == ViewMode::Hidden) {
+    *showing = true;
+  }
+
+  return base::unique_fd(dup(release_fence_.get()));
+}
+
+bool DisplayView::IsHit(const vec3& view_location, const vec3& view_direction,
+                        vec3* hit_location, vec2* hit_location_in_window_coord,
+                        bool test_ime) {
+  mat4 m = initial_head_matrix_ * translate_;
+  if (test_ime)
+    m = m * ime_translate_;
+  mat4 inverse = (m * scale_).inverse();
+  vec4 transformed_loc =
+      inverse * vec4(view_location[0], view_location[1], view_location[2], 1);
+  vec4 transformed_dir = inverse * vec4(view_direction[0], view_direction[1],
+                                        view_direction[2], 0);
+
+  if (transformed_dir.z() >= 0 || transformed_loc.z() <= 0)
+    return false;
+
+  float distance = -transformed_loc.z() / transformed_dir.z();
+  vec4 transformed_hit_loc = transformed_loc + transformed_dir * distance;
+  if (transformed_hit_loc.x() < -1 || transformed_hit_loc.x() > 1)
+    return false;
+  if (transformed_hit_loc.y() < -1 || transformed_hit_loc.y() > 1)
+    return false;
+
+  hit_location_in_window_coord->x() =
+      (1 + transformed_hit_loc.x()) / 2 * size_.x();
+  hit_location_in_window_coord->y() =
+      (1 - transformed_hit_loc.y()) / 2 * size_.y();
+
+  *hit_location = view_location + view_direction * distance;
+  return true;
+}
+
+void DisplayView::DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
+                               const mat4& head_matrix, float fade_value) {
+  if (textures_.empty())
+    return;
+
+  program_->Use();
+  mat4 mvp = perspective * eye_matrix * head_matrix;
+  GLint view_projection_location =
+      glGetUniformLocation(program_->GetProgram(), "uViewProjection");
+  glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
+
+  GLint alpha_location = glGetUniformLocation(program_->GetProgram(), "uAlpha");
+
+  GLint tex_location = glGetUniformLocation(program_->GetProgram(), "tex");
+  glUniform1i(tex_location, 0);
+  glActiveTexture(GL_TEXTURE0);
+
+  for (const auto& texture_layer : textures_) {
+    switch (texture_layer.blending) {
+      case HWC2_BLEND_MODE_PREMULTIPLIED:
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+        break;
+      case HWC2_BLEND_MODE_COVERAGE:
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        break;
+      default:
+        break;
+    }
+
+    glUniform1f(alpha_location, fade_value * texture_layer.alpha);
+
+    glBindTexture(GL_TEXTURE_2D, texture_layer.texture->id());
+
+    mat4 layer_transform =
+        GetLayerTransform(texture_layer, size_.x(), size_.y());
+
+    mat4 transform =
+        initial_head_matrix_ * translate_ * scale_ * layer_transform;
+    DrawWithTransform(transform, *program_);
+
+    glDisable(GL_BLEND);
+  }
+
+  if (has_ime_) {
+    ime_top_left_ = vec2(static_cast<float>(ime_texture_.display_frame.left),
+                         static_cast<float>(ime_texture_.display_frame.top));
+    ime_size_ = vec2(static_cast<float>(ime_texture_.display_frame.right -
+                                        ime_texture_.display_frame.left),
+                     static_cast<float>(ime_texture_.display_frame.bottom -
+                                        ime_texture_.display_frame.top));
+
+    DrawDimOverlay(mvp, textures_[0], ime_top_left_, ime_top_left_ + ime_size_);
+
+    DrawIme();
+  }
+}
+
+void DisplayView::UpdateReleaseFence() {
+  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  EGLSyncKHR sync =
+      eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+  if (sync != EGL_NO_SYNC_KHR) {
+    // Need to flush in order to get the fence FD.
+    glFlush();
+    base::unique_fd fence(eglDupNativeFenceFDANDROID(display, sync));
+    eglDestroySyncKHR(display, sync);
+    release_fence_ = std::move(fence);
+  } else {
+    ALOGE("Failed to create sync fence");
+    release_fence_ = base::unique_fd();
+  }
+}
+
+void DisplayView::DrawIme() {
+  program_->Use();
+  glBindTexture(GL_TEXTURE_2D, ime_texture_.texture->id());
+
+  mat4 layer_transform = GetLayerTransform(ime_texture_, size_.x(), size_.y());
+
+  mat4 transform = initial_head_matrix_ * translate_ * ime_translate_ * scale_ *
+                   layer_transform;
+
+  DrawWithTransform(transform, *program_);
+}
+
+void DisplayView::DrawDimOverlay(const mat4& mvp, const TextureLayer& layer,
+                                 const vec2& top_left,
+                                 const vec2& bottom_right) {
+  overlay_program_->Use();
+  glUniformMatrix4fv(
+      glGetUniformLocation(overlay_program_->GetProgram(), "uViewProjection"),
+      1, 0, mvp.data());
+  glUniform4f(glGetUniformLocation(overlay_program_->GetProgram(), "uCoords"),
+              top_left.x() / size_.x(), top_left.y() / size_.y(),
+              bottom_right.x() / size_.x(), bottom_right.y() / size_.y());
+  glEnable(GL_BLEND);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  mat4 layer_transform = GetLayerTransform(layer, size_.x(), size_.y());
+
+  mat4 transform = initial_head_matrix_ * translate_ * scale_ * layer_transform;
+  DrawWithTransform(transform, *overlay_program_);
+  glDisable(GL_BLEND);
+}
+
+void DisplayView::DrawWithTransform(const mat4& transform,
+                                    const ShaderProgram& program) {
+  GLint transform_location =
+      glGetUniformLocation(program.GetProgram(), "uTransform");
+  glUniformMatrix4fv(transform_location, 1, 0, transform.data());
+
+  glEnableVertexAttribArray(0);
+  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, kVertices);
+  glEnableVertexAttribArray(1);
+  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, kTextureVertices);
+  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+bool DisplayView::UpdateHitInfo(const vec3& view_location,
+                                const vec3& view_direction,
+                                vec3* hit_location) {
+  bool is_hit = false;
+  if (has_ime_) {
+    // This will set allow_input_ and hit_location_in_window_coord_.
+    is_hit = IsImeHit(view_location, view_direction, hit_location);
+  } else {
+    is_hit = IsHit(view_location, view_direction, hit_location,
+                   &hit_location_in_window_coord_, false);
+    allow_input_ = is_hit;
+  }
+  return is_hit;
+}
+
+bool DisplayView::IsImeHit(const vec3& view_location,
+                           const vec3& view_direction, vec3* hit_location) {
+  // First, check if the IME window is hit.
+  bool is_hit = IsHit(view_location, view_direction, hit_location,
+                      &hit_location_in_window_coord_, true);
+  if (is_hit) {
+    // If it is, check if the window coordinate is in the IME region;
+    // if so then we are done.
+    if (IsInside(hit_location_in_window_coord_, ime_top_left_,
+                 ime_top_left_ + ime_size_)) {
+      allow_input_ = true;
+      return true;
+    }
+  }
+
+  allow_input_ = false;
+  // Check if we have hit the main window.
+  is_hit = IsHit(view_location, view_direction, hit_location,
+                 &hit_location_in_window_coord_, false);
+  if (is_hit) {
+    // Only allow input if we are not hitting the region hidden by the IME.
+    // Allowing input here would cause clicks on the main window to actually
+    // be clicks on the IME.
+    if (!IsInside(hit_location_in_window_coord_, ime_top_left_,
+                  ime_top_left_ + ime_size_)) {
+      allow_input_ = true;
+    }
+  }
+  return is_hit;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/display_view.h b/services/vr/vr_window_manager/display_view.h
new file mode 100644
index 0000000..0a27781
--- /dev/null
+++ b/services/vr/vr_window_manager/display_view.h
@@ -0,0 +1,107 @@
+#ifndef VR_WINDOW_MANAGER_DISPLAY_VIEW_H_
+#define VR_WINDOW_MANAGER_DISPLAY_VIEW_H_
+
+#include <private/dvr/graphics/mesh.h>
+#include <private/dvr/graphics/shader_program.h>
+
+#include "hwc_callback.h"
+#include "surface_flinger_view.h"
+
+namespace android {
+namespace dvr {
+
+enum class ViewMode {
+  Hidden,
+  VR,
+  App,
+};
+
+class DisplayView {
+ public:
+  DisplayView(uint32_t id, int touchpad_id);
+  ~DisplayView();
+
+  // Calls to these 3 functions must be synchronized.
+  base::unique_fd OnFrame(std::unique_ptr<HwcCallback::Frame> frame,
+                          bool debug_mode, bool* showing);
+  void AdvanceFrame();
+  void UpdateReleaseFence();
+
+  void OnDrawFrame(SurfaceFlingerView* surface_flinger_view, bool debug_mode);
+  void DrawEye(EyeType eye, const mat4& perspective, const mat4& eye_matrix,
+               const mat4& head_matrix, const vec2& size, float fade_value);
+
+  void Recenter(const mat4& initial);
+
+  bool UpdateHitInfo(const vec3& view_location, const vec3& view_direction,
+                     vec3* hit_location);
+
+  void SetPrograms(ShaderProgram* program, ShaderProgram* overlay_program);
+
+  bool visible() const { return current_frame_.visibility != ViewMode::Hidden; }
+  bool allow_input() const { return allow_input_; }
+  const vec2& hit_location() const { return hit_location_in_window_coord_; }
+  uint32_t id() const { return id_; }
+  int touchpad_id() const { return touchpad_id_; }
+
+ private:
+  bool IsHit(const vec3& view_location, const vec3& view_direction,
+             vec3* hit_location, vec2* hit_location_in_window_coord,
+             bool test_ime);
+  bool IsImeHit(const vec3& view_location, const vec3& view_direction,
+                vec3* hit_location);
+  void DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
+                    const mat4& head_matrix, float fade_value);
+  void DrawIme();
+  void DrawDimOverlay(const mat4& mvp, const TextureLayer& layer,
+                      const vec2& top_left, const vec2& bottom_right);
+  void DrawWithTransform(const mat4& transform, const ShaderProgram& program);
+
+  uint32_t id_;
+  int touchpad_id_;
+
+  uint32_t current_vr_app_;
+
+  ShaderProgram* program_;
+  ShaderProgram* overlay_program_;
+
+  mat4 initial_head_matrix_;
+  mat4 scale_;
+  mat4 translate_;
+  mat4 ime_translate_;
+  vec2 size_;
+
+  std::vector<TextureLayer> textures_;
+  TextureLayer ime_texture_;
+
+  bool allow_input_ = false;
+  vec2 hit_location_in_window_coord_;
+  vec2 ime_top_left_;
+  vec2 ime_size_;
+  bool has_ime_ = false;
+
+  struct PendingFrame {
+    PendingFrame() = default;
+    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) {}
+
+    void operator=(PendingFrame&& r) {
+      frame.reset(r.frame.release());
+      visibility = r.visibility;
+    }
+
+    std::unique_ptr<HwcCallback::Frame> frame;
+    ViewMode visibility = ViewMode::Hidden;
+  };
+  std::deque<PendingFrame> pending_frames_;
+  PendingFrame current_frame_;
+  base::unique_fd release_fence_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_DISPLAY_VIEW_H_
diff --git a/services/vr/vr_window_manager/hwc_callback.cpp b/services/vr/vr_window_manager/hwc_callback.cpp
index 05ec64a..b755c60 100644
--- a/services/vr/vr_window_manager/hwc_callback.cpp
+++ b/services/vr/vr_window_manager/hwc_callback.cpp
@@ -38,7 +38,8 @@
 HwcCallback::~HwcCallback() {
 }
 
-base::unique_fd HwcCallback::OnNewFrame(const ComposerView::Frame& frame) {
+base::unique_fd HwcCallback::OnNewFrame(const ComposerView::Frame& display_frame) {
+  auto& frame = display_frame.layers;
   std::vector<HwcLayer> hwc_frame(frame.size());
   for (size_t i = 0; i < frame.size(); ++i) {
     hwc_frame[i] = HwcLayer{
@@ -53,12 +54,13 @@
     };
   }
 
-  return client_->OnFrame(
-      std::make_unique<Frame>(std::move(hwc_frame)));
+  return client_->OnFrame(std::make_unique<Frame>(
+      std::move(hwc_frame), display_frame.display_id, display_frame.removed));
 }
 
-HwcCallback::Frame::Frame(std::vector<HwcLayer>&& layers)
-    : layers_(std::move(layers)) {}
+HwcCallback::Frame::Frame(std::vector<HwcLayer>&& layers, uint32_t display_id,
+                          bool removed)
+    : display_id_(display_id), removed_(removed), layers_(std::move(layers)) {}
 
 HwcCallback::FrameStatus HwcCallback::Frame::Finish() {
   if (status_ == FrameStatus::kUnfinished)
diff --git a/services/vr/vr_window_manager/hwc_callback.h b/services/vr/vr_window_manager/hwc_callback.h
index be56856..b8aa51b 100644
--- a/services/vr/vr_window_manager/hwc_callback.h
+++ b/services/vr/vr_window_manager/hwc_callback.h
@@ -81,12 +81,16 @@
 
   class Frame {
   public:
-    Frame(std::vector<HwcLayer>&& layers);
+    Frame(std::vector<HwcLayer>&& layers, uint32_t display_id, bool removed);
 
     FrameStatus Finish();
     const std::vector<HwcLayer>& layers() const { return layers_; }
+    uint32_t display_id() const { return display_id_; }
+    bool removed() const { return removed_; }
 
   private:
+    uint32_t display_id_;
+    bool removed_;
     std::vector<HwcLayer> layers_;
     FrameStatus status_ = FrameStatus::kUnfinished;
   };
diff --git a/services/vr/vr_window_manager/shell_view.cpp b/services/vr/vr_window_manager/shell_view.cpp
index 03d582c..72a2c26 100644
--- a/services/vr/vr_window_manager/shell_view.cpp
+++ b/services/vr/vr_window_manager/shell_view.cpp
@@ -15,12 +15,6 @@
 
 namespace {
 
-constexpr float kLayerScaleFactor = 3.0f;
-
-constexpr unsigned int kVRAppLayerCount = 2;
-
-constexpr unsigned int kMaximumPendingFrames = 8;
-
 const std::string kVertexShader = SHADER0([]() {
   layout(location = 0) in vec4 aPosition;
   layout(location = 1) in vec4 aTexCoord;
@@ -79,40 +73,6 @@
   void main() { fragColor = vec4(0.8, 0.2, 0.2, 1.0); }
 });
 
-const GLfloat kVertices[] = {
-  -1, -1, 0,
-   1, -1, 0,
-  -1,  1, 0,
-   1,  1, 0,
-};
-
-const GLfloat kTextureVertices[] = {
-  0, 1,
-  1, 1,
-  0, 0,
-  1, 0,
-};
-
-// Returns true if the given point is inside the given rect.
-bool IsInside(const vec2& pt, const vec2& tl, const vec2& br) {
-  return pt.x() >= tl.x() && pt.x() <= br.x() &&
-    pt.y() >= tl.y() && pt.y() <= br.y();
-}
-
-mat4 GetScalingMatrix(float width, float height) {
-  float xscale = 1, yscale = 1;
-  float ar = width / height;
-  if (ar > 1)
-    yscale = 1.0 / ar;
-  else
-    xscale = ar;
-
-  xscale *= kLayerScaleFactor;
-  yscale *= kLayerScaleFactor;
-
-  return mat4(Eigen::Scaling<float>(xscale, yscale, 1.0));
-}
-
 mat4 GetHorizontallyAlignedMatrixFromPose(const Posef& pose) {
   vec3 position = pose.GetPosition();
   quat view_quaternion = pose.GetRotation();
@@ -134,112 +94,13 @@
   return m;
 }
 
-// Helper function that applies the crop transform to the texture layer and
-// positions (and scales) the texture layer in the appropriate location in the
-// display space.
-mat4 GetLayerTransform(const TextureLayer& texture_layer, float display_width,
-                       float display_height) {
-  // Map from vertex coordinates to [0, 1] coordinates:
-  //  1) Flip y since in vertex coordinates (-1, -1) is at the bottom left and
-  //     in texture coordinates (0, 0) is at the top left.
-  //  2) Translate by (1, 1) to map vertex coordinates to [0, 2] on x and y.
-  //  3) Scale by 1 / 2 to map coordinates to [0, 1] on x  and y.
-  mat4 unit_space(
-      Eigen::AlignedScaling3f(0.5f, 0.5f, 1.0f) *
-      Eigen::Translation3f(1.0f, 1.0f, 0.0f) *
-      Eigen::AlignedScaling3f(1.0f, -1.0f, 1.0f));
-
-  mat4 texture_space(Eigen::AlignedScaling3f(
-      texture_layer.texture->width(), texture_layer.texture->height(), 1.0f));
-
-  // 1) Translate the layer to crop the left and top edge.
-  // 2) Scale the layer such that the cropped right and bottom edges map outside
-  //    the exture region.
-  float crop_width = texture_layer.crop.right - texture_layer.crop.left;
-  float crop_height = texture_layer.crop.bottom - texture_layer.crop.top;
-  mat4 texture_crop(
-      Eigen::AlignedScaling3f(
-          texture_layer.texture->width() / crop_width,
-          texture_layer.texture->height() / crop_height,
-          1.0f) *
-      Eigen::Translation3f(
-          -texture_layer.crop.left, -texture_layer.crop.top, 0.0f));
-
-  mat4 display_space(
-      Eigen::AlignedScaling3f(display_width, display_height, 1.0f));
-
-  // 1) Scale the texture to fit the display frame.
-  // 2) Translate the texture in the display frame location.
-  float display_frame_width = texture_layer.display_frame.right -
-      texture_layer.display_frame.left;
-  float display_frame_height = texture_layer.display_frame.bottom -
-      texture_layer.display_frame.top;
-  mat4 display_frame(
-      Eigen::Translation3f(
-          texture_layer.display_frame.left,
-          texture_layer.display_frame.top,
-          0.0f) *
-      Eigen::AlignedScaling3f(
-          display_frame_width / display_width,
-          display_frame_height / display_height,
-          1.0f));
-
-  mat4 layer_transform = unit_space.inverse() * display_space.inverse() *
-      display_frame * display_space * texture_space.inverse() * texture_crop *
-      texture_space * unit_space;
-  return layer_transform;
+int GetTouchIdForDisplay(uint32_t display) {
+  return display == 1 ? VirtualTouchpad::PRIMARY : VirtualTouchpad::VIRTUAL;
 }
 
-// Determine if ths frame should be shown or hidden.
-ViewMode CalculateVisibilityFromLayerConfig(const HwcCallback::Frame& frame,
-                                            uint32_t vr_app) {
-  auto& layers = frame.layers();
-
-  // TODO(achaulk): Figure out how to identify the current VR app for 2D app
-  // detection.
-
-  size_t index;
-  // Skip all layers that we don't know about.
-  for (index = 0; index < layers.size(); index++) {
-    if (layers[index].type != 0xFFFFFFFF && layers[index].type != 0)
-      break;
-  }
-
-  if (index == layers.size())
-    return ViewMode::Hidden;
-
-  if (layers[index].type != 1) {
-    // We don't have a VR app layer? Abort.
-    return ViewMode::Hidden;
-  }
-
-  // This is the VR app, ignore it.
-  index++;
-
-  // Now, find a dim layer if it exists.
-  // If it does, ignore any layers behind it for visibility determination.
-  for (size_t i = index; i < layers.size(); i++) {
-    if (layers[i].appid == HwcCallback::HwcLayer::kSurfaceFlingerLayer) {
-      index = i + 1;
-    }
-  }
-
-  // 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 ViewMode::VR;
-  }
-  return ViewMode::Hidden;
-}
-
-
 }  // namespace
 
-ShellView::ShellView() {
-  ime_translate_ = mat4(Eigen::Translation3f(0.0f, -0.5f, 0.25f));
-  ime_top_left_ = vec2(0, 0);
-  ime_size_ = vec2(0, 0);
-}
+ShellView::ShellView() {}
 
 ShellView::~ShellView() {}
 
@@ -248,8 +109,6 @@
   if (ret)
     return ret;
 
-  translate_ = Eigen::Translation3f(0, 0, -2.5f);
-
   virtual_touchpad_ = VirtualTouchpadClient::Create();
   const status_t touchpad_status = virtual_touchpad_->Attach();
   if (touchpad_status != OK) {
@@ -286,6 +145,9 @@
   controller_mesh_->SetVertices(kNumControllerMeshVertices,
                                 kControllerMeshVertices);
 
+  for (auto& display : displays_)
+    display->SetPrograms(program_.get(), overlay_program_.get());
+
   initialized_ = true;
 
   return 0;
@@ -302,13 +164,13 @@
 }
 
 void ShellView::EnableDebug(bool debug) {
-  ALOGI("EnableDebug(%d)", (int)debug); // XXX TODO delete
+  ALOGI("EnableDebug(%d)", (int)debug);  // XXX TODO delete
   QueueTask(debug ? MainThreadTask::EnableDebugMode
                   : MainThreadTask::DisableDebugMode);
 }
 
 void ShellView::VrMode(bool mode) {
-  ALOGI("VrMode(%d)", (int)mode); // XXX TODO delete
+  ALOGI("VrMode(%d)", (int)mode);  // XXX TODO delete
   QueueTask(mode ? MainThreadTask::EnteringVrMode
                  : MainThreadTask::ExitingVrMode);
 }
@@ -320,63 +182,114 @@
   result.appendFormat("debug_mode = %s\n\n", debug_mode_ ? "true" : "false");
 }
 
-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();
-    }
-  }
-}
-
 void ShellView::OnDrawFrame() {
-  textures_.clear();
-  has_ime_ = false;
+  bool visible = false;
 
   {
-    std::unique_lock<std::mutex> l(pending_frame_mutex_);
-    AdvanceFrame();
-  }
+    std::unique_lock<std::mutex> l(display_frame_mutex_);
 
-  bool visible = current_frame_.visibility != ViewMode::Hidden;
+    // Move any new displays into the list.
+    if (!new_displays_.empty()) {
+      for (auto& display : new_displays_) {
+        display->Recenter(GetHorizontallyAlignedMatrixFromPose(last_pose_));
+        display->SetPrograms(program_.get(), overlay_program_.get());
+        displays_.emplace_back(display.release());
+      }
+      new_displays_.clear();
+    }
+
+    // Remove any old displays from the list now.
+    if (!removed_displays_.empty()) {
+      for (auto& display : removed_displays_) {
+        displays_.erase(std::find_if(
+            displays_.begin(), displays_.end(),
+            [display](auto& ptr) { return display == ptr.get(); }));
+      }
+      removed_displays_.clear();
+    }
+
+    for (auto& display : displays_) {
+      display->AdvanceFrame();
+      visible = visible || display->visible();
+    }
+  }
 
   if (!debug_mode_ && visible != is_visible_) {
-    SetVisibility(current_frame_.visibility != ViewMode::Hidden);
+    SetVisibility(visible);
   }
 
-  if (!debug_mode_ && !visible)
-    return;
-
-  ime_texture_ = TextureLayer();
-
-  surface_flinger_view_->GetTextures(*current_frame_.frame.get(), &textures_,
-                                     &ime_texture_, debug_mode_,
-                                     current_frame_.visibility == ViewMode::VR);
-  has_ime_ = ime_texture_.texture != nullptr;
+  for (auto& display : displays_) {
+    display->OnDrawFrame(surface_flinger_view_.get(), debug_mode_);
+  }
 }
 
-void ShellView::DrawEye(EyeType /* eye */, const mat4& perspective,
+void ShellView::OnEndFrame() {
+  std::unique_lock<std::mutex> l(display_frame_mutex_);
+  for (auto& display : displays_) {
+    display->UpdateReleaseFence();
+  }
+}
+
+DisplayView* ShellView::FindOrCreateDisplay(uint32_t id) {
+  for (auto& display : displays_) {
+    if (display->id() == id) {
+      return display.get();
+    }
+  }
+
+  // It might be pending addition.
+  for (auto& display : new_displays_) {
+    if (display->id() == id) {
+      return display.get();
+    }
+  }
+
+  auto display = new DisplayView(id, GetTouchIdForDisplay(id));
+  new_displays_.emplace_back(display);
+  return display;
+}
+
+base::unique_fd ShellView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame) {
+  std::unique_lock<std::mutex> l(display_frame_mutex_);
+  DisplayView* display = FindOrCreateDisplay(frame->display_id());
+
+  if (frame->removed()) {
+    removed_displays_.push_back(display);
+    return base::unique_fd();
+  }
+
+  bool showing = false;
+
+  base::unique_fd fd(display->OnFrame(std::move(frame), debug_mode_, &showing));
+
+  if (showing)
+    QueueTask(MainThreadTask::Show);
+
+  return fd;
+}
+
+void ShellView::DrawEye(EyeType eye, const mat4& perspective,
                         const mat4& eye_matrix, const mat4& head_matrix) {
-  if (should_recenter_) {
+  if (should_recenter_ && !displays_.empty()) {
     // Position the quad horizontally aligned in the direction the user
     // is facing, effectively taking out head roll.
-    initial_head_matrix_ = GetHorizontallyAlignedMatrixFromPose(last_pose_);
+    displays_[0]->Recenter(GetHorizontallyAlignedMatrixFromPose(last_pose_));
     should_recenter_ = false;
   }
 
   size_ = vec2(surface_flinger_view_->width(), surface_flinger_view_->height());
-  scale_ = GetScalingMatrix(size_.x(), size_.y());
-
-  DrawOverlays(perspective, eye_matrix, head_matrix);
 
   // TODO(alexst): Replicate controller rendering from VR Home.
   // Current approach in the function below is a quick visualization.
   DrawController(perspective, eye_matrix, head_matrix);
 
-  // TODO: Make sure reticle is shown only over visible overlays.
+  for (auto& display : displays_) {
+    if (display->visible()) {
+      display->DrawEye(eye, perspective, eye_matrix, head_matrix, size_,
+                       fade_value_);
+    }
+  }
+
   DrawReticle(perspective, eye_matrix, head_matrix);
 }
 
@@ -387,7 +300,7 @@
 
 bool ShellView::OnClick(bool down) {
   if (down) {
-    if (!is_touching_ && allow_input_) {
+    if (!is_touching_ && active_display_ && active_display_->allow_input()) {
       is_touching_ = true;
     }
   } else {
@@ -397,224 +310,6 @@
   return true;
 }
 
-base::unique_fd ShellView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame) {
-  ViewMode visibility =
-      CalculateVisibilityFromLayerConfig(*frame.get(), current_vr_app_);
-
-  if (visibility == ViewMode::Hidden && debug_mode_)
-    visibility = ViewMode::VR;
-
-  if (frame->layers().empty())
-    current_vr_app_ = 0;
-  else
-    current_vr_app_ = frame->layers().front().appid;
-
-  std::unique_lock<std::mutex> l(pending_frame_mutex_);
-
-  pending_frames_.emplace_back(std::move(frame), visibility);
-
-  if (pending_frames_.size() > kMaximumPendingFrames) {
-    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 != ViewMode::Hidden &&
-      current_frame_.visibility == ViewMode::Hidden) {
-    QueueTask(MainThreadTask::Show);
-  }
-
-  return base::unique_fd(dup(release_fence_.get()));
-}
-
-bool ShellView::IsHit(const vec3& view_location, const vec3& view_direction,
-                      vec3* hit_location, vec2* hit_location_in_window_coord,
-                      bool test_ime) {
-  mat4 m = initial_head_matrix_ * translate_;
-  if (test_ime)
-    m = m * ime_translate_;
-  mat4 inverse = (m * scale_).inverse();
-  vec4 transformed_loc =
-      inverse * vec4(view_location[0], view_location[1], view_location[2], 1);
-  vec4 transformed_dir = inverse * vec4(view_direction[0], view_direction[1],
-                                        view_direction[2], 0);
-
-  if (transformed_dir.z() >= 0 || transformed_loc.z() <= 0)
-    return false;
-
-  float distance = -transformed_loc.z() / transformed_dir.z();
-  vec4 transformed_hit_loc = transformed_loc + transformed_dir * distance;
-  if (transformed_hit_loc.x() < -1 || transformed_hit_loc.x() > 1)
-    return false;
-  if (transformed_hit_loc.y() < -1 || transformed_hit_loc.y() > 1)
-    return false;
-
-  hit_location_in_window_coord->x() =
-      (1 + transformed_hit_loc.x()) / 2 * size_.x();
-  hit_location_in_window_coord->y() =
-      (1 - transformed_hit_loc.y()) / 2 * size_.y();
-
-  *hit_location = view_location + view_direction * distance;
-  return true;
-}
-
-void ShellView::DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
-                             const mat4& head_matrix) {
-  if (textures_.empty())
-    return;
-
-  program_->Use();
-  mat4 mvp = perspective * eye_matrix * head_matrix;
-  GLint view_projection_location =
-      glGetUniformLocation(program_->GetProgram(), "uViewProjection");
-  glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
-
-  GLint alpha_location =
-      glGetUniformLocation(program_->GetProgram(), "uAlpha");
-
-  GLint tex_location = glGetUniformLocation(program_->GetProgram(), "tex");
-  glUniform1i(tex_location, 0);
-  glActiveTexture(GL_TEXTURE0);
-
-  for (const auto& texture_layer : textures_) {
-    switch (texture_layer.blending) {
-      case HWC2_BLEND_MODE_PREMULTIPLIED:
-        glEnable(GL_BLEND);
-        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-        break;
-      case HWC2_BLEND_MODE_COVERAGE:
-        glEnable(GL_BLEND);
-        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-        break;
-      default:
-        break;
-    }
-
-    glUniform1f(alpha_location, fade_value_ * texture_layer.alpha);
-
-    glBindTexture(GL_TEXTURE_2D, texture_layer.texture->id());
-
-    mat4 layer_transform = GetLayerTransform(texture_layer, size_.x(),
-                                             size_.y());
-
-    mat4 transform = initial_head_matrix_ * translate_ * scale_ *
-        layer_transform;
-    DrawWithTransform(transform, *program_);
-
-    glDisable(GL_BLEND);
-  }
-
-  if (has_ime_) {
-    ime_top_left_ = vec2(static_cast<float>(ime_texture_.display_frame.left),
-                         static_cast<float>(ime_texture_.display_frame.top));
-    ime_size_ = vec2(static_cast<float>(ime_texture_.display_frame.right -
-                                        ime_texture_.display_frame.left),
-                     static_cast<float>(ime_texture_.display_frame.bottom -
-                                        ime_texture_.display_frame.top));
-
-    DrawDimOverlay(mvp, textures_[0], ime_top_left_, ime_top_left_ + ime_size_);
-
-    DrawIme();
-  }
-
-  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-  EGLSyncKHR sync = eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID,
-                                     nullptr);
-  if (sync != EGL_NO_SYNC_KHR) {
-    // Need to flush in order to get the fence FD.
-    glFlush();
-    base::unique_fd fence(eglDupNativeFenceFDANDROID(display, sync));
-    eglDestroySyncKHR(display, sync);
-    UpdateReleaseFence(std::move(fence));
-  } else {
-    ALOGE("Failed to create sync fence");
-    UpdateReleaseFence(base::unique_fd());
-  }
-}
-
-void ShellView::DrawIme() {
-  program_->Use();
-  glBindTexture(GL_TEXTURE_2D, ime_texture_.texture->id());
-
-  mat4 layer_transform = GetLayerTransform(ime_texture_, size_.x(), size_.y());
-
-  mat4 transform = initial_head_matrix_ * translate_ * ime_translate_ * scale_ *
-              layer_transform;
-
-  DrawWithTransform(transform, *program_);
-}
-
-void ShellView::DrawDimOverlay(const mat4& mvp, const TextureLayer& layer, const vec2& top_left,
-                    const vec2& bottom_right) {
-  overlay_program_->Use();
-  glUniformMatrix4fv(
-      glGetUniformLocation(overlay_program_->GetProgram(), "uViewProjection"),
-      1, 0, mvp.data());
-  glUniform4f(glGetUniformLocation(overlay_program_->GetProgram(), "uCoords"),
-              top_left.x() / size_.x(), top_left.y() / size_.y(),
-              bottom_right.x() / size_.x(), bottom_right.y() / size_.y());
-  glEnable(GL_BLEND);
-  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-  mat4 layer_transform =
-      GetLayerTransform(layer, size_.x(), size_.y());
-
-  mat4 transform =
-      initial_head_matrix_ * translate_ * scale_ * layer_transform;
-  DrawWithTransform(transform, *overlay_program_);
-  glDisable(GL_BLEND);
-}
-
-void ShellView::DrawWithTransform(const mat4& transform,
-                                  const ShaderProgram& program) {
-  GLint transform_location =
-      glGetUniformLocation(program.GetProgram(), "uTransform");
-  glUniformMatrix4fv(transform_location, 1, 0, transform.data());
-
-  glEnableVertexAttribArray(0);
-  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, kVertices);
-  glEnableVertexAttribArray(1);
-  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, kTextureVertices);
-  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-}
-
-bool ShellView::IsImeHit(const vec3& view_location, const vec3& view_direction,
-                vec3 *hit_location) {
-  // First, check if the IME window is hit.
-  bool is_hit = IsHit(view_location, view_direction, hit_location,
-                      &hit_location_in_window_coord_, true);
-  if (is_hit) {
-    // If it is, check if the window coordinate is in the IME region;
-    // if so then we are done.
-    if (IsInside(hit_location_in_window_coord_, ime_top_left_,
-                 ime_top_left_ + ime_size_)) {
-      allow_input_ = true;
-      return true;
-    }
-  }
-
-  allow_input_ = false;
-  // Check if we have hit the main window.
-  is_hit = IsHit(view_location, view_direction, hit_location,
-                 &hit_location_in_window_coord_, false);
-  if (is_hit) {
-    // Only allow input if we are not hitting the region hidden by the IME.
-    // Allowing input here would cause clicks on the main window to actually
-    // be clicks on the IME.
-    if (!IsInside(hit_location_in_window_coord_, ime_top_left_,
-                  ime_top_left_ + ime_size_)) {
-      allow_input_ = true;
-    }
-  }
-  return is_hit;
-}
-
 void ShellView::DrawReticle(const mat4& perspective, const mat4& eye_matrix,
                             const mat4& head_matrix) {
   reticle_->Hide();
@@ -653,30 +348,47 @@
     }
   }
 
-  vec3 view_direction = vec3(view_quaternion * vec3(0, 0, -1));
-
   vec3 hit_location;
+  active_display_ =
+      FindActiveDisplay(pointer_location, view_quaternion, &hit_location);
 
-  bool is_hit;
-  if(has_ime_) {
-    // This will set allow_input_ and hit_location_in_window_coord_.
-    is_hit = IsImeHit(pointer_location, view_direction, &hit_location);
-  } else {
-    is_hit = IsHit(pointer_location, view_direction, &hit_location,
-                   &hit_location_in_window_coord_, false);
-    allow_input_ = is_hit;
-  }
-
-  if (is_hit) {
+  if (active_display_) {
     reticle_->ShowAt(
         Eigen::Translation3f(hit_location) * view_quaternion.matrix(),
-        allow_input_ ? vec3(1, 0, 0) : vec3(0, 0, 0));
+        active_display_->allow_input() ? vec3(1, 0, 0) : vec3(0, 0, 0));
     Touch();
   }
 
   reticle_->Draw(perspective, eye_matrix, head_matrix);
 }
 
+DisplayView* ShellView::FindActiveDisplay(const vec3& position,
+                                          const quat& quaternion,
+                                          vec3* hit_location) {
+  vec3 direction = vec3(quaternion * vec3(0, 0, -1));
+  vec3 temp_hit;
+
+  DisplayView* best_display = nullptr;
+  vec3 best_hit;
+
+  auto is_better = [&best_hit, &position](DisplayView*, const vec3& hit) {
+    return (hit - position).squaredNorm() < (best_hit - position).squaredNorm();
+  };
+
+  for (auto& display : displays_) {
+    if (display->UpdateHitInfo(position, direction, &temp_hit)) {
+      if (!best_display || is_better(display.get(), temp_hit)) {
+        best_display = display.get();
+        best_hit = temp_hit;
+      }
+    }
+  }
+
+  if (best_display)
+    *hit_location = best_hit;
+  return best_display;
+}
+
 void ShellView::DrawController(const mat4& perspective, const mat4& eye_matrix,
                                const mat4& head_matrix) {
   if (!shmem_controller_active_)
@@ -712,12 +424,16 @@
     return;
   }
 
+  if (!active_display_)
+    return;
+
+  const vec2& hit_location = active_display_->hit_location();
+
   // Device is portrait, but in landscape when in VR.
   // Rotate touch input appropriately.
   const android::status_t status = virtual_touchpad_->Touch(
-      VirtualTouchpad::PRIMARY,
-      1.0f - hit_location_in_window_coord_.y() / size_.y(),
-      hit_location_in_window_coord_.x() / size_.x(),
+      active_display_->touchpad_id(),
+      1.0f - hit_location.y() / size_.y(), hit_location.x() / size_.x(),
       is_touching_ ? 1.0f : 0.0f);
   if (status != OK) {
     ALOGE("touch failed: %d", status);
@@ -727,7 +443,7 @@
 bool ShellView::OnTouchpadButton(bool down, int button) {
   int buttons = touchpad_buttons_;
   if (down) {
-    if (allow_input_) {
+    if (active_display_ && active_display_->allow_input()) {
       buttons |= button;
     }
   } else {
@@ -742,18 +458,16 @@
     return false;
   }
 
+  if (!active_display_)
+    return true;
+
   const android::status_t status = virtual_touchpad_->ButtonState(
-      VirtualTouchpad::PRIMARY, touchpad_buttons_);
+      active_display_->touchpad_id(), touchpad_buttons_);
   if (status != OK) {
     ALOGE("touchpad button failed: %d %d", touchpad_buttons_, status);
   }
   return true;
 }
 
-void ShellView::UpdateReleaseFence(base::unique_fd fence) {
-  std::lock_guard<std::mutex> guard(pending_frame_mutex_);
-  release_fence_ = std::move(fence);
-}
-
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/vr_window_manager/shell_view.h b/services/vr/vr_window_manager/shell_view.h
index 49456c6..c10bd27 100644
--- a/services/vr/vr_window_manager/shell_view.h
+++ b/services/vr/vr_window_manager/shell_view.h
@@ -8,6 +8,7 @@
 
 #include "VirtualTouchpadClient.h"
 #include "application.h"
+#include "display_view.h"
 #include "reticle.h"
 #include "shell_view_binder_interface.h"
 #include "surface_flinger_view.h"
@@ -15,12 +16,6 @@
 namespace android {
 namespace dvr {
 
-enum class ViewMode {
-  Hidden,
-  VR,
-  App,
-};
-
 class ShellView : public Application,
                   public android::dvr::ShellViewBinderInterface,
                   public HwcCallback::Client {
@@ -41,91 +36,54 @@
  protected:
   void DrawEye(EyeType eye, const mat4& perspective, const mat4& eye_matrix,
                const mat4& head_matrix) override;
+  void OnDrawFrame() override;
+  void OnEndFrame() override;
   void OnVisibilityChanged(bool visible) override;
 
-  void DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
-                    const mat4& head_matrix);
   void DrawReticle(const mat4& perspective, const mat4& eye_matrix,
                    const mat4& head_matrix);
-  void DrawIme();
-  void DrawDimOverlay(const mat4& mvp, const TextureLayer& layer,
-                      const vec2& top_left, const vec2& bottom_right);
   void DrawController(const mat4& perspective, const mat4& eye_matrix,
                       const mat4& head_matrix);
 
-  bool IsHit(const vec3& view_location, const vec3& view_direction,
-             vec3* hit_location, vec2* hit_location_in_window_coord,
-             bool test_ime);
-  bool IsImeHit(const vec3& view_location, const vec3& view_direction,
-                vec3 *hit_location);
   void Touch();
   bool OnTouchpadButton(bool down, int button);
 
-  void OnDrawFrame() override;
-  void DrawWithTransform(const mat4& transform, const ShaderProgram& program);
-
   bool OnClick(bool down);
 
-  void AdvanceFrame();
+  DisplayView* FindActiveDisplay(const vec3& position, const quat& quaternion,
+                                 vec3* hit_location);
 
-  void UpdateReleaseFence(base::unique_fd fence);
 
   // HwcCallback::Client:
   base::unique_fd OnFrame(std::unique_ptr<HwcCallback::Frame> frame) override;
+  DisplayView* FindOrCreateDisplay(uint32_t id);
 
   std::unique_ptr<ShaderProgram> program_;
   std::unique_ptr<ShaderProgram> overlay_program_;
   std::unique_ptr<ShaderProgram> controller_program_;
 
-  uint32_t current_vr_app_;
-
-  // Used to center the scene when the shell becomes visible.
-  bool should_recenter_ = true;
-  mat4 initial_head_matrix_;
-  mat4 scale_;
-  mat4 translate_;
-  mat4 ime_translate_;
-  vec2 size_;
-
   std::unique_ptr<SurfaceFlingerView> surface_flinger_view_;
   std::unique_ptr<Reticle> reticle_;
   sp<VirtualTouchpad> virtual_touchpad_;
-  std::vector<TextureLayer> textures_;
-  TextureLayer ime_texture_;
-
-  bool is_touching_ = false;
-  bool allow_input_ = false;
-  int touchpad_buttons_ = 0;
-  vec2 hit_location_in_window_coord_;
-  vec2 ime_top_left_;
-  vec2 ime_size_;
-  bool has_ime_ = false;
 
   std::unique_ptr<Mesh<vec3, vec3, vec2>> controller_mesh_;
 
-  struct PendingFrame {
-    PendingFrame() = default;
-    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) {}
+  bool is_touching_ = false;
+  int touchpad_buttons_ = 0;
+  vec2 size_;
 
-    void operator=(PendingFrame&& r) {
-      frame.reset(r.frame.release());
-      visibility = r.visibility;
-    }
+  // Used to center the scene when the shell becomes visible.
+  bool should_recenter_ = true;
 
-    std::unique_ptr<HwcCallback::Frame> frame;
-    ViewMode visibility = ViewMode::Hidden;
-  };
-  std::deque<PendingFrame> pending_frames_;
-  std::mutex pending_frame_mutex_;
-  PendingFrame current_frame_;
+  std::mutex display_frame_mutex_;
+
+  std::vector<std::unique_ptr<DisplayView>> displays_;
+  std::vector<std::unique_ptr<DisplayView>> new_displays_;
+  std::vector<DisplayView*> removed_displays_;
+  DisplayView* active_display_ = nullptr;
 
   mat4 controller_translate_;
 
-  base::unique_fd release_fence_;
-
   ShellView(const ShellView&) = delete;
   void operator=(const ShellView&) = delete;
 };
diff --git a/vulkan/Android.bp b/vulkan/Android.bp
index ba3cf79..3f077a2 100644
--- a/vulkan/Android.bp
+++ b/vulkan/Android.bp
@@ -16,7 +16,10 @@
     name: "libvulkan_headers",
     from: "include",
     to: "",
-    srcs: ["include/vulkan/**/*.h"],
+    srcs: [
+        "include/vulkan/vk_platform.h",
+        "include/vulkan/vulkan.h",
+    ],
     license: "include/vulkan/NOTICE",
 }
 
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 47404d5..7e96b4c 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -17,7 +17,7 @@
 #include <algorithm>
 
 #include <log/log.h>
-#include <gui/BufferQueue.h>
+#include <ui/BufferQueueDefs.h>
 #include <sync/sync.h>
 #include <utils/StrongPointer.h>
 #include <utils/Vector.h>
@@ -207,7 +207,7 @@
         // or by passing ownership e.g. to ANativeWindow::cancelBuffer().
         int dequeue_fence;
         bool dequeued;
-    } images[android::BufferQueue::NUM_BUFFER_SLOTS];
+    } images[android::BufferQueueDefs::NUM_BUFFER_SLOTS];
 
     android::Vector<TimingInfo> timing;
 };