Merge "Revert "SF: Test coverage for OutputLayer::updateGeometryState""
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index a065a4c..916af69 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -116,7 +116,7 @@
         INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002,
         INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
     };
-    
+
     /* These values are filled in by the WM and passed through SurfaceFlinger
      * unless specified otherwise.
      */
@@ -165,6 +165,8 @@
     int32_t displayId;
     int32_t portalToDisplayId = ADISPLAY_ID_NONE;
     InputApplicationInfo applicationInfo;
+    bool replaceTouchableRegionWithCrop;
+    wp<IBinder> touchableRegionCropHandle;
 
     void addTouchableRegion(const Rect& region);
 
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 0510492..8b663bc 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -91,6 +91,7 @@
         "BufferQueueConsumer.cpp",
         "BufferQueueCore.cpp",
         "BufferQueueProducer.cpp",
+        "BufferQueueThreadState.cpp",
         "BufferSlot.cpp",
         "ConsumerBase.cpp",
         "CpuConsumer.cpp",
@@ -128,6 +129,7 @@
         "libbase",
         "libsync",
         "libbinder",
+        "libhwbinder",
         "libbufferhub",
         "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
         "libpdx_default_transport",
@@ -143,12 +145,16 @@
         "libhidltransport",
         "android.hidl.token@1.0-utils",
         "android.hardware.graphics.bufferqueue@1.0",
+        "libvndksupport",
     ],
 
     // bufferhub is not used when building libgui for vendors
     target: {
         vendor: {
-            cflags: ["-DNO_BUFFERHUB", "-DNO_INPUT"],
+            cflags: [
+                "-DNO_BUFFERHUB",
+                "-DNO_INPUT",
+            ],
             exclude_srcs: [
                 "BufferHubConsumer.cpp",
                 "BufferHubProducer.cpp",
@@ -158,7 +164,7 @@
                 "libbufferhub",
                 "libbufferhubqueue",
                 "libpdx_default_transport",
-                "libinput"
+                "libinput",
             ],
         },
     },
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 3837c3e..f2d5c8e 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -34,9 +34,10 @@
 #include <gui/IConsumerListener.h>
 #include <gui/IProducerListener.h>
 
-#include <binder/IPCThreadState.h>
+#include <private/gui/BufferQueueThreadState.h>
 #ifndef __ANDROID_VNDK__
 #include <binder/PermissionCache.h>
+#include <vndksupport/linker.h>
 #endif
 
 #include <system/window.h>
@@ -758,19 +759,29 @@
         return savedErrno ? -savedErrno : UNKNOWN_ERROR;
     }
 
-    const IPCThreadState* ipc = IPCThreadState::self();
-    const uid_t uid = ipc->getCallingUid();
+    bool denied = false;
+    const uid_t uid = BufferQueueThreadState::getCallingUid();
 #ifndef __ANDROID_VNDK__
     // permission check can't be done for vendors as vendors have no access to
-    // the PermissionController
-    const pid_t pid = ipc->getCallingPid();
-    if ((uid != shellUid) &&
-        !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
-        outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer "
-                "from pid=%d, uid=%d\n", pid, uid);
+    // the PermissionController. We need to do a runtime check as well, since
+    // the system variant of libgui can be loaded in a vendor process. For eg:
+    // if a HAL uses an llndk library that depends on libgui (libmediandk etc).
+    if (!android_is_in_vendor_process()) {
+        const pid_t pid = BufferQueueThreadState::getCallingPid();
+        if ((uid != shellUid) &&
+            !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
+            outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer "
+                                    "from pid=%d, uid=%d\n",
+                                    pid, uid);
+            denied = true;
+        }
+    }
 #else
     if (uid != shellUid) {
+        denied = true;
+    }
 #endif
+    if (denied) {
         android_errorWriteWithInfoLog(0x534e4554, "27046057",
                 static_cast<int32_t>(uid), nullptr, 0);
         return PERMISSION_DENIED;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 5e250a4..a462b03 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -35,6 +35,7 @@
 #include <gui/GLConsumer.h>
 #include <gui/IConsumerListener.h>
 #include <gui/IProducerListener.h>
+#include <private/gui/BufferQueueThreadState.h>
 
 #include <utils/Log.h>
 #include <utils/Trace.h>
@@ -1210,7 +1211,7 @@
             status = BAD_VALUE;
             break;
     }
-    mCore->mConnectedPid = IPCThreadState::self()->getCallingPid();
+    mCore->mConnectedPid = BufferQueueThreadState::getCallingPid();
     mCore->mBufferHasBeenQueued = false;
     mCore->mDequeueBufferCannotBlock = false;
     if (mDequeueTimeout < 0) {
@@ -1233,7 +1234,7 @@
         Mutex::Autolock lock(mCore->mMutex);
 
         if (mode == DisconnectMode::AllLocal) {
-            if (IPCThreadState::self()->getCallingPid() != mCore->mConnectedPid) {
+            if (BufferQueueThreadState::getCallingPid() != mCore->mConnectedPid) {
                 return NO_ERROR;
             }
             api = BufferQueueCore::CURRENTLY_CONNECTED_API;
diff --git a/libs/gui/BufferQueueThreadState.cpp b/libs/gui/BufferQueueThreadState.cpp
new file mode 100644
index 0000000..3b531ec
--- /dev/null
+++ b/libs/gui/BufferQueueThreadState.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/IPCThreadState.h>
+#include <hwbinder/IPCThreadState.h>
+#include <private/gui/BufferQueueThreadState.h>
+#include <unistd.h>
+
+namespace android {
+
+uid_t BufferQueueThreadState::getCallingUid() {
+    if (hardware::IPCThreadState::self()->isServingCall()) {
+        return hardware::IPCThreadState::self()->getCallingUid();
+    }
+    return IPCThreadState::self()->getCallingUid();
+}
+
+pid_t BufferQueueThreadState::getCallingPid() {
+    if (hardware::IPCThreadState::self()->isServingCall()) {
+        return hardware::IPCThreadState::self()->getCallingPid();
+    }
+    return IPCThreadState::self()->getCallingPid();
+}
+
+} // namespace android
diff --git a/libs/gui/include/private/gui/BufferQueueThreadState.h b/libs/gui/include/private/gui/BufferQueueThreadState.h
new file mode 100644
index 0000000..67dcf62
--- /dev/null
+++ b/libs/gui/include/private/gui/BufferQueueThreadState.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android {
+
+// TODO: Replace this with b/127962003
+class BufferQueueThreadState {
+public:
+    static pid_t getCallingPid();
+    static uid_t getCallingUid();
+};
+
+} // namespace android
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 5c5613d..5a60347 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -98,7 +98,8 @@
     output.writeInt32(portalToDisplayId);
     applicationInfo.write(output);
     output.write(touchableRegion);
-
+    output.writeBool(replaceTouchableRegionWithCrop);
+    output.writeWeakBinder(touchableRegionCropHandle);
     return OK;
 }
 
@@ -140,6 +141,8 @@
     ret.portalToDisplayId = from.readInt32();
     ret.applicationInfo = InputApplicationInfo::read(from);
     from.read(ret.touchableRegion);
+    ret.replaceTouchableRegionWithCrop = from.readBool();
+    ret.touchableRegionCropHandle = from.readWeakBinder();
 
     return ret;
 }
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 09dd72b..6db18ab 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -37,6 +37,7 @@
 }
 
 TEST(InputWindowInfo, Parcelling) {
+    sp<IBinder> touchableRegionCropHandle = new BBinder();
     InputWindowInfo i;
     i.token = new BBinder();
     i.name = "Foobar";
@@ -62,6 +63,8 @@
     i.inputFeatures = 29;
     i.displayId = 34;
     i.portalToDisplayId = 2;
+    i.replaceTouchableRegionWithCrop = true;
+    i.touchableRegionCropHandle = touchableRegionCropHandle;
 
     Parcel p;
     i.write(p);
@@ -92,6 +95,8 @@
     ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
     ASSERT_EQ(i.displayId, i2.displayId);
     ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
+    ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop);
+    ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
 }
 
 } // namespace test
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 8069a1a..f651309 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -432,10 +432,15 @@
 }
 
 GLESRenderEngine::~GLESRenderEngine() {
-    for (const auto& image : mFramebufferImageCache) {
-        eglDestroyImageKHR(mEGLDisplay, image.second);
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    unbindFrameBuffer(mDrawingBuffer.get());
+    mDrawingBuffer = nullptr;
+    while (!mFramebufferImageCache.empty()) {
+        EGLImageKHR expired = mFramebufferImageCache.front().second;
+        mFramebufferImageCache.pop_front();
+        eglDestroyImageKHR(mEGLDisplay, expired);
     }
-    mFramebufferImageCache.clear();
+    mImageCache.clear();
     eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
     eglTerminate(mEGLDisplay);
 }
@@ -627,6 +632,16 @@
 
 status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
                                                      sp<Fence> bufferFence) {
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    return bindExternalTextureBufferLocked(texName, buffer, bufferFence);
+}
+
+status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
+                                                           sp<GraphicBuffer> buffer,
+                                                           sp<Fence> bufferFence) {
+    if (buffer == nullptr) {
+        return BAD_VALUE;
+    }
     ATRACE_CALL();
     auto cachedImage = mImageCache.find(buffer->getId());
 
@@ -675,6 +690,7 @@
 }
 
 void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
     const auto& cachedImage = mImageCache.find(bufferId);
     if (cachedImage != mImageCache.end()) {
         ALOGV("Destroying image for buffer: %" PRIu64, bufferId);
@@ -771,7 +787,7 @@
 EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer,
                                                              bool isProtected) {
     sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
-    uint32_t bufferId = graphicBuffer->getId();
+    uint64_t bufferId = graphicBuffer->getId();
     for (const auto& image : mFramebufferImageCache) {
         if (image.first == bufferId) {
             return image.second;
@@ -810,114 +826,125 @@
         sync_wait(bufferFence.get(), -1);
     }
 
-    BindNativeBufferAsFramebuffer fbo(*this, buffer);
-
-    if (fbo.getStatus() != NO_ERROR) {
-        ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
-              buffer->handle);
-        checkErrors();
-        return fbo.getStatus();
+    if (buffer == nullptr) {
+        ALOGE("No output buffer provided. Aborting GPU composition.");
+        return BAD_VALUE;
     }
 
-    // clear the entire buffer, sometimes when we reuse buffers we'd persist
-    // ghost images otherwise.
-    // we also require a full transparent framebuffer for overlays. This is
-    // probably not quite efficient on all GPUs, since we could filter out
-    // opaque layers.
-    clearWithColor(0.0, 0.0, 0.0, 0.0);
+    {
+        std::lock_guard<std::mutex> lock(mRenderingMutex);
 
-    setViewportAndProjection(display.physicalDisplay, display.clip);
+        BindNativeBufferAsFramebuffer fbo(*this, buffer);
 
-    setOutputDataSpace(display.outputDataspace);
-    setDisplayMaxLuminance(display.maxLuminance);
-
-    mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
-    mState.projectionMatrix = projectionMatrix;
-    if (!display.clearRegion.isEmpty()) {
-        glDisable(GL_BLEND);
-        fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
-    }
-
-    Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
-    for (auto layer : layers) {
-        mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
-
-        const FloatRect bounds = layer.geometry.boundaries;
-        Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
-        position[0] = vec2(bounds.left, bounds.top);
-        position[1] = vec2(bounds.left, bounds.bottom);
-        position[2] = vec2(bounds.right, bounds.bottom);
-        position[3] = vec2(bounds.right, bounds.top);
-
-        setupLayerCropping(layer, mesh);
-        setColorTransform(display.colorTransform * layer.colorTransform);
-
-        bool usePremultipliedAlpha = true;
-        bool disableTexture = true;
-        bool isOpaque = false;
-
-        if (layer.source.buffer.buffer != nullptr) {
-            disableTexture = false;
-            isOpaque = layer.source.buffer.isOpaque;
-
-            sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
-            bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
-                                      layer.source.buffer.fence);
-
-            usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
-            Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
-            mat4 texMatrix = layer.source.buffer.textureTransform;
-
-            texture.setMatrix(texMatrix.asArray());
-            texture.setFiltering(layer.source.buffer.useTextureFiltering);
-
-            texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
-            setSourceY410BT2020(layer.source.buffer.isY410BT2020);
-
-            renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
-            texCoords[0] = vec2(0.0, 0.0);
-            texCoords[1] = vec2(0.0, 1.0);
-            texCoords[2] = vec2(1.0, 1.0);
-            texCoords[3] = vec2(1.0, 0.0);
-            setupLayerTexturing(texture);
-        }
-
-        const half3 solidColor = layer.source.solidColor;
-        const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
-        // Buffer sources will have a black solid color ignored in the shader,
-        // so in that scenario the solid color passed here is arbitrary.
-        setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
-                           layer.geometry.roundedCornersRadius);
-        if (layer.disableBlending) {
-            glDisable(GL_BLEND);
-        }
-        setSourceDataSpace(layer.sourceDataspace);
-
-        drawMesh(mesh);
-
-        // Cleanup if there's a buffer source
-        if (layer.source.buffer.buffer != nullptr) {
-            disableBlending();
-            setSourceY410BT2020(false);
-            disableTexturing();
-        }
-    }
-
-    *drawFence = flush();
-    // If flush failed or we don't support native fences, we need to force the
-    // gl command stream to be executed.
-    if (drawFence->get() < 0) {
-        bool success = finish();
-        if (!success) {
-            ALOGE("Failed to flush RenderEngine commands");
+        if (fbo.getStatus() != NO_ERROR) {
+            ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+                  buffer->handle);
             checkErrors();
-            // Chances are, something illegal happened (either the caller passed
-            // us bad parameters, or we messed up our shader generation).
-            return INVALID_OPERATION;
+            return fbo.getStatus();
         }
-    }
 
-    checkErrors();
+        // clear the entire buffer, sometimes when we reuse buffers we'd persist
+        // ghost images otherwise.
+        // we also require a full transparent framebuffer for overlays. This is
+        // probably not quite efficient on all GPUs, since we could filter out
+        // opaque layers.
+        clearWithColor(0.0, 0.0, 0.0, 0.0);
+
+        setViewportAndProjection(display.physicalDisplay, display.clip);
+
+        setOutputDataSpace(display.outputDataspace);
+        setDisplayMaxLuminance(display.maxLuminance);
+
+        mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
+        mState.projectionMatrix = projectionMatrix;
+        if (!display.clearRegion.isEmpty()) {
+            glDisable(GL_BLEND);
+            fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
+        }
+
+        Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
+        for (auto layer : layers) {
+            mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
+
+            const FloatRect bounds = layer.geometry.boundaries;
+            Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+            position[0] = vec2(bounds.left, bounds.top);
+            position[1] = vec2(bounds.left, bounds.bottom);
+            position[2] = vec2(bounds.right, bounds.bottom);
+            position[3] = vec2(bounds.right, bounds.top);
+
+            setupLayerCropping(layer, mesh);
+            setColorTransform(display.colorTransform * layer.colorTransform);
+
+            bool usePremultipliedAlpha = true;
+            bool disableTexture = true;
+            bool isOpaque = false;
+
+            if (layer.source.buffer.buffer != nullptr) {
+                disableTexture = false;
+                isOpaque = layer.source.buffer.isOpaque;
+
+                sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
+                bindExternalTextureBufferLocked(layer.source.buffer.textureName, gBuf,
+                                                layer.source.buffer.fence);
+
+                usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
+                Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
+                mat4 texMatrix = layer.source.buffer.textureTransform;
+
+                texture.setMatrix(texMatrix.asArray());
+                texture.setFiltering(layer.source.buffer.useTextureFiltering);
+
+                texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
+                setSourceY410BT2020(layer.source.buffer.isY410BT2020);
+
+                renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
+                texCoords[0] = vec2(0.0, 0.0);
+                texCoords[1] = vec2(0.0, 1.0);
+                texCoords[2] = vec2(1.0, 1.0);
+                texCoords[3] = vec2(1.0, 0.0);
+                setupLayerTexturing(texture);
+            }
+
+            const half3 solidColor = layer.source.solidColor;
+            const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+            // Buffer sources will have a black solid color ignored in the shader,
+            // so in that scenario the solid color passed here is arbitrary.
+            setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
+                               layer.geometry.roundedCornersRadius);
+            if (layer.disableBlending) {
+                glDisable(GL_BLEND);
+            }
+            setSourceDataSpace(layer.sourceDataspace);
+
+            drawMesh(mesh);
+
+            // Cleanup if there's a buffer source
+            if (layer.source.buffer.buffer != nullptr) {
+                disableBlending();
+                setSourceY410BT2020(false);
+                disableTexturing();
+            }
+        }
+
+        if (drawFence != nullptr) {
+            *drawFence = flush();
+        }
+        // If flush failed or we don't support native fences, we need to force the
+        // gl command stream to be executed.
+        if (drawFence == nullptr || drawFence->get() < 0) {
+            bool success = finish();
+            if (!success) {
+                ALOGE("Failed to flush RenderEngine commands");
+                checkErrors();
+                // Chances are, something illegal happened (either the caller passed
+                // us bad parameters, or we messed up our shader generation).
+                return INVALID_OPERATION;
+            }
+        }
+
+        checkErrors();
+    }
     return NO_ERROR;
 }
 
@@ -1331,6 +1358,20 @@
     return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
 }
 
+bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) {
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    const auto& cachedImage = mImageCache.find(bufferId);
+    return cachedImage != mImageCache.end();
+}
+
+bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
+                       [=](std::pair<uint64_t, EGLImageKHR> image) {
+                           return image.first == bufferId;
+                       });
+}
+
 // FlushTracer implementation
 GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) {
     mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this);
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 728882a..efb6ef0 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -56,7 +56,7 @@
                      EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy,
                      EGLContext protectedContext, EGLSurface protectedDummy,
                      uint32_t imageCacheSize);
-    ~GLESRenderEngine() override;
+    ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
 
     std::unique_ptr<Framebuffer> createFramebuffer() override;
     std::unique_ptr<Image> createImage() override;
@@ -74,8 +74,9 @@
     void genTextures(size_t count, uint32_t* names) override;
     void deleteTextures(size_t count, uint32_t const* names) override;
     void bindExternalTextureImage(uint32_t texName, const Image& image) override;
-    status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence);
-    void unbindExternalTextureBuffer(uint64_t bufferId);
+    status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence)
+            EXCLUDES(mRenderingMutex);
+    void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
     status_t bindFrameBuffer(Framebuffer* framebuffer) override;
     void unbindFrameBuffer(Framebuffer* framebuffer) override;
     void checkErrors() const override;
@@ -85,7 +86,7 @@
     bool useProtectedContext(bool useProtectedContext) override;
     status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers,
                         ANativeWindowBuffer* buffer, base::unique_fd&& bufferFence,
-                        base::unique_fd* drawFence) override;
+                        base::unique_fd* drawFence) EXCLUDES(mRenderingMutex) override;
 
     // internal to RenderEngine
     EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
@@ -93,6 +94,12 @@
     // Creates an output image for rendering to
     EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected);
 
+    // Test-only methods
+    // Returns true iff mImageCache contains an image keyed by bufferId
+    bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+    // Returns true iff mFramebufferImageCache contains an image keyed by bufferId
+    bool isFramebufferImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+
 protected:
     Framebuffer* getFramebufferForDrawing() override;
     void dump(std::string& result) override;
@@ -197,10 +204,17 @@
     const bool mUseColorManagement = false;
 
     // Cache of GL images that we'll store per GraphicBuffer ID
-    // TODO: Layer should call back on destruction instead to clean this up,
-    // as it has better system utilization at the potential expense of a
-    // more complicated interface.
-    std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache;
+    std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
+    // Mutex guarding rendering operations, so that:
+    // 1. GL operations aren't interleaved, and
+    // 2. Internal state related to rendering that is potentially modified by
+    // multiple threads is guaranteed thread-safe.
+    std::mutex mRenderingMutex;
+
+    // See bindExternalTextureBuffer above, but requiring that mRenderingMutex
+    // is held.
+    status_t bindExternalTextureBufferLocked(uint32_t texName, sp<GraphicBuffer> buffer,
+                                             sp<Fence> fence) REQUIRES(mRenderingMutex);
 
     std::unique_ptr<Framebuffer> mDrawingBuffer;
 
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index a2bbaff..8c93cf4 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -19,6 +19,7 @@
 #include <renderengine/RenderEngine.h>
 #include <sync/sync.h>
 #include <ui/PixelFormat.h>
+#include "../gl/GLESRenderEngine.h"
 
 constexpr int DEFAULT_DISPLAY_WIDTH = 128;
 constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
@@ -27,6 +28,19 @@
 namespace android {
 
 struct RenderEngineTest : public ::testing::Test {
+    static void SetUpTestSuite() {
+        sRE = renderengine::gl::GLESRenderEngine::create(static_cast<int32_t>(
+                                                                 ui::PixelFormat::RGBA_8888),
+                                                         0, 1);
+    }
+
+    static void TearDownTestSuite() {
+        // The ordering here is important - sCurrentBuffer must live longer
+        // than RenderEngine to avoid a null reference on tear-down.
+        sRE = nullptr;
+        sCurrentBuffer = nullptr;
+    }
+
     static sp<GraphicBuffer> allocateDefaultBuffer() {
         return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
                                  HAL_PIXEL_FORMAT_RGBA_8888, 1,
@@ -101,12 +115,12 @@
                     DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET);
     }
 
-    static void invokeDraw(renderengine::DisplaySettings settings,
-                           std::vector<renderengine::LayerSettings> layers,
-                           sp<GraphicBuffer> buffer) {
+    void invokeDraw(renderengine::DisplaySettings settings,
+                    std::vector<renderengine::LayerSettings> layers, sp<GraphicBuffer> buffer) {
         base::unique_fd fence;
         status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(),
                                           base::unique_fd(), &fence);
+        sCurrentBuffer = buffer;
 
         int fd = fence.release();
         if (fd >= 0) {
@@ -115,9 +129,12 @@
         }
 
         ASSERT_EQ(NO_ERROR, status);
+        if (layers.size() > 0) {
+            ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+        }
     }
 
-    static void drawEmptyLayers() {
+    void drawEmptyLayers() {
         renderengine::DisplaySettings settings;
         std::vector<renderengine::LayerSettings> layers;
         // Meaningless buffer since we don't do any drawing
@@ -200,17 +217,22 @@
 
     void clearRegion();
 
-    // Dumb hack to get aroud the fact that tear-down for renderengine isn't
-    // well defined right now, so we can't create multiple instances
-    static std::unique_ptr<renderengine::RenderEngine> sRE;
+    // Keep around the same renderengine object to save on initialization time.
+    // For now, exercise the GL backend directly so that some caching specifics
+    // can be tested without changing the interface.
+    static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE;
+    // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
+    // be freed *after* RenderEngine is destroyed, so that the EGL image is
+    // destroyed first.
+    static sp<GraphicBuffer> sCurrentBuffer;
 
     sp<GraphicBuffer> mBuffer;
 
     std::vector<uint32_t> mTexNames;
 };
 
-std::unique_ptr<renderengine::RenderEngine> RenderEngineTest::sRE =
-        renderengine::RenderEngine::create(static_cast<int32_t>(ui::PixelFormat::RGBA_8888), 0, 1);
+std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr;
+sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr;
 
 struct ColorSourceVariant {
     static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
@@ -245,7 +267,7 @@
                           RenderEngineTest* fixture) {
         sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
         uint32_t texName;
-        RenderEngineTest::sRE->genTextures(1, &texName);
+        fixture->sRE->genTextures(1, &texName);
         fixture->mTexNames.push_back(texName);
 
         uint8_t* pixels;
@@ -740,6 +762,38 @@
     drawEmptyLayers();
 }
 
+TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) {
+    renderengine::DisplaySettings settings;
+    std::vector<renderengine::LayerSettings> layers;
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = fullscreenRect().toFloatRect();
+    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+    layers.push_back(layer);
+    base::unique_fd fence;
+    status_t status = sRE->drawLayers(settings, layers, nullptr, base::unique_fd(), &fence);
+
+    ASSERT_EQ(BAD_VALUE, status);
+}
+
+TEST_F(RenderEngineTest, drawLayers_nullOutputFence) {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<renderengine::LayerSettings> layers;
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = fullscreenRect().toFloatRect();
+    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+    layer.alpha = 1.0;
+    layers.push_back(layer);
+
+    status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(),
+                                      base::unique_fd(), nullptr);
+    sCurrentBuffer = mBuffer;
+    ASSERT_EQ(NO_ERROR, status);
+    expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
 TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
     fillRedBuffer<ColorSourceVariant>();
 }
@@ -912,4 +966,41 @@
     clearRegion();
 }
 
+TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = fullscreenRect().toFloatRect();
+    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+
+    layers.push_back(layer);
+    invokeDraw(settings, layers, mBuffer);
+    uint64_t bufferId = layer.source.buffer.buffer->getId();
+    EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+    sRE->unbindExternalTextureBuffer(bufferId);
+    EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+}
+
+TEST_F(RenderEngineTest, drawLayers_bindExternalBufferWithNullBuffer) {
+    status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr);
+    ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineTest, drawLayers_bindExternalBufferCachesImages) {
+    sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+    uint32_t texName;
+    sRE->genTextures(1, &texName);
+    mTexNames.push_back(texName);
+
+    sRE->bindExternalTextureBuffer(texName, buf, nullptr);
+    uint64_t bufferId = buf->getId();
+    EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+    sRE->unbindExternalTextureBuffer(bufferId);
+    EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+}
+
 } // namespace android
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index d13942d..28c3f7b 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -381,6 +381,37 @@
     return (getOrientation() & ROT_INVALID) ? false : true;
 }
 
+mat4 Transform::asMatrix4() const {
+    // Internally Transform uses a 3x3 matrix since the transform is meant for
+    // two-dimensional values. An equivalent 4x4 matrix means inserting an extra
+    // row and column which adds as an identity transform on the third
+    // dimension.
+
+    mat4 m = mat4{mat4::NO_INIT}; // NO_INIT since we explicitly set every element
+
+    m[0][0] = mMatrix[0][0];
+    m[0][1] = mMatrix[0][1];
+    m[0][2] = 0.f;
+    m[0][3] = mMatrix[0][2];
+
+    m[1][0] = mMatrix[1][0];
+    m[1][1] = mMatrix[1][1];
+    m[1][2] = 0.f;
+    m[1][3] = mMatrix[1][2];
+
+    m[2][0] = 0.f;
+    m[2][1] = 0.f;
+    m[2][2] = 1.f;
+    m[2][3] = 0.f;
+
+    m[3][0] = mMatrix[2][0];
+    m[3][1] = mMatrix[2][1];
+    m[3][2] = 0.f;
+    m[3][3] = mMatrix[2][2];
+
+    return m;
+}
+
 void Transform::dump(std::string& out, const char* name) const {
     using android::base::StringAppendF;
 
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index dcb26cf..f29a370 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -22,6 +22,7 @@
 #include <string>
 
 #include <hardware/hardware.h>
+#include <math/mat4.h>
 #include <math/vec2.h>
 #include <math/vec3.h>
 #include <ui/Point.h>
@@ -88,6 +89,9 @@
     vec2 transform(const vec2& v) const;
     vec3 transform(const vec3& v) const;
 
+    // Expands from the internal 3x3 matrix to an equivalent 4x4 matrix
+    mat4 asMatrix4() const;
+
     Transform inverse() const;
 
     // for debugging
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 4751e5f..1b2b180 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -304,7 +304,9 @@
         setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::DEVICE);
     }
 
-    ui::Dataspace dataspace = isColorSpaceAgnostic() ? targetDataspace : mCurrentDataSpace;
+    ui::Dataspace dataspace = isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN
+            ? targetDataspace
+            : mCurrentDataSpace;
     error = hwcLayer->setDataspace(dataspace);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace,
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 3364399..fcc2d97 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -114,7 +114,9 @@
 
     setCompositionType(display, Hwc2::IComposerClient::Composition::SOLID_COLOR);
 
-    const ui::Dataspace dataspace = isColorSpaceAgnostic() ? targetDataspace : mCurrentDataSpace;
+    const ui::Dataspace dataspace =
+            isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN ? targetDataspace
+                                                                                : mCurrentDataSpace;
     error = hwcLayer->setDataspace(dataspace);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index ef9418c..512a0b4 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -278,14 +278,12 @@
     return reduce(mBounds, activeTransparentRegion);
 }
 
-ui::Transform Layer::getTransformWithScale() const {
+ui::Transform Layer::getBufferScaleTransform() const {
     // If the layer is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
     // it isFixedSize) then there may be additional scaling not accounted
-    // for in the transform. We need to mirror this scaling to child surfaces
-    // or we will break the contract where WM can treat child surfaces as
-    // pixels in the parent surface.
+    // for in the layer transform.
     if (!isFixedSize() || !mActiveBuffer) {
-        return mEffectiveTransform;
+        return {};
     }
 
     // If the layer is a buffer state layer, the active width and height
@@ -293,24 +291,40 @@
     const uint32_t activeWidth = getActiveWidth(getDrawingState());
     const uint32_t activeHeight = getActiveHeight(getDrawingState());
     if (activeWidth >= UINT32_MAX && activeHeight >= UINT32_MAX) {
-        return mEffectiveTransform;
+        return {};
     }
 
-    int bufferWidth;
-    int bufferHeight;
-    if ((mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) == 0) {
-        bufferWidth = mActiveBuffer->getWidth();
-        bufferHeight = mActiveBuffer->getHeight();
-    } else {
-        bufferHeight = mActiveBuffer->getWidth();
-        bufferWidth = mActiveBuffer->getHeight();
+    int bufferWidth = mActiveBuffer->getWidth();
+    int bufferHeight = mActiveBuffer->getHeight();
+
+    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+        std::swap(bufferWidth, bufferHeight);
     }
+
     float sx = activeWidth / static_cast<float>(bufferWidth);
     float sy = activeHeight / static_cast<float>(bufferHeight);
 
     ui::Transform extraParentScaling;
     extraParentScaling.set(sx, 0, 0, sy);
-    return mEffectiveTransform * extraParentScaling;
+    return extraParentScaling;
+}
+
+ui::Transform Layer::getTransformWithScale(const ui::Transform& bufferScaleTransform) const {
+    // We need to mirror this scaling to child surfaces or we will break the contract where WM can
+    // treat child surfaces as pixels in the parent surface.
+    if (!isFixedSize() || !mActiveBuffer) {
+        return mEffectiveTransform;
+    }
+    return mEffectiveTransform * bufferScaleTransform;
+}
+
+FloatRect Layer::getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const {
+    // We need the pre scaled layer bounds when computing child bounds to make sure the child is
+    // cropped to its parent layer after any buffer transform scaling is applied.
+    if (!isFixedSize() || !mActiveBuffer) {
+        return mBounds;
+    }
+    return bufferScaleTransform.inverse().transform(mBounds);
 }
 
 void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) {
@@ -322,7 +336,7 @@
     // Transform parent bounds to layer space
     parentBounds = getActiveTransform(s).inverse().transform(parentBounds);
 
-    // Calculate display frame
+    // Calculate source bounds
     mSourceBounds = computeSourceBounds(parentBounds);
 
     // Calculate bounds by croping diplay frame with layer crop and parent bounds
@@ -335,8 +349,12 @@
 
     mBounds = bounds;
     mScreenBounds = mEffectiveTransform.transform(mBounds);
+
+    // Add any buffer scaling to the layer's children.
+    ui::Transform bufferScaleTransform = getBufferScaleTransform();
     for (const sp<Layer>& child : mDrawingChildren) {
-        child->computeBounds(mBounds, getTransformWithScale());
+        child->computeBounds(getBoundsPreScaling(bufferScaleTransform),
+                             getTransformWithScale(bufferScaleTransform));
     }
 }
 
@@ -1436,7 +1454,9 @@
 void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
     for (const sp<Layer>& child : mDrawingChildren) {
         child->mDrawingParent = newParent;
-        child->computeBounds(newParent->mBounds, newParent->getTransformWithScale());
+        child->computeBounds(newParent->mBounds,
+                             newParent->getTransformWithScale(
+                                     newParent->getBufferScaleTransform()));
     }
 }
 
@@ -1796,8 +1816,24 @@
     mDrawingParent = mCurrentParent;
 }
 
+static wp<Layer> extractLayerFromBinder(const wp<IBinder>& weakBinderHandle) {
+    if (weakBinderHandle == nullptr) {
+        return nullptr;
+    }
+    sp<IBinder> binderHandle = weakBinderHandle.promote();
+    if (binderHandle == nullptr) {
+        return nullptr;
+    }
+    sp<Layer::Handle> handle = static_cast<Layer::Handle*>(binderHandle.get());
+    if (handle == nullptr) {
+        return nullptr;
+    }
+    return handle->owner;
+}
+
 void Layer::setInputInfo(const InputWindowInfo& info) {
     mCurrentState.inputInfo = info;
+    mCurrentState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle);
     mCurrentState.modified = true;
     mCurrentState.inputInfoChanged = true;
     setTransactionFlags(eTransactionNeeded);
@@ -1986,6 +2022,18 @@
     // bounds.
     info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
     info.visible = canReceiveInput();
+
+    auto cropLayer = mDrawingState.touchableRegionCrop.promote();
+    if (info.replaceTouchableRegionWithCrop) {
+        if (cropLayer == nullptr) {
+            info.touchableRegion = Region(Rect{mScreenBounds});
+        } else {
+            info.touchableRegion = Region(Rect{cropLayer->mScreenBounds});
+        }
+    } else if (cropLayer != nullptr) {
+        info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds});
+    }
+
     return info;
 }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 78377d7..89063da 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -184,6 +184,7 @@
 
         bool inputInfoChanged;
         InputWindowInfo inputInfo;
+        wp<Layer> touchableRegionCrop;
 
         // dataspace is only used by BufferStateLayer and ColorLayer
         ui::Dataspace dataspace;
@@ -360,9 +361,15 @@
     // Compute bounds for the layer and cache the results.
     void computeBounds(FloatRect parentBounds, ui::Transform parentTransform);
 
+    // Returns the buffer scale transform if a scaling mode is set.
+    ui::Transform getBufferScaleTransform() const;
+
     // Get effective layer transform, taking into account all its parent transform with any
     // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE.
-    ui::Transform getTransformWithScale() const;
+    ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const;
+
+    // Returns the bounds of the layer without any buffer scaling.
+    FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const;
 
     int32_t getSequence() const { return sequence; }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 83e53fa..08a728a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -157,6 +157,28 @@
     return false;
 }
 
+bool isHdrColorMode(const ColorMode colorMode) {
+    switch (colorMode) {
+        case ColorMode::BT2100_PQ:
+        case ColorMode::BT2100_HLG:
+            return true;
+        case ColorMode::DISPLAY_P3:
+        case ColorMode::ADOBE_RGB:
+        case ColorMode::DCI_P3:
+        case ColorMode::BT2020:
+        case ColorMode::DISPLAY_BT2020:
+        case ColorMode::NATIVE:
+        case ColorMode::STANDARD_BT601_625:
+        case ColorMode::STANDARD_BT601_625_UNADJUSTED:
+        case ColorMode::STANDARD_BT601_525:
+        case ColorMode::STANDARD_BT601_525_UNADJUSTED:
+        case ColorMode::STANDARD_BT709:
+        case ColorMode::SRGB:
+            return false;
+    }
+    return false;
+}
+
 ui::Transform::orientation_flags fromSurfaceComposerRotation(ISurfaceComposer::Rotation rotation) {
     switch (rotation) {
         case ISurfaceComposer::eRotateNone:
@@ -1836,7 +1858,9 @@
 
             const auto& displayState = display->getState();
             layer->setPerFrameData(displayDevice, displayState.transform, displayState.viewport,
-                                   displayDevice->getSupportedPerFrameMetadata(), targetDataspace);
+                                   displayDevice->getSupportedPerFrameMetadata(),
+                                   isHdrColorMode(displayState.colorMode) ? Dataspace::UNKNOWN
+                                                                          : targetDataspace);
         }
     }
 
@@ -3288,17 +3312,7 @@
         clientCompositionDisplay.physicalDisplay = displayState.scissor;
         clientCompositionDisplay.clip = displayState.scissor;
         const ui::Transform& displayTransform = displayState.transform;
-        mat4 m;
-        m[0][0] = displayTransform[0][0];
-        m[0][1] = displayTransform[0][1];
-        m[0][3] = displayTransform[0][2];
-        m[1][0] = displayTransform[1][0];
-        m[1][1] = displayTransform[1][1];
-        m[1][3] = displayTransform[1][2];
-        m[3][0] = displayTransform[2][0];
-        m[3][1] = displayTransform[2][1];
-        m[3][3] = displayTransform[2][2];
-        clientCompositionDisplay.globalTransform = m;
+        clientCompositionDisplay.globalTransform = displayTransform.asMatrix4();
 
         const auto* profile = display->getDisplayColorProfile();
         Dataspace outputDataspace = Dataspace::UNKNOWN;
@@ -5591,18 +5605,7 @@
     // buffer bounds.
     clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight);
     ui::Transform transform = renderArea.getTransform();
-    mat4 m;
-    m[0][0] = transform[0][0];
-    m[0][1] = transform[0][1];
-    m[0][3] = transform[0][2];
-    m[1][0] = transform[1][0];
-    m[1][1] = transform[1][1];
-    m[1][3] = transform[1][2];
-    m[3][0] = transform[2][0];
-    m[3][1] = transform[2][1];
-    m[3][3] = transform[2][2];
-
-    clientCompositionDisplay.globalTransform = m;
+    clientCompositionDisplay.globalTransform = transform.asMatrix4();
     mat4 rotMatrix;
     // Displacement for repositioning the clipping rectangle after rotating it
     // with the rotation hint.
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index f1c4347..db78f1d 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -60,7 +60,7 @@
 
 void SurfaceTracing::notify(const char* where) {
     std::lock_guard<std::mutex> sfLock(mFlinger.mDrawingStateLock);
-    mWhere = strdup(where);
+    mWhere = where;
     mConditionalVariable.notify_one();
 }
 
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index a75ac61..9484480 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -80,7 +80,7 @@
 
     const SurfaceFlinger& mFlinger;
 
-    char* mWhere;
+    const char* mWhere = "";
     status_t mLastErr = NO_ERROR;
     std::thread mThread;
     std::condition_variable mConditionalVariable;
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index bc5642e..d62afa5 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -4307,7 +4307,7 @@
 protected:
     void SetUp() override {
         LayerUpdateTest::SetUp();
-        mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+        mChild = createSurface(mClient, "Child surface", 10, 15, PIXEL_FORMAT_RGBA_8888, 0,
                                mFGSurfaceControl.get());
         fillSurfaceRGBA8(mChild, 200, 200, 200);
 
@@ -4413,6 +4413,32 @@
     }
 }
 
+// A child with a scale transform should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildLayerScalingCroppedByParent) {
+    asTransaction([&](Transaction& t) {
+        t.setPosition(mFGSurfaceControl, 0, 0);
+        t.setPosition(mChild, 0, 0);
+    });
+
+    // Find the boundary between the parent and child.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(9, 9);
+        mCapture->expectFGColor(10, 10);
+    }
+
+    asTransaction([&](Transaction& t) { t.setMatrix(mChild, 10.0, 0, 0, 10.0); });
+
+    // The child should fill its parent bounds and be cropped by it.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(63, 63);
+        mCapture->expectBGColor(64, 64);
+    }
+}
+
 TEST_F(ChildLayerTest, ChildLayerAlpha) {
     fillSurfaceRGBA8(mBGSurfaceControl, 0, 0, 254);
     fillSurfaceRGBA8(mFGSurfaceControl, 254, 0, 0);
@@ -4649,8 +4675,8 @@
         mCapture = screenshot();
         // We've positioned the child in the top left.
         mCapture->expectChildColor(0, 0);
-        // But it's only 10x10.
-        mCapture->expectFGColor(10, 10);
+        // But it's only 10x15.
+        mCapture->expectFGColor(10, 15);
     }
 
     asTransaction([&](Transaction& t) {
@@ -4664,9 +4690,9 @@
         // We've positioned the child in the top left.
         mCapture->expectChildColor(0, 0);
         mCapture->expectChildColor(10, 10);
-        mCapture->expectChildColor(19, 19);
-        // And now it should be scaled all the way to 20x20
-        mCapture->expectFGColor(20, 20);
+        mCapture->expectChildColor(19, 29);
+        // And now it should be scaled all the way to 20x30
+        mCapture->expectFGColor(20, 30);
     }
 }
 
@@ -4682,8 +4708,9 @@
         mCapture = screenshot();
         // We've positioned the child in the top left.
         mCapture->expectChildColor(0, 0);
-        // But it's only 10x10.
-        mCapture->expectFGColor(10, 10);
+        mCapture->expectChildColor(9, 14);
+        // But it's only 10x15.
+        mCapture->expectFGColor(10, 15);
     }
     // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
     // the WM specified state size.
@@ -4704,6 +4731,115 @@
     }
 }
 
+// A child with a buffer transform from its parents should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferTransform) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+        t.setSize(mChild, 100, 100);
+    });
+    fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+    {
+        mCapture = screenshot();
+
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(63, 63);
+        mCapture->expectBGColor(64, 64);
+    }
+
+    asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
+    sp<Surface> s = mFGSurfaceControl->getSurface();
+    auto anw = static_cast<ANativeWindow*>(s.get());
+    // Apply a 90 transform on the buffer.
+    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+    native_window_set_buffers_dimensions(anw, 64, 128);
+    fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+    waitForPostedBuffers();
+
+    // The child should be cropped by the new parent bounds.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(99, 63);
+        mCapture->expectFGColor(100, 63);
+        mCapture->expectBGColor(128, 64);
+    }
+}
+
+// A child with a scale transform from its parents should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferScale) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+        t.setSize(mChild, 200, 200);
+    });
+    fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+    {
+        mCapture = screenshot();
+
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(63, 63);
+        mCapture->expectBGColor(64, 64);
+    }
+
+    asTransaction([&](Transaction& t) {
+        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        // Set a scaling by 2.
+        t.setSize(mFGSurfaceControl, 128, 128);
+    });
+
+    // Child should inherit its parents scale but should be cropped by its parent bounds.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(127, 127);
+        mCapture->expectBGColor(128, 128);
+    }
+}
+
+// Regression test for b/127368943
+// Child should ignore the buffer transform but apply parent scale transform.
+TEST_F(ChildLayerTest, ChildrenWithParentBufferTransformAndScale) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+    });
+
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(9, 14);
+        mCapture->expectFGColor(10, 15);
+    }
+
+    // Change the size of the foreground to 128 * 64 so we can test rotation as well.
+    asTransaction([&](Transaction& t) {
+        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        t.setSize(mFGSurfaceControl, 128, 64);
+    });
+    sp<Surface> s = mFGSurfaceControl->getSurface();
+    auto anw = static_cast<ANativeWindow*>(s.get());
+    // Apply a 90 transform on the buffer and submit a buffer half the expected size so that we
+    // have an effective scale of 2.0 applied to the buffer along with a rotation transform.
+    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+    native_window_set_buffers_dimensions(anw, 32, 64);
+    fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+    waitForPostedBuffers();
+
+    // The child should ignore the buffer transform but apply the 2.0 scale from parent.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(19, 29);
+        mCapture->expectFGColor(20, 30);
+    }
+}
+
 TEST_F(ChildLayerTest, Bug36858924) {
     // Destroy the child layer
     mChild.clear();
@@ -4857,6 +4993,7 @@
         mCapture->checkPixel(10, 10, 255, 255, 255);
     }
 }
+
 class BoundlessLayerTest : public LayerUpdateTest {
 protected:
     std::unique_ptr<ScreenCapture> mCapture;