Merge "[SurfaceFlinger] Allow force a supported color mode."
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index a639951..852aa79 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1958,6 +1958,11 @@
         /* child -- drop privileges before continuing */
         drop_capabilities(uid);
 
+        // Clear BOOTCLASSPATH.
+        // Let dex2oat use the BCP from boot image, excluding updatable BCP
+        // modules for AOT to avoid app recompilation after their upgrades.
+        unsetenv("BOOTCLASSPATH");
+
         SetDex2OatScheduling(boot_complete);
         if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {
             PLOG(ERROR) << "flock(" << out_oat_path << ") failed";
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index cc24ab3..48b07c4 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/statvfs.h>
+#include <sys/stat.h>
 #include <sys/xattr.h>
 
 #include <android-base/file.h>
@@ -70,27 +71,28 @@
 
 static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) {
     const std::string fullPath = get_full_path(path);
-    ::mkdir(fullPath.c_str(), mode);
-    ::chown(fullPath.c_str(), owner, group);
-    ::chmod(fullPath.c_str(), mode);
+    EXPECT_EQ(::mkdir(fullPath.c_str(), mode), 0);
+    EXPECT_EQ(::chown(fullPath.c_str(), owner, group), 0);
+    EXPECT_EQ(::chmod(fullPath.c_str(), mode), 0);
 }
 
 static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) {
     int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode);
-    ::fchown(fd, owner, group);
-    ::fchmod(fd, mode);
-    ::close(fd);
+    EXPECT_NE(fd, -1);
+    EXPECT_EQ(::fchown(fd, owner, group), 0);
+    EXPECT_EQ(::fchmod(fd, mode), 0);
+    EXPECT_EQ(::close(fd), 0);
 }
 
 static int stat_gid(const char* path) {
     struct stat buf;
-    ::stat(get_full_path(path).c_str(), &buf);
+    EXPECT_EQ(::stat(get_full_path(path).c_str(), &buf), 0);
     return buf.st_gid;
 }
 
 static int stat_mode(const char* path) {
     struct stat buf;
-    ::stat(get_full_path(path).c_str(), &buf);
+    EXPECT_EQ(::stat(get_full_path(path).c_str(), &buf), 0);
     return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
 }
 
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 68011bb..a96c9a0 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -16,7 +16,7 @@
 
 cc_library {
     name: "libbinder_ndk",
-    vendor_available: false,
+    vendor_available: true,
 
     export_include_dirs: [
         "include_ndk",
@@ -50,6 +50,13 @@
         "libandroid_runtime",
     ],
 
+    header_libs: [
+        "jni_headers",
+    ],
+    export_header_lib_headers: [
+        "jni_headers",
+    ],
+
     version_script: "libbinder_ndk.map.txt",
     stubs: {
         symbol_file: "libbinder_ndk.map.txt",
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index c20d54b..75fe2d3 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -156,9 +156,9 @@
     mDriverPath = path;
 }
 
-void GraphicsEnv::setGpuStats(const std::string driverPackageName,
-                              const std::string driverVersionName, const uint64_t driverVersionCode,
-                              const std::string appPackageName) {
+void GraphicsEnv::setGpuStats(const std::string& driverPackageName,
+                              const std::string& driverVersionName,
+                              const uint64_t driverVersionCode, const std::string& appPackageName) {
     ATRACE_CALL();
 
     ALOGV("setGpuStats:\n"
@@ -316,28 +316,28 @@
         return false;
     }
 
-    return mUseAngle;
+    return (mUseAngle == YES) ? true : false;
 }
 
 void GraphicsEnv::updateUseAngle() {
-    mUseAngle = false;
+    mUseAngle = NO;
 
     const char* ANGLE_PREFER_ANGLE = "angle";
     const char* ANGLE_PREFER_NATIVE = "native";
 
     if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
         ALOGV("User set \"Developer Options\" to force the use of ANGLE");
-        mUseAngle = true;
+        mUseAngle = YES;
     } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
         ALOGV("User set \"Developer Options\" to force the use of Native");
-        mUseAngle = false;
+        mUseAngle = NO;
     } else {
         // The "Developer Options" value wasn't set to force the use of ANGLE.  Need to temporarily
         // load ANGLE and call the updatable opt-in/out logic:
         void* featureSo = loadLibrary("feature_support");
         if (featureSo) {
             ALOGV("loaded ANGLE's opt-in/out logic from namespace");
-            mUseAngle = checkAngleRules(featureSo);
+            mUseAngle = checkAngleRules(featureSo) ? YES : NO;
             dlclose(featureSo);
             featureSo = nullptr;
         } else {
@@ -349,6 +349,13 @@
 void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
                                const std::string developerOptIn, const int rulesFd,
                                const long rulesOffset, const long rulesLength) {
+    if (mUseAngle != UNKNOWN) {
+        // We've already figured out an answer for this app, so just return.
+        ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(),
+              (mUseAngle == YES) ? "true" : "false");
+        return;
+    }
+
     ALOGV("setting ANGLE path to '%s'", path.c_str());
     mAnglePath = path;
     ALOGV("setting ANGLE app name to '%s'", appName.c_str());
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 98a6395..762a27b 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -27,9 +27,9 @@
 public:
     explicit BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
 
-    virtual void setGpuStats(const std::string driverPackageName,
-                             const std::string driverVersionName, const uint64_t driverVersionCode,
-                             const std::string appPackageName) {
+    virtual void setGpuStats(const std::string& driverPackageName,
+                             const std::string& driverVersionName, const uint64_t driverVersionCode,
+                             const std::string& appPackageName) {
         Parcel data, reply;
         data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
 
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index d4edfa0..c4482b7 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -47,8 +47,8 @@
     //     /data/app/com.example.driver/base.apk!/lib/arm64-v8a
     void setDriverPath(const std::string path);
     android_namespace_t* getDriverNamespace();
-    void setGpuStats(const std::string driverPackageName, const std::string driverVersionName,
-                     const uint64_t versionCode, const std::string appPackageName);
+    void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
+                     const uint64_t versionCode, const std::string& appPackageName);
     void sendGpuStats();
 
     bool shouldUseAngle(std::string appName);
@@ -74,6 +74,8 @@
     const std::string& getDebugLayersGLES();
 
 private:
+    enum UseAngle { UNKNOWN, YES, NO };
+
     void* loadLibrary(std::string name);
     bool checkAngleRules(void* so);
     void updateUseAngle();
@@ -85,7 +87,7 @@
     std::string mAngleAppName;
     std::string mAngleDeveloperOptIn;
     std::vector<char> mRulesBuffer;
-    bool mUseAngle;
+    UseAngle mUseAngle = UNKNOWN;
     std::string mDebugLayers;
     std::string mDebugLayersGLES;
     std::string mLayerPaths;
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index c080c53..1e74d60 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -32,9 +32,9 @@
     DECLARE_META_INTERFACE(GpuService);
 
     // set GPU stats from GraphicsEnvironment.
-    virtual void setGpuStats(const std::string driverPackageName,
-                             const std::string driverVersionName, const uint64_t driverVersionCode,
-                             const std::string appPackageName) = 0;
+    virtual void setGpuStats(const std::string& driverPackageName,
+                             const std::string& driverVersionName, const uint64_t driverVersionCode,
+                             const std::string& appPackageName) = 0;
 };
 
 class BnGpuService : public BnInterface<IGpuService> {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index f40eb6c..0510492 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -104,6 +104,7 @@
         "IGraphicBufferConsumer.cpp",
         "IGraphicBufferProducer.cpp",
         "IProducerListener.cpp",
+        "IRegionSamplingListener.cpp",
         "ISurfaceComposer.cpp",
         "ISurfaceComposerClient.cpp",
         "ITransactionCompletedListener.cpp",
diff --git a/libs/gui/IRegionSamplingListener.cpp b/libs/gui/IRegionSamplingListener.cpp
new file mode 100644
index 0000000..40cbfce
--- /dev/null
+++ b/libs/gui/IRegionSamplingListener.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "IRegionSamplingListener"
+//#define LOG_NDEBUG 0
+
+#include <gui/IRegionSamplingListener.h>
+
+namespace android {
+
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+    ON_SAMPLE_COLLECTED = IBinder::FIRST_CALL_TRANSACTION,
+    LAST = ON_SAMPLE_COLLECTED,
+};
+
+} // Anonymous namespace
+
+class BpRegionSamplingListener : public SafeBpInterface<IRegionSamplingListener> {
+public:
+    explicit BpRegionSamplingListener(const sp<IBinder>& impl)
+          : SafeBpInterface<IRegionSamplingListener>(impl, "BpRegionSamplingListener") {}
+
+    ~BpRegionSamplingListener() override;
+
+    void onSampleCollected(float medianLuma) override {
+        callRemoteAsync<decltype(
+                &IRegionSamplingListener::onSampleCollected)>(Tag::ON_SAMPLE_COLLECTED, medianLuma);
+    }
+};
+
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpRegionSamplingListener::~BpRegionSamplingListener() = default;
+
+IMPLEMENT_META_INTERFACE(RegionSamplingListener, "android.gui.IRegionSamplingListener");
+
+status_t BnRegionSamplingListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                                              uint32_t flags) {
+    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+    auto tag = static_cast<Tag>(code);
+    switch (tag) {
+        case Tag::ON_SAMPLE_COLLECTED:
+            return callLocalAsync(data, reply, &IRegionSamplingListener::onSampleCollected);
+    }
+}
+
+} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index fc9185d..f77eeb2 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -26,6 +26,7 @@
 
 #include <gui/IDisplayEventConnection.h>
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/IRegionSamplingListener.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerDebugInfo.h>
@@ -754,6 +755,57 @@
         error = reply.readBool(outIsWideColorDisplay);
         return error;
     }
+
+    virtual status_t addRegionSamplingListener(const Rect& samplingArea,
+                                               const sp<IBinder>& stopLayerHandle,
+                                               const sp<IRegionSamplingListener>& listener) {
+        Parcel data, reply;
+        status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (error != NO_ERROR) {
+            ALOGE("addRegionSamplingListener: Failed to write interface token");
+            return error;
+        }
+        error = data.write(samplingArea);
+        if (error != NO_ERROR) {
+            ALOGE("addRegionSamplingListener: Failed to write sampling area");
+            return error;
+        }
+        error = data.writeStrongBinder(stopLayerHandle);
+        if (error != NO_ERROR) {
+            ALOGE("addRegionSamplingListener: Failed to write stop layer handle");
+            return error;
+        }
+        error = data.writeStrongBinder(IInterface::asBinder(listener));
+        if (error != NO_ERROR) {
+            ALOGE("addRegionSamplingListener: Failed to write listener");
+            return error;
+        }
+        error = remote()->transact(BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER, data, &reply);
+        if (error != NO_ERROR) {
+            ALOGE("addRegionSamplingListener: Failed to transact");
+        }
+        return error;
+    }
+
+    virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) {
+        Parcel data, reply;
+        status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (error != NO_ERROR) {
+            ALOGE("addRegionSamplingListener: Failed to write interface token");
+            return error;
+        }
+        error = data.writeStrongBinder(IInterface::asBinder(listener));
+        if (error != NO_ERROR) {
+            ALOGE("addRegionSamplingListener: Failed to write listener");
+            return error;
+        }
+        error = remote()->transact(BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER, data,
+                                   &reply);
+        if (error != NO_ERROR) {
+            ALOGE("addRegionSamplingListener: Failed to transact");
+        }
+        return error;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -1233,6 +1285,38 @@
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             return reply->writeUint64Vector(getPhysicalDisplayIds());
         }
+        case ADD_REGION_SAMPLING_LISTENER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            Rect samplingArea;
+            status_t result = data.read(samplingArea);
+            if (result != NO_ERROR) {
+                ALOGE("addRegionSamplingListener: Failed to read sampling area");
+                return result;
+            }
+            sp<IBinder> stopLayerHandle;
+            result = data.readNullableStrongBinder(&stopLayerHandle);
+            if (result != NO_ERROR) {
+                ALOGE("addRegionSamplingListener: Failed to read stop layer handle");
+                return result;
+            }
+            sp<IRegionSamplingListener> listener;
+            result = data.readNullableStrongBinder(&listener);
+            if (result != NO_ERROR) {
+                ALOGE("addRegionSamplingListener: Failed to read listener");
+                return result;
+            }
+            return addRegionSamplingListener(samplingArea, stopLayerHandle, listener);
+        }
+        case REMOVE_REGION_SAMPLING_LISTENER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IRegionSamplingListener> listener;
+            status_t result = data.readNullableStrongBinder(&listener);
+            if (result != NO_ERROR) {
+                ALOGE("removeRegionSamplingListener: Failed to read listener");
+                return result;
+            }
+            return removeRegionSamplingListener(listener);
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/include/gui/IRegionSamplingListener.h b/libs/gui/include/gui/IRegionSamplingListener.h
new file mode 100644
index 0000000..1803d9a
--- /dev/null
+++ b/libs/gui/include/gui/IRegionSamplingListener.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#include <binder/IInterface.h>
+#include <binder/SafeInterface.h>
+
+namespace android {
+
+class IRegionSamplingListener : public IInterface {
+public:
+    DECLARE_META_INTERFACE(RegionSamplingListener)
+
+    virtual void onSampleCollected(float medianLuma) = 0;
+};
+
+class BnRegionSamplingListener : public SafeBnInterface<IRegionSamplingListener> {
+public:
+    BnRegionSamplingListener()
+          : SafeBnInterface<IRegionSamplingListener>("BnRegionSamplingListener") {}
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t flags = 0) override;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index a2db7bc..1a0b6bb 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -50,6 +50,7 @@
 class IDisplayEventConnection;
 class IGraphicBufferProducer;
 class ISurfaceComposerClient;
+class IRegionSamplingListener;
 class Rect;
 enum class FrameEvent;
 
@@ -338,6 +339,26 @@
      */
     virtual status_t isWideColorDisplay(const sp<IBinder>& token,
                                         bool* outIsWideColorDisplay) const = 0;
+
+    /* Registers a listener to stream median luma updates from SurfaceFlinger.
+     *
+     * The sampling area is bounded by both samplingArea and the given stopLayerHandle
+     * (i.e., only layers behind the stop layer will be captured and sampled).
+     *
+     * Multiple listeners may be provided so long as they have independent listeners.
+     * If multiple listeners are provided, the effective sampling region for each listener will
+     * be bounded by whichever stop layer has a lower Z value.
+     *
+     * Requires the same permissions as captureLayers and captureScreen.
+     */
+    virtual status_t addRegionSamplingListener(const Rect& samplingArea,
+                                               const sp<IBinder>& stopLayerHandle,
+                                               const sp<IRegionSamplingListener>& listener) = 0;
+
+    /*
+     * Removes a listener that was streaming median luma updates from SurfaceFlinger.
+     */
+    virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -383,6 +404,8 @@
         IS_WIDE_COLOR_DISPLAY,
         GET_DISPLAY_NATIVE_PRIMARIES,
         GET_PHYSICAL_DISPLAY_IDS,
+        ADD_REGION_SAMPLING_LISTENER,
+        REMOVE_REGION_SAMPLING_LISTENER,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index bec9299..f127853 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -669,6 +669,16 @@
 
     status_t isWideColorDisplay(const sp<IBinder>&, bool*) const override { return NO_ERROR; }
 
+    status_t addRegionSamplingListener(const Rect& /*samplingArea*/,
+                                       const sp<IBinder>& /*stopLayerHandle*/,
+                                       const sp<IRegionSamplingListener>& /*listener*/) override {
+        return NO_ERROR;
+    }
+    status_t removeRegionSamplingListener(
+            const sp<IRegionSamplingListener>& /*listener*/) override {
+        return NO_ERROR;
+    }
+
 protected:
     IBinder* onAsBinder() override { return nullptr; }
 
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 8ef4896..994e953 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -122,6 +122,54 @@
     return gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence);
 }
 
+int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage,
+        int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) {
+    if (!buffer || !outPlanes) return BAD_VALUE;
+
+    if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
+                  AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+        ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
+                " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+        return BAD_VALUE;
+    }
+
+    usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
+    GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    Rect bounds;
+    if (!rect) {
+        bounds.set(Rect(gBuffer->getWidth(), gBuffer->getHeight()));
+    } else {
+        bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
+    }
+    int format = AHardwareBuffer_convertFromPixelFormat(uint32_t(gBuffer->getPixelFormat()));
+    memset(outPlanes->planes, 0, sizeof(outPlanes->planes));
+    if (AHardwareBuffer_formatIsYuv(format)) {
+      android_ycbcr yuvData;
+      int result = gBuffer->lockAsyncYCbCr(usage, bounds, &yuvData, fence);
+      if (result == 0) {
+        outPlanes->planeCount = 3;
+        outPlanes->planes[0].data = yuvData.y;
+        outPlanes->planes[0].pixelStride = 1;
+        outPlanes->planes[0].rowStride = yuvData.ystride;
+        outPlanes->planes[1].data = yuvData.cb;
+        outPlanes->planes[1].pixelStride = yuvData.chroma_step;
+        outPlanes->planes[1].rowStride = yuvData.cstride;
+        outPlanes->planes[2].data = yuvData.cr;
+        outPlanes->planes[2].pixelStride = yuvData.chroma_step;
+        outPlanes->planes[2].rowStride = yuvData.cstride;
+      } else {
+        outPlanes->planeCount = 0;
+      }
+      return result;
+    } else {
+      const uint32_t pixelStride = AHardwareBuffer_bytesPerPixel(format);
+      outPlanes->planeCount = 1;
+      outPlanes->planes[0].pixelStride = pixelStride;
+      outPlanes->planes[0].rowStride = gBuffer->getStride() * pixelStride;
+      return gBuffer->lockAsync(usage, usage, bounds, &outPlanes->planes[0].data, fence);
+    }
+}
+
 int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) {
     if (!buffer) return BAD_VALUE;
 
@@ -375,6 +423,19 @@
             ALOGE_IF(log, "AHARDWAREBUFFER_FORMAT_BLOB cannot be encoded as video");
             return false;
         }
+    } else if (AHardwareBuffer_formatIsYuv(desc->format)) {
+        if (desc->layers != 1) {
+            ALOGE_IF(log, "Layers must be 1 for YUV formats.");
+            return false;
+        }
+        const uint64_t yuvInvalidGpuMask =
+            AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE |
+            AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP;
+        if (desc->usage & yuvInvalidGpuMask) {
+            ALOGE_IF(log, "Invalid usage flags specified for YUV format; "
+                    "mip-mapping and cube-mapping are not allowed.");
+            return false;
+        }
     } else {
         if (desc->usage & AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA) {
             ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA requires AHARDWAREBUFFER_FORMAT_BLOB");
@@ -474,6 +535,7 @@
         case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
         case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
         case AHARDWAREBUFFER_FORMAT_S8_UINT:
+        case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
             // VNDK formats only -- unfortunately we can't differentiate from where we're called
         case AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM:
         case AHARDWAREBUFFER_FORMAT_YV12:
@@ -484,7 +546,6 @@
         case AHARDWAREBUFFER_FORMAT_RAW12:
         case AHARDWAREBUFFER_FORMAT_RAW_OPAQUE:
         case AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED:
-        case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
         case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
         case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
         case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
@@ -495,6 +556,40 @@
     }
 }
 
+bool AHardwareBuffer_formatIsYuv(uint32_t format) {
+    switch (format) {
+        case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
+        case AHARDWAREBUFFER_FORMAT_YV12:
+        case AHARDWAREBUFFER_FORMAT_Y8:
+        case AHARDWAREBUFFER_FORMAT_Y16:
+        case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
+        case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
+        case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
+            return true;
+        default:
+            return false;
+    }
+}
+
+uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) {
+  switch (format) {
+      case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
+      case AHARDWAREBUFFER_FORMAT_D16_UNORM:
+          return 2;
+      case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
+      case AHARDWAREBUFFER_FORMAT_D24_UNORM:
+          return 3;
+      case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
+      case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
+      case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
+      case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
+      case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
+          return 4;
+      default:
+          return 0;
+  }
+}
+
 uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t hal_format) {
     return hal_format;
 }
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index bf688f8..ddfd1d1 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -40,6 +40,12 @@
 // whether this AHardwareBuffer format is valid
 bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format);
 
+// whether this is a YUV type format
+bool AHardwareBuffer_formatIsYuv(uint32_t format);
+
+// number of bytes per pixel or 0 if unknown or multi-planar
+uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format);
+
 // convert AHardwareBuffer format to HAL format (note: this is a no-op)
 uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format);
 
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 2796c75..02c7c1b 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -150,6 +150,14 @@
      *   OpenGL ES: GL_STENCIL_INDEX8
      */
     AHARDWAREBUFFER_FORMAT_S8_UINT                  = 0x35,
+
+    /**
+     * YUV 420 888 format.
+     * Must have an even width and height. Can be accessed in OpenGL
+     * shaders through an external sampler. Does not support mip-maps
+     * cube-maps or multi-layered textures.
+     */
+    AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420             = 0x23,
 };
 
 /**
@@ -302,6 +310,24 @@
 } AHardwareBuffer_Desc;
 
 /**
+ * Holds data for a single image plane.
+ */
+typedef struct AHardwareBuffer_Plane {
+    void*       data;        ///< Points to first byte in plane
+    uint32_t    pixelStride; ///< Distance in bytes from the color channel of one pixel to the next
+    uint32_t    rowStride;   ///< Distance in bytes from the first value of one row of the image to
+                             ///  the first value of the next row.
+} AHardwareBuffer_Plane;
+
+/**
+ * Holds all image planes that contain the pixel data.
+ */
+typedef struct AHardwareBuffer_Planes {
+    uint32_t               planeCount; ///< Number of distinct planes
+    AHardwareBuffer_Plane  planes[4];     ///< Array of image planes
+} AHardwareBuffer_Planes;
+
+/**
  * Opaque handle for a native hardware buffer.
  */
 typedef struct AHardwareBuffer AHardwareBuffer;
@@ -323,7 +349,7 @@
         AHardwareBuffer** outBuffer) __INTRODUCED_IN(26);
 /**
  * Acquire a reference on the given AHardwareBuffer object.
- * 
+ *
  * This prevents the object from being deleted until the last reference
  * is removed.
  */
@@ -396,6 +422,34 @@
         int32_t fence, const ARect* rect, void** outVirtualAddress) __INTRODUCED_IN(26);
 
 /**
+ * Lock a potentially multi-planar AHardwareBuffer for direct CPU access.
+ *
+ * This function is similar to AHardwareBuffer_lock, but can lock multi-planar
+ * formats. The locked planes are returned in the \a outPlanes argument. Note,
+ * that multi-planar should not be confused with multi-layer images, which this
+ * locking function does not support.
+ *
+ * YUV formats are always represented by three separate planes of data, one for
+ * each color plane. The order of planes in the array is guaranteed such that
+ * plane #0 is always Y, plane #1 is always U (Cb), and plane #2 is always V
+ * (Cr). All other formats are represented by a single plane.
+ *
+ * Additional information always accompanies the buffers, describing the row
+ * stride and the pixel stride for each plane.
+ *
+ * In case the buffer cannot be locked, \a outPlanes will contain zero planes.
+ *
+ * See the AHardwareBuffer_lock documentation for all other locking semantics.
+ *
+ * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags
+ * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer
+ * has more than one layer. Error number if the lock fails for any other
+ * reason.
+ */
+int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage,
+        int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) __INTRODUCED_IN(29);
+
+/**
  * Unlock the AHardwareBuffer from direct CPU access.
  *
  * Must be called after all changes to the buffer are completed by the
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h
index 6c9ec34..3392d7f 100644
--- a/libs/nativewindow/include/vndk/hardware_buffer.h
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -73,8 +73,6 @@
     AHARDWAREBUFFER_FORMAT_RAW_OPAQUE               = 0x24,
     /* same as HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED */
     AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED   = 0x22,
-    /* same as HAL_PIXEL_FORMAT_YCBCR_420_888 */
-    AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420             = 0x23,
     /* same as HAL_PIXEL_FORMAT_YCBCR_422_SP */
     AHARDWAREBUFFER_FORMAT_YCbCr_422_SP             = 0x10,
     /* same as HAL_PIXEL_FORMAT_YCRCB_420_SP */
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
index 7693fcb..604665b 100644
--- a/libs/ui/BufferHubBuffer.cpp
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -47,8 +47,8 @@
     return buffer->isValid() ? std::move(buffer) : nullptr;
 }
 
-std::unique_ptr<BufferHubBuffer> BufferHubBuffer::import(const native_handle_t* token) {
-    if (token == nullptr) {
+std::unique_ptr<BufferHubBuffer> BufferHubBuffer::import(const sp<NativeHandle>& token) {
+    if (token == nullptr || token.get() == nullptr) {
         ALOGE("%s: token cannot be nullptr!", __FUNCTION__);
         return nullptr;
     }
@@ -79,10 +79,10 @@
     sp<IBufferClient> client;
     BufferTraits bufferTraits;
     IBufferHub::allocateBuffer_cb alloc_cb = [&](const auto& status, const auto& outClient,
-                                                 const auto& traits) {
+                                                 const auto& outTraits) {
         ret = status;
         client = std::move(outClient);
-        bufferTraits = std::move(traits);
+        bufferTraits = std::move(outTraits);
     };
 
     if (!bufferhub->allocateBuffer(desc, static_cast<uint32_t>(userMetadataSize), alloc_cb)
@@ -105,7 +105,7 @@
     mBufferClient = std::move(client);
 }
 
-BufferHubBuffer::BufferHubBuffer(const native_handle_t* token) {
+BufferHubBuffer::BufferHubBuffer(const sp<NativeHandle>& token) {
     sp<IBufferHub> bufferhub = IBufferHub::getService();
     if (bufferhub.get() == nullptr) {
         ALOGE("%s: BufferHub service not found!", __FUNCTION__);
@@ -116,15 +116,15 @@
     sp<IBufferClient> client;
     BufferTraits bufferTraits;
     IBufferHub::importBuffer_cb import_cb = [&](const auto& status, const auto& outClient,
-                                                const auto& traits) {
+                                                const auto& outTraits) {
         ret = status;
         client = std::move(outClient);
-        bufferTraits = std::move(traits);
+        bufferTraits = std::move(outTraits);
     };
 
     // hidl_handle(native_handle_t*) simply creates a raw pointer reference withouth ownership
     // transfer.
-    if (!bufferhub->importBuffer(hidl_handle(token), import_cb).isOk()) {
+    if (!bufferhub->importBuffer(hidl_handle(token.get()->handle()), import_cb).isOk()) {
         ALOGE("%s: importBuffer transaction failed!", __FUNCTION__);
         return;
     } else if (ret != BufferHubStatus::NO_ERROR) {
@@ -209,10 +209,10 @@
     }
 
     // Populate shortcuts to the atomics in metadata.
-    auto metadata_header = mMetadata.metadataHeader();
-    mBufferState = &metadata_header->buffer_state;
-    mFenceState = &metadata_header->fence_state;
-    mActiveClientsBitMask = &metadata_header->active_clients_bit_mask;
+    auto metadataHeader = mMetadata.metadataHeader();
+    mBufferState = &metadataHeader->buffer_state;
+    mFenceState = &metadataHeader->fence_state;
+    mActiveClientsBitMask = &metadataHeader->active_clients_bit_mask;
     // The C++ standard recommends (but does not require) that lock-free atomic operations are
     // also address-free, that is, suitable for communication between processes using shared
     // memory.
@@ -328,7 +328,7 @@
             mEventFd.get() >= 0 && mMetadata.isValid() && mBufferClient != nullptr;
 }
 
-native_handle_t* BufferHubBuffer::duplicate() {
+sp<NativeHandle> BufferHubBuffer::duplicate() {
     if (mBufferClient == nullptr) {
         ALOGE("%s: missing BufferClient!", __FUNCTION__);
         return nullptr;
@@ -352,7 +352,7 @@
         return nullptr;
     }
 
-    return native_handle_clone(token.getNativeHandle());
+    return NativeHandle::create(native_handle_clone(token.getNativeHandle()), /*ownsHandle=*/true);
 }
 
 } // namespace android
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
index eac8c84..5c09032 100644
--- a/libs/ui/include/ui/BufferHubBuffer.h
+++ b/libs/ui/include/ui/BufferHubBuffer.h
@@ -23,6 +23,7 @@
 #include <ui/BufferHubDefs.h>
 #include <ui/BufferHubEventFd.h>
 #include <ui/BufferHubMetadata.h>
+#include <utils/NativeHandle.h>
 
 namespace android {
 
@@ -33,10 +34,8 @@
                                                    uint32_t layerCount, uint32_t format,
                                                    uint64_t usage, size_t userMetadataSize);
 
-    // Imports the given token to a BufferHubBuffer. Not taking ownership of the token. Caller
-    // should close and destroy the token after calling this function regardless of output.
-    // TODO(b/122543147): use a movable wrapper for token
-    static std::unique_ptr<BufferHubBuffer> import(const native_handle_t* token);
+    // Imports the given token to a BufferHubBuffer. Not taking ownership of the token.
+    static std::unique_ptr<BufferHubBuffer> import(const sp<NativeHandle>& token);
 
     BufferHubBuffer(const BufferHubBuffer&) = delete;
     void operator=(const BufferHubBuffer&) = delete;
@@ -100,17 +99,15 @@
 
     // Creates a token that stands for this BufferHubBuffer client and could be used for Import to
     // create another BufferHubBuffer. The new BufferHubBuffer will share the same underlying
-    // gralloc buffer and ashmem region for metadata. Note that the caller owns the token and
-    // should free it after use.
+    // gralloc buffer and ashmem region for metadata. Not taking ownership of the token.
     // Returns a valid token on success, nullptr on failure.
-    // TODO(b/122543147): use a movable wrapper for token
-    native_handle_t* duplicate();
+    sp<NativeHandle> duplicate();
 
 private:
     BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
                     uint64_t usage, size_t userMetadataSize);
 
-    BufferHubBuffer(const native_handle_t* token);
+    BufferHubBuffer(const sp<NativeHandle>& token);
 
     int initWithBufferTraits(const frameworks::bufferhub::V1_0::BufferTraits& bufferTraits);
 
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index 3087a90..634bce1 100644
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -86,13 +86,10 @@
     b1ClientMask = b1->clientStateMask();
     ASSERT_NE(b1ClientMask, 0U);
 
-    native_handle_t* token = b1->duplicate();
+    sp<NativeHandle> token = b1->duplicate();
     ASSERT_THAT(token, NotNull());
 
-    // TODO(b/122543147): use a movalbe wrapper for token
     b2 = BufferHubBuffer::import(token);
-    native_handle_close(token);
-    native_handle_delete(token);
     ASSERT_THAT(b2, NotNull());
 
     b2ClientMask = b2->clientStateMask();
@@ -137,16 +134,14 @@
     ASSERT_THAT(b1, NotNull());
     EXPECT_TRUE(b1->isValid());
 
-    native_handle_t* token = b1->duplicate();
-    EXPECT_TRUE(token);
+    sp<NativeHandle> token = b1->duplicate();
+    ASSERT_THAT(token, NotNull());
 
     // The detached buffer should still be valid.
     EXPECT_TRUE(b1->isConnected());
     EXPECT_TRUE(b1->isValid());
 
     std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
-    native_handle_close(token);
-    native_handle_delete(token);
 
     ASSERT_THAT(b2, NotNull());
     EXPECT_TRUE(b2->isValid());
@@ -197,16 +192,13 @@
     ASSERT_THAT(b1, NotNull());
     EXPECT_TRUE(b1->isValid());
 
-    native_handle_t* token = b1->duplicate();
-    EXPECT_TRUE(token);
+    sp<NativeHandle> token = b1->duplicate();
+    ASSERT_THAT(token, NotNull());
 
     // Explicitly destroy b1. Backend buffer should be freed and token becomes invalid
     b1.reset();
 
-    // TODO(b/122543147): use a movalbe wrapper for token
     std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
-    native_handle_close(token);
-    native_handle_delete(token);
 
     // Import should fail with INVALID_TOKEN
     EXPECT_THAT(b2, IsNull());
@@ -222,7 +214,7 @@
     native_handle_t* token = native_handle_create(/*numFds=*/0, /*numInts=*/1);
     token->data[0] = 0;
 
-    auto b1 = BufferHubBuffer::import(token);
+    auto b1 = BufferHubBuffer::import(NativeHandle::create(token, /*ownHandle=*/true));
     native_handle_delete(token);
 
     EXPECT_THAT(b1, IsNull());
@@ -425,13 +417,10 @@
 
     // Create a consumer of the buffer and test if the consumer can acquire the
     // buffer if producer posts.
-    // TODO(b/122543147): use a movalbe wrapper for token
-    native_handle_t* token = b1->duplicate();
-    ASSERT_TRUE(token);
+    sp<NativeHandle> token = b1->duplicate();
+    ASSERT_THAT(token, NotNull());
 
     std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
-    native_handle_close(token);
-    native_handle_delete(token);
 
     ASSERT_THAT(b2, NotNull());
     ASSERT_NE(b1->clientStateMask(), b2->clientStateMask());
@@ -450,13 +439,10 @@
 
     // Create a consumer of the buffer and test if the consumer can acquire the
     // buffer if producer posts.
-    // TODO(b/122543147): use a movalbe wrapper for token
-    native_handle_t* token = b1->duplicate();
-    ASSERT_TRUE(token);
+    sp<NativeHandle> token = b1->duplicate();
+    ASSERT_THAT(token, NotNull());
 
     std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
-    native_handle_close(token);
-    native_handle_delete(token);
 
     ASSERT_THAT(b2, NotNull());
     ASSERT_NE(b1->clientStateMask(), b2->clientStateMask());
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
index 90ac1c2..ade08e7 100644
--- a/services/bufferhub/BufferHubService.cpp
+++ b/services/bufferhub/BufferHubService.cpp
@@ -73,7 +73,13 @@
             buildBufferInfo(bufferInfoStorage, node->id(), node->addNewActiveClientsBitToMask(),
                             node->userMetadataSize(), node->metadata().ashmemFd(),
                             node->eventFd().get());
-    BufferTraits bufferTraits = {/*bufferDesc=*/description,
+    // During the gralloc allocation carried out by BufferNode, gralloc allocator will populate the
+    // fields of its HardwareBufferDescription (i.e. strides) according to the actual
+    // gralloc implementation. We need to read those fields back and send them to the client via
+    // BufferTraits.
+    HardwareBufferDescription allocatedBufferDesc;
+    memcpy(&allocatedBufferDesc, &node->bufferDesc(), sizeof(AHardwareBuffer_Desc));
+    BufferTraits bufferTraits = {/*bufferDesc=*/allocatedBufferDesc,
                                  /*bufferHandle=*/hidl_handle(node->bufferHandle()),
                                  /*bufferInfo=*/std::move(bufferInfo)};
 
diff --git a/services/bufferhub/BufferNode.cpp b/services/bufferhub/BufferNode.cpp
index 4f877b2..1efb27e 100644
--- a/services/bufferhub/BufferNode.cpp
+++ b/services/bufferhub/BufferNode.cpp
@@ -28,17 +28,17 @@
 }
 
 // Allocates a new BufferNode.
-BufferNode::BufferNode(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
-                       uint64_t usage, size_t user_metadata_size, int id)
+BufferNode::BufferNode(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
+                       uint64_t usage, size_t userMetadataSize, int id)
       : mId(id) {
-    uint32_t out_stride = 0;
+    uint32_t outStride = 0;
     // graphicBufferId is not used in GraphicBufferAllocator::allocate
     // TODO(b/112338294) After move to the service folder, stop using the
     // hardcoded service name "bufferhub".
-    int ret = GraphicBufferAllocator::get().allocate(width, height, format, layer_count, usage,
+    int ret = GraphicBufferAllocator::get().allocate(width, height, format, layerCount, usage,
                                                      const_cast<const native_handle_t**>(
                                                              &mBufferHandle),
-                                                     &out_stride,
+                                                     &outStride,
                                                      /*graphicBufferId=*/0,
                                                      /*requestor=*/"bufferhub");
 
@@ -49,12 +49,12 @@
 
     mBufferDesc.width = width;
     mBufferDesc.height = height;
-    mBufferDesc.layers = layer_count;
+    mBufferDesc.layers = layerCount;
     mBufferDesc.format = format;
     mBufferDesc.usage = usage;
-    mBufferDesc.stride = out_stride;
+    mBufferDesc.stride = outStride;
 
-    mMetadata = BufferHubMetadata::create(user_metadata_size);
+    mMetadata = BufferHubMetadata::create(userMetadataSize);
     if (!mMetadata.isValid()) {
         ALOGE("%s: Failed to allocate metadata.", __FUNCTION__);
         return;
@@ -83,23 +83,23 @@
 
 uint32_t BufferNode::addNewActiveClientsBitToMask() {
     uint32_t currentActiveClientsBitMask = getActiveClientsBitMask();
-    uint32_t client_state_mask = 0U;
+    uint32_t clientStateMask = 0U;
     uint32_t updatedActiveClientsBitMask = 0U;
     do {
-        client_state_mask =
+        clientStateMask =
                 BufferHubDefs::findNextAvailableClientStateMask(currentActiveClientsBitMask);
-        if (client_state_mask == 0U) {
+        if (clientStateMask == 0U) {
             ALOGE("%s: reached the maximum number of channels per buffer node: %d.", __FUNCTION__,
                   BufferHubDefs::kMaxNumberOfClients);
             errno = E2BIG;
             return 0U;
         }
-        updatedActiveClientsBitMask = currentActiveClientsBitMask | client_state_mask;
+        updatedActiveClientsBitMask = currentActiveClientsBitMask | clientStateMask;
     } while (!(mActiveClientsBitMask->compare_exchange_weak(currentActiveClientsBitMask,
                                                             updatedActiveClientsBitMask,
                                                             std::memory_order_acq_rel,
                                                             std::memory_order_acquire)));
-    return client_state_mask;
+    return clientStateMask;
 }
 
 void BufferNode::removeClientsBitFromMask(const uint32_t& value) {
diff --git a/services/bufferhub/include/bufferhub/BufferClient.h b/services/bufferhub/include/bufferhub/BufferClient.h
index 66ed4bd..644b403 100644
--- a/services/bufferhub/include/bufferhub/BufferClient.h
+++ b/services/bufferhub/include/bufferhub/BufferClient.h
@@ -72,4 +72,4 @@
 } // namespace frameworks
 } // namespace android
 
-#endif
\ No newline at end of file
+#endif
diff --git a/services/bufferhub/include/bufferhub/BufferNode.h b/services/bufferhub/include/bufferhub/BufferNode.h
index 04970fd..62a8d63 100644
--- a/services/bufferhub/include/bufferhub/BufferNode.h
+++ b/services/bufferhub/include/bufferhub/BufferNode.h
@@ -16,8 +16,8 @@
 class BufferNode {
 public:
     // Allocates a new BufferNode.
-    BufferNode(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
-               uint64_t usage, size_t user_metadata_size, int id = -1);
+    BufferNode(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
+               uint64_t usage, size_t userMetadataSize, int id = -1);
 
     ~BufferNode();
 
diff --git a/services/bufferhub/tests/BufferNode_test.cpp b/services/bufferhub/tests/BufferNode_test.cpp
index b9f1c81..2dfd4fc 100644
--- a/services/bufferhub/tests/BufferNode_test.cpp
+++ b/services/bufferhub/tests/BufferNode_test.cpp
@@ -26,79 +26,78 @@
 class BufferNodeTest : public ::testing::Test {
 protected:
     void SetUp() override {
-        buffer_node =
+        mBufferNode =
                 new BufferNode(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
-        ASSERT_TRUE(buffer_node->isValid());
+        ASSERT_TRUE(mBufferNode->isValid());
     }
 
     void TearDown() override {
-        if (buffer_node != nullptr) {
-            delete buffer_node;
+        if (mBufferNode != nullptr) {
+            delete mBufferNode;
         }
     }
 
-    BufferNode* buffer_node = nullptr;
+    BufferNode* mBufferNode = nullptr;
 };
 
 TEST_F(BufferNodeTest, TestCreateBufferNode) {
-    EXPECT_EQ(buffer_node->userMetadataSize(), kUserMetadataSize);
+    EXPECT_EQ(mBufferNode->userMetadataSize(), kUserMetadataSize);
     // Test the handle just allocated is good (i.e. able to be imported)
     GraphicBufferMapper& mapper = GraphicBufferMapper::get();
     const native_handle_t* outHandle;
     status_t ret =
-            mapper.importBuffer(buffer_node->bufferHandle(), buffer_node->bufferDesc().width,
-                                buffer_node->bufferDesc().height, buffer_node->bufferDesc().layers,
-                                buffer_node->bufferDesc().format, buffer_node->bufferDesc().usage,
-                                buffer_node->bufferDesc().stride, &outHandle);
+            mapper.importBuffer(mBufferNode->bufferHandle(), mBufferNode->bufferDesc().width,
+                                mBufferNode->bufferDesc().height, mBufferNode->bufferDesc().layers,
+                                mBufferNode->bufferDesc().format, mBufferNode->bufferDesc().usage,
+                                mBufferNode->bufferDesc().stride, &outHandle);
     EXPECT_EQ(ret, OK);
     EXPECT_THAT(outHandle, NotNull());
 }
 
 TEST_F(BufferNodeTest, TestaddNewActiveClientsBitToMask_twoNewClients) {
-    uint32_t new_client_state_mask_1 = buffer_node->addNewActiveClientsBitToMask();
-    EXPECT_EQ(buffer_node->getActiveClientsBitMask(), new_client_state_mask_1);
+    uint32_t newClientStateMask1 = mBufferNode->addNewActiveClientsBitToMask();
+    EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), newClientStateMask1);
 
     // Request and add a new client_state_mask again.
     // Active clients bit mask should be the union of the two new
     // client_state_masks.
-    uint32_t new_client_state_mask_2 = buffer_node->addNewActiveClientsBitToMask();
-    EXPECT_EQ(buffer_node->getActiveClientsBitMask(),
-              new_client_state_mask_1 | new_client_state_mask_2);
+    uint32_t newClientStateMask2 = mBufferNode->addNewActiveClientsBitToMask();
+    EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), newClientStateMask1 | newClientStateMask2);
 }
 
 TEST_F(BufferNodeTest, TestaddNewActiveClientsBitToMask_32NewClients) {
-    uint32_t new_client_state_mask = 0U;
-    uint32_t current_mask = 0U;
-    uint32_t expected_mask = 0U;
+    uint32_t newClientStateMask = 0U;
+    uint32_t currentMask = 0U;
+    uint32_t expectedMask = 0U;
 
     for (int i = 0; i < BufferHubDefs::kMaxNumberOfClients; ++i) {
-        new_client_state_mask = buffer_node->addNewActiveClientsBitToMask();
-        EXPECT_NE(new_client_state_mask, 0U);
-        EXPECT_FALSE(new_client_state_mask & current_mask);
-        expected_mask = current_mask | new_client_state_mask;
-        current_mask = buffer_node->getActiveClientsBitMask();
-        EXPECT_EQ(current_mask, expected_mask);
+        newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
+        EXPECT_NE(newClientStateMask, 0U);
+        EXPECT_FALSE(newClientStateMask & currentMask);
+        expectedMask = currentMask | newClientStateMask;
+        currentMask = mBufferNode->getActiveClientsBitMask();
+        EXPECT_EQ(currentMask, expectedMask);
     }
 
     // Method should fail upon requesting for more than maximum allowable clients.
-    new_client_state_mask = buffer_node->addNewActiveClientsBitToMask();
-    EXPECT_EQ(new_client_state_mask, 0U);
+    newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
+    EXPECT_EQ(newClientStateMask, 0U);
     EXPECT_EQ(errno, E2BIG);
 }
 
 TEST_F(BufferNodeTest, TestRemoveActiveClientsBitFromMask) {
-    buffer_node->addNewActiveClientsBitToMask();
-    uint32_t current_mask = buffer_node->getActiveClientsBitMask();
-    uint32_t new_client_state_mask = buffer_node->addNewActiveClientsBitToMask();
-    EXPECT_NE(buffer_node->getActiveClientsBitMask(), current_mask);
+    mBufferNode->addNewActiveClientsBitToMask();
+    uint32_t currentMask = mBufferNode->getActiveClientsBitMask();
+    uint32_t newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
+    EXPECT_NE(mBufferNode->getActiveClientsBitMask(), currentMask);
 
-    buffer_node->removeClientsBitFromMask(new_client_state_mask);
-    EXPECT_EQ(buffer_node->getActiveClientsBitMask(), current_mask);
+    mBufferNode->removeClientsBitFromMask(newClientStateMask);
+    EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), currentMask);
 
     // Remove the test_mask again to the active client bit mask should not modify
     // the value of active clients bit mask.
-    buffer_node->removeClientsBitFromMask(new_client_state_mask);
-    EXPECT_EQ(buffer_node->getActiveClientsBitMask(), current_mask);
+    mBufferNode->removeClientsBitFromMask(newClientStateMask);
+    EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), currentMask);
 }
 
 } // namespace
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 9906dea..68c185c 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -37,9 +37,9 @@
 
 GpuService::GpuService() = default;
 
-void GpuService::setGpuStats(const std::string driverPackageName,
-                             const std::string driverVersionName, const uint64_t driverVersionCode,
-                             const std::string appPackageName) {
+void GpuService::setGpuStats(const std::string& driverPackageName,
+                             const std::string& driverVersionName, const uint64_t driverVersionCode,
+                             const std::string& appPackageName) {
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mStateLock);
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index edfd364..5304fa1 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -37,8 +37,8 @@
 
 private:
     // IGpuService interface
-    void setGpuStats(const std::string driverPackageName, const std::string driverVersionName,
-                     const uint64_t driverVersionCode, const std::string appPackageName);
+    void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
+                     const uint64_t driverVersionCode, const std::string& appPackageName);
 
     // GpuStats access must be protected by mStateLock
     std::mutex mStateLock;
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index 09a004c..3905ed0 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -705,13 +705,12 @@
 }
 
 void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
+    NotifyMotionArgs copyArgs = NotifyMotionArgs(*args);
     if (mMotionClassifier && isTouchEvent(*args)) {
         // We only cover touch events, for now.
-        NotifyMotionArgs newArgs = NotifyMotionArgs(*args);
-        newArgs.classification = mMotionClassifier->classify(newArgs);
-        args = &newArgs;
+        copyArgs.classification = mMotionClassifier->classify(copyArgs);
     }
-    mListener->notifyMotion(args);
+    mListener->notifyMotion(&copyArgs);
 }
 
 void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 63748bf..89eee6b 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -206,7 +206,7 @@
             memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
         }
 
-        const Rect win{computeBounds()};
+        const Rect win{getBounds()};
         const float bufferWidth = getBufferSize(s).getWidth();
         const float bufferHeight = getBufferSize(s).getHeight();
 
@@ -654,6 +654,38 @@
     return mCompositionLayer;
 }
 
+FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const {
+    const State& s(getDrawingState());
+
+    // If we have a sideband stream, or we are scaling the buffer then return the layer size since
+    // we cannot determine the buffer size.
+    if ((s.sidebandStream != nullptr) ||
+        (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) {
+        return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
+    }
+
+    if (mActiveBuffer == nullptr) {
+        return parentBounds;
+    }
+
+    uint32_t bufWidth = mActiveBuffer->getWidth();
+    uint32_t bufHeight = mActiveBuffer->getHeight();
+
+    // Undo any transformations on the buffer and return the result.
+    if (mCurrentTransform & ui::Transform::ROT_90) {
+        std::swap(bufWidth, bufHeight);
+    }
+
+    if (getTransformToDisplayInverse()) {
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufWidth, bufHeight);
+        }
+    }
+
+    return FloatRect(0, 0, bufWidth, bufHeight);
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index c118b78..0f8a350 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -191,6 +191,8 @@
     Rect getBufferSize(const State& s) const override;
 
     std::shared_ptr<compositionengine::Layer> mCompositionLayer;
+
+    FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 3915757..0313c32 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -232,7 +232,7 @@
                     getTransformToDisplayInverse(), mFreezeGeometryUpdates);
 
     const nsecs_t expectedPresentTime = mFlinger->mUseScheduler
-            ? mFlinger->mScheduler->mPrimaryDispSync->expectedPresentTime()
+            ? mFlinger->mScheduler->expectedPresentTime()
             : mFlinger->mPrimaryDispSync->expectedPresentTime();
 
     // updateTexImage() below might drop the some buffers at the head of the queue if there is a
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index b0506fe..2d6da76 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -314,7 +314,7 @@
     // if the display frame is not defined, use the parent bounds as the buffer size.
     const auto& p = mDrawingParent.promote();
     if (p != nullptr) {
-        Rect parentBounds = Rect(p->computeBounds(Region()));
+        Rect parentBounds = Rect(p->getBounds(Region()));
         if (!parentBounds.isEmpty()) {
             return parentBounds;
         }
@@ -326,6 +326,18 @@
     }
     return Rect::INVALID_RECT;
 }
+
+FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const {
+    const State& s(getDrawingState());
+    // for buffer state layers we use the display frame size as the buffer size.
+    if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
+        return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
+    }
+
+    // if the display frame is not defined, use the parent bounds as the buffer size.
+    return parentBounds;
+}
+
 // -----------------------------------------------------------------------
 
 // -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index ef287b9..3320c5b 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -88,6 +88,7 @@
                                       uint64_t /*frameNumber*/) override {}
 
     Rect getBufferSize(const State& s) const override;
+    FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
     // -----------------------------------------------------------------------
 
     // -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 46e587d..51e1f00 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -294,80 +294,83 @@
     return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
 }
 
-Rect Layer::computeScreenBounds(bool reduceTransparentRegion) const {
-    const State& s(getDrawingState());
-    Region transparentRegion = reduceTransparentRegion ? getActiveTransparentRegion(s) : Region();
-    FloatRect bounds = computeBounds(transparentRegion);
+Rect Layer::getScreenBounds(bool reduceTransparentRegion) const {
+    if (!reduceTransparentRegion) {
+        return Rect{mScreenBounds};
+    }
+
+    FloatRect bounds = getBounds();
     ui::Transform t = getTransform();
     // Transform to screen space.
     bounds = t.transform(bounds);
     return Rect{bounds};
 }
 
-FloatRect Layer::computeBounds() const {
+FloatRect Layer::getBounds() const {
     const State& s(getDrawingState());
-    return computeBounds(getActiveTransparentRegion(s));
+    return getBounds(getActiveTransparentRegion(s));
 }
 
-FloatRect Layer::computeBounds(const Region& activeTransparentRegion) const {
-    const State& s(getDrawingState());
-    Rect bounds = getCroppedBufferSize(s);
-    FloatRect floatBounds = bounds.toFloatRect();
-    if (bounds.isValid()) {
-        // Layer has bounds. Pass in our bounds as a special case. Then pass on to our parents so
-        // that they can clip it.
-        floatBounds = cropChildBounds(floatBounds);
-    } else {
-        // Layer does not have bounds, so we fill to our parent bounds. This is done by getting our
-        // parent bounds and inverting the transform to get the maximum bounds we can have that
-        // will fit within our parent bounds.
-        const auto& p = mDrawingParent.promote();
-        if (p != nullptr) {
-            ui::Transform t = s.active_legacy.transform;
-            // When calculating the parent bounds for purposes of clipping, we don't need to
-            // constrain the parent to its transparent region. The transparent region is an
-            // optimization based on the buffer contents of the layer, but does not affect the
-            // space allocated to it by policy, and thus children should be allowed to extend into
-            // the parent's transparent region.
-            // One of the main uses is a parent window with a child sitting behind the parent
-            // window, marked by a transparent region. When computing the parent bounds from the
-            // parent's perspective we pass in the transparent region to reduce buffer allocation
-            // size. When computing the parent bounds from the child's perspective, we pass in an
-            // empty transparent region in order to extend into the the parent bounds.
-            floatBounds = p->computeBounds(Region());
-            // Transform back to layer space.
-            floatBounds = t.inverse().transform(floatBounds);
-        }
-    }
-
+FloatRect Layer::getBounds(const Region& activeTransparentRegion) const {
     // Subtract the transparent region and snap to the bounds.
-    return reduce(floatBounds, activeTransparentRegion);
+    return reduce(mBounds, activeTransparentRegion);
 }
 
-FloatRect Layer::cropChildBounds(const FloatRect& childBounds) const {
+ui::Transform Layer::getTransformWithScale() 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.
+    if (!isFixedSize() || !getBE().compositionInfo.mBuffer) {
+        return mEffectiveTransform;
+    }
+
+    int bufferWidth;
+    int bufferHeight;
+    if ((mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) == 0) {
+        bufferWidth = getBE().compositionInfo.mBuffer->getWidth();
+        bufferHeight = getBE().compositionInfo.mBuffer->getHeight();
+    } else {
+        bufferHeight = getBE().compositionInfo.mBuffer->getWidth();
+        bufferWidth = getBE().compositionInfo.mBuffer->getHeight();
+    }
+    float sx = getActiveWidth(getDrawingState()) / static_cast<float>(bufferWidth);
+    float sy = getActiveHeight(getDrawingState()) / static_cast<float>(bufferHeight);
+    ui::Transform extraParentScaling;
+    extraParentScaling.set(sx, 0, 0, sy);
+    return mEffectiveTransform * extraParentScaling;
+}
+
+void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) {
     const State& s(getDrawingState());
-    Rect bounds = getCroppedBufferSize(s);
-    FloatRect croppedBounds = childBounds;
 
-    // If the layer has bounds, then crop the passed in child bounds and pass
-    // it to our parents so they can crop it as well. If the layer has no bounds,
-    // then pass on the child bounds.
-    if (bounds.isValid()) {
-        croppedBounds = croppedBounds.intersect(bounds.toFloatRect());
-    }
+    // Calculate effective layer transform
+    mEffectiveTransform = parentTransform * getActiveTransform(s);
 
-    const auto& p = mDrawingParent.promote();
-    if (p != nullptr) {
-        // Transform to parent space and allow parent layer to crop the
-        // child bounds as well.
-        ui::Transform t = s.active_legacy.transform;
-        croppedBounds = t.transform(croppedBounds);
-        croppedBounds = p->cropChildBounds(croppedBounds);
-        croppedBounds = t.inverse().transform(croppedBounds);
+    // Transform parent bounds to layer space
+    parentBounds = getActiveTransform(s).inverse().transform(parentBounds);
+
+    // Calculate display frame
+    mSourceBounds = computeSourceBounds(parentBounds);
+
+    // Calculate bounds by croping diplay frame with layer crop and parent bounds
+    FloatRect bounds = mSourceBounds;
+    const Rect layerCrop = getCrop(s);
+    if (!layerCrop.isEmpty()) {
+        bounds = mSourceBounds.intersect(layerCrop.toFloatRect());
     }
-    return croppedBounds;
+    bounds = bounds.intersect(parentBounds);
+
+    mBounds = bounds;
+    mScreenBounds = mEffectiveTransform.transform(mBounds);
+    for (const sp<Layer>& child : mDrawingChildren) {
+        child->computeBounds(mBounds, getTransformWithScale());
+    }
 }
 
+
+
 Rect Layer::getCroppedBufferSize(const State& s) const {
     Rect size = getBufferSize(s);
     Rect crop = getCrop(s);
@@ -389,7 +392,7 @@
     // if there are no window scaling involved, this operation will map to full
     // pixels in the buffer.
 
-    FloatRect activeCropFloat = computeBounds();
+    FloatRect activeCropFloat = getBounds();
     ui::Transform t = getTransform();
     // Transform to screen space.
     activeCropFloat = t.transform(activeCropFloat);
@@ -550,9 +553,9 @@
                 Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom));
     }
 
-    // computeBounds returns a FloatRect to provide more accuracy during the
+    // getBounds returns a FloatRect to provide more accuracy during the
     // transformation. We then round upon constructing 'frame'.
-    Rect frame{t.transform(computeBounds(activeTransparentRegion))};
+    Rect frame{t.transform(getBounds(activeTransparentRegion))};
     if (!frame.intersect(display->getViewport(), &frame)) {
         frame.clear();
     }
@@ -721,7 +724,7 @@
 bool Layer::prepareClientLayer(const RenderArea& /*renderArea*/, const Region& /*clip*/,
                                bool useIdentityTransform, Region& /*clearRegion*/,
                                renderengine::LayerSettings& layer) {
-    FloatRect bounds = computeBounds();
+    FloatRect bounds = getBounds();
     half alpha = getAlpha();
     layer.geometry.boundaries = bounds;
     if (useIdentityTransform) {
@@ -841,7 +844,7 @@
                             renderengine::Mesh& mesh,
                             bool useIdentityTransform) const {
     const ui::Transform renderAreaTransform(renderArea.getTransform());
-    FloatRect win = computeBounds();
+    FloatRect win = getBounds();
 
     vec2 lt = vec2(win.left, win.top);
     vec2 lb = vec2(win.left, win.bottom);
@@ -1680,6 +1683,7 @@
 void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
     for (const sp<Layer>& child : mDrawingChildren) {
         child->mDrawingParent = newParent;
+        child->computeBounds(newParent->mBounds, newParent->getTransformWithScale());
     }
 }
 
@@ -2007,34 +2011,7 @@
 }
 
 ui::Transform Layer::getTransform() const {
-    ui::Transform t;
-    const auto& p = mDrawingParent.promote();
-    if (p != nullptr) {
-        t = p->getTransform();
-
-        // If the parent 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 in child surfaces
-        // or we will break the contract where WM can treat child surfaces as
-        // pixels in the parent surface.
-        if (p->isFixedSize() && p->getBE().compositionInfo.mBuffer != nullptr) {
-            int bufferWidth;
-            int bufferHeight;
-            if ((p->mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) == 0) {
-                bufferWidth = p->getBE().compositionInfo.mBuffer->getWidth();
-                bufferHeight = p->getBE().compositionInfo.mBuffer->getHeight();
-            } else {
-                bufferHeight = p->getBE().compositionInfo.mBuffer->getWidth();
-                bufferWidth = p->getBE().compositionInfo.mBuffer->getHeight();
-            }
-            float sx = p->getActiveWidth(p->getDrawingState()) / static_cast<float>(bufferWidth);
-            float sy = p->getActiveHeight(p->getDrawingState()) / static_cast<float>(bufferHeight);
-            ui::Transform extraParentScaling;
-            extraParentScaling.set(sx, 0, 0, sy);
-            t = t * extraParentScaling;
-        }
-    }
-    return t * getActiveTransform(getDrawingState());
+    return mEffectiveTransform;
 }
 
 half Layer::getAlpha() const {
@@ -2066,7 +2043,7 @@
         }
     }
     const float radius = getDrawingState().cornerRadius;
-    return radius > 0 ? RoundedCornerState(computeBounds(), radius) : RoundedCornerState();
+    return radius > 0 ? RoundedCornerState(getBounds(), radius) : RoundedCornerState();
 }
 
 void Layer::commitChildList() {
@@ -2181,6 +2158,10 @@
     for (const auto& entry : state.metadata.mMap) {
         (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
     }
+    LayerProtoHelper::writeToProto(mEffectiveTransform, layerInfo->mutable_effective_transform());
+    LayerProtoHelper::writeToProto(mSourceBounds, layerInfo->mutable_source_bounds());
+    LayerProtoHelper::writeToProto(mScreenBounds, layerInfo->mutable_screen_bounds());
+    LayerProtoHelper::writeToProto(mBounds, layerInfo->mutable_bounds());
 }
 
 void Layer::writeToProto(LayerProto* layerInfo, DisplayId displayId) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8b6d4c7..9241e71 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -349,8 +349,15 @@
 
     void computeGeometry(const RenderArea& renderArea, renderengine::Mesh& mesh,
                          bool useIdentityTransform) const;
-    FloatRect computeBounds(const Region& activeTransparentRegion) const;
-    FloatRect computeBounds() const;
+    FloatRect getBounds(const Region& activeTransparentRegion) const;
+    FloatRect getBounds() const;
+
+    // Compute bounds for the layer and cache the results.
+    void computeBounds(FloatRect parentBounds, ui::Transform parentTransform);
+
+    // 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;
 
     int32_t getSequence() const { return sequence; }
 
@@ -643,7 +650,7 @@
     ssize_t removeChild(const sp<Layer>& layer);
     sp<Layer> getParent() const { return mCurrentParent.promote(); }
     bool hasParent() const { return getParent() != nullptr; }
-    Rect computeScreenBounds(bool reduceTransparentRegion = true) const;
+    Rect getScreenBounds(bool reduceTransparentRegion = true) const;
     bool setChildLayer(const sp<Layer>& childLayer, int32_t z);
     bool setChildRelativeLayer(const sp<Layer>& childLayer,
             const sp<IBinder>& relativeToHandle, int32_t relativeZ);
@@ -660,6 +667,15 @@
      */
     virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
 
+    /**
+     * Returns the source bounds. If the bounds are not defined, it is inferred from the
+     * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
+     * For the root layer, this is the display viewport size.
+     */
+    virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const {
+        return parentBounds;
+    }
+
 protected:
     // constant
     sp<SurfaceFlinger> mFlinger;
@@ -879,19 +895,27 @@
     LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                           const std::vector<Layer*>& layersInTree);
     /**
-     * Retuns the child bounds in layer space cropped to its bounds as well all its parent bounds.
-     * The cropped bounds must be transformed back from parent layer space to child layer space by
-     * applying the inverse of the child's transformation.
-     */
-    FloatRect cropChildBounds(const FloatRect& childBounds) const;
-
-    /**
      * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
      * INVALID_RECT if the layer has no buffer and no crop.
      * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
      * bounds are constrained by its parent bounds.
      */
     Rect getCroppedBufferSize(const Layer::State& s) const;
+
+    // Cached properties computed from drawing state
+    // Effective transform taking into account parent transforms and any parent scaling.
+    ui::Transform mEffectiveTransform;
+
+    // Bounds of the layer before any transformation is applied and before it has been cropped
+    // by its parents.
+    FloatRect mSourceBounds;
+
+    // Bounds of the layer in layer space. This is the mSourceBounds cropped by its layer crop and
+    // its parent bounds.
+    FloatRect mBounds;
+
+    // Layer bounds in screen space.
+    FloatRect mScreenBounds;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.cpp b/services/surfaceflinger/Scheduler/IdleTimer.cpp
index e975e65..b28b1aa 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.cpp
+++ b/services/surfaceflinger/Scheduler/IdleTimer.cpp
@@ -22,8 +22,9 @@
 namespace android {
 namespace scheduler {
 
-IdleTimer::IdleTimer(const Interval& interval, const TimeoutCallback& timeoutCallback)
-      : mInterval(interval), mTimeoutCallback(timeoutCallback) {}
+IdleTimer::IdleTimer(const Interval& interval, const ResetCallback& resetCallback,
+                     const TimeoutCallback& timeoutCallback)
+      : mInterval(interval), mResetCallback(resetCallback), mTimeoutCallback(timeoutCallback) {}
 
 IdleTimer::~IdleTimer() {
     stop();
@@ -49,11 +50,34 @@
 }
 
 void IdleTimer::loop() {
-    std::lock_guard<std::mutex> lock(mMutex);
-    while (mState != TimerState::STOPPED) {
-        if (mState == TimerState::IDLE) {
-            mCondition.wait(mMutex);
-        } else if (mState == TimerState::RESET) {
+    while (true) {
+        bool triggerReset = false;
+        bool triggerTimeout = false;
+        {
+            std::lock_guard<std::mutex> lock(mMutex);
+            if (mState == TimerState::STOPPED) {
+                break;
+            }
+
+            if (mState == TimerState::IDLE) {
+                mCondition.wait(mMutex);
+                continue;
+            }
+
+            if (mState == TimerState::RESET) {
+                triggerReset = true;
+            }
+        }
+        if (triggerReset && mResetCallback) {
+            mResetCallback();
+        }
+
+        { // lock the mutex again. someone might have called stop meanwhile
+            std::lock_guard<std::mutex> lock(mMutex);
+            if (mState == TimerState::STOPPED) {
+                break;
+            }
+
             auto triggerTime = std::chrono::steady_clock::now() + mInterval;
             mState = TimerState::WAITING;
             while (mState == TimerState::WAITING) {
@@ -62,14 +86,14 @@
                 if (waitTime > zero) mCondition.wait_for(mMutex, waitTime);
                 if (mState == TimerState::WAITING &&
                     (triggerTime - std::chrono::steady_clock::now()) <= zero) {
-                    if (mTimeoutCallback) {
-                        mTimeoutCallback();
-                    }
-
+                    triggerTimeout = true;
                     mState = TimerState::IDLE;
                 }
             }
         }
+        if (triggerTimeout && mTimeoutCallback) {
+            mTimeoutCallback();
+        }
     }
 } // namespace scheduler
 
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.h b/services/surfaceflinger/Scheduler/IdleTimer.h
index aee3fa3..19f1267 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.h
+++ b/services/surfaceflinger/Scheduler/IdleTimer.h
@@ -32,9 +32,11 @@
 class IdleTimer {
 public:
     using Interval = std::chrono::milliseconds;
+    using ResetCallback = std::function<void()>;
     using TimeoutCallback = std::function<void()>;
 
-    IdleTimer(const Interval& interval, const TimeoutCallback& timeoutCallback);
+    IdleTimer(const Interval& interval, const ResetCallback& resetCallback,
+              const TimeoutCallback& timeoutCallback);
     ~IdleTimer();
 
     void start();
@@ -62,6 +64,9 @@
     // Interval after which timer expires.
     const Interval mInterval;
 
+    // Callback that happens when timer resets.
+    const ResetCallback mResetCallback;
+
     // Callback that happens when timer expires.
     const TimeoutCallback mTimeoutCallback;
 };
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index a0a4455..ab1f460 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -59,10 +59,10 @@
     const int highFpsEarlyGlAppOffsetNs = atoi(value);
 
     // TODO(b/122905996): Define these in device.mk.
-    property_get("debug.sf.high_fps_late_app_phase_offset_ns", value, "1000000");
+    property_get("debug.sf.high_fps_late_app_phase_offset_ns", value, "2000000");
     const int highFpsLateAppOffsetNs = atoi(value);
 
-    property_get("debug.sf.high_fps_late_sf_phase_offset_ns", value, "8000000");
+    property_get("debug.sf.high_fps_late_sf_phase_offset_ns", value, "1000000");
     const int highFpsLateSfOffsetNs = atoi(value);
 
     mDefaultRefreshRateOffsets.early = {earlySfOffsetNs != -1 ? earlySfOffsetNs
@@ -83,7 +83,7 @@
                                                                       : highFpsLateAppOffsetNs,
                                        highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
                                                                        : highFpsLateSfOffsetNs};
-    mHighRefreshRateOffsets.late = {highFpsLateAppOffsetNs, highFpsLateSfOffsetNs};
+    mHighRefreshRateOffsets.late = {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs};
 }
 
 PhaseOffsets::Offsets PhaseOffsets::getCurrentOffsets() const {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 2f581d2..af439f7 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -69,12 +69,13 @@
     mEventControlThread = std::make_unique<impl::EventControlThread>(function);
 
     char value[PROPERTY_VALUE_MAX];
-    property_get("debug.sf.set_idle_timer_ms", value, "30");
+    property_get("debug.sf.set_idle_timer_ms", value, "0");
     mSetIdleTimerMs = atoi(value);
 
     if (mSetIdleTimerMs > 0) {
         mIdleTimer =
                 std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetIdleTimerMs),
+                                                       [this] { resetTimerCallback(); },
                                                        [this] { expiredTimerCallback(); });
         mIdleTimer->start();
     }
@@ -87,7 +88,6 @@
 
 sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
         const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
-        ResetIdleTimerCallback resetIdleTimerCallback,
         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
     const int64_t id = sNextId++;
     ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
@@ -97,8 +97,7 @@
                             std::move(interceptCallback));
 
     auto eventThreadConnection =
-            createConnectionInternal(eventThread.get(), std::move(resyncCallback),
-                                     std::move(resetIdleTimerCallback));
+            createConnectionInternal(eventThread.get(), std::move(resyncCallback));
     mConnections.emplace(id,
                          std::make_unique<Connection>(new ConnectionHandle(id),
                                                       eventThreadConnection,
@@ -115,26 +114,17 @@
                                                std::move(interceptCallback), connectionName);
 }
 
-sp<EventThreadConnection> Scheduler::createConnectionInternal(
-        EventThread* eventThread, ResyncCallback&& resyncCallback,
-        ResetIdleTimerCallback&& resetIdleTimerCallback) {
+sp<EventThreadConnection> Scheduler::createConnectionInternal(EventThread* eventThread,
+                                                              ResyncCallback&& resyncCallback) {
     return eventThread->createEventConnection(std::move(resyncCallback),
-                                              [this,
-                                               resetIdleTimerCallback =
-                                                       std::move(resetIdleTimerCallback)] {
-                                                  resetIdleTimer();
-                                                  if (resetIdleTimerCallback) {
-                                                      resetIdleTimerCallback();
-                                                  }
-                                              });
+                                              [this] { resetIdleTimer(); });
 }
 
 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
-        const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback,
-        ResetIdleTimerCallback resetIdleTimerCallback) {
+        const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback) {
     RETURN_VALUE_IF_INVALID(nullptr);
     return createConnectionInternal(mConnections[handle->id]->thread.get(),
-                                    std::move(resyncCallback), std::move(resetIdleTimerCallback));
+                                    std::move(resyncCallback));
 }
 
 EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
@@ -200,9 +190,15 @@
 }
 
 void Scheduler::setVsyncPeriod(const nsecs_t period) {
+    std::lock_guard<std::mutex> lock(mHWVsyncLock);
     mPrimaryDispSync->reset();
     mPrimaryDispSync->setPeriod(period);
-    enableHardwareVsync();
+
+    if (!mPrimaryHWVsyncEnabled) {
+        mPrimaryDispSync->beginResync();
+        mEventControlThread->setVsyncEnabled(true);
+        mPrimaryHWVsyncEnabled = true;
+    }
 }
 
 void Scheduler::addResyncSample(const nsecs_t timestamp) {
@@ -238,6 +234,19 @@
     mHWVsyncAvailable = makeAvailable;
 }
 
+bool Scheduler::getHWSyncAvailable() {
+    std::lock_guard<std::mutex> lock(mHWVsyncLock);
+    return mHWVsyncAvailable;
+}
+
+nsecs_t Scheduler::expectedPresentTime() {
+    return mPrimaryDispSync->expectedPresentTime();
+}
+
+void Scheduler::dumpPrimaryDispSync(std::string& result) const {
+    mPrimaryDispSync->dump(result);
+}
+
 void Scheduler::addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
                                             const std::string layerName) {
     // This is V1 logic. It calculates the average FPS based on the timestamp frequency
@@ -259,6 +268,11 @@
     mExpiredTimerCallback = expiredTimerCallback;
 }
 
+void Scheduler::setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback) {
+    std::lock_guard<std::mutex> lock(mCallbackLock);
+    mResetTimerCallback = resetTimerCallback;
+}
+
 void Scheduler::updateFrameSkipping(const int64_t skipCount) {
     ATRACE_INT("FrameSkipCount", skipCount);
     if (mSkipCount != skipCount) {
@@ -347,6 +361,13 @@
 void Scheduler::resetIdleTimer() {
     if (mIdleTimer) {
         mIdleTimer->reset();
+    }
+}
+
+void Scheduler::resetTimerCallback() {
+    std::lock_guard<std::mutex> lock(mCallbackLock);
+    if (mResetTimerCallback) {
+        mResetTimerCallback();
         ATRACE_INT("ExpiredIdleTimer", 0);
     }
 }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 4abf027..d628e40 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -37,6 +37,7 @@
 class Scheduler {
 public:
     using ExpiredIdleTimerCallback = std::function<void()>;
+    using ResetIdleTimerCallback = std::function<void()>;
 
     // Enum to indicate whether to start the transaction early, or at vsync time.
     enum class TransactionStart { EARLY, NORMAL };
@@ -72,12 +73,11 @@
 
     /** Creates an EventThread connection. */
     sp<ConnectionHandle> createConnection(const char* connectionName, int64_t phaseOffsetNs,
-                                          ResyncCallback, ResetIdleTimerCallback,
+                                          ResyncCallback,
                                           impl::EventThread::InterceptVSyncsCallback);
 
     sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle,
-                                                             ResyncCallback,
-                                                             ResetIdleTimerCallback);
+                                                             ResyncCallback);
 
     // Getter methods.
     EventThread* getEventThread(const sp<ConnectionHandle>& handle);
@@ -109,6 +109,9 @@
     void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
     void setIgnorePresentFences(bool ignore);
     void makeHWSyncAvailable(bool makeAvailable);
+    // returns HWSyncAvailable flag to SF would enable HW vsync based on this
+    bool getHWSyncAvailable();
+    nsecs_t expectedPresentTime();
     // Adds the present time for given layer to the history of present times.
     void addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
                                      const std::string layerName);
@@ -116,9 +119,14 @@
     void incrementFrameCounter();
     // Callback that gets invoked once the idle timer expires.
     void setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback);
+    // Callback that gets invoked once the idle timer is reset.
+    void setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback);
     // Returns relevant information about Scheduler for dumpsys purposes.
     std::string doDump();
 
+    // calls DispSync::dump() on primary disp sync
+    void dumpPrimaryDispSync(std::string& result) const;
+
 protected:
     virtual std::unique_ptr<EventThread> makeEventThread(
             const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
@@ -126,8 +134,7 @@
 
 private:
     // Creates a connection on the given EventThread and forwards the given callbacks.
-    sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&,
-                                                       ResetIdleTimerCallback&&);
+    sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&);
 
     nsecs_t calculateAverage() const;
     void updateFrameSkipping(const int64_t skipCount);
@@ -139,13 +146,11 @@
     void determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime);
     // Function that resets the idle timer.
     void resetIdleTimer();
+    // Function that is called when the timer resets.
+    void resetTimerCallback();
     // Function that is called when the timer expires.
     void expiredTimerCallback();
 
-    // TODO(b/113612090): Instead of letting BufferQueueLayer to access mDispSync directly, it
-    // should make request to Scheduler to compute next refresh.
-    friend class BufferQueueLayer;
-
     // If fences from sync Framework are supported.
     const bool mHasSyncFramework;
 
@@ -186,6 +191,7 @@
 
     std::mutex mCallbackLock;
     ExpiredIdleTimerCallback mExpiredTimerCallback GUARDED_BY(mCallbackLock);
+    ExpiredIdleTimerCallback mResetTimerCallback GUARDED_BY(mCallbackLock);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index d7ec733..0bf3ceb 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -124,7 +124,7 @@
             changed = true;
         }
         if (desired.app != current.app) {
-            if (mSfConnectionHandle != nullptr) {
+            if (mAppConnectionHandle != nullptr) {
                 mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app);
             } else {
                 mAppEventThread->setPhaseOffset(desired.app);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 954ecda..c98220d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -351,10 +351,6 @@
     }
     ALOGV("Primary Display Orientation is set to %2d.", SurfaceFlinger::primaryDisplayOrientation);
 
-    mPrimaryDispSync =
-            getFactory().createDispSync("PrimaryDispSync", SurfaceFlinger::hasSyncFramework,
-                                        SurfaceFlinger::dispSyncPresentTimeOffset);
-
     auto surfaceFlingerConfigsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
     if (surfaceFlingerConfigsServiceV1_2) {
         surfaceFlingerConfigsServiceV1_2->getDisplayNativePrimaries(
@@ -400,6 +396,12 @@
     property_get("debug.sf.use_scheduler", value, "0");
     mUseScheduler = atoi(value);
 
+    if (!mUseScheduler) {
+        mPrimaryDispSync =
+                getFactory().createDispSync("PrimaryDispSync", SurfaceFlinger::hasSyncFramework,
+                                            SurfaceFlinger::dispSyncPresentTimeOffset);
+    }
+
     const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
     mVsyncModulator.setPhaseOffsets(early, gl, late);
 
@@ -633,17 +635,13 @@
         mPhaseOffsets->setRefreshRateType(
                 scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
 
-        auto resetIdleTimerCallback =
-                std::bind(&SurfaceFlinger::setRefreshRateTo, this, RefreshRateType::PERFORMANCE);
-
         mAppConnectionHandle =
-                mScheduler->createConnection("appConnection", mPhaseOffsets->getCurrentAppOffset(),
-                                             resyncCallback, resetIdleTimerCallback,
+                mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
+                                             resyncCallback,
                                              impl::EventThread::InterceptVSyncsCallback());
         mSfConnectionHandle =
-                mScheduler->createConnection("sfConnection", mPhaseOffsets->getCurrentSfOffset(),
-                                             resyncCallback, resetIdleTimerCallback,
-                                             [this](nsecs_t timestamp) {
+                mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
+                                             resyncCallback, [this](nsecs_t timestamp) {
                                                  mInterceptor->saveVSyncEvent(timestamp);
                                              });
 
@@ -719,8 +717,10 @@
         }
     }
 
-    mEventControlThread = getFactory().createEventControlThread(
-            [this](bool enabled) { setPrimaryVsyncEnabled(enabled); });
+    if (!mUseScheduler) {
+        mEventControlThread = getFactory().createEventControlThread(
+                [this](bool enabled) { setPrimaryVsyncEnabled(enabled); });
+    }
 
     // initialize our drawing state
     mDrawingState = mCurrentState;
@@ -745,6 +745,10 @@
             Mutex::Autolock lock(mStateLock);
             setRefreshRateTo(RefreshRateType::DEFAULT);
         });
+        mScheduler->setResetIdleTimerCallback([this] {
+            Mutex::Autolock lock(mStateLock);
+            setRefreshRateTo(RefreshRateType::PERFORMANCE);
+        });
 
         mRefreshRateStats =
                 std::make_unique<scheduler::RefreshRateStats>(getHwComposer().getConfigs(
@@ -825,14 +829,12 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken,
-                                           Vector<DisplayInfo>* configs) {
+status_t SurfaceFlinger::getDisplayConfigsLocked(const sp<IBinder>& displayToken,
+                                                 Vector<DisplayInfo>* configs) {
     if (!displayToken || !configs) {
         return BAD_VALUE;
     }
 
-    ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
-
     const auto displayId = getPhysicalDisplayIdLocked(displayToken);
     if (!displayId) {
         return NAME_NOT_FOUND;
@@ -960,22 +962,19 @@
     return display->getActiveConfig();
 }
 
-status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
-    ATRACE_NAME("setActiveConfigSync");
-    postMessageSync(new LambdaMessage(
-            [&]() NO_THREAD_SAFETY_ANALYSIS { setActiveConfigInternal(displayToken, mode); }));
-    return NO_ERROR;
-}
+void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode) {
+    ATRACE_CALL();
 
-void SurfaceFlinger::setActiveConfigInternal(const sp<IBinder>& displayToken, int mode) {
     Vector<DisplayInfo> configs;
-    getDisplayConfigs(displayToken, &configs);
+    // Lock is acquired by setRefreshRateTo.
+    getDisplayConfigsLocked(displayToken, &configs);
     if (mode < 0 || mode >= static_cast<int>(configs.size())) {
         ALOGE("Attempt to set active config %d for display with %zu configs", mode, configs.size());
         return;
     }
 
-    const auto display = getDisplayDevice(displayToken);
+    // Lock is acquired by setRefreshRateTo.
+    const auto display = getDisplayDeviceLocked(displayToken);
     if (!display) {
         ALOGE("Attempt to set active config %d for invalid display token %p", mode,
               displayToken.get());
@@ -991,23 +990,95 @@
         return;
     }
 
-    int currentMode = display->getActiveConfig();
-    if (mode == currentMode) {
-        // Don't update config if we are already running in the desired mode.
+    // Don't check against the current mode yet. Worst case we set the desired
+    // config twice.
+    {
+        std::lock_guard<std::mutex> lock(mActiveConfigLock);
+        mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken};
+    }
+    // This will trigger HWC refresh without resetting the idle timer.
+    repaintEverythingForHWC();
+}
+
+status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
+    ATRACE_CALL();
+    postMessageSync(new LambdaMessage(
+            [&]() NO_THREAD_SAFETY_ANALYSIS { setDesiredActiveConfig(displayToken, mode); }));
+    return NO_ERROR;
+}
+
+void SurfaceFlinger::setActiveConfigInHWC() {
+    ATRACE_CALL();
+
+    const auto display = getDisplayDevice(mUpcomingActiveConfig.displayToken);
+    if (!display) {
         return;
     }
-    if (mUseScheduler) {
-        mRefreshRateStats->setConfigMode(mode);
-    }
-
     const auto displayId = display->getId();
     LOG_ALWAYS_FATAL_IF(!displayId);
 
-    display->setActiveConfig(mode);
-    getHwComposer().setActiveConfig(*displayId, mode);
+    ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId);
+    getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
+    mSetActiveConfigState = SetActiveConfigState::NOTIFIED_HWC;
+    ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
+}
 
-    ATRACE_INT("ActiveConfigMode", mode);
+void SurfaceFlinger::setActiveConfigInternal() {
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lock(mActiveConfigLock);
+    if (mUseScheduler) {
+        mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
+    }
+
+    const auto display = getDisplayDeviceLocked(mUpcomingActiveConfig.displayToken);
+    display->setActiveConfig(mUpcomingActiveConfig.configId);
+
+    mSetActiveConfigState = SetActiveConfigState::NONE;
+    ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
+
     resyncToHardwareVsync(true, getVsyncPeriod());
+    ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
+}
+
+bool SurfaceFlinger::updateSetActiveConfigStateMachine() NO_THREAD_SAFETY_ANALYSIS {
+    // Store the local variable to release the lock.
+    ActiveConfigInfo desiredActiveConfig;
+    {
+        std::lock_guard<std::mutex> lock(mActiveConfigLock);
+        desiredActiveConfig = mDesiredActiveConfig;
+    }
+
+    const auto display = getDisplayDevice(desiredActiveConfig.displayToken);
+    if (display) {
+        if (mSetActiveConfigState == SetActiveConfigState::NONE &&
+            display->getActiveConfig() != desiredActiveConfig.configId) {
+            // Step 1) Desired active config was set, it is different than the
+            // config currently in use. Notify HWC.
+            mUpcomingActiveConfig = desiredActiveConfig;
+            setActiveConfigInHWC();
+        } else if (mSetActiveConfigState == SetActiveConfigState::NOTIFIED_HWC) {
+            // Step 2) HWC was notified and we received refresh from it.
+            mSetActiveConfigState = SetActiveConfigState::REFRESH_RECEIVED;
+            ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
+            repaintEverythingForHWC();
+            // We do not want to give another frame to HWC while we are transitioning.
+            return false;
+        } else if (mSetActiveConfigState == SetActiveConfigState::REFRESH_RECEIVED &&
+                   !(mPreviousPresentFence != Fence::NO_FENCE &&
+                     (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled))) {
+            // Step 3) We received the present fence from the HWC, so we assume it
+            // successfully updated the config, hence we update SF.
+            setActiveConfigInternal();
+            // If the config changed again while we were transitioning, restart the
+            // process.
+            if (desiredActiveConfig != mUpcomingActiveConfig) {
+                mUpcomingActiveConfig = desiredActiveConfig;
+                setActiveConfigInHWC(); // sets the state to NOTIFY_HWC
+            }
+        }
+    }
+    return true;
 }
 
 status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
@@ -1256,6 +1327,17 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::addRegionSamplingListener(
+        const Rect& /*samplingArea*/, const sp<IBinder>& /*stopLayerHandle*/,
+        const sp<IRegionSamplingListener>& /*listener*/) {
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeRegionSamplingListener(
+        const sp<IRegionSamplingListener>& /*listener*/) {
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
@@ -1266,16 +1348,10 @@
     });
 
     if (mUseScheduler) {
-        auto resetIdleTimerCallback = [this] {
-            Mutex::Autolock lock(mStateLock);
-            setRefreshRateTo(RefreshRateType::PERFORMANCE);
-        };
-
         const auto& handle = vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle
                                                                        : mAppConnectionHandle;
 
-        return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback),
-                                                        std::move(resetIdleTimerCallback));
+        return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback));
     } else {
         if (vsyncSource == eVsyncSourceSurfaceFlinger) {
             return mSFEventThread->createEventConnection(resyncCallback, ResetIdleTimerCallback());
@@ -1335,6 +1411,7 @@
 }
 
 void SurfaceFlinger::enableHardwareVsync() {
+    assert(!mUseScheduler);
     Mutex::Autolock _l(mHWVsyncLock);
     if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
         mPrimaryDispSync->beginResync();
@@ -1345,17 +1422,23 @@
 
 void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
     Mutex::Autolock _l(mHWVsyncLock);
-
-    if (makeAvailable) {
-        mHWVsyncAvailable = true;
-        // TODO(b/113612090): This is silly, but necessary evil until we turn on the flag for good.
-        if (mUseScheduler) {
+    // TODO(b/113612090): This is silly, but necessary evil until we turn on the flag for good.
+    if (mUseScheduler) {
+        if (makeAvailable) {
             mScheduler->makeHWSyncAvailable(true);
+        } else if (!mScheduler->getHWSyncAvailable()) {
+            // Hardware vsync is not currently available, so abort the resync
+            // attempt for now
+            return;
         }
-    } else if (!mHWVsyncAvailable) {
-        // Hardware vsync is not currently available, so abort the resync
-        // attempt for now
-        return;
+    } else {
+        if (makeAvailable) {
+            mHWVsyncAvailable = true;
+        } else if (!mHWVsyncAvailable) {
+            // Hardware vsync is not currently available, so abort the resync
+            // attempt for now
+            return;
+        }
     }
 
     if (period <= 0) {
@@ -1377,6 +1460,7 @@
 }
 
 void SurfaceFlinger::disableHardwareVsync(bool makeUnavailable) {
+    assert(!mUseScheduler);
     Mutex::Autolock _l(mHWVsyncLock);
     if (mPrimaryHWVsyncEnabled) {
         mEventControlThread->setVsyncEnabled(false);
@@ -1485,8 +1569,7 @@
         // TODO(b/113612090): There should be a better way at determining which config
         // has the right refresh rate.
         if (std::abs(fps - newFps) <= 1) {
-            setActiveConfigInternal(getInternalDisplayTokenLocked(), i);
-            ATRACE_INT("FPS", newFps);
+            setDesiredActiveConfig(getInternalDisplayTokenLocked(), i);
         }
     }
 }
@@ -1630,14 +1713,16 @@
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
+            if (!updateSetActiveConfigStateMachine()) {
+                break;
+            }
+
             if (mUseScheduler) {
                 // This call is made each time SF wakes up and creates a new frame.
                 mScheduler->incrementFrameCounter();
             }
-            bool frameMissed = !mHadClientComposition &&
-                    mPreviousPresentFence != Fence::NO_FENCE &&
-                    (mPreviousPresentFence->getSignalTime() ==
-                            Fence::SIGNAL_TIME_PENDING);
+            bool frameMissed = !mHadClientComposition && mPreviousPresentFence != Fence::NO_FENCE &&
+                    (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled);
             mFrameMissedCount += frameMissed;
             ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
             if (frameMissed) {
@@ -1755,9 +1840,13 @@
     ATRACE_CALL();
     bool refreshNeeded = handlePageFlip();
 
+    if (mVisibleRegionsDirty) {
+        computeLayerBounds();
+    }
+
     for (auto& layer : mLayersPendingRefresh) {
         Region visibleReg;
-        visibleReg.set(layer->computeScreenBounds());
+        visibleReg.set(layer->getScreenBounds());
         invalidateLayerStack(layer, visibleReg);
     }
     mLayersPendingRefresh.clear();
@@ -2236,6 +2325,21 @@
 }
 #pragma clang optimize on // b/119477596
 
+void SurfaceFlinger::computeLayerBounds() {
+    for (const auto& pair : mDisplays) {
+        const auto& displayDevice = pair.second;
+        const auto display = displayDevice->getCompositionDisplay();
+        for (const auto& layer : mDrawingState.layersSortedByZ) {
+            // only consider the layers on the given layer stack
+            if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+                continue;
+            }
+
+            layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform());
+        }
+    }
+}
+
 void SurfaceFlinger::rebuildLayerStacks() {
     ATRACE_CALL();
     ALOGV("rebuildLayerStacks");
@@ -2987,11 +3091,8 @@
         mDrawingState.traverseInZOrder([&](Layer* layer) {
             if (mLayersPendingRemoval.indexOf(layer) >= 0) {
                 // this layer is not visible anymore
-                // TODO: we could traverse the tree from front to back and
-                //       compute the actual visible region
-                // TODO: we could cache the transformed region
                 Region visibleReg;
-                visibleReg.set(layer->computeScreenBounds());
+                visibleReg.set(layer->getScreenBounds());
                 invalidateLayerStack(layer, visibleReg);
             }
         });
@@ -3055,7 +3156,12 @@
 
 void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) {
     if (layer->hasReadyFrame()) {
-        const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+        nsecs_t expectedPresentTime;
+        if (mUseScheduler) {
+            expectedPresentTime = mScheduler->expectedPresentTime();
+        } else {
+            expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+        }
         if (layer->shouldPresentNow(expectedPresentTime)) {
             bool ignored = false;
             layer->latchBuffer(ignored, systemTime(), Fence::NO_FENCE);
@@ -3155,7 +3261,7 @@
         // handle hidden surfaces by setting the visible region to empty
         if (CC_LIKELY(layer->isVisible())) {
             const bool translucent = !layer->isOpaque(s);
-            Rect bounds(layer->computeScreenBounds());
+            Rect bounds(layer->getScreenBounds());
 
             visibleRegion.set(bounds);
             ui::Transform tr = layer->getTransform();
@@ -3287,7 +3393,12 @@
     mDrawingState.traverseInZOrder([&](Layer* layer) {
         if (layer->hasReadyFrame()) {
             frameQueued = true;
-            const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+            nsecs_t expectedPresentTime;
+            if (mUseScheduler) {
+                expectedPresentTime = mScheduler->expectedPresentTime();
+            } else {
+                expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+            }
             if (layer->shouldPresentNow(expectedPresentTime)) {
                 mLayersWithQueuedFrames.push_back(layer);
             } else {
@@ -3628,7 +3739,12 @@
 
 bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
                                                    const Vector<ComposerState>& states) {
-    const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+    nsecs_t expectedPresentTime;
+    if (mUseScheduler) {
+        expectedPresentTime = mScheduler->expectedPresentTime();
+    } else {
+        expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+    }
     // Do not present if the desiredPresentTime has not passed unless it is more than one second
     // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
     if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
@@ -4449,7 +4565,13 @@
                 {"--clear-layer-stats"s, dumper([this](std::string&) { mLayerStats.clear(); })},
                 {"--disable-layer-stats"s, dumper([this](std::string&) { mLayerStats.disable(); })},
                 {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
-                {"--dispsync"s, dumper([this](std::string& s) { mPrimaryDispSync->dump(s); })},
+                {"--dispsync"s, dumper([this](std::string& s) {
+                     if (mUseScheduler) {
+                         mScheduler->dumpPrimaryDispSync(s);
+                     } else {
+                         mPrimaryDispSync->dump(s);
+                     }
+                 })},
                 {"--dump-layer-stats"s, dumper([this](std::string& s) { mLayerStats.dump(s); })},
                 {"--enable-layer-stats"s, dumper([this](std::string&) { mLayerStats.enable(); })},
                 {"--frame-composition"s, dumper(&SurfaceFlinger::dumpFrameCompositionInfo)},
@@ -5030,7 +5152,9 @@
             return OK;
         }
         case CAPTURE_LAYERS:
-        case CAPTURE_SCREEN: {
+        case CAPTURE_SCREEN:
+        case ADD_REGION_SAMPLING_LISTENER:
+        case REMOVE_REGION_SAMPLING_LISTENER: {
             // codes that require permission check
             IPCThreadState* ipc = IPCThreadState::self();
             const int pid = ipc->getCallingPid();
@@ -5452,11 +5576,14 @@
             const sp<Layer>& oldParent;
             const sp<Layer>& newParent;
 
-            ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent)
+            ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
+                               const Rect& drawingBounds)
                   : oldParent(oldParent), newParent(newParent) {
+                // Compute and cache the bounds for the new parent layer.
+                newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform());
                 oldParent->setChildrenDrawingParent(newParent);
             }
-            ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
+            ~ReparentForDrawing() { newParent->setChildrenDrawingParent(oldParent); }
         };
 
         void render(std::function<void()> drawLayers) override {
@@ -5474,7 +5601,7 @@
                         LayerCreationArgs(mFlinger, nullptr, String8("Screenshot Parent"),
                                           bounds.getWidth(), bounds.getHeight(), 0));
 
-                ReparentForDrawing reparent(mLayer, screenshotParentLayer);
+                ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
                 drawLayers();
             }
         }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 10ec5ee..a0f83a2 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -437,7 +437,12 @@
                            const Rect& sourceCrop, float frameScale, bool childrenOnly) override;
     status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
     status_t getDisplayConfigs(const sp<IBinder>& displayToken,
-                               Vector<DisplayInfo>* configs) override;
+                               Vector<DisplayInfo>* configs) override {
+        Mutex::Autolock _l(mStateLock);
+        return getDisplayConfigsLocked(displayToken, configs);
+    }
+    status_t getDisplayConfigsLocked(const sp<IBinder>& displayToken, Vector<DisplayInfo>* configs)
+            REQUIRES(mStateLock);
     int getActiveConfig(const sp<IBinder>& displayToken) override;
     status_t getDisplayColorModes(const sp<IBinder>& displayToken,
                                   Vector<ui::ColorMode>* configs) override;
@@ -471,7 +476,9 @@
     status_t getProtectedContentSupport(bool* outSupported) const override;
     status_t isWideColorDisplay(const sp<IBinder>& displayToken,
                                 bool* outIsWideColorDisplay) const override;
-
+    status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
+                                       const sp<IRegionSamplingListener>& listener) override;
+    status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
     /* ------------------------------------------------------------------------
      * DeathRecipient interface
      */
@@ -503,8 +510,18 @@
 
     // called on the main thread in response to initializeDisplays()
     void onInitializeDisplays() REQUIRES(mStateLock);
-    // called on the main thread in response to setActiveConfig()
-    void setActiveConfigInternal(const sp<IBinder>& displayToken, int mode) REQUIRES(mStateLock);
+    // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
+    void setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode) REQUIRES(mStateLock);
+    // Calls setActiveConfig in HWC.
+    void setActiveConfigInHWC();
+    // Once HWC has returned the present fence, this sets the active config and a new refresh
+    // rate in SF. It also triggers HWC vsync.
+    void setActiveConfigInternal() REQUIRES(mStateLock);
+    // Active config is updated on INVALIDATE call in a state machine-like manner. When the
+    // desired config was set, HWC needs to update the pannel on the next refresh, and when
+    // we receive the fence back, we know that the process was complete. It returns whether
+    // the invalidate process should continue.
+    bool updateSetActiveConfigStateMachine();
     // called on the main thread in response to setPowerMode()
     void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
 
@@ -591,6 +608,9 @@
             const sp<Layer>& parent,
             bool addToCurrentState);
 
+    // Traverse through all the layers and compute and cache its bounds.
+    void computeLayerBounds();
+
     /* ------------------------------------------------------------------------
      * Boot animation, on/off animations and screen capture
      */
@@ -1093,6 +1113,34 @@
     sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
     std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
 
+    // The following structs are used for configuring active config state at a desired time,
+    // which is once per vsync at invalidate time.
+    enum SetActiveConfigState {
+        NONE,            // not in progress
+        NOTIFIED_HWC,    // call to HWC has been made
+        REFRESH_RECEIVED // onRefresh was received from HWC
+    };
+    std::atomic<SetActiveConfigState> mSetActiveConfigState = SetActiveConfigState::NONE;
+
+    struct ActiveConfigInfo {
+        int configId;
+        sp<IBinder> displayToken;
+
+        bool operator!=(const ActiveConfigInfo& other) const {
+            if (configId != other.configId) {
+                return true;
+            }
+            return (displayToken != other.displayToken);
+        }
+    };
+    std::mutex mActiveConfigLock;
+    // This bit is set once we start setting the config. We read from this bit during the
+    // process. If at the end, this bit is different than mDesiredActiveConfig, we restart
+    // the process.
+    ActiveConfigInfo mUpcomingActiveConfig; // Always read and written on the main thread.
+    // This bit can be set at any point in time when the system wants the new config.
+    ActiveConfigInfo mDesiredActiveConfig GUARDED_BY(mActiveConfigLock);
+
     /* ------------------------------------------------------------------------ */
     sp<IInputFlinger> mInputFlinger;
 
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 4c756d9..72cbfac 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -91,6 +91,11 @@
   optional float corner_radius = 41;
   // Metadata map. May be empty.
   map<int32, bytes> metadata = 42;
+
+  optional TransformProto effective_transform = 43;
+  optional FloatRectProto source_bounds = 44;
+  optional FloatRectProto bounds = 45;
+  optional FloatRectProto screen_bounds = 46;
 }
 
 message PositionProto {
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index ee79c18..3fb8708 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -746,7 +746,7 @@
         layerDrawingState.active.h = 100;
         layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
                                         LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
-
+        layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform());
         layer->setVisibleRegion(Region(Rect(0, 0, 100, 100)));
 
         return layer;
diff --git a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
index dc63260..5e82225 100644
--- a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #undef LOG_TAG
 #define LOG_TAG "SchedulerUnittests"
 
@@ -44,6 +43,7 @@
     // Note that there can be false-negatives if the callback happens later.
     static constexpr auto waitTimeForUnexpected3msCallback = 6ms;
 
+    AsyncCallRecorder<void (*)()> mResetTimerCallback;
     AsyncCallRecorder<void (*)()> mExpiredTimerCallback;
 
     std::unique_ptr<IdleTimer> mIdleTimer;
@@ -56,31 +56,39 @@
 
 namespace {
 TEST_F(IdleTimerTest, createAndDestroyTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, [] {});
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, [] {}, [] {});
 }
 
 TEST_F(IdleTimerTest, startStopTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mResetTimerCallback.getInvocable(),
+                                                        mExpiredTimerCallback.getInvocable());
     auto startTime = std::chrono::steady_clock::now();
     mIdleTimer->start();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     // The idle timer fires after 30ms, so there should be no callback within
-    // 25ms (waiting for a ballback for the full 30ms would be problematic).
+    // 25ms (waiting for a callback for the full 30ms would be problematic).
     bool callbackCalled = mExpiredTimerCallback.waitForCall(25ms).has_value();
     // Under ideal conditions there should be no event. But occasionally
     // it is possible that the wait just prior takes more than 30ms, and
     // a callback is observed. We check the elapsed time since before the IdleTimer
     // thread was started as a sanity check to not have a flakey test.
     EXPECT_FALSE(callbackCalled && std::chrono::steady_clock::now() - startTime < 30ms);
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(25));
+    EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
     mIdleTimer->stop();
 }
 
 TEST_F(IdleTimerTest, resetTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(),
+                                                        mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     // Observe any event that happens in about 25ms. We don't care if one was
     // observed or not.
     mExpiredTimerCallback.waitForCall(25ms).has_value();
     mIdleTimer->reset();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     // There may have been a race with the reset. Clear any callbacks we
     // received right afterwards.
     clearPendingCallbacks();
@@ -93,60 +101,110 @@
     mIdleTimer->stop();
     // Final quick check that no more callback were observed.
     EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
 }
 
-TEST_F(IdleTimerTest, startNotCalledTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
-    // The start hasn't happened, so the callback does not happen.
+TEST_F(IdleTimerTest, resetBackToBackTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(),
+                                                        mExpiredTimerCallback.getInvocable());
+    mIdleTimer->start();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+
+    mIdleTimer->reset();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+    mIdleTimer->reset();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+    mIdleTimer->reset();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+    mIdleTimer->reset();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+    // A single callback should be generated after 30ms
+    EXPECT_TRUE(
+            mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback + 30ms).has_value());
     mIdleTimer->stop();
     // Final quick check that no more callback were observed.
     EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+}
+
+TEST_F(IdleTimerTest, startNotCalledTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                        mExpiredTimerCallback.getInvocable());
+    // The start hasn't happened, so the callback does not happen.
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForCall(1us).has_value());
+    mIdleTimer->stop();
+    // Final quick check that no more callback were observed.
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
 }
 
 TEST_F(IdleTimerTest, idleTimerIdlesTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                        mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+
     // A callback should be generated after 3ms
     EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
     // After one event, it should be idle, and not generate another.
     EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
     // Once reset, it should generate another
     mIdleTimer->reset();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall(1ms).has_value());
     EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
     mIdleTimer->stop();
     // Final quick check that no more callback were observed.
     EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
 }
 
 TEST_F(IdleTimerTest, timeoutCallbackExecutionTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
-
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                        mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall(1us).has_value());
     EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
     mIdleTimer->stop();
 }
 
 TEST_F(IdleTimerTest, noCallbacksAfterStopAndResetTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                        mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall(1ms).has_value());
     EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+
     mIdleTimer->stop();
     clearPendingCallbacks();
     mIdleTimer->reset();
     EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
 }
 
 TEST_F(IdleTimerTest, noCallbacksAfterStopTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                        mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall(1ms).has_value());
+
     mIdleTimer->stop();
     clearPendingCallbacks();
     mIdleTimer->reset();
+
     // No more idle events should be observed
     EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
 }
 
 } // namespace
 } // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 26b6d0c..ec76538 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -82,7 +82,6 @@
             .WillRepeatedly(Return(mEventThreadConnection));
 
     mConnectionHandle = mScheduler->createConnection("appConnection", 16, ResyncCallback(),
-                                                     ResetIdleTimerCallback(),
                                                      impl::EventThread::InterceptVSyncsCallback());
     EXPECT_TRUE(mConnectionHandle != nullptr);
 }
@@ -103,8 +102,7 @@
     // exceptions, just gracefully continues.
     sp<IDisplayEventConnection> returnedValue;
     ASSERT_NO_FATAL_FAILURE(
-            returnedValue = mScheduler->createDisplayEventConnection(nullptr, ResyncCallback(),
-                                                                     ResetIdleTimerCallback()));
+            returnedValue = mScheduler->createDisplayEventConnection(nullptr, ResyncCallback()));
     EXPECT_TRUE(returnedValue == nullptr);
     EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr);
     EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr);
@@ -125,8 +123,7 @@
     sp<IDisplayEventConnection> returnedValue;
     ASSERT_NO_FATAL_FAILURE(
             returnedValue =
-                    mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback(),
-                                                             ResetIdleTimerCallback()));
+                    mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback()));
     EXPECT_TRUE(returnedValue == nullptr);
     EXPECT_TRUE(mScheduler->getEventThread(connectionHandle) == nullptr);
     EXPECT_TRUE(mScheduler->getEventConnection(connectionHandle) == nullptr);
@@ -155,8 +152,7 @@
     sp<IDisplayEventConnection> returnedValue;
     ASSERT_NO_FATAL_FAILURE(
             returnedValue =
-                    mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback(),
-                                                             ResetIdleTimerCallback()));
+                    mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback()));
     EXPECT_TRUE(returnedValue != nullptr);
     ASSERT_EQ(returnedValue, mEventThreadConnection);