Merge "CE: Unit test coverage for CompositionEngine::present()"
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
index 7f5177b..6efa4f7 100644
--- a/include/android/sharedmem.h
+++ b/include/android/sharedmem.h
@@ -21,7 +21,7 @@
 
 /**
  * @file sharedmem.h
- * @brief Shared memory buffers that can be shared across process.
+ * @brief Shared memory buffers that can be shared between processes.
  */
 
 #ifndef ANDROID_SHARED_MEMORY_H
@@ -61,11 +61,15 @@
  *
  * Use close() to release the shared memory region.
  *
+ * Use {@link android.os.ParcelFileDescriptor} to pass the file descriptor to
+ * another process. File descriptors may also be sent to other processes over a Unix domain
+ * socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and cmsg(3) man pages for more information.
+ *
  * Available since API level 26.
  *
  * \param name an optional name.
  * \param size size of the shared memory region
- * \return file descriptor that denotes the shared memory; error code on failure.
+ * \return file descriptor that denotes the shared memory; -1 and sets errno on failure, or -EINVAL if the error is that size was 0.
  */
 int ASharedMemory_create(const char *name, size_t size) __INTRODUCED_IN(26);
 
@@ -109,7 +113,7 @@
  * \param fd   file descriptor of the shared memory region.
  * \param prot any bitwise-or'ed combination of PROT_READ, PROT_WRITE, PROT_EXEC denoting
  *             updated access. Note access can only be removed, but not added back.
- * \return 0 for success, error code on failure.
+ * \return 0 for success, -1 and sets errno on failure.
  */
 int ASharedMemory_setProt(int fd, int prot) __INTRODUCED_IN(26);
 
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 2a4ded8..56d95a7 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -56,9 +56,11 @@
 /**
  * This interface has the stability of the system image.
  */
-void AIBinder_markSystemStability(AIBinder* binder);
+__attribute__((weak)) void AIBinder_markSystemStability(AIBinder* binder);
 
 static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) {
+    if (AIBinder_markSystemStability == nullptr) return;
+
     AIBinder_markSystemStability(binder);
 }
 
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 2859111..354703b 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -124,12 +124,8 @@
     return env;
 }
 
-int GraphicsEnv::getCanLoadSystemLibraries() {
-    if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
-        // Return an integer value since this crosses library boundaries
-        return 1;
-    }
-    return 0;
+bool GraphicsEnv::isDebuggable() {
+    return prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0;
 }
 
 void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path,
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 83448d4..c6dc1f8 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -33,8 +33,16 @@
 public:
     static GraphicsEnv& getInstance();
 
-    // Check if device is debuggable.
-    int getCanLoadSystemLibraries();
+    // Check if the process is debuggable. It returns false except in any of the
+    // following circumstances:
+    // 1. ro.debuggable=1 (global debuggable enabled).
+    // 2. android:debuggable="true" in the manifest for an individual app.
+    // 3. An app which explicitly calls prctl(PR_SET_DUMPABLE, 1).
+    // 4. GraphicsEnv calls prctl(PR_SET_DUMPABLE, 1) in the presence of
+    //    <meta-data android:name="com.android.graphics.injectLayers.enable"
+    //               android:value="true"/>
+    //    in the application manifest.
+    bool isDebuggable();
 
     /*
      * Apis for updatable driver
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
new file mode 100644
index 0000000..63e0734
--- /dev/null
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2015 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 "Choreographer"
+//#define LOG_NDEBUG 0
+
+#include <cinttypes>
+#include <queue>
+#include <thread>
+
+#include <android/choreographer.h>
+#include <androidfw/DisplayEventDispatcher.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <utils/Looper.h>
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+static inline const char* toString(bool value) {
+    return value ? "true" : "false";
+}
+
+struct FrameCallback {
+    AChoreographer_frameCallback callback;
+    AChoreographer_frameCallback64 callback64;
+    void* data;
+    nsecs_t dueTime;
+
+    inline bool operator<(const FrameCallback& rhs) const {
+        // Note that this is intentionally flipped because we want callbacks due sooner to be at
+        // the head of the queue
+        return dueTime > rhs.dueTime;
+    }
+};
+
+
+class Choreographer : public DisplayEventDispatcher, public MessageHandler {
+public:
+    void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+                                  AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
+
+    enum {
+        MSG_SCHEDULE_CALLBACKS = 0,
+        MSG_SCHEDULE_VSYNC = 1
+    };
+    virtual void handleMessage(const Message& message) override;
+
+    static Choreographer* getForThread();
+
+protected:
+    virtual ~Choreographer() = default;
+
+private:
+    explicit Choreographer(const sp<Looper>& looper);
+    Choreographer(const Choreographer&) = delete;
+
+    void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
+    void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+    void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+                               int32_t configId) override;
+
+    void scheduleCallbacks();
+
+    // Protected by mLock
+    std::priority_queue<FrameCallback> mCallbacks;
+
+    mutable Mutex mLock;
+
+    const sp<Looper> mLooper;
+    const std::thread::id mThreadId;
+};
+
+
+static thread_local Choreographer* gChoreographer;
+Choreographer* Choreographer::getForThread() {
+    if (gChoreographer == nullptr) {
+        sp<Looper> looper = Looper::getForThread();
+        if (!looper.get()) {
+            ALOGW("No looper prepared for thread");
+            return nullptr;
+        }
+        gChoreographer = new Choreographer(looper);
+        status_t result = gChoreographer->initialize();
+        if (result != OK) {
+            ALOGW("Failed to initialize");
+            return nullptr;
+        }
+    }
+    return gChoreographer;
+}
+
+Choreographer::Choreographer(const sp<Looper>& looper) :
+    DisplayEventDispatcher(looper), mLooper(looper), mThreadId(std::this_thread::get_id()) {
+}
+
+void Choreographer::postFrameCallbackDelayed(
+        AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    FrameCallback callback{cb, cb64, data, now + delay};
+    {
+        AutoMutex _l{mLock};
+        mCallbacks.push(callback);
+    }
+    if (callback.dueTime <= now) {
+        if (std::this_thread::get_id() != mThreadId) {
+            Message m{MSG_SCHEDULE_VSYNC};
+            mLooper->sendMessage(this, m);
+        } else {
+            scheduleVsync();
+        }
+    } else {
+        Message m{MSG_SCHEDULE_CALLBACKS};
+        mLooper->sendMessageDelayed(delay, this, m);
+    }
+}
+
+void Choreographer::scheduleCallbacks() {
+    AutoMutex _{mLock};
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    if (mCallbacks.top().dueTime <= now) {
+        ALOGV("choreographer %p ~ scheduling vsync", this);
+        scheduleVsync();
+        return;
+    }
+}
+
+// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
+// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
+// the internal display implicitly.
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
+    std::vector<FrameCallback> callbacks{};
+    {
+        AutoMutex _l{mLock};
+        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+        while (!mCallbacks.empty() && mCallbacks.top().dueTime < now) {
+            callbacks.push_back(mCallbacks.top());
+            mCallbacks.pop();
+        }
+    }
+    for (const auto& cb : callbacks) {
+        if (cb.callback64 != nullptr) {
+            cb.callback64(timestamp, cb.data);
+        } else if (cb.callback != nullptr) {
+            cb.callback(timestamp, cb.data);
+        }
+    }
+}
+
+void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
+    ALOGV("choreographer %p ~ received hotplug event (displayId=%"
+            ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.",
+            this, displayId, toString(connected));
+}
+
+void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId,
+                                          int32_t configId) {
+    ALOGV("choreographer %p ~ received config changed event (displayId=%"
+            ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%s), ignoring.",
+            this, displayId, toString(configId));
+}
+
+void Choreographer::handleMessage(const Message& message) {
+    switch (message.what) {
+    case MSG_SCHEDULE_CALLBACKS:
+        scheduleCallbacks();
+        break;
+    case MSG_SCHEDULE_VSYNC:
+        scheduleVsync();
+        break;
+    }
+}
+
+}
+
+/* Glue for the NDK interface */
+
+using android::Choreographer;
+
+static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
+    return reinterpret_cast<Choreographer*>(choreographer);
+}
+
+static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) {
+    return reinterpret_cast<AChoreographer*>(choreographer);
+}
+
+AChoreographer* AChoreographer_getInstance() {
+    return Choreographer_to_AChoreographer(Choreographer::getForThread());
+}
+
+void AChoreographer_postFrameCallback(AChoreographer* choreographer,
+        AChoreographer_frameCallback callback, void* data) {
+    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+            callback, nullptr, data, 0);
+}
+void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
+        AChoreographer_frameCallback callback, void* data, long delayMillis) {
+    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+            callback, nullptr, data, ms2ns(delayMillis));
+}
+void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
+        AChoreographer_frameCallback64 callback, void* data) {
+    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+            nullptr, callback, data, 0);
+}
+void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
+        AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) {
+    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+            nullptr, callback, data, ms2ns(delayMillis));
+}
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index 66ebdfd..45b935a 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -12,6 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+ndk_headers {
+    name: "libachoreographer_ndk_headers",
+    from: "include/android",
+    to: "android",
+    srcs: ["include/android/*.h"],
+    license: "NOTICE",
+}
+
 cc_library_headers {
     name: "libnativedisplay_headers",
     export_include_dirs: ["include"],
@@ -33,10 +41,12 @@
     ],
 
     srcs: [
+        "AChoreographer.cpp",
         "ADisplay.cpp",
     ],
 
     shared_libs: [
+        "libandroidfw",
         "libgui",
         "liblog",
         "libui",
diff --git a/libs/nativedisplay/MODULE_LICENSE_APACHE2 b/libs/nativedisplay/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/nativedisplay/MODULE_LICENSE_APACHE2
diff --git a/libs/nativedisplay/NOTICE b/libs/nativedisplay/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/nativedisplay/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/include/android/choreographer.h b/libs/nativedisplay/include/android/choreographer.h
similarity index 100%
rename from include/android/choreographer.h
rename to libs/nativedisplay/include/android/choreographer.h
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
new file mode 100644
index 0000000..3b29c18
--- /dev/null
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -0,0 +1,10 @@
+LIBNATIVEDISPLAY {
+  global:
+    AChoreographer_getInstance; # apex # introduced=30
+    AChoreographer_postFrameCallback; # apex # introduced=30
+    AChoreographer_postFrameCallbackDelayed; # apex # introduced=30
+    AChoreographer_postFrameCallback64; # apex # introduced=30
+    AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30
+  local:
+    *;
+};
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
index ac01dc8..44a1c0b 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -151,7 +151,7 @@
 const char kSystemLayerLibraryDir[] = "/data/local/debug/gles";
 
 std::string LayerLoader::GetDebugLayers() {
-    // Layers can be specified at the Java level in GraphicsEnvironemnt
+    // Layers can be specified at the Java level in GraphicsEnvironment
     // gpu_debug_layers_gles = layer1:layer2:layerN
     std::string debug_layers = android::GraphicsEnv::getInstance().getDebugLayersGLES();
 
@@ -337,7 +337,7 @@
 
     // Only enable the system search path for non-user builds
     std::string system_path;
-    if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+    if (android::GraphicsEnv::getInstance().isDebuggable()) {
         system_path = kSystemLayerLibraryDir;
     }
 
@@ -379,14 +379,12 @@
                 // any symbol dependencies will be resolved by system libraries. They
                 // can't safely use libc++_shared, for example. Which is one reason
                 // (among several) we only allow them in non-user builds.
-                void* handle = nullptr;
                 auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace();
                 if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) {
-                    bool native_bridge = false;
                     char* error_message = nullptr;
-                    handle = OpenNativeLibraryInNamespace(
-                        app_namespace, layer.c_str(), &native_bridge, &error_message);
-                    if (!handle) {
+                    dlhandle_ = OpenNativeLibraryInNamespace(
+                        app_namespace, layer.c_str(), &native_bridge_, &error_message);
+                    if (!dlhandle_) {
                         ALOGE("Failed to load layer %s with error: %s", layer.c_str(),
                               error_message);
                         android::NativeLoaderFreeErrorMessage(error_message);
@@ -394,11 +392,11 @@
                     }
 
                 } else {
-                    handle = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL);
+                    dlhandle_ = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL);
                 }
 
-                if (handle) {
-                    ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)handle,
+                if (dlhandle_) {
+                    ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)dlhandle_,
                           layers[i].c_str());
                 } else {
                     // If the layer is found but can't be loaded, try setenforce 0
@@ -411,8 +409,7 @@
                 std::string init_func = "AndroidGLESLayer_Initialize";
                 ALOGV("Looking for entrypoint %s", init_func.c_str());
 
-                layer_init_func LayerInit =
-                        reinterpret_cast<layer_init_func>(dlsym(handle, init_func.c_str()));
+                layer_init_func LayerInit = GetTrampoline<layer_init_func>(init_func.c_str());
                 if (LayerInit) {
                     ALOGV("Found %s for layer %s", init_func.c_str(), layer.c_str());
                     layer_init_.push_back(LayerInit);
@@ -425,8 +422,7 @@
                 std::string setup_func = "AndroidGLESLayer_GetProcAddress";
                 ALOGV("Looking for entrypoint %s", setup_func.c_str());
 
-                layer_setup_func LayerSetup =
-                        reinterpret_cast<layer_setup_func>(dlsym(handle, setup_func.c_str()));
+                layer_setup_func LayerSetup = GetTrampoline<layer_setup_func>(setup_func.c_str());
                 if (LayerSetup) {
                     ALOGV("Found %s for layer %s", setup_func.c_str(), layer.c_str());
                     layer_setup_.push_back(LayerSetup);
diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h
index e401b44..1e2783f 100644
--- a/opengl/libs/EGL/egl_layers.h
+++ b/opengl/libs/EGL/egl_layers.h
@@ -21,10 +21,15 @@
 #include <unordered_map>
 #include <vector>
 
-#include <EGL/egldefs.h>
+#include <android/dlext.h>
+#include <dlfcn.h>
 
+#include <EGL/egldefs.h>
 #include "egl_platform_entries.h"
 
+#include <nativebridge/native_bridge.h>
+#include <nativeloader/native_loader.h>
+
 typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
 
 namespace android {
@@ -54,10 +59,21 @@
     std::vector<layer_setup_func> layer_setup_;
 
 private:
-    LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0){};
+    LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0), dlhandle_(nullptr), native_bridge_(false){};
     bool layers_loaded_;
     bool initialized_;
     unsigned current_layer_;
+    void* dlhandle_;
+    bool native_bridge_;
+
+    template<typename Func = void*>
+    Func GetTrampoline(const char* name) const {
+        if (native_bridge_) {
+            return reinterpret_cast<Func>(android::NativeBridgeGetTrampoline(
+                dlhandle_, name, nullptr, 0));
+        }
+        return reinterpret_cast<Func>(dlsym(dlhandle_, name));
+    }
 };
 
 }; // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index dc61e49..6896da7 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -400,11 +400,9 @@
     ATRACE_CALL();
     // Add this buffer from our internal queue tracker
     { // Autolock scope
-        if (mFlinger->mUseSmart90ForVideo) {
-            const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
-            mFlinger->mScheduler->recordLayerHistory(this, presentTime,
-                                                     item.mHdrMetadata.validTypes != 0);
-        }
+        const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
+        const bool isHDR = item.mHdrMetadata.validTypes != 0;
+        mFlinger->mScheduler->recordLayerHistory(this, presentTime, isHDR);
 
         Mutex::Autolock lock(mQueueItemLock);
         // Reset the frame number tracker when we receive the first buffer after
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 5768edd..33cd0dc 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -247,11 +247,8 @@
                                            FrameTracer::FrameEvent::POST);
     mCurrentState.desiredPresentTime = desiredPresentTime;
 
-    if (mFlinger->mUseSmart90ForVideo) {
-        const nsecs_t presentTime = (desiredPresentTime == -1) ? 0 : desiredPresentTime;
-        mFlinger->mScheduler->recordLayerHistory(this, presentTime,
-                                                 mCurrentState.hdrMetadata.validTypes != 0);
-    }
+    const bool isHDR = mCurrentState.hdrMetadata.validTypes != 0;
+    mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime, isHDR);
 
     return true;
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index aa638b7..1953005 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -551,6 +551,10 @@
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
 
+    if (!getState().isEnabled) {
+        return;
+    }
+
     for (auto* layer : getOutputLayersOrderedByZ()) {
         layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame,
                                       refreshArgs.devOptForceClientComposition);
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 95ae888..7fce520 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -483,6 +483,31 @@
 }
 
 /*
+ * Output::updateAndWriteCompositionState()
+ */
+
+TEST_F(OutputTest, updateAndWriteCompositionState_takesEarlyOutIfNotEnabled) {
+    mOutput->editState().isEnabled = false;
+
+    CompositionRefreshArgs args;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputTest, updateAndWriteCompositionState_updatesLayers) {
+    mOutput->editState().isEnabled = true;
+    mock::OutputLayer* outputLayer = new StrictMock<mock::OutputLayer>();
+    mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(outputLayer));
+
+    EXPECT_CALL(*outputLayer, updateCompositionState(true, true)).Times(1);
+    EXPECT_CALL(*outputLayer, writeStateToHWC(true)).Times(1);
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = true;
+    args.devOptForceClientComposition = true;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+/*
  * Output::prepareFrame()
  */
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d5a9ae1..00d932d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -120,7 +120,10 @@
 
     mCallingPid = args.callingPid;
     mCallingUid = args.callingUid;
-    mFlinger->onLayerCreated(this);
+}
+
+void Layer::onFirstRef() {
+    mFlinger->onLayerFirstRef(this);
 }
 
 Layer::~Layer() {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index fb72391..bed5ad3 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -222,6 +222,8 @@
     explicit Layer(const LayerCreationArgs& args);
     virtual ~Layer();
 
+    void onFirstRef() override;
+
     int getWindowType() const { return mWindowType; }
 
     void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 15ac8ca..bd9aca1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -28,6 +28,7 @@
 namespace android {
 
 class Layer;
+class TestableScheduler;
 
 namespace scheduler {
 
@@ -59,6 +60,7 @@
 
 private:
     friend class LayerHistoryTest;
+    friend TestableScheduler;
 
     using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
     using LayerInfos = std::vector<LayerPair>;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index f3b0d56..6d9dd43 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -25,6 +25,8 @@
       : mLowRefreshRate(lowRefreshRate), mHighRefreshRate(highRefreshRate) {}
 
 void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+    lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
+
     // Buffers can come with a present time far in the future. That keeps them relevant.
     mLastUpdatedTime = std::max(lastPresentTime, now);
     mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index 4870a3b..a90d05e 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -17,6 +17,7 @@
 #include "OneShotTimer.h"
 
 #include <chrono>
+#include <sstream>
 #include <thread>
 
 namespace android {
@@ -98,7 +99,7 @@
             mTimeoutCallback();
         }
     }
-} // namespace scheduler
+}
 
 void OneShotTimer::reset() {
     {
@@ -108,5 +109,11 @@
     mCondition.notify_all();
 }
 
+std::string OneShotTimer::dump() const {
+    std::ostringstream stream;
+    stream << mInterval.count() << " ms";
+    return stream.str();
+}
+
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index 921631e..b005754 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -39,8 +39,6 @@
                  const TimeoutCallback& timeoutCallback);
     ~OneShotTimer();
 
-    const Interval& interval() const { return mInterval; }
-
     // Initializes and turns on the idle timer.
     void start();
     // Stops the idle timer and any held resources.
@@ -48,6 +46,8 @@
     // Resets the wakeup time and fires the reset callback.
     void reset();
 
+    std::string dump() const;
+
 private:
     // Enum to track in what state is the timer.
     enum class TimerState {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 71b3500..55fd603 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -20,6 +20,7 @@
 
 #include "Scheduler.h"
 
+#include <android-base/stringprintf.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
@@ -66,9 +67,11 @@
         mRefreshRateConfigs(refreshRateConfig) {
     using namespace sysprop;
 
-    char value[PROPERTY_VALUE_MAX];
-    property_get("debug.sf.set_idle_timer_ms", value, "0");
-    const int setIdleTimerMs = atoi(value);
+    if (property_get_bool("debug.sf.use_smart_90_for_video", 0) || use_smart_90_for_video(false)) {
+        mLayerHistory.emplace();
+    }
+
+    const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
 
     if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
         const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback
@@ -327,26 +330,28 @@
 }
 
 void Scheduler::registerLayer(Layer* layer) {
-    uint32_t defaultFps, performanceFps;
-    if (mRefreshRateConfigs.refreshRateSwitchingSupported()) {
-        defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
-        const auto type = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
-                ? RefreshRateType::DEFAULT
-                : RefreshRateType::PERFORMANCE;
-        performanceFps = mRefreshRateConfigs.getRefreshRateFromType(type).fps;
-    } else {
-        defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps;
-        performanceFps = defaultFps;
-    }
-    mLayerHistory.registerLayer(layer, defaultFps, performanceFps);
+    if (!mLayerHistory) return;
+
+    const auto type = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
+            ? RefreshRateType::DEFAULT
+            : RefreshRateType::PERFORMANCE;
+
+    const auto lowFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
+    const auto highFps = mRefreshRateConfigs.getRefreshRateFromType(type).fps;
+
+    mLayerHistory->registerLayer(layer, lowFps, highFps);
 }
 
 void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, bool isHDR) {
-    mLayerHistory.record(layer, presentTime, isHDR, systemTime());
+    if (mLayerHistory) {
+        mLayerHistory->record(layer, presentTime, isHDR, systemTime());
+    }
 }
 
-void Scheduler::updateFpsBasedOnContent() {
-    auto [refreshRate, isHDR] = mLayerHistory.summarize(systemTime());
+void Scheduler::chooseRefreshRateForContent() {
+    if (!mLayerHistory) return;
+
+    auto [refreshRate, isHDR] = mLayerHistory->summarize(systemTime());
     const uint32_t refreshRateRound = std::round(refreshRate);
     RefreshRateType newRefreshRateType;
     {
@@ -393,7 +398,9 @@
 
     // Touch event will boost the refresh rate to performance.
     // Clear Layer History to get fresh FPS detection
-    mLayerHistory.clear();
+    if (mLayerHistory) {
+        mLayerHistory->clear();
+    }
 }
 
 void Scheduler::setDisplayPowerState(bool normal) {
@@ -408,7 +415,9 @@
 
     // Display Power event will boost the refresh rate to performance.
     // Clear Layer History to get fresh FPS detection
-    mLayerHistory.clear();
+    if (mLayerHistory) {
+        mLayerHistory->clear();
+    }
 }
 
 void Scheduler::kernelIdleTimerCallback(TimerState state) {
@@ -446,15 +455,17 @@
 }
 
 void Scheduler::dump(std::string& result) const {
-    std::ostringstream stream;
-    if (mIdleTimer) {
-        stream << "+  Idle timer interval: " << mIdleTimer->interval().count() << " ms\n";
-    }
-    if (mTouchTimer) {
-        stream << "+  Touch timer interval: " << mTouchTimer->interval().count() << " ms\n";
-    }
+    using base::StringAppendF;
+    const char* const states[] = {"off", "on"};
 
-    result.append(stream.str());
+    const bool supported = mRefreshRateConfigs.refreshRateSwitchingSupported();
+    StringAppendF(&result, "+  Refresh rate switching: %s\n", states[supported]);
+    StringAppendF(&result, "+  Content detection: %s\n", states[mLayerHistory.has_value()]);
+
+    StringAppendF(&result, "+  Idle timer: %s\n",
+                  mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
+    StringAppendF(&result, "+  Touch timer: %s\n\n",
+                  mTouchTimer ? mTouchTimer->dump().c_str() : states[0]);
 }
 
 template <class T>
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index c983475..346896c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -105,8 +105,8 @@
     void registerLayer(Layer*);
     void recordLayerHistory(Layer*, nsecs_t presentTime, bool isHDR);
 
-    // Updates FPS based on the most content presented.
-    void updateFpsBasedOnContent();
+    // Detects content using layer history, and selects a matching refresh rate.
+    void chooseRefreshRateForContent();
 
     // Called by Scheduler to change refresh rate.
     void setChangeRefreshRateCallback(ChangeRefreshRateCallback&&);
@@ -184,8 +184,8 @@
     std::unique_ptr<DispSync> mPrimaryDispSync;
     std::unique_ptr<EventControlThread> mEventControlThread;
 
-    // Historical information about individual layers. Used for predicting the refresh rate.
-    scheduler::LayerHistory mLayerHistory;
+    // Used to choose refresh rate if content detection is enabled.
+    std::optional<scheduler::LayerHistory> mLayerHistory;
 
     // Whether to use idle timer callbacks that support the kernel timer.
     const bool mSupportKernelTimer;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 76fd51f..ec15bad 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -356,14 +356,6 @@
     auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
     mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
 
-    mUseSmart90ForVideo = use_smart_90_for_video(false);
-    property_get("debug.sf.use_smart_90_for_video", value, "0");
-
-    int int_value = atoi(value);
-    if (int_value) {
-        mUseSmart90ForVideo = true;
-    }
-
     property_get("debug.sf.luma_sampling", value, "1");
     mLumaSampling = atoi(value);
 
@@ -1643,11 +1635,7 @@
                 mGpuFrameMissedCount++;
             }
 
-            if (mUseSmart90ForVideo) {
-                // This call is made each time SF wakes up and creates a new frame. It is part
-                // of video detection feature.
-                mScheduler->updateFpsBasedOnContent();
-            }
+            mScheduler->chooseRefreshRateForContent();
 
             if (performSetActiveConfig()) {
                 break;
@@ -3947,9 +3935,6 @@
 
 void SurfaceFlinger::dumpVSync(std::string& result) const {
     mScheduler->dump(result);
-    StringAppendF(&result, "+  Refresh rate switching: %s\n",
-                  mRefreshRateConfigs->refreshRateSwitchingSupported() ? "on" : "off");
-    StringAppendF(&result, "+  Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off");
 
     mRefreshRateStats->dump(result);
     result.append("\n");
@@ -5502,7 +5487,7 @@
     return nullptr;
 }
 
-void SurfaceFlinger::onLayerCreated(Layer* layer) {
+void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
     mNumLayers++;
     mScheduler->registerLayer(layer);
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e7ad295..7d3da43 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -310,7 +310,7 @@
     bool authenticateSurfaceTextureLocked(
         const sp<IGraphicBufferProducer>& bufferProducer) const;
 
-    void onLayerCreated(Layer*);
+    void onLayerFirstRef(Layer*);
     void onLayerDestroyed(Layer*);
 
     TransactionCompletedThread& getTransactionCompletedThread() {
@@ -1093,7 +1093,6 @@
     /* ------------------------------------------------------------------------
      * Scheduler
      */
-    bool mUseSmart90ForVideo = false;
     std::unique_ptr<Scheduler> mScheduler;
     scheduler::ConnectionHandle mAppConnectionHandle;
     scheduler::ConnectionHandle mSfConnectionHandle;
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 143a7a0..32f997f 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -168,6 +168,7 @@
 
     std::unordered_set<HWC2::Capability> mDefaultCapabilities = {HWC2::Capability::SidebandStream};
 
+    bool mDisplayOff = false;
     TestableSurfaceFlinger mFlinger;
     sp<DisplayDevice> mDisplay;
     sp<DisplayDevice> mExternalDisplay;
@@ -534,69 +535,85 @@
     }
 
     static void setupHwcSetGeometryCallExpectations(CompositionTest* test) {
-        // TODO: Coverage of other values
-        EXPECT_CALL(*test->mComposer,
-                    setLayerBlendMode(HWC_DISPLAY, HWC_LAYER, LayerProperties::BLENDMODE))
-                .Times(1);
-        // TODO: Coverage of other values for origin
-        EXPECT_CALL(*test->mComposer,
-                    setLayerDisplayFrame(HWC_DISPLAY, HWC_LAYER,
-                                         IComposerClient::Rect({0, 0, LayerProperties::WIDTH,
-                                                                LayerProperties::HEIGHT})))
-                .Times(1);
-        EXPECT_CALL(*test->mComposer,
-                    setLayerPlaneAlpha(HWC_DISPLAY, HWC_LAYER, LayerProperties::COLOR[3]))
-                .Times(1);
-        // TODO: Coverage of other values
-        EXPECT_CALL(*test->mComposer, setLayerZOrder(HWC_DISPLAY, HWC_LAYER, 0u)).Times(1);
-        // TODO: Coverage of other values
-        EXPECT_CALL(*test->mComposer, setLayerInfo(HWC_DISPLAY, HWC_LAYER, 0u, 0u)).Times(1);
+        if (!test->mDisplayOff) {
+            // TODO: Coverage of other values
+            EXPECT_CALL(*test->mComposer,
+                        setLayerBlendMode(HWC_DISPLAY, HWC_LAYER, LayerProperties::BLENDMODE))
+                    .Times(1);
+            // TODO: Coverage of other values for origin
+            EXPECT_CALL(*test->mComposer,
+                        setLayerDisplayFrame(HWC_DISPLAY, HWC_LAYER,
+                                             IComposerClient::Rect({0, 0, LayerProperties::WIDTH,
+                                                                    LayerProperties::HEIGHT})))
+                    .Times(1);
+            EXPECT_CALL(*test->mComposer,
+                        setLayerPlaneAlpha(HWC_DISPLAY, HWC_LAYER, LayerProperties::COLOR[3]))
+                    .Times(1);
+            // TODO: Coverage of other values
+            EXPECT_CALL(*test->mComposer, setLayerZOrder(HWC_DISPLAY, HWC_LAYER, 0u)).Times(1);
+            // TODO: Coverage of other values
+            EXPECT_CALL(*test->mComposer, setLayerInfo(HWC_DISPLAY, HWC_LAYER, 0u, 0u)).Times(1);
 
-        // These expectations retire on saturation as the code path these
-        // expectations are for appears to make an extra call to them.
-        // TODO: Investigate this extra call
-        EXPECT_CALL(*test->mComposer, setLayerTransform(HWC_DISPLAY, HWC_LAYER, DEFAULT_TRANSFORM))
-                .Times(AtLeast(1))
-                .RetiresOnSaturation();
+            // These expectations retire on saturation as the code path these
+            // expectations are for appears to make an extra call to them.
+            // TODO: Investigate this extra call
+            EXPECT_CALL(*test->mComposer,
+                        setLayerTransform(HWC_DISPLAY, HWC_LAYER, DEFAULT_TRANSFORM))
+                    .Times(AtLeast(1))
+                    .RetiresOnSaturation();
+        }
     }
 
     static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
-                                       IComposerClient::FRect({0.f, 0.f, LayerProperties::WIDTH,
-                                                               LayerProperties::HEIGHT})))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
+                                           IComposerClient::FRect({0.f, 0.f, LayerProperties::WIDTH,
+                                                                   LayerProperties::HEIGHT})))
+                    .Times(1);
+        }
     }
 
     static void setupHwcSetSourceCropColorCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
-                                       IComposerClient::FRect({0.f, 0.f, 0.f, 0.f})))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
+                                           IComposerClient::FRect({0.f, 0.f, 0.f, 0.f})))
+                    .Times(1);
+        }
     }
 
     static void setupHwcSetPerFrameCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerVisibleRegion(HWC_DISPLAY, HWC_LAYER,
-                                          std::vector<IComposerClient::Rect>({IComposerClient::Rect(
-                                                  {0, 0, LayerProperties::WIDTH,
-                                                   LayerProperties::HEIGHT})})))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerVisibleRegion(HWC_DISPLAY, HWC_LAYER,
+                                              std::vector<IComposerClient::Rect>(
+                                                      {IComposerClient::Rect(
+                                                              {0, 0, LayerProperties::WIDTH,
+                                                               LayerProperties::HEIGHT})})))
+                    .Times(1);
+        }
     }
 
     static void setupHwcSetPerFrameColorCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _))
+                    .Times(1);
 
-        // TODO: use COLOR
-        EXPECT_CALL(*test->mComposer,
-                    setLayerColor(HWC_DISPLAY, HWC_LAYER,
-                                  IComposerClient::Color({0xff, 0xff, 0xff, 0xff})))
-                .Times(1);
+            // TODO: use COLOR
+            EXPECT_CALL(*test->mComposer,
+                        setLayerColor(HWC_DISPLAY, HWC_LAYER,
+                                      IComposerClient::Color({0xff, 0xff, 0xff, 0xff})))
+                    .Times(1);
+        }
     }
 
     static void setupHwcSetPerFrameBufferCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
-        EXPECT_CALL(*test->mComposer, setLayerBuffer(HWC_DISPLAY, HWC_LAYER, _, _, _)).Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _))
+                    .Times(1);
+            EXPECT_CALL(*test->mComposer, setLayerBuffer(HWC_DISPLAY, HWC_LAYER, _, _, _)).Times(1);
+        }
 
         setupBufferLayerPostFrameCallExpectations(test);
     }
@@ -793,6 +810,9 @@
 
         sp<L> layer = factory();
 
+        // Layer should be registered with scheduler.
+        EXPECT_EQ(1, test->mFlinger.scheduler()->layerHistorySize());
+
         Mock::VerifyAndClear(test->mComposer);
         Mock::VerifyAndClear(test->mRenderEngine);
         Mock::VerifyAndClear(test->mMessageQueue);
@@ -828,6 +848,10 @@
 
         test->mDisplay->getCompositionDisplay()->clearOutputLayers();
         test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
+
+        // Layer should be unregistered with scheduler.
+        test->mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
+        EXPECT_EQ(0, test->mFlinger.scheduler()->layerHistorySize());
     }
 };
 
@@ -940,9 +964,11 @@
     static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(CompositionType);
 
     static void setupHwcSetCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, CompositionType))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, CompositionType))
+                    .Times(1);
+        }
     }
 
     static void setupHwcGetCallExpectations(CompositionTest* test) {
@@ -956,9 +982,11 @@
     static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(FinalCompositionType);
 
     static void setupHwcSetCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, InitialCompositionType))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, InitialCompositionType))
+                    .Times(1);
+        }
     }
 
     static void setupHwcGetCallExpectations(CompositionTest* test) {
@@ -1341,6 +1369,7 @@
  */
 
 TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyGeometry) {
+    mDisplayOff = true;
     displayRefreshCompositionDirtyGeometry<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
             KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
@@ -1348,6 +1377,7 @@
 }
 
 TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyFrame) {
+    mDisplayOff = true;
     displayRefreshCompositionDirtyFrame<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
             KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
@@ -1355,6 +1385,7 @@
 }
 
 TEST_F(CompositionTest, displayOffREComposedNormalBufferLayer) {
+    mDisplayOff = true;
     displayRefreshCompositionDirtyFrame<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
             ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 9a962bc..e93d31e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -29,10 +29,10 @@
 
     LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
 
-    LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
-    const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
+    LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
+    const LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
 
-    size_t layerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mLayerInfos.size(); }
+    size_t layerCount() const { return mScheduler->layerHistorySize(); }
     size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
 
     size_t frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index ae6aa89..40c00c4 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -21,6 +21,7 @@
 
 #include "Scheduler/DispSync.h"
 #include "Scheduler/EventThread.h"
+#include "Scheduler/LayerHistory.h"
 #include "Scheduler/Scheduler.h"
 
 namespace android {
@@ -28,18 +29,26 @@
 class TestableScheduler : public Scheduler {
 public:
     explicit TestableScheduler(const scheduler::RefreshRateConfigs& configs)
-          : Scheduler([](bool) {}, configs) {}
+          : Scheduler([](bool) {}, configs) {
+        mLayerHistory.emplace();
+    }
 
     TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
                       std::unique_ptr<EventControlThread> eventControlThread,
                       const scheduler::RefreshRateConfigs& configs)
-          : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs) {}
+          : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs) {
+        mLayerHistory.emplace();
+    }
 
     // Used to inject mock event thread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
         return Scheduler::createConnection(std::move(eventThread));
     }
 
+    size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
+        return mLayerHistory->mLayerInfos.size();
+    }
+
     /* ------------------------------------------------------------------------
      * Read-write access to private data to set up preconditions and assert
      * post-conditions.
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 4725344..7e988a1 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -124,7 +124,8 @@
     };
 
     void AddImplicitLayers() {
-        if (!is_instance_ || !driver::Debuggable())
+        if (!is_instance_ ||
+            !android::GraphicsEnv::getInstance().isDebuggable())
             return;
 
         GetLayersFromSettings();
@@ -370,7 +371,8 @@
 
    private:
     bool EnableDebugCallback() const {
-        return (is_instance_ && driver::Debuggable() &&
+        return (is_instance_ &&
+                android::GraphicsEnv::getInstance().isDebuggable() &&
                 property_get_bool("debug.vulkan.enable_callback", false));
     }
 
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 0fb3a29..90a73e2 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -753,10 +753,6 @@
 
 }  // anonymous namespace
 
-bool Debuggable() {
-    return prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0;
-}
-
 bool OpenHAL() {
     return Hal::Open();
 }
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 7edadea..23c717c 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -100,7 +100,6 @@
     DeviceDriverTable driver;
 };
 
-bool Debuggable();
 bool OpenHAL();
 const VkAllocationCallbacks& GetDefaultAllocator();
 
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index 22f92d6..a14fed2 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -461,8 +461,7 @@
 void DiscoverLayers() {
     ATRACE_CALL();
 
-    if (property_get_bool("ro.debuggable", false) &&
-        prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+    if (android::GraphicsEnv::getInstance().isDebuggable()) {
         DiscoverLayersInPathList(kSystemLayerLibraryDir);
     }
     if (!android::GraphicsEnv::getInstance().getLayerPaths().empty())