Merge "Check atomics in shared memory are lock free"
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 854244f..2e9701f 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -139,16 +139,7 @@
     srcs: ["otapreopt_chroot.cpp"],
     shared_libs: [
         "libbase",
-        "libjsoncpp",
         "liblog",
-        "libselinux",
-        "libziparchive",
-    ],
-    static_libs: [
-        "libapex",
-        "libapexd",
-        "libavb",
-        "libdm",
     ],
 }
 
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index a3dfa2d..e90cf3b 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -17,7 +17,6 @@
 #include <fcntl.h>
 #include <linux/unistd.h>
 #include <sys/mount.h>
-#include <sys/stat.h>
 #include <sys/wait.h>
 
 #include <sstream>
@@ -25,9 +24,6 @@
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
-#include <selinux/android.h>
-
-#include <apexd.h>
 
 #include "installd_constants.h"
 #include "otapreopt_utils.h"
@@ -142,32 +138,6 @@
       UNUSED(product_result);
     }
 
-    // Setup APEX mount point and its security context.
-    // The logic here is similar to the one in system/core/rootdir/init.rc:
-    //
-    //   mount tmpfs tmpfs /apex nodev noexec nosuid
-    //   chmod 0755 /apex
-    //   chown root root /apex
-    //   restorecon /apex
-    //
-    if (mount("tmpfs", "/postinstall/apex", "tmpfs", MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr)
-        != 0) {
-      PLOG(ERROR) << "Failed to mount tmpfs in /postinstall/apex";
-      exit(209);
-    }
-    if (chmod("/postinstall/apex", 0755) != 0) {
-      PLOG(ERROR) << "Failed to chmod /postinstall/apex to 0755";
-      exit(210);
-    }
-    if (chown("/postinstall/apex", 0, 0) != 0) {
-      PLOG(ERROR) << "Failed to chown /postinstall/apex to root:root";
-      exit(211);
-    }
-    if (selinux_android_restorecon("/postinstall/apex", 0) < 0) {
-      PLOG(ERROR) << "Failed to restorecon /postinstall/apex";
-      exit(212);
-    }
-
     // Chdir into /postinstall.
     if (chdir("/postinstall") != 0) {
         PLOG(ERROR) << "Unable to chdir into /postinstall.";
@@ -185,18 +155,6 @@
         exit(205);
     }
 
-    // Try to mount APEX packages in "/apex" in the chroot dir. We need at least
-    // the Android Runtime APEX, as it is required by otapreopt to run dex2oat.
-    {
-      // The logic here is (partially) copied and adapted from
-      // system/apex/apexd/apexd_main.cpp.
-
-      // Only scan the APEX directory under /system (within the chroot dir).
-      // Note that this leaves around the loop devices created and used by
-      // libapexd's code, but this is fine, as we expect to reboot soon after.
-      apex::scanPackagesDirAndActivate(apex::kApexPackageSystemDir);
-    }
-
     // Now go on and run otapreopt.
 
     // Incoming:  cmd + status-fd + target-slot + cmd... + null      | Incoming | = argc + 1
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index a6d7a78..c706d91 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -376,23 +376,23 @@
     }
 
     mServicesTable.setDescription(
-            "All binderized services (registered services through hwservicemanager)");
+            "| All binderized services (registered with hwservicemanager)");
     mPassthroughRefTable.setDescription(
-            "All interfaces that getService() has ever return as a passthrough interface;\n"
-            "PIDs / processes shown below might be inaccurate because the process\n"
-            "might have relinquished the interface or might have died.\n"
-            "The Server / Server CMD column can be ignored.\n"
-            "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
-            "the library and successfully fetched the passthrough implementation.");
+            "| All interfaces that getService() has ever returned as a passthrough interface;\n"
+            "| PIDs / processes shown below might be inaccurate because the process\n"
+            "| might have relinquished the interface or might have died.\n"
+            "| The Server / Server CMD column can be ignored.\n"
+            "| The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
+            "| the library and successfully fetched the passthrough implementation.");
     mImplementationsTable.setDescription(
-            "All available passthrough implementations (all -impl.so files).\n"
-            "These may return subclasses through their respective HIDL_FETCH_I* functions.");
+            "| All available passthrough implementations (all -impl.so files).\n"
+            "| These may return subclasses through their respective HIDL_FETCH_I* functions.");
     mManifestHalsTable.setDescription(
-            "All HALs that are in VINTF manifest.");
+            "| All HALs that are in VINTF manifest.");
     mLazyHalsTable.setDescription(
-            "All HALs that are declared in VINTF manifest:\n"
-            "   - as hwbinder HALs but are not registered to hwservicemanager, and\n"
-            "   - as hwbinder/passthrough HALs with no implementation.");
+            "| All HALs that are declared in VINTF manifest:\n"
+            "|    - as hwbinder HALs but are not registered to hwservicemanager, and\n"
+            "|    - as hwbinder/passthrough HALs with no implementation.");
 }
 
 bool ListCommand::addEntryWithInstance(const TableEntry& entry,
@@ -972,10 +972,10 @@
         thiz->mSelectedColumns.push_back(TableColumnType::VINTF);
         return OK;
     }, "print VINTF info. This column contains a comma-separated list of:\n"
-       "    - DM: device manifest\n"
-       "    - DC: device compatibility matrix\n"
-       "    - FM: framework manifest\n"
-       "    - FC: framework compatibility matrix"});
+       "    - DM: if the HAL is in the device manifest\n"
+       "    - DC: if the HAL is in the device compatibility matrix\n"
+       "    - FM: if the HAL is in the framework manifest\n"
+       "    - FC: if the HAL is in the framework compatibility matrix"});
     mOptions.push_back({'S', "service-status", no_argument, v++, [](ListCommand* thiz, const char*) {
         thiz->mSelectedColumns.push_back(TableColumnType::SERVICE_STATUS);
         return OK;
@@ -1054,7 +1054,7 @@
         return OK;
     }, "comma-separated list of one or more sections.\nThe output is restricted to the selected "
        "section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|"
-       "passthrough_libs), and (v|vintf).\nDefault is `bcl`."});
+       "passthrough_libs), (v|vintf), and (z|lazy).\nDefault is `bcl`."});
 }
 
 // Create 'longopts' argument to getopt_long. Caller is responsible for maintaining
@@ -1150,7 +1150,7 @@
     }
 
     if (mSelectedColumns.empty()) {
-        mSelectedColumns = {TableColumnType::RELEASED,
+        mSelectedColumns = {TableColumnType::VINTF, TableColumnType::RELEASED,
                             TableColumnType::INTERFACE_NAME, TableColumnType::THREADS,
                             TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS};
     }
@@ -1210,7 +1210,7 @@
     err() << "list:" << std::endl
           << "    lshal" << std::endl
           << "    lshal list" << std::endl
-          << "        List all hals with default ordering and columns (`lshal list -liepc`)" << std::endl
+          << "        List all hals with default ordering and columns (`lshal list -Vliepc`)" << std::endl
           << "    lshal list [-h|--help]" << std::endl
           << "        -h, --help: Print help message for list (`lshal help list`)" << std::endl
           << "    lshal [list] [OPTIONS...]" << std::endl;
diff --git a/data/etc/android.hardware.face.xml b/data/etc/android.hardware.biometrics.face.xml
similarity index 93%
rename from data/etc/android.hardware.face.xml
rename to data/etc/android.hardware.biometrics.face.xml
index abd23fb..7fa0bf9 100644
--- a/data/etc/android.hardware.face.xml
+++ b/data/etc/android.hardware.biometrics.face.xml
@@ -16,5 +16,5 @@
 
 <!-- This is the standard set of features for a biometric face authentication sensor. -->
 <permissions>
-    <feature name="android.hardware.face" />
+    <feature name="android.hardware.biometrics.face" />
 </permissions>
diff --git a/data/etc/android.hardware.biometrics.fingerprint.xml b/data/etc/android.hardware.biometrics.fingerprint.xml
new file mode 100644
index 0000000..e5af541
--- /dev/null
+++ b/data/etc/android.hardware.biometrics.fingerprint.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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.
+  -->
+
+<!-- This is the standard set of features for a biometric fingerprint sensor. -->
+<permissions>
+    <feature name="android.hardware.biometrics.fingerprint" />
+</permissions>
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
new file mode 100644
index 0000000..d68f274
--- /dev/null
+++ b/include/input/TouchVideoFrame.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 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 _LIBINPUT_TOUCHVIDEOFRAME_H
+#define _LIBINPUT_TOUCHVIDEOFRAME_H
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <vector>
+
+namespace android {
+
+/**
+ * Represents data from a single scan of the touchscreen device.
+ * Similar in concept to a video frame, but the touch strength is used as
+ * the values instead.
+ */
+class TouchVideoFrame {
+public:
+    TouchVideoFrame(uint32_t width, uint32_t height, std::vector<int16_t> data,
+            const struct timeval& timestamp) :
+            mWidth(width), mHeight(height), mData(std::move(data)), mTimestamp(timestamp) {
+    }
+
+    /**
+     * Width of the frame
+     */
+    uint32_t getWidth() const { return mWidth; }
+    /**
+     * Height of the frame
+     */
+    uint32_t getHeight() const { return mHeight; }
+    /**
+     * The touch strength data.
+     * The array is a 2-D row-major matrix, with dimensions (height, width).
+     * Total size of the array should equal getHeight() * getWidth().
+     * Data is allowed to be negative.
+     */
+    const std::vector<int16_t>& getData() const { return mData; }
+    /**
+     * Time at which the heatmap was taken.
+     */
+    const struct timeval& getTimestamp() const { return mTimestamp; }
+
+private:
+    uint32_t mWidth;
+    uint32_t mHeight;
+    std::vector<int16_t> mData;
+    struct timeval mTimestamp;
+};
+
+} // namespace android
+
+#endif // _LIBINPUT_TOUCHVIDEOFRAME_H
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
index fcdf7af..f3bc31b 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
@@ -43,7 +43,7 @@
     if (length < 0) return false;
 
     std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
-    if (length > vec->max_size()) return false;
+    if (static_cast<size_t>(length) > vec->max_size()) return false;
 
     vec->resize(length);
     *outBuffer = vec->data();
@@ -65,7 +65,7 @@
 
     *vec = std::optional<std::vector<T>>(std::vector<T>{});
 
-    if (length > (*vec)->max_size()) return false;
+    if (static_cast<size_t>(length) > (*vec)->max_size()) return false;
     (*vec)->resize(length);
 
     *outBuffer = (*vec)->data();
@@ -88,7 +88,7 @@
     if (length < 0) return false;
 
     std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
-    if (length > vec->max_size()) return false;
+    if (static_cast<size_t>(length) > vec->max_size()) return false;
 
     vec->resize(length);
     return true;
@@ -116,7 +116,7 @@
 
     *vec = std::optional<std::vector<T>>(std::vector<T>{});
 
-    if (length > (*vec)->max_size()) return false;
+    if (static_cast<size_t>(length) > (*vec)->max_size()) return false;
     (*vec)->resize(length);
 
     return true;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 95b1038..1be55e6 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -59,7 +59,15 @@
     if (err != NO_ERROR) {
         return err;
     }
-    err = output->writeInt64(presentTime);
+    if (presentFence) {
+        err = output->writeBool(true);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        err = output->write(*presentFence);
+    } else {
+        err = output->writeBool(false);
+    }
     if (err != NO_ERROR) {
         return err;
     }
@@ -71,10 +79,18 @@
     if (err != NO_ERROR) {
         return err;
     }
-    err = input->readInt64(&presentTime);
+    bool hasFence = false;
+    err = input->readBool(&hasFence);
     if (err != NO_ERROR) {
         return err;
     }
+    if (hasFence) {
+        presentFence = new Fence();
+        err = input->read(*presentFence);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
     return input->readParcelableVector(&surfaceStats);
 }
 
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 5c41c21..8acfa7a 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -21,6 +21,7 @@
 #include <binder/Parcelable.h>
 #include <binder/SafeInterface.h>
 
+#include <ui/Fence.h>
 #include <utils/Timers.h>
 
 #include <cstdint>
@@ -65,7 +66,7 @@
     status_t readFromParcel(const Parcel* input) override;
 
     nsecs_t latchTime = -1;
-    nsecs_t presentTime = -1;
+    sp<Fence> presentFence = nullptr;
     std::vector<SurfaceStats> surfaceStats;
 };
 
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 86e9c23..60542bd 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -24,11 +24,15 @@
 
 #include <memory>
 
+#include <android/native_window.h>
+
 #include <binder/Binder.h>
 #include <binder/IServiceManager.h>
 #include <binder/Parcel.h>
 #include <binder/ProcessState.h>
 
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <gui/SurfaceControl.h>
 
@@ -37,6 +41,7 @@
 #include <input/InputTransport.h>
 #include <input/Input.h>
 
+#include <ui/DisplayInfo.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
@@ -44,6 +49,8 @@
 namespace android {
 namespace test {
 
+using Transaction = SurfaceComposerClient::Transaction;
+
 sp<IInputFlinger> getInputFlinger() {
    sp<IBinder> input(defaultServiceManager()->getService(
             String16("inputflinger")));
@@ -58,9 +65,8 @@
 
 class InputSurface {
 public:
-    InputSurface(const sp<SurfaceComposerClient>& scc, int width, int height) {
-        mSurfaceControl = scc->createSurface(String8("Test Surface"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                             ISurfaceComposerClient::eFXSurfaceColor);
+    InputSurface(const sp<SurfaceControl> &sc, int width, int height) {
+        mSurfaceControl = sc;
 
         InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
         mServerChannel->setToken(new BBinder());
@@ -73,6 +79,31 @@
         mInputConsumer = new InputConsumer(mClientChannel);
     }
 
+    static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient> &scc,
+                                                               int width, int height) {
+        sp<SurfaceControl> surfaceControl =
+                scc->createSurface(String8("Test Surface"), 0 /* bufHeight */, 0 /* bufWidth */,
+                                   PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+        return std::make_unique<InputSurface>(surfaceControl, width, height);
+    }
+
+    static std::unique_ptr<InputSurface> makeBufferInputSurface(
+            const sp<SurfaceComposerClient> &scc, int width, int height) {
+        sp<SurfaceControl> surfaceControl =
+                scc->createSurface(String8("Test Buffer Surface"), width, height,
+                                   PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
+        return std::make_unique<InputSurface>(surfaceControl, width, height);
+    }
+
+    static std::unique_ptr<InputSurface> makeContainerInputSurface(
+            const sp<SurfaceComposerClient> &scc, int width, int height) {
+        sp<SurfaceControl> surfaceControl =
+                scc->createSurface(String8("Test Container Surface"), 0 /* bufHeight */,
+                                   0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceContainer);
+        return std::make_unique<InputSurface>(surfaceControl, width, height);
+    }
+
     InputEvent* consumeEvent() {
         waitForEventAvailable();
 
@@ -180,6 +211,15 @@
     void SetUp() {
         mComposerClient = new SurfaceComposerClient;
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+        DisplayInfo info;
+        auto display = mComposerClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+        SurfaceComposerClient::getDisplayInfo(display, &info);
+
+        // After a new buffer is queued, SurfaceFlinger is notified and will
+        // latch the new buffer on next vsync.  Let's heuristically wait for 3
+        // vsyncs.
+        mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
     }
 
     void TearDown() {
@@ -187,10 +227,23 @@
     }
 
     std::unique_ptr<InputSurface> makeSurface(int width, int height) {
-        return std::make_unique<InputSurface>(mComposerClient, width, height);
+        return InputSurface::makeColorInputSurface(mComposerClient, width, height);
+    }
+
+    void postBuffer(const sp<SurfaceControl> &layer) {
+        // wait for previous transactions (such as setSize) to complete
+        Transaction().apply(true);
+        ANativeWindow_Buffer buffer = {};
+        EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr));
+        ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
+        // Request an empty transaction to get applied synchronously to ensure the buffer is
+        // latched.
+        Transaction().apply(true);
+        usleep(mBufferPostDelay);
     }
 
     sp<SurfaceComposerClient> mComposerClient;
+    int32_t mBufferPostDelay;
 };
 
 void injectTap(int x, int y) {
@@ -267,5 +320,124 @@
     surface->expectTap(1, 1);
 }
 
+// Surface Insets are set to offset the client content and draw a border around the client surface
+// (such as shadows in dialogs). Inputs sent to the client are offset such that 0,0 is the start
+// of the client content.
+TEST_F(InputSurfacesTest, input_respects_surface_insets) {
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+    bgSurface->showAt(100, 100);
+
+    fgSurface->mInputInfo.surfaceInset = 5;
+    fgSurface->showAt(100, 100);
+
+    injectTap(106, 106);
+    fgSurface->expectTap(1, 1);
+
+    injectTap(101, 101);
+    bgSurface->expectTap(1, 1);
+}
+
+// Ensure a surface whose insets are cropped, handles the touch offset correctly. ref:b/120413463
+TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) {
+    std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
+    parentSurface->showAt(100, 100);
+
+    childSurface->mInputInfo.surfaceInset = 10;
+    childSurface->showAt(100, 100);
+
+    childSurface->doTransaction([&](auto &t, auto &sc) {
+        t.setPosition(sc, -5, -5);
+        t.reparent(sc, parentSurface->mSurfaceControl->getHandle());
+    });
+
+    injectTap(106, 106);
+    childSurface->expectTap(1, 1);
+
+    injectTap(101, 101);
+    parentSurface->expectTap(1, 1);
+}
+
+// Ensure we ignore transparent region when getting screen bounds when positioning input frame.
+TEST_F(InputSurfacesTest, input_ignores_transparent_region) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->doTransaction([](auto &t, auto &sc) {
+        Region transparentRegion(Rect(0, 0, 10, 10));
+        t.setTransparentRegionHint(sc, transparentRegion);
+    });
+    surface->showAt(100, 100);
+    injectTap(101, 101);
+    surface->expectTap(1, 1);
+}
+
+// Ensure we send the input to the right surface when the surface visibility changes due to the
+// first buffer being submitted. ref: b/120839715
+TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) {
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> bufferSurface =
+            InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
+
+    bgSurface->showAt(10, 10);
+    bufferSurface->showAt(10, 10);
+
+    injectTap(11, 11);
+    bgSurface->expectTap(1, 1);
+
+    postBuffer(bufferSurface->mSurfaceControl);
+    injectTap(11, 11);
+    bufferSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) {
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> bufferSurface =
+            InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
+    postBuffer(bufferSurface->mSurfaceControl);
+
+    bgSurface->showAt(10, 10);
+    bufferSurface->showAt(10, 10);
+
+    injectTap(11, 11);
+    bufferSurface->expectTap(1, 1);
+
+    bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
+
+    injectTap(11, 11);
+    bgSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_color_layer_alpha) {
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+
+    bgSurface->showAt(10, 10);
+    fgSurface->showAt(10, 10);
+
+    injectTap(11, 11);
+    fgSurface->expectTap(1, 1);
+
+    fgSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
+
+    injectTap(11, 11);
+    bgSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) {
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> containerSurface =
+            InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
+
+    bgSurface->showAt(10, 10);
+    containerSurface->showAt(10, 10);
+
+    injectTap(11, 11);
+    containerSurface->expectTap(1, 1);
+
+    containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); });
+
+    injectTap(11, 11);
+    bgSurface->expectTap(1, 1);
+}
 }
 }
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 1395551..53b0e4c 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -242,23 +242,43 @@
 
     bool useContextPriority = extensions.hasContextPriority() &&
             (featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT);
-    EGLContext ctxt = createEglContext(display, config, EGL_NO_CONTEXT, useContextPriority);
+    EGLContext protectedContext = EGL_NO_CONTEXT;
+    if (extensions.hasProtectedContent()) {
+        protectedContext = createEglContext(display, config, nullptr, useContextPriority,
+                                            Protection::PROTECTED);
+        ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
+    }
+
+    EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,
+                                       Protection::UNPROTECTED);
 
     // if can't create a GL context, we can only abort.
     LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
 
     EGLSurface dummy = EGL_NO_SURFACE;
     if (!extensions.hasSurfacelessContext()) {
-        dummy = createDummyEglPbufferSurface(display, config, hwcFormat);
+        dummy = createDummyEglPbufferSurface(display, config, hwcFormat, Protection::UNPROTECTED);
         LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
     }
-
     EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
     LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
-
     extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
                                  glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
 
+    // In order to have protected contents in GPU composition, the OpenGL ES extension
+    // GL_EXT_protected_textures must be supported. If it's not supported, reset
+    // protected context to EGL_NO_CONTEXT to indicate that protected contents is not supported.
+    if (!extensions.hasProtectedTexture()) {
+        protectedContext = EGL_NO_CONTEXT;
+    }
+
+    EGLSurface protectedDummy = EGL_NO_SURFACE;
+    if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
+        protectedDummy =
+                createDummyEglPbufferSurface(display, config, hwcFormat, Protection::PROTECTED);
+        ALOGE_IF(protectedDummy == EGL_NO_SURFACE, "can't create protected dummy pbuffer");
+    }
+
     // now figure out what version of GL did we actually get
     GlesVersion version = parseGlesVersion(extensions.getVersion());
 
@@ -271,7 +291,8 @@
             break;
         case GLES_VERSION_2_0:
         case GLES_VERSION_3_0:
-            engine = std::make_unique<GLESRenderEngine>(featureFlags, display, config, ctxt, dummy);
+            engine = std::make_unique<GLESRenderEngine>(featureFlags, display, config, ctxt, dummy,
+                                                        protectedContext, protectedDummy);
             break;
     }
 
@@ -326,12 +347,15 @@
 }
 
 GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EGLConfig config,
-                                   EGLContext ctxt, EGLSurface dummy)
+                                   EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext,
+                                   EGLSurface protectedDummy)
       : renderengine::impl::RenderEngine(featureFlags),
         mEGLDisplay(display),
         mEGLConfig(config),
         mEGLContext(ctxt),
         mDummySurface(dummy),
+        mProtectedEGLContext(protectedContext),
+        mProtectedDummySurface(protectedDummy),
         mVpWidth(0),
         mVpHeight(0),
         mUseColorManagement(featureFlags & USE_COLOR_MANAGEMENT) {
@@ -341,6 +365,17 @@
     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
     glPixelStorei(GL_PACK_ALIGNMENT, 4);
 
+    // Initialize protected EGL Context.
+    if (mProtectedEGLContext != EGL_NO_CONTEXT) {
+        EGLBoolean success = eglMakeCurrent(display, mProtectedDummySurface, mProtectedDummySurface,
+                                            mProtectedEGLContext);
+        ALOGE_IF(!success, "can't make protected context current");
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+        glPixelStorei(GL_PACK_ALIGNMENT, 4);
+        success = eglMakeCurrent(display, mDummySurface, mDummySurface, mEGLContext);
+        LOG_ALWAYS_FATAL_IF(!success, "can't make default context current");
+    }
+
     const uint16_t protTexData[] = {0};
     glGenTextures(1, &mProtectedTexName);
     glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
@@ -395,7 +430,8 @@
 }
 
 void GLESRenderEngine::primeCache() const {
-    ProgramCache::getInstance().primeCache(mFeatureFlags & USE_COLOR_MANAGEMENT);
+    ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
+                                           mFeatureFlags & USE_COLOR_MANAGEMENT);
 }
 
 bool GLESRenderEngine::isCurrent() const {
@@ -539,6 +575,10 @@
     const GLenum target = GL_TEXTURE_EXTERNAL_OES;
 
     glBindTexture(target, texName);
+    if (supportsProtectedContent()) {
+        glTexParameteri(target, GL_TEXTURE_PROTECTED_EXT,
+                        glImage.isProtected() ? GL_TRUE : GL_FALSE);
+    }
     if (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) {
         glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(glImage.getEGLImage()));
     }
@@ -552,6 +592,10 @@
 
     // Bind the texture and turn our EGLImage into a texture
     glBindTexture(GL_TEXTURE_2D, textureName);
+    if (supportsProtectedContent()) {
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_PROTECTED_EXT,
+                        mInProtectedContext ? GL_TRUE : GL_FALSE);
+    }
     glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglImage);
 
     // Bind the Framebuffer to render into
@@ -584,6 +628,26 @@
     } while (true);
 }
 
+bool GLESRenderEngine::supportsProtectedContent() const {
+    return mProtectedEGLContext != EGL_NO_CONTEXT;
+}
+
+bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) {
+    if (useProtectedContext == mInProtectedContext) {
+        return true;
+    }
+    if (useProtectedContext && mProtectedEGLContext == EGL_NO_CONTEXT) {
+        return false;
+    }
+    const EGLSurface surface = useProtectedContext ? mProtectedDummySurface : mDummySurface;
+    const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
+    const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
+    if (success) {
+        mInProtectedContext = useProtectedContext;
+    }
+    return success;
+}
+
 status_t GLESRenderEngine::drawLayers(const DisplaySettings& /*settings*/,
                                       const std::vector<LayerSettings>& /*layers*/,
                                       ANativeWindowBuffer* const /*buffer*/,
@@ -822,7 +886,9 @@
                     Description::dataSpaceToTransferFunction(outputTransfer);
         }
 
-        ProgramCache::getInstance().useProgram(managedState);
+        ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
+                                                                   : mEGLContext,
+                                               managedState);
 
         glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
 
@@ -833,7 +899,9 @@
             writePPM(out.str().c_str(), mVpWidth, mVpHeight);
         }
     } else {
-        ProgramCache::getInstance().useProgram(mState);
+        ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
+                                                                   : mEGLContext,
+                                               mState);
 
         glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
     }
@@ -857,17 +925,18 @@
 
 void GLESRenderEngine::dump(std::string& result) {
     const GLExtensions& extensions = GLExtensions::getInstance();
+    ProgramCache& cache = ProgramCache::getInstance();
 
     StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
     StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
-
     StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
                   extensions.getVersion());
     StringAppendF(&result, "%s\n", extensions.getExtensions());
-
-    StringAppendF(&result, "RenderEngine program cache size: %zu\n",
-                  ProgramCache::getInstance().getSize());
-
+    StringAppendF(&result, "RenderEngine is in protected context : %d\n", mInProtectedContext);
+    StringAppendF(&result, "RenderEngine program cache size for unprotected context: %zu\n",
+                  cache.getSize(mEGLContext));
+    StringAppendF(&result, "RenderEngine program cache size for protected context: %zu\n",
+                  cache.getSize(mProtectedEGLContext));
     StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n",
                   dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
                   dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str());
@@ -892,7 +961,8 @@
 }
 
 EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
-                                              EGLContext shareContext, bool useContextPriority) {
+                                              EGLContext shareContext, bool useContextPriority,
+                                              Protection protection) {
     EGLint renderableType = 0;
     if (config == EGL_NO_CONFIG) {
         renderableType = EGL_OPENGL_ES3_BIT;
@@ -911,13 +981,17 @@
     }
 
     std::vector<EGLint> contextAttributes;
-    contextAttributes.reserve(5);
+    contextAttributes.reserve(7);
     contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
     contextAttributes.push_back(contextClientVersion);
     if (useContextPriority) {
         contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
         contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
     }
+    if (protection == Protection::PROTECTED) {
+        contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+        contextAttributes.push_back(EGL_TRUE);
+    }
     contextAttributes.push_back(EGL_NONE);
 
     EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data());
@@ -938,17 +1012,21 @@
 }
 
 EGLSurface GLESRenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
-                                                          int hwcFormat) {
+                                                          int hwcFormat, Protection protection) {
     EGLConfig dummyConfig = config;
     if (dummyConfig == EGL_NO_CONFIG) {
         dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
     }
     std::vector<EGLint> attributes;
-    attributes.reserve(5);
+    attributes.reserve(7);
     attributes.push_back(EGL_WIDTH);
     attributes.push_back(1);
     attributes.push_back(EGL_HEIGHT);
     attributes.push_back(1);
+    if (protection == Protection::PROTECTED) {
+        attributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+        attributes.push_back(EGL_TRUE);
+    }
     attributes.push_back(EGL_NONE);
 
     return eglCreatePbufferSurface(display, dummyConfig, attributes.data());
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 21d5b81..07e5585 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -45,7 +45,8 @@
     static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
 
     GLESRenderEngine(uint32_t featureFlags, // See RenderEngine::FeatureFlag
-                     EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy);
+                     EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy,
+                     EGLContext protectedContext, EGLSurface protectedDummy);
     ~GLESRenderEngine() override;
 
     std::unique_ptr<Framebuffer> createFramebuffer() override;
@@ -68,6 +69,9 @@
     void unbindFrameBuffer(Framebuffer* framebuffer) override;
     void checkErrors() const override;
 
+    bool isProtected() const override { return mInProtectedContext; }
+    bool supportsProtectedContent() const override;
+    bool useProtectedContext(bool useProtectedContext) override;
     status_t drawLayers(const DisplaySettings& settings, const std::vector<LayerSettings>& layers,
                         ANativeWindowBuffer* const buffer,
                         base::unique_fd* displayFence) const override;
@@ -112,20 +116,22 @@
 
     static GlesVersion parseGlesVersion(const char* str);
     static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
-                                       EGLContext shareContext, bool useContextPriority);
+                                       EGLContext shareContext, bool useContextPriority,
+                                       Protection protection);
     static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
-                                                   int hwcFormat);
+                                                   int hwcFormat, Protection protection);
 
     // A data space is considered HDR data space if it has BT2020 color space
     // with PQ or HLG transfer function.
     bool isHdrDataSpace(const ui::Dataspace dataSpace) const;
     bool needsXYZTransformMatrix() const;
-    void setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy);
 
     EGLDisplay mEGLDisplay;
     EGLConfig mEGLConfig;
     EGLContext mEGLContext;
     EGLSurface mDummySurface;
+    EGLContext mProtectedEGLContext;
+    EGLSurface mProtectedDummySurface;
     GLuint mProtectedTexName;
     GLint mMaxViewportDims[2];
     GLint mMaxTextureSize;
@@ -146,6 +152,7 @@
     mat4 mBt2020ToSrgb;
     mat4 mBt2020ToDisplayP3;
 
+    bool mInProtectedContext = false;
     int32_t mFboHeight = 0;
 
     // Current dataspace of layer being rendered
diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp
index ce83dd5..2924b0e 100644
--- a/libs/renderengine/gl/GLExtensions.cpp
+++ b/libs/renderengine/gl/GLExtensions.cpp
@@ -60,6 +60,11 @@
     mRenderer = (char const*)renderer;
     mVersion = (char const*)version;
     mExtensions = (char const*)extensions;
+
+    ExtensionSet extensionSet(mExtensions.c_str());
+    if (extensionSet.hasExtension("GL_EXT_protected_textures")) {
+        mHasProtectedTexture = true;
+    }
 }
 
 char const* GLExtensions::getVendor() const {
diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h
index 2a654d5..ef00009 100644
--- a/libs/renderengine/gl/GLExtensions.h
+++ b/libs/renderengine/gl/GLExtensions.h
@@ -40,6 +40,7 @@
     bool hasProtectedContent() const { return mHasProtectedContent; }
     bool hasContextPriority() const { return mHasContextPriority; }
     bool hasSurfacelessContext() const { return mHasSurfacelessContext; }
+    bool hasProtectedTexture() const { return mHasProtectedTexture; }
 
     void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
                            GLubyte const* extensions);
@@ -65,12 +66,12 @@
     bool mHasProtectedContent = false;
     bool mHasContextPriority = false;
     bool mHasSurfacelessContext = false;
+    bool mHasProtectedTexture = false;
 
     String8 mVendor;
     String8 mRenderer;
     String8 mVersion;
     String8 mExtensions;
-
     String8 mEGLVersion;
     String8 mEGLExtensions;
 
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index f4de91a..4a519bb 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -39,7 +39,7 @@
     eglDestroyImageKHR(mEGLDisplay, mEGLImage);
 }
 
-bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer) {
+bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) {
     if (mEGLImage != EGL_NO_IMAGE_KHR) {
         eglDestroyImageKHR(mEGLDisplay, mEGLImage);
         mEGLImage = EGL_NO_IMAGE_KHR;
@@ -48,8 +48,13 @@
     }
 
     if (nativeBuffer) {
+        EGLint attributes[] = {
+                isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
+                isProtected ? EGL_TRUE : EGL_NONE,
+                EGL_NONE,
+        };
         mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
-                                      nativeBuffer, nullptr);
+                                      nativeBuffer, attributes);
         if (mEGLImage == EGL_NO_IMAGE_KHR) {
             return false;
         }
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index 358ab47..5043c59 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -35,7 +35,7 @@
     explicit GLFramebuffer(const GLESRenderEngine& engine);
     ~GLFramebuffer() override;
 
-    bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer) override;
+    bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) override;
     EGLImageKHR getEGLImage() const { return mEGLImage; }
     uint32_t getTextureName() const { return mTextureName; }
     uint32_t getFramebufferName() const { return mFramebufferName; }
diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp
index a9529a7..587cb31 100644
--- a/libs/renderengine/gl/GLImage.cpp
+++ b/libs/renderengine/gl/GLImage.cpp
@@ -65,6 +65,7 @@
             ALOGE("failed to create EGLImage: %#x", eglGetError());
             return false;
         }
+        mProtected = isProtected;
     }
 
     return true;
diff --git a/libs/renderengine/gl/GLImage.h b/libs/renderengine/gl/GLImage.h
index c897d8e..59d6ce3 100644
--- a/libs/renderengine/gl/GLImage.h
+++ b/libs/renderengine/gl/GLImage.h
@@ -39,10 +39,12 @@
     bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) override;
 
     EGLImageKHR getEGLImage() const { return mEGLImage; }
+    bool isProtected() const { return mProtected; }
 
 private:
     EGLDisplay mEGLDisplay;
     EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR;
+    bool mProtected = false;
 
     DISALLOW_COPY_AND_ASSIGN(GLImage);
 };
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 41870d2..bf2354d 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -77,7 +77,8 @@
     return f;
 }
 
-void ProgramCache::primeCache(bool useColorManagement) {
+void ProgramCache::primeCache(EGLContext context, bool useColorManagement) {
+    auto& cache = mCaches[context];
     uint32_t shaderCount = 0;
     uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK
         | Key::ROUNDED_CORNERS_MASK;
@@ -92,8 +93,8 @@
         if (tex != Key::TEXTURE_OFF && tex != Key::TEXTURE_EXT && tex != Key::TEXTURE_2D) {
             continue;
         }
-        if (mCache.count(shaderKey) == 0) {
-            mCache.emplace(shaderKey, generateProgram(shaderKey));
+        if (cache.count(shaderKey) == 0) {
+            cache.emplace(shaderKey, generateProgram(shaderKey));
             shaderCount++;
         }
     }
@@ -109,8 +110,8 @@
             shaderKey.set(Key::OPACITY_MASK,
                           (i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT);
             shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE);
-            if (mCache.count(shaderKey) == 0) {
-                mCache.emplace(shaderKey, generateProgram(shaderKey));
+            if (cache.count(shaderKey) == 0) {
+                cache.emplace(shaderKey, generateProgram(shaderKey));
                 shaderCount++;
             }
         }
@@ -695,20 +696,21 @@
     return std::make_unique<Program>(needs, vs.string(), fs.string());
 }
 
-void ProgramCache::useProgram(const Description& description) {
+void ProgramCache::useProgram(EGLContext context, const Description& description) {
     // generate the key for the shader based on the description
     Key needs(computeKey(description));
 
     // look-up the program in the cache
-    auto it = mCache.find(needs);
-    if (it == mCache.end()) {
+    auto& cache = mCaches[context];
+    auto it = cache.find(needs);
+    if (it == cache.end()) {
         // we didn't find our program, so generate one...
         nsecs_t time = systemTime();
-        it = mCache.emplace(needs, generateProgram(needs)).first;
+        it = cache.emplace(needs, generateProgram(needs)).first;
         time = systemTime() - time;
 
-        ALOGV(">>> generated new program: needs=%08X, time=%u ms (%zu programs)", needs.mKey,
-              uint32_t(ns2ms(time)), mCache.size());
+        ALOGV(">>> generated new program for context %p: needs=%08X, time=%u ms (%zu programs)",
+              context, needs.mKey, uint32_t(ns2ms(time)), cache.size());
     }
 
     // here we have a suitable program for this description
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index 653aaf0..400ad74 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -20,6 +20,7 @@
 #include <memory>
 #include <unordered_map>
 
+#include <EGL/egl.h>
 #include <GLES2/gl2.h>
 #include <renderengine/private/Description.h>
 #include <utils/Singleton.h>
@@ -178,13 +179,13 @@
     ~ProgramCache() = default;
 
     // Generate shaders to populate the cache
-    void primeCache(bool useColorManagement);
+    void primeCache(const EGLContext context, bool useColorManagement);
 
-    size_t getSize() const { return mCache.size(); }
+    size_t getSize(const EGLContext context) { return mCaches[context].size(); }
 
     // useProgram lookup a suitable program in the cache or generates one
     // if none can be found.
-    void useProgram(const Description& description);
+    void useProgram(const EGLContext context, const Description& description);
 
 private:
     // compute a cache Key from a Description
@@ -206,7 +207,8 @@
 
     // Key/Value map used for caching Programs. Currently the cache
     // is never shrunk (and the GL program objects are never deleted).
-    std::unordered_map<Key, std::unique_ptr<Program>, Key::Hash> mCache;
+    std::unordered_map<EGLContext, std::unordered_map<Key, std::unique_ptr<Program>, Key::Hash>>
+            mCaches;
 };
 
 } // namespace gl
diff --git a/libs/renderengine/include/renderengine/Framebuffer.h b/libs/renderengine/include/renderengine/Framebuffer.h
index 558b9c7..66eb9ef 100644
--- a/libs/renderengine/include/renderengine/Framebuffer.h
+++ b/libs/renderengine/include/renderengine/Framebuffer.h
@@ -27,7 +27,7 @@
 public:
     virtual ~Framebuffer() = default;
 
-    virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer) = 0;
+    virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) = 0;
 };
 
 } // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 8eaa2d2..5e88159 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -53,6 +53,11 @@
 class RenderEngine;
 }
 
+enum class Protection {
+    UNPROTECTED = 1,
+    PROTECTED = 2,
+};
+
 class RenderEngine {
 public:
     enum FeatureFlag {
@@ -150,6 +155,10 @@
 
     // ----- BEGIN NEW INTERFACE -----
 
+    virtual bool isProtected() const = 0;
+    virtual bool supportsProtectedContent() const = 0;
+    virtual bool useProtectedContext(bool useProtectedContext) = 0;
+
     // Renders layers for a particular display via GPU composition. This method
     // should be called for every display that needs to be rendered via the GPU.
     // @param settings The display-wide settings that should be applied prior to
@@ -182,12 +191,12 @@
 public:
     BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer)
           : mEngine(engine), mFramebuffer(mEngine.createFramebuffer()), mStatus(NO_ERROR) {
-        mStatus = mFramebuffer->setNativeWindowBuffer(buffer)
+        mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected())
                 ? mEngine.bindFrameBuffer(mFramebuffer.get())
                 : NO_MEMORY;
     }
     ~BindNativeBufferAsFramebuffer() {
-        mFramebuffer->setNativeWindowBuffer(nullptr);
+        mFramebuffer->setNativeWindowBuffer(nullptr, false);
         mEngine.unbindFrameBuffer(mFramebuffer.get());
     }
     status_t getStatus() const { return mStatus; }
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index a894f20..1b339a0 100644
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -165,194 +165,6 @@
     return;
 }
 
-TEST_F(BufferHubBufferTest, AllocateAndFreeBuffer) {
-    // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
-    sp<IBufferHub> bufferHub = IBufferHub::getService();
-    ASSERT_NE(nullptr, bufferHub.get());
-
-    // Stride is an output, rfu0 and rfu1 are reserved data slot for future use.
-    AHardwareBuffer_Desc aDesc = {kWidth, kHeight,        kLayerCount,  kFormat,
-                                  kUsage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL};
-    HardwareBufferDescription desc;
-    memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription));
-
-    sp<IBufferClient> client;
-    BufferHubStatus ret;
-    IBufferHub::allocateBuffer_cb callback = [&](const auto& outClient, const auto& outStatus) {
-        client = outClient;
-        ret = outStatus;
-    };
-    EXPECT_TRUE(bufferHub->allocateBuffer(desc, kUserMetadataSize, callback).isOk());
-    EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
-    ASSERT_NE(nullptr, client.get());
-
-    EXPECT_EQ(BufferHubStatus::NO_ERROR, client->close());
-    EXPECT_EQ(BufferHubStatus::CLIENT_CLOSED, client->close());
-}
-
-TEST_F(BufferHubBufferTest, DuplicateFreedBuffer) {
-    // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
-    sp<IBufferHub> bufferHub = IBufferHub::getService();
-    ASSERT_NE(nullptr, bufferHub.get());
-
-    // Stride is an output, rfu0 and rfu1 are reserved data slot for future use.
-    AHardwareBuffer_Desc aDesc = {kWidth, kHeight,        kLayerCount,  kFormat,
-                                  kUsage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL};
-    HardwareBufferDescription desc;
-    memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription));
-
-    sp<IBufferClient> client;
-    BufferHubStatus ret;
-    IBufferHub::allocateBuffer_cb callback = [&](const auto& outClient, const auto& outStatus) {
-        client = outClient;
-        ret = outStatus;
-    };
-    EXPECT_TRUE(bufferHub->allocateBuffer(desc, kUserMetadataSize, callback).isOk());
-    EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
-    ASSERT_NE(nullptr, client.get());
-
-    EXPECT_EQ(BufferHubStatus::NO_ERROR, client->close());
-
-    hidl_handle token;
-    IBufferClient::duplicate_cb dup_cb = [&](const auto& outToken, const auto& status) {
-        token = outToken;
-        ret = status;
-    };
-    EXPECT_TRUE(client->duplicate(dup_cb).isOk());
-    EXPECT_EQ(ret, BufferHubStatus::CLIENT_CLOSED);
-    EXPECT_EQ(token.getNativeHandle(), nullptr);
-}
-
-TEST_F(BufferHubBufferTest, DuplicateAndImportBuffer) {
-    // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
-    sp<IBufferHub> bufferhub = IBufferHub::getService();
-    ASSERT_NE(nullptr, bufferhub.get());
-
-    // Stride is an output, rfu0 and rfu1 are reserved data slot for future use.
-    AHardwareBuffer_Desc aDesc = {kWidth, kHeight,        kLayerCount,  kFormat,
-                                  kUsage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL};
-    HardwareBufferDescription desc;
-    memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription));
-
-    sp<IBufferClient> client;
-    BufferHubStatus ret;
-    IBufferHub::allocateBuffer_cb alloc_cb = [&](const auto& outClient, const auto& status) {
-        client = outClient;
-        ret = status;
-    };
-    ASSERT_TRUE(bufferhub->allocateBuffer(desc, kUserMetadataSize, alloc_cb).isOk());
-    EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
-    ASSERT_NE(nullptr, client.get());
-
-    hidl_handle token;
-    IBufferClient::duplicate_cb dup_cb = [&](const auto& outToken, const auto& status) {
-        token = outToken;
-        ret = status;
-    };
-    ASSERT_TRUE(client->duplicate(dup_cb).isOk());
-    EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
-    ASSERT_NE(token.getNativeHandle(), nullptr);
-    EXPECT_EQ(token->numInts, 1);
-    EXPECT_EQ(token->numFds, 0);
-
-    sp<IBufferClient> client2;
-    IBufferHub::importBuffer_cb import_cb = [&](const auto& outClient, const auto& status) {
-        ret = status;
-        client2 = outClient;
-    };
-    ASSERT_TRUE(bufferhub->importBuffer(token, import_cb).isOk());
-    EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
-    EXPECT_NE(nullptr, client2.get());
-    // TODO(b/116681016): once BufferNode.id() is exposed via BufferHubBuffer, check origin.id =
-    // improted.id here.
-}
-
-// nullptr must not crash the service
-TEST_F(BufferHubBufferTest, ImportNullToken) {
-    // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
-    sp<IBufferHub> bufferhub = IBufferHub::getService();
-    ASSERT_NE(nullptr, bufferhub.get());
-
-    hidl_handle nullToken;
-    sp<IBufferClient> client;
-    BufferHubStatus ret;
-    IBufferHub::importBuffer_cb import_cb = [&](const auto& outClient, const auto& status) {
-        client = outClient;
-        ret = status;
-    };
-    ASSERT_TRUE(bufferhub->importBuffer(nullToken, import_cb).isOk());
-    EXPECT_EQ(ret, BufferHubStatus::INVALID_TOKEN);
-    EXPECT_EQ(nullptr, client.get());
-}
-
-// This test has a very little chance to fail (number of existing tokens / 2 ^ 32)
-TEST_F(BufferHubBufferTest, ImportInvalidToken) {
-    // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
-    sp<IBufferHub> bufferhub = IBufferHub::getService();
-    ASSERT_NE(nullptr, bufferhub.get());
-
-    native_handle_t* tokenHandle = native_handle_create(/*numFds=*/0, /*numInts=*/1);
-    tokenHandle->data[0] = 0;
-
-    hidl_handle invalidToken(tokenHandle);
-    sp<IBufferClient> client;
-    BufferHubStatus ret;
-    IBufferHub::importBuffer_cb import_cb = [&](const auto& outClient, const auto& status) {
-        client = outClient;
-        ret = status;
-    };
-    ASSERT_TRUE(bufferhub->importBuffer(invalidToken, import_cb).isOk());
-    EXPECT_EQ(ret, BufferHubStatus::INVALID_TOKEN);
-    EXPECT_EQ(nullptr, client.get());
-
-    native_handle_delete(tokenHandle);
-}
-
-TEST_F(BufferHubBufferTest, ImportFreedBuffer) {
-    // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
-    sp<IBufferHub> bufferhub = IBufferHub::getService();
-    ASSERT_NE(nullptr, bufferhub.get());
-
-    // Stride is an output, rfu0 and rfu1 are reserved data slot for future use.
-    AHardwareBuffer_Desc aDesc = {kWidth, kHeight,        kLayerCount,  kFormat,
-                                  kUsage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL};
-    HardwareBufferDescription desc;
-    memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription));
-
-    sp<IBufferClient> client;
-    BufferHubStatus ret;
-    IBufferHub::allocateBuffer_cb alloc_cb = [&](const auto& outClient, const auto& status) {
-        client = outClient;
-        ret = status;
-    };
-    ASSERT_TRUE(bufferhub->allocateBuffer(desc, kUserMetadataSize, alloc_cb).isOk());
-    EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
-    ASSERT_NE(nullptr, client.get());
-
-    hidl_handle token;
-    IBufferClient::duplicate_cb dup_cb = [&](const auto& outToken, const auto& status) {
-        token = outToken;
-        ret = status;
-    };
-    ASSERT_TRUE(client->duplicate(dup_cb).isOk());
-    EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
-    ASSERT_NE(token.getNativeHandle(), nullptr);
-    EXPECT_EQ(token->numInts, 1);
-    EXPECT_EQ(token->numFds, 0);
-
-    // Close the client. Now the token should be invalid.
-    client->close();
-
-    sp<IBufferClient> client2;
-    IBufferHub::importBuffer_cb import_cb = [&](const auto& outClient, const auto& status) {
-        client2 = outClient;
-        ret = status;
-    };
-    EXPECT_TRUE(bufferhub->importBuffer(token, import_cb).isOk());
-    EXPECT_EQ(ret, BufferHubStatus::INVALID_TOKEN);
-    EXPECT_EQ(nullptr, client2.get());
-}
-
 TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromReleasedState) {
     ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
 
diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
index 6cb6541..1359f4c 100644
--- a/libs/vr/libbufferhub/buffer_hub-test.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -1,7 +1,8 @@
 #include <gtest/gtest.h>
 #include <poll.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/consumer_buffer.h>
+#include <private/dvr/producer_buffer.h>
 #include <sys/epoll.h>
 #include <sys/eventfd.h>
 #include <ui/BufferHubBuffer.h>
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
deleted file mode 100644
index 1daeed9..0000000
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_
-#define ANDROID_DVR_BUFFER_HUB_CLIENT_H_
-
-// TODO(b/116855254): This header is completely deprecated and replaced by
-// consumer_buffer.h and producer_buffer.h. Remove this file once all references
-// to it has been removed.
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/producer_buffer.h>
-
-#endif  // ANDROID_DVR_BUFFER_HUB_CLIENT_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 def7c6b..53ab2b2 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
@@ -13,10 +13,11 @@
 // in these headers and their dependencies.
 #include <pdx/client.h>
 #include <pdx/status.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/buffer_hub_queue_parcelable.h>
 #include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/consumer_buffer.h>
 #include <private/dvr/epoll_file_descriptor.h>
+#include <private/dvr/producer_buffer.h>
 
 #if defined(__clang__)
 #pragma clang diagnostic pop
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
index fd6ca43..159d6dc 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -1,8 +1,9 @@
 #include <base/logging.h>
 #include <binder/Parcel.h>
 #include <dvr/dvr_api.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/consumer_buffer.h>
+#include <private/dvr/producer_buffer.h>
 
 #include <gtest/gtest.h>
 #include <poll.h>
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
index 974c231..fdeeb70 100644
--- a/libs/vr/libdisplay/display_manager_client.cpp
+++ b/libs/vr/libdisplay/display_manager_client.cpp
@@ -1,7 +1,6 @@
 #include "include/private/dvr/display_manager_client.h"
 
 #include <pdx/default_transport/client_channel_factory.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/buffer_hub_queue_client.h>
 #include <private/dvr/display_protocol.h>
 #include <utils/Log.h>
diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h
index caf3182..f8f5b3d 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_client.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_client.h
@@ -5,7 +5,6 @@
 #include <hardware/hwcomposer.h>
 #include <pdx/client.h>
 #include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/buffer_hub_queue_client.h>
 #include <private/dvr/display_protocol.h>
 
diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp
index baf1f2f..c11706f 100644
--- a/libs/vr/libdvr/dvr_buffer.cpp
+++ b/libs/vr/libdvr/dvr_buffer.cpp
@@ -2,7 +2,8 @@
 
 #include <android/hardware_buffer.h>
 #include <dvr/dvr_shared_buffers.h>
-#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/consumer_buffer.h>
+#include <private/dvr/producer_buffer.h>
 #include <ui/GraphicBuffer.h>
 
 #include "dvr_internal.h"
diff --git a/libs/vr/libdvr/dvr_display_manager.cpp b/libs/vr/libdvr/dvr_display_manager.cpp
index 852f9a4..fe91b14 100644
--- a/libs/vr/libdvr/dvr_display_manager.cpp
+++ b/libs/vr/libdvr/dvr_display_manager.cpp
@@ -2,8 +2,8 @@
 
 #include <dvr/dvr_buffer.h>
 #include <pdx/rpc/variant.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/consumer_buffer.h>
 #include <private/dvr/display_client.h>
 #include <private/dvr/display_manager_client.h>
 
diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h
index 1a200aa..9e35a39 100644
--- a/libs/vr/libvrflinger/acquired_buffer.h
+++ b/libs/vr/libvrflinger/acquired_buffer.h
@@ -2,7 +2,7 @@
 #define ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
 
 #include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/consumer_buffer.h>
 
 #include <memory>
 
@@ -43,7 +43,7 @@
 
   // Accessors for the underlying BufferConsumer, the acquire fence, and the
   // use-case specific sequence value from the acquisition (see
-  // private/dvr/buffer_hub_client.h).
+  // private/dvr/consumer_buffer.h).
   std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
   int acquire_fence() const { return acquire_fence_.Get(); }
 
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
index 6fad58e..e0f2edd 100644
--- a/libs/vr/libvrflinger/display_service.h
+++ b/libs/vr/libvrflinger/display_service.h
@@ -4,7 +4,6 @@
 #include <dvr/dvr_api.h>
 #include <pdx/service.h>
 #include <pdx/status.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/bufferhub_rpc.h>
 #include <private/dvr/display_protocol.h>
 
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index 94a2337..6c25b3e 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -22,7 +22,6 @@
 #include <dvr/dvr_vsync.h>
 #include <pdx/file_handle.h>
 #include <pdx/rpc/variant.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/shared_buffer_helpers.h>
 #include <private/dvr/vsync_service.h>
 
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
index 710d75a..c72f75e 100644
--- a/libs/vr/libvrsensor/pose_client.cpp
+++ b/libs/vr/libvrsensor/pose_client.cpp
@@ -8,8 +8,8 @@
 #include <pdx/client.h>
 #include <pdx/default_transport/client_channel_factory.h>
 #include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/consumer_buffer.h>
 #include <private/dvr/display_client.h>
 #include <private/dvr/pose-ipc.h>
 #include <private/dvr/shared_buffer_helpers.h>
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 50e30c2..f5cf3dc 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -554,33 +554,17 @@
     property_get("ro.product.manufacturer", manufacturer, "UNSET");
     property_get("ro.product.model", model, "UNSET");
 
-    // TODO: Replace this with the new function name once the version-1 API is removed:
-    fpANGLEGetUtilityAPI ANGLEGetUtilityAPI =
-            (fpANGLEGetUtilityAPI)dlsym(so, "ANGLEGetUtilityAPI");
+    fpANGLEGetFeatureSupportUtilAPIVersion ANGLEGetFeatureSupportUtilAPIVersion =
+            (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so, "ANGLEGetFeatureSupportUtilAPIVersion");
 
-    if (ANGLEGetUtilityAPI) {
+    if (ANGLEGetFeatureSupportUtilAPIVersion) {
 
         // Negotiate the interface version by requesting most recent known to the platform
         unsigned int versionToUse = 2;
-        // TODO: Replace this with the new function name once the version-1 API is removed:
-        if ((ANGLEGetUtilityAPI)(&versionToUse)) {
+        if ((ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) {
 
             // Add and remove versions below as needed
             switch(versionToUse) {
-            case 1: {
-                ALOGV("Using version 1 of ANGLE feature-support library");
-                fpAndroidUseANGLEForApplication AndroidUseANGLEForApplication =
-                        (fpAndroidUseANGLEForApplication)dlsym(so, "AndroidUseANGLEForApplication");
-
-                if (AndroidUseANGLEForApplication) {
-                    use_angle = (AndroidUseANGLEForApplication)(rules_fd, rules_offset,
-                                                                rules_length, app_name_str.c_str(),
-                                                                manufacturer, model);
-                } else {
-                    ALOGW("Cannot find AndroidUseANGLEForApplication in ANGLE feature-support library");
-                }
-            }
-            break;
             case 2: {
                 ALOGV("Using version 2 of ANGLE feature-support library");
                 void* rules_handle = nullptr;
@@ -647,7 +631,7 @@
             ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, requested version %u", versionToUse);
         }
     } else {
-        ALOGW("Cannot find ANGLEGetUtilityAPI function");
+        ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function");
     }
 
     ALOGV("Close temporarily-loaded ANGLE opt-in/out logic");
diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp
index 5709e1e..f7802b9 100644
--- a/services/inputflinger/EventHub.cpp
+++ b/services/inputflinger/EventHub.cpp
@@ -69,8 +69,12 @@
 
 namespace android {
 
+static constexpr bool DEBUG = false;
+
 static const char *WAKE_LOCK_ID = "KeyEvents";
 static const char *DEVICE_PATH = "/dev/input";
+// v4l2 devices go directly into /dev
+static const char *VIDEO_DEVICE_PATH = "/dev";
 
 static inline const char* toString(bool value) {
     return value ? "true" : "false";
@@ -98,6 +102,13 @@
     }
 }
 
+/**
+ * Return true if name matches "v4l-touch*"
+ */
+static bool isV4lTouchNode(const char* name) {
+    return strstr(name, "v4l-touch") == name;
+}
+
 // --- Global Functions ---
 
 uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
@@ -204,18 +215,21 @@
     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
 
     mEpollFd = epoll_create1(EPOLL_CLOEXEC);
-    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
+    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
 
     mINotifyFd = inotify_init();
-    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
-    LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s.  errno=%d",
-            DEVICE_PATH, errno);
+    mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
+    LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s",
+            DEVICE_PATH, strerror(errno));
+    mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);
+    LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",
+            VIDEO_DEVICE_PATH, strerror(errno));
 
     struct epoll_event eventItem;
     memset(&eventItem, 0, sizeof(eventItem));
     eventItem.events = EPOLLIN;
     eventItem.data.fd = mINotifyFd;
-    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
+    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
 
     int wakeFds[2];
@@ -1063,26 +1077,45 @@
         AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE,
 };
 
-status_t EventHub::registerDeviceForEpollLocked(Device* device) {
-    struct epoll_event eventItem;
-    memset(&eventItem, 0, sizeof(eventItem));
-    eventItem.events = EPOLLIN;
-    if (mUsingEpollWakeup) {
-        eventItem.events |= EPOLLWAKEUP;
-    }
-    eventItem.data.fd = device->fd;
-    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, device->fd, &eventItem)) {
-        ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);
+status_t EventHub::registerFdForEpoll(int fd) {
+    struct epoll_event eventItem = {};
+    eventItem.events = EPOLLIN | EPOLLWAKEUP;
+    eventItem.data.fd = fd;
+    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
+        ALOGE("Could not add fd to epoll instance: %s", strerror(errno));
         return -errno;
     }
     return OK;
 }
 
+status_t EventHub::unregisterFdFromEpoll(int fd) {
+    if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, nullptr)) {
+        ALOGW("Could not remove fd from epoll instance: %s", strerror(errno));
+        return -errno;
+    }
+    return OK;
+}
+
+status_t EventHub::registerDeviceForEpollLocked(Device* device) {
+    if (device == nullptr) {
+        if (DEBUG) {
+            LOG_ALWAYS_FATAL("Cannot call registerDeviceForEpollLocked with null Device");
+        }
+        return BAD_VALUE;
+    }
+    status_t result = registerFdForEpoll(device->fd);
+    if (result != OK) {
+        ALOGE("Could not add input device fd to epoll for device %" PRId32, device->id);
+    }
+    return result;
+}
+
 status_t EventHub::unregisterDeviceFromEpollLocked(Device* device) {
     if (device->hasValidFd()) {
-        if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, nullptr)) {
-            ALOGW("Could not remove device fd from epoll instance.  errno=%d", errno);
-            return -errno;
+        status_t result = unregisterFdFromEpoll(device->fd);
+        if (result != OK) {
+            ALOGW("Could not remove input device fd from epoll for device %" PRId32, device->id);
+            return result;
         }
     }
     return OK;
@@ -1103,7 +1136,7 @@
 
     // Get device name.
     if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
-        //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));
+        ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno));
     } else {
         buffer[sizeof(buffer) - 1] = '\0';
         identifier.name = buffer;
@@ -1646,8 +1679,6 @@
 
 status_t EventHub::readNotifyLocked() {
     int res;
-    char devname[PATH_MAX];
-    char *filename;
     char event_buf[512];
     int event_size;
     int event_pos = 0;
@@ -1661,22 +1692,27 @@
         ALOGW("could not get event, %s\n", strerror(errno));
         return -1;
     }
-    //printf("got %d bytes of event information\n", res);
-
-    strcpy(devname, DEVICE_PATH);
-    filename = devname + strlen(devname);
-    *filename++ = '/';
 
     while(res >= (int)sizeof(*event)) {
         event = (struct inotify_event *)(event_buf + event_pos);
-        //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
         if(event->len) {
-            strcpy(filename, event->name);
-            if(event->mask & IN_CREATE) {
-                openDeviceLocked(devname);
-            } else {
-                ALOGI("Removing device '%s' due to inotify event\n", devname);
-                closeDeviceByPathLocked(devname);
+            if (event->wd == mInputWd) {
+                std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name);
+                if(event->mask & IN_CREATE) {
+                    openDeviceLocked(filename.c_str());
+                } else {
+                    ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
+                    closeDeviceByPathLocked(filename.c_str());
+                }
+            }
+            else if (event->wd == mVideoWd) {
+                if (isV4lTouchNode(event->name)) {
+                    std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name);
+                    ALOGV("Received an inotify event for a video device %s", filename.c_str());
+                }
+            }
+            else {
+                LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event->wd);
             }
         }
         event_size = sizeof(*event) + event->len;
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 23cceb4..b4eb370 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -74,14 +74,16 @@
         int32_t buttonState, int32_t edgeFlags, uint32_t deviceTimestamp,
         uint32_t pointerCount,
         const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-        float xPrecision, float yPrecision, nsecs_t downTime) :
+        float xPrecision, float yPrecision, nsecs_t downTime,
+        const std::vector<TouchVideoFrame>& videoFrames) :
         NotifyArgs(sequenceNum), eventTime(eventTime), deviceId(deviceId), source(source),
         displayId(displayId), policyFlags(policyFlags),
         action(action), actionButton(actionButton),
         flags(flags), metaState(metaState), buttonState(buttonState),
         edgeFlags(edgeFlags), deviceTimestamp(deviceTimestamp),
         pointerCount(pointerCount),
-        xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime) {
+        xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime),
+        videoFrames(videoFrames) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
         this->pointerCoords[i].copyFrom(pointerCoords[i]);
@@ -95,7 +97,8 @@
         metaState(other.metaState), buttonState(other.buttonState),
         edgeFlags(other.edgeFlags),
         deviceTimestamp(other.deviceTimestamp), pointerCount(other.pointerCount),
-        xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime) {
+        xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime),
+        videoFrames(other.videoFrames) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         pointerProperties[i].copyFrom(other.pointerProperties[i]);
         pointerCoords[i].copyFrom(other.pointerCoords[i]);
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 7e00f42..2d342bc 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -2642,7 +2642,6 @@
                 mOrientation = internalViewport->orientation;
             }
         }
-        getPolicy()->updatePointerDisplay();
         bumpGeneration();
     }
 }
@@ -2788,7 +2787,7 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
-        displayId = mPointerController->getDisplayId();
+        displayId = ADISPLAY_ID_DEFAULT;
     } else {
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
@@ -2833,7 +2832,7 @@
                         AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
                         metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                         /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                        mXPrecision, mYPrecision, downTime);
+                        mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
                 getListener()->notifyMotion(&releaseArgs);
             }
         }
@@ -2842,7 +2841,7 @@
                 displayId, policyFlags, motionEventAction, 0, 0, metaState, currentButtonState,
                 AMOTION_EVENT_EDGE_FLAG_NONE,
                 /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                mXPrecision, mYPrecision, downTime);
+                mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
 
         if (buttonsPressed) {
@@ -2854,7 +2853,7 @@
                         mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS,
                         actionButton, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                         /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                        mXPrecision, mYPrecision, downTime);
+                        mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
                 getListener()->notifyMotion(&pressArgs);
             }
         }
@@ -2868,7 +2867,7 @@
                     mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
                     metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                     /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                    mXPrecision, mYPrecision, downTime);
+                    mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&hoverArgs);
         }
 
@@ -2882,7 +2881,7 @@
                     AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, currentButtonState,
                     AMOTION_EVENT_EDGE_FLAG_NONE,
                     /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                    mXPrecision, mYPrecision, downTime);
+                    mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&scrollArgs);
         }
     }
@@ -3014,7 +3013,7 @@
                 AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, 0,
                 AMOTION_EVENT_EDGE_FLAG_NONE,
                 /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                0, 0, 0);
+                0, 0, 0, /* videoFrames */ {});
         getListener()->notifyMotion(&scrollArgs);
     }
 
@@ -3609,9 +3608,6 @@
             (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
         if (mPointerController == nullptr) {
             mPointerController = getPolicy()->obtainPointerController(getDeviceId());
-            getPolicy()->updatePointerDisplay();
-        } else if (viewportChanged) {
-            getPolicy()->updatePointerDisplay();
         }
     } else {
         mPointerController.clear();
@@ -5399,12 +5395,12 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
 
-        int32_t displayId = mPointerController->getDisplayId();
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, displayId, mSource,
-                mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+                mSource, mViewport.displayId, policyFlags,
+                AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
                 metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                 /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                0, 0, mPointerGesture.downTime);
+                0, 0, mPointerGesture.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -6307,7 +6303,6 @@
 void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
         bool down, bool hovering) {
     int32_t metaState = getContext()->getGlobalMetaState();
-    int32_t displayId = mViewport.displayId;
 
     if (mPointerController != nullptr) {
         if (down || hovering) {
@@ -6318,7 +6313,6 @@
         } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
             mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
         }
-        displayId = mPointerController->getDisplayId();
     }
 
     if (mPointerSimple.down && !down) {
@@ -6326,12 +6320,12 @@
 
         // Send up.
         NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
-                mSource, displayId, policyFlags,
-                 AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, 0,
-                 /* deviceTimestamp */ 0,
-                 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
-                 mOrientedXPrecision, mOrientedYPrecision,
-                 mPointerSimple.downTime);
+                mSource, mViewport.displayId, policyFlags,
+                AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, 0,
+                /* deviceTimestamp */ 0,
+                1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
+                mOrientedXPrecision, mOrientedYPrecision,
+                mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -6339,13 +6333,13 @@
         mPointerSimple.hovering = false;
 
         // Send hover exit.
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
-                mSource, displayId, policyFlags,
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), 
+                mSource, mViewport.displayId, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, 0,
                 /* deviceTimestamp */ 0,
                 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
-                mPointerSimple.downTime);
+                mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -6355,24 +6349,24 @@
             mPointerSimple.downTime = when;
 
             // Send down.
-            NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
-                    mSource, displayId, policyFlags,
+            NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), 
+                    mSource, mViewport.displayId, policyFlags,
                     AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, 0,
                     /* deviceTimestamp */ 0,
                     1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                     mOrientedXPrecision, mOrientedYPrecision,
-                    mPointerSimple.downTime);
+                    mPointerSimple.downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&args);
         }
 
         // Send move.
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
-                mSource, displayId, policyFlags,
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), 
+                mSource, mViewport.displayId, policyFlags,
                 AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, 0,
                 /* deviceTimestamp */ 0,
                 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
-                mPointerSimple.downTime);
+                mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -6381,26 +6375,26 @@
             mPointerSimple.hovering = true;
 
             // Send hover enter.
-            NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
-                    mSource, displayId, policyFlags,
+            NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), 
+                    mSource, mViewport.displayId, policyFlags,
                     AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
                     mCurrentRawState.buttonState, 0,
                     /* deviceTimestamp */ 0,
                     1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                     mOrientedXPrecision, mOrientedYPrecision,
-                    mPointerSimple.downTime);
+                    mPointerSimple.downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&args);
         }
 
         // Send hover move.
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
-                mSource, displayId, policyFlags,
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), 
+                mSource, mViewport.displayId, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
                 mCurrentRawState.buttonState, 0,
                 /* deviceTimestamp */ 0,
                 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
-                mPointerSimple.downTime);
+                mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -6416,13 +6410,13 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
-                mSource, displayId, policyFlags,
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), 
+                mSource, mViewport.displayId, policyFlags,
                 AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, 0,
                 /* deviceTimestamp */ 0,
                 1, &mPointerSimple.currentProperties, &pointerCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
-                mPointerSimple.downTime);
+                mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -6479,13 +6473,12 @@
             ALOG_ASSERT(false);
         }
     }
-    int32_t displayId = mPointerController != nullptr ?
-            mPointerController->getDisplayId() : mViewport.displayId;
-    NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
-            source, displayId, policyFlags,
+
+    NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), 
+            source, mViewport.displayId, policyFlags,
             action, actionButton, flags, metaState, buttonState, edgeFlags,
             deviceTimestamp, pointerCount, pointerProperties, pointerCoords,
-            xPrecision, yPrecision, downTime);
+            xPrecision, yPrecision, downTime, /* videoFrames */ {});
     getListener()->notifyMotion(&args);
 }
 
@@ -7410,7 +7403,7 @@
             AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags,
             AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
             /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-            0, 0, 0);
+            0, 0, 0, /* videoFrames */ {});
     getListener()->notifyMotion(&args);
 }
 
diff --git a/services/inputflinger/include/EventHub.h b/services/inputflinger/include/EventHub.h
index e2c7e82..1ddb978 100644
--- a/services/inputflinger/include/EventHub.h
+++ b/services/inputflinger/include/EventHub.h
@@ -391,6 +391,8 @@
     bool isDeviceEnabled(int32_t deviceId);
     status_t enableDevice(int32_t deviceId);
     status_t disableDevice(int32_t deviceId);
+    status_t registerFdForEpoll(int fd);
+    status_t unregisterFdFromEpoll(int fd);
     status_t registerDeviceForEpollLocked(Device* device);
     status_t unregisterDeviceFromEpollLocked(Device* device);
 
@@ -450,6 +452,9 @@
     int mWakeReadPipeFd;
     int mWakeWritePipeFd;
 
+    int mInputWd;
+    int mVideoWd;
+
     // Epoll FD list size hint.
     static const int EPOLL_SIZE_HINT = 8;
 
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index f3a30ab..2442cc0 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -17,7 +17,10 @@
 #ifndef _UI_INPUT_LISTENER_H
 #define _UI_INPUT_LISTENER_H
 
+#include <vector>
+
 #include <input/Input.h>
+#include <input/TouchVideoFrame.h>
 #include <utils/RefBase.h>
 #include <utils/Vector.h>
 
@@ -110,6 +113,7 @@
     float xPrecision;
     float yPrecision;
     nsecs_t downTime;
+    std::vector<TouchVideoFrame> videoFrames;
 
     inline NotifyMotionArgs() { }
 
@@ -119,7 +123,8 @@
             int32_t metaState, int32_t buttonState,
             int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount,
             const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-            float xPrecision, float yPrecision, nsecs_t downTime);
+            float xPrecision, float yPrecision, nsecs_t downTime,
+            const std::vector<TouchVideoFrame>& videoFrames);
 
     NotifyMotionArgs(const NotifyMotionArgs& other);
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 5a78df7..fe1c50b 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -342,9 +342,6 @@
     /* Gets the affine calibration associated with the specified device. */
     virtual TouchAffineTransformation getTouchAffineTransformation(
             const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
-
-    /* Update the pointer controller associated with the specified display. */
-    virtual void updatePointerDisplay() = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index e60b3f4..e94dd94 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -58,9 +58,6 @@
     /* Gets the absolute location of the pointer. */
     virtual void getPosition(float* outX, float* outY) const = 0;
 
-    /* Gets the id of the display where the pointer should be shown. */
-    virtual int32_t getDisplayId() const = 0;
-
     enum Transition {
         // Fade/unfade immediately.
         TRANSITION_IMMEDIATE,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 855247f..167a624 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -57,7 +57,6 @@
     float mMinX, mMinY, mMaxX, mMaxY;
     float mX, mY;
     int32_t mButtonState;
-    int32_t mDisplayId;
 
 protected:
     virtual ~FakePointerController() { }
@@ -65,7 +64,7 @@
 public:
     FakePointerController() :
         mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0),
-        mButtonState(0), mDisplayId(ADISPLAY_ID_DEFAULT) {
+        mButtonState(0) {
     }
 
     void setBounds(float minX, float minY, float maxX, float maxY) {
@@ -76,10 +75,6 @@
         mMaxY = maxY;
     }
 
-    void setDisplayId(int32_t displayId) {
-        mDisplayId = displayId;
-    }
-
     virtual void setPosition(float x, float y) {
         mX = x;
         mY = y;
@@ -98,10 +93,6 @@
         *outY = mY;
     }
 
-    virtual int32_t getDisplayId() const {
-        return mDisplayId;
-    }
-
 private:
     virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const {
         *outMinX = mMinX;
@@ -271,9 +262,6 @@
     virtual std::string getDeviceAlias(const InputDeviceIdentifier&) {
         return "";
     }
-
-    virtual void updatePointerDisplay() {
-    }
 };
 
 
@@ -3177,30 +3165,6 @@
     ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
 }
 
-TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
-    addMapperAndConfigure(mapper);
-
-    // Setup PointerController for second display.
-    constexpr int32_t SECOND_DISPLAY_ID = 1;
-    mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
-    mFakePointerController->setPosition(100, 200);
-    mFakePointerController->setButtonState(0);
-    mFakePointerController->setDisplayId(SECOND_DISPLAY_ID);
-
-    NotifyMotionArgs args;
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
-    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
-    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-    ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
-    ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId);
-}
-
 
 // --- TouchInputMapperTest ---
 
@@ -4699,6 +4663,7 @@
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
 }
 
+
 // --- MultiTouchInputMapperTest ---
 
 class MultiTouchInputMapperTest : public TouchInputMapperTest {
@@ -6319,30 +6284,4 @@
     ASSERT_EQ(DISPLAY_ID, args.displayId);
 }
 
-TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
-    // Setup PointerController for second display.
-    sp<FakePointerController> fakePointerController = new FakePointerController();
-    fakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
-    fakePointerController->setPosition(100, 200);
-    fakePointerController->setButtonState(0);
-    fakePointerController->setDisplayId(SECONDARY_DISPLAY_ID);
-    mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
-
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
-
-    // Check source is mouse that would obtain the PointerController.
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
-
-    NotifyMotionArgs motionArgs;
-    processPosition(mapper, 100, 100);
-    processSync(mapper);
-
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
-    ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
-}
-
 } // namespace android
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 55d68f6..98ae286 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -71,6 +71,10 @@
     // isVisible - true if this layer is visible, false otherwise
     bool isVisible() const override EXCLUDES(mStateMutex);
 
+    // isProtected - true if the layer may contain protected content in the
+    // GRALLOC_USAGE_PROTECTED sense.
+    bool isProtected() const override;
+
     // isFixedSize - true if content has a fixed size
     bool isFixedSize() const override;
 
@@ -155,13 +159,6 @@
 
     virtual void setHwcLayerBuffer(DisplayId displayId) EXCLUDES(mStateMutex) = 0;
 
-    // -----------------------------------------------------------------------
-
-public:
-    // isProtected - true if the layer may contain protected content in the
-    // GRALLOC_USAGE_PROTECTED sense.
-    bool isProtected() const;
-
 protected:
     // Loads the corresponding system property once per process
     static bool latchUnsignaledBuffers();
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 70d52f1..3341b98 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -454,7 +454,8 @@
     mConsumer->setContentsChangedListener(this);
     mConsumer->setName(mName);
 
-    if (mFlinger->isLayerTripleBufferingDisabled()) {
+    // BufferQueueCore::mMaxDequeuedBufferCount is default to 1
+    if (!mFlinger->isLayerTripleBufferingDisabled()) {
         mProducer->setMaxDequeuedBufferCount(2);
     }
 
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 3ad07ae..2003fb1 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -366,6 +366,15 @@
     return mDisplaySurface->prepareFrame(compositionType);
 }
 
+void DisplayDevice::setProtected(bool useProtected) {
+    uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
+    if (useProtected) {
+        usageFlags |= GRALLOC_USAGE_PROTECTED;
+    }
+    const int status = native_window_set_usage(mNativeWindow.get(), usageFlags);
+    ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for protected content: %d", status);
+}
+
 sp<GraphicBuffer> DisplayDevice::dequeueBuffer() {
     int fd;
     ANativeWindowBuffer* buffer;
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index ba85432..2c2a3ab 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -146,6 +146,7 @@
                           ui::Dataspace* outDataspace, ui::ColorMode* outMode,
                           ui::RenderIntent* outIntent) const;
 
+    void setProtected(bool useProtected);
     // Queues the drawn buffer for consumption by HWC.
     void queueBuffer(HWComposer& hwc);
     // Allocates a buffer as scratch space for GPU composition
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 57ef65a..fb75e4c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -399,6 +399,12 @@
     bool isHiddenByPolicy() const EXCLUDES(mStateMutex);
 
     /*
+     * isProtected - true if the layer may contain protected content in the
+     * GRALLOC_USAGE_PROTECTED sense.
+     */
+    virtual bool isProtected() const { return false; }
+
+    /*
      * isFixedSize - true if content has a fixed size
      */
     virtual bool isFixedSize() const { return true; }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e6a43c5..b68346f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -379,7 +379,7 @@
     mUseHwcVirtualDisplays = atoi(value);
     ALOGI_IF(mUseHwcVirtualDisplays, "Enabling HWC virtual displays");
 
-    property_get("ro.sf.disable_triple_buffer", value, "1");
+    property_get("ro.sf.disable_triple_buffer", value, "0");
     mLayerTripleBufferingDisabled = atoi(value);
     ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
 
@@ -2736,7 +2736,7 @@
 
     commitTransaction();
 
-    if ((inputChanged || mVisibleRegionsDirty) && mInputFlinger) {
+    if (inputChanged || mVisibleRegionsDirty) {
         updateInputWindows();
     }
 
@@ -2746,6 +2746,10 @@
 void SurfaceFlinger::updateInputWindows() {
     ATRACE_CALL();
 
+    if (mInputFlinger == nullptr) {
+        return;
+    }
+
     Vector<InputWindowInfo> inputHandles;
 
     mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
@@ -3015,6 +3019,11 @@
 
     mVisibleRegionsDirty |= visibleRegions;
 
+    if (visibleRegions) {
+        // Update input window info if the layer receives its first buffer.
+        updateInputWindows();
+    }
+
     // If we will need to wake up at some time in the future to deal with a
     // queued frame that shouldn't be displayed during this vsync period, wake
     // up during the next vsync period to check again.
@@ -5439,10 +5448,11 @@
         ALOGW("FB is protected: PERMISSION_DENIED");
         return PERMISSION_DENIED;
     }
+    auto& engine(getRenderEngine());
 
     // this binds the given EGLImage as a framebuffer for the
     // duration of this scope.
-    renderengine::BindNativeBufferAsFramebuffer bufferBond(getRenderEngine(), buffer);
+    renderengine::BindNativeBufferAsFramebuffer bufferBond(engine, buffer);
     if (bufferBond.getStatus() != NO_ERROR) {
         ALOGE("got ANWB binding error while taking screenshot");
         return INVALID_OPERATION;
@@ -5454,9 +5464,9 @@
     // dependent on the context's EGLConfig.
     renderScreenImplLocked(renderArea, traverseLayers, useIdentityTransform);
 
-    base::unique_fd syncFd = getRenderEngine().flush();
+    base::unique_fd syncFd = engine.flush();
     if (syncFd < 0) {
-        getRenderEngine().finish();
+        engine.finish();
     }
     *outSyncFd = syncFd.release();
 
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 389118a..a1a8692 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -151,18 +151,6 @@
     while (mKeepRunning) {
         mConditionVariable.wait(mMutex);
 
-        // Present fence should fire almost immediately. If the fence has not signaled in 100ms,
-        // there is a major problem and it will probably never fire.
-        nsecs_t presentTime = -1;
-        if (mPresentFence) {
-            status_t status = mPresentFence->wait(100);
-            if (status == NO_ERROR) {
-                presentTime = mPresentFence->getSignalTime();
-            } else {
-                ALOGE("present fence has not signaled, err %d", status);
-            }
-        }
-
         // We should never hit this case. The release fences from the previous frame should have
         // signaled long before the current frame is presented.
         for (const auto& fence : mPreviousReleaseFences) {
@@ -188,17 +176,11 @@
 
                 // If the transaction has been latched
                 if (transactionStats.latchTime >= 0) {
-                    // If the present time is < 0, this transaction has been latched but not
-                    // presented. Skip it for now. This can happen when a new transaction comes
-                    // in between the latch and present steps. sendCallbacks is called by
-                    // SurfaceFlinger when the transaction is received to ensure that if the
-                    // transaction that didn't update state it still got a callback.
-                    if (presentTime < 0) {
+                    if (!mPresentFence) {
                         sendCallback = false;
                         break;
                     }
-
-                    transactionStats.presentTime = presentTime;
+                    transactionStats.presentFence = mPresentFence;
                 }
             }
             // If the listener has no pending transactions and all latched transactions have been
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index cef598c..e62fc6e 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -2502,12 +2502,12 @@
     }
 
     void verifyTransactionStats(const TransactionStats& transactionStats) const {
-        const auto& [latchTime, presentTime, surfaceStats] = transactionStats;
+        const auto& [latchTime, presentFence, surfaceStats] = transactionStats;
         if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
             ASSERT_GE(latchTime, 0) << "bad latch time";
-            ASSERT_GE(presentTime, 0) << "bad present time";
+            ASSERT_NE(presentFence, nullptr);
         } else {
-            ASSERT_EQ(presentTime, -1) << "transaction shouldn't have been presented";
+            ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented";
             ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
         }
 
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 5a6aa92..b7c09ed 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -286,8 +286,10 @@
     static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) {
         // Called once with a non-null value to set a framebuffer, and then
         // again with nullptr to clear it.
-        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull())).WillOnce(Return(true));
-        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull())).WillOnce(Return(true));
+        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull(), false))
+                .WillOnce(Return(true));
+        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull(), false))
+                .WillOnce(Return(true));
 
         EXPECT_CALL(*test->mRenderEngine, checkErrors()).WillRepeatedly(Return());
         EXPECT_CALL(*test->mRenderEngine, createFramebuffer())
@@ -344,8 +346,10 @@
                                              Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                                              ui::Transform::ROT_0))
                 .Times(1);
-        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull())).WillOnce(Return(true));
-        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull())).WillOnce(Return(true));
+        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull(), false))
+                .WillOnce(Return(true));
+        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull(), false))
+                .WillOnce(Return(true));
         EXPECT_CALL(*test->mRenderEngine, createFramebuffer())
                 .WillOnce(Return(
                         ByMove(std::unique_ptr<renderengine::Framebuffer>(test->mReFrameBuffer))));
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
index 11e5631..1bee271 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
+++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
@@ -74,6 +74,9 @@
     MOCK_METHOD1(drawMesh, void(const Mesh&));
     MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
     MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
+    MOCK_CONST_METHOD0(isProtected, bool());
+    MOCK_CONST_METHOD0(supportsProtectedContent, bool());
+    MOCK_METHOD1(useProtectedContext, bool(bool));
     MOCK_CONST_METHOD4(drawLayers,
                        status_t(const DisplaySettings&, const std::vector<LayerSettings>&,
                                 ANativeWindowBuffer* const, base::unique_fd*));
@@ -84,8 +87,7 @@
     Image();
     ~Image() override;
 
-    MOCK_METHOD2(setNativeWindowBuffer,
-                 bool(ANativeWindowBuffer* buffer, bool isProtected));
+    MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool));
 };
 
 class Framebuffer : public renderengine::Framebuffer {
@@ -93,7 +95,7 @@
     Framebuffer();
     ~Framebuffer() override;
 
-    MOCK_METHOD1(setNativeWindowBuffer, bool(ANativeWindowBuffer*));
+    MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool));
 };
 
 } // namespace mock