Merge "SF: Add tests for hole punch" into sc-dev
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 307e21c..4b64203 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -60,9 +60,6 @@
       ]
     },
     {
-      "name": "libsurfaceflinger_unittest"
-    },
-    {
       "name": "CtsGraphicsTestCases",
       "options": [
         {
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index a546236..3f180d9 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -47,6 +47,9 @@
         "libutils",
         "server_configurable_flags",
     ],
+    static_libs: [
+        "libasync_safe",
+    ],
     export_shared_lib_headers: [
         "libbinder",
     ],
@@ -250,6 +253,7 @@
     ],
 
     static_libs: [
+        "libasync_safe",
         "libdiskusage",
         "libotapreoptparameters",
     ],
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 0cf50a3..204953c 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -36,6 +36,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <async_safe/log.h>
 #include <cutils/fs.h>
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
@@ -727,7 +728,8 @@
 
         if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) {
             if (errno != EWOULDBLOCK) {
-                PLOG(WARNING) << "Error locking profile " << package_name;
+                async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Error locking profile %s: %d",
+                        package_name.c_str(), errno);
             }
             // This implies that the app owning this profile is running
             // (and has acquired the lock).
@@ -735,13 +737,15 @@
             // The app never acquires the lock for the reference profiles of primary apks.
             // Only dex2oat from installd will do that. Since installd is single threaded
             // we should not see this case. Nevertheless be prepared for it.
-            PLOG(WARNING) << "Failed to flock " << package_name;
+            async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Failed to flock %s: %d",
+                    package_name.c_str(), errno);
             return false;
         }
 
         bool truncated = ftruncate(out_fd.get(), 0) == 0;
         if (!truncated) {
-            PLOG(WARNING) << "Could not truncate " << package_name;
+            async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Could not truncate %s: %d",
+                    package_name.c_str(), errno);
         }
 
         // Copy over data.
@@ -755,7 +759,8 @@
             write(out_fd.get(), buffer, bytes);
         }
         if (flock(out_fd.get(), LOCK_UN) != 0) {
-            PLOG(WARNING) << "Error unlocking profile " << package_name;
+            async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Error unlocking profile %s: %d",
+                    package_name.c_str(), errno);
         }
         // Use _exit since we don't want to run the global destructors in the child.
         // b/62597429
@@ -1513,7 +1518,8 @@
 
         // Validate the path structure.
         if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) {
-            LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+            async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                    "Could not validate secondary dex path %s", dex_path.c_str());
             _exit(kSecondaryDexDexoptAnalyzerSkippedValidatePath);
         }
 
@@ -1809,7 +1815,8 @@
         drop_capabilities(uid);
 
         if (flock(out_oat.fd(), LOCK_EX | LOCK_NB) != 0) {
-            PLOG(ERROR) << "flock(" << out_oat.path() << ") failed";
+            async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "flock(%s) failed",
+                    out_oat.path().c_str());
             _exit(DexoptReturnCodes::kFlock);
         }
 
@@ -1904,7 +1911,8 @@
         const char* volume_uuid_cstr = volume_uuid ? volume_uuid->c_str() : nullptr;
         if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr,
                 uid, storage_flag)) {
-            LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+            async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                    "Could not validate secondary dex path %s", dex_path.c_str());
             _exit(kReconcileSecondaryDexValidationError);
         }
 
@@ -1917,7 +1925,8 @@
             case kSecondaryDexAccessIOError: _exit(kReconcileSecondaryDexAccessIOError);
             case kSecondaryDexAccessPermissionError: _exit(kReconcileSecondaryDexValidationError);
             default:
-                LOG(ERROR) << "Unexpected result from check_secondary_dex_access: " << access_check;
+                async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                        "Unexpected result from check_secondary_dex_access: %d", access_check);
                 _exit(kReconcileSecondaryDexValidationError);
         }
 
@@ -1930,7 +1939,7 @@
             std::string error_msg;
             if (!create_secondary_dex_oat_layout(
                     dex_path,isas[i], oat_dir, oat_isa_dir, oat_path, &error_msg)) {
-                LOG(ERROR) << error_msg;
+                async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "%s", error_msg.c_str());
                 _exit(kReconcileSecondaryDexValidationError);
             }
 
@@ -1957,7 +1966,8 @@
             result = rmdir_if_empty(oat_dir) && result;
         }
         if (!result) {
-            PLOG(ERROR) << "Failed to clean secondary dex artifacts for location " << dex_path;
+            async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                    "Could not validate secondary dex path %s", dex_path.c_str());
         }
         _exit(result ? kReconcileSecondaryDexCleanedUp : kReconcileSecondaryDexAccessIOError);
     }
@@ -2030,7 +2040,8 @@
         pipe_read.reset();
 
         if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr, uid, storage_flag)) {
-            LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+            async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                    "Could not validate secondary dex path %s", dex_path.c_str());
             _exit(DexoptReturnCodes::kHashValidatePath);
         }
 
@@ -2041,6 +2052,8 @@
                 _exit(0);
             }
             PLOG(ERROR) << "Failed to open secondary dex " << dex_path;
+            async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                    "Failed to open secondary dex %s: %d", dex_path.c_str(), errno);
             _exit(DexoptReturnCodes::kHashOpenPath);
         }
 
@@ -2053,7 +2066,8 @@
             if (bytes_read == 0) {
                 break;
             } else if (bytes_read == -1) {
-                PLOG(ERROR) << "Failed to read secondary dex " << dex_path;
+                async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                        "Failed to read secondary dex %s: %d", dex_path.c_str(), errno);
                 _exit(DexoptReturnCodes::kHashReadDex);
             }
 
diff --git a/cmds/installd/file_parsing.h b/cmds/installd/file_parsing.h
index 3e2f815..88801ca 100644
--- a/cmds/installd/file_parsing.h
+++ b/cmds/installd/file_parsing.h
@@ -19,18 +19,14 @@
 
 #include <fstream>
 #include <functional>
-#include <string>
+#include <string_view>
+#include "android-base/unique_fd.h"
 
 namespace android {
 namespace installd {
 
-bool ParseFile(const std::string& strFile, std::function<bool (const std::string&)> parse) {
-    std::ifstream input_stream(strFile);
-
-    if (!input_stream.is_open()) {
-        return false;
-    }
-
+template<typename Func>
+bool ParseFile(std::istream& input_stream, Func parse) {
     while (!input_stream.eof()) {
         // Read the next line.
         std::string line;
@@ -54,6 +50,15 @@
     return true;
 }
 
+template<typename Func>
+bool ParseFile(std::string_view str_file, Func parse) {
+  std::ifstream ifs(str_file);
+  if (!ifs.is_open()) {
+    return false;
+  }
+  return ParseFile(ifs, parse);
+}
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index ed31ad9..6aa32b8 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -26,6 +26,7 @@
 #include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/stat.h>
+#include <sys/mman.h>
 
 #include <android-base/logging.h>
 #include <android-base/macros.h>
@@ -36,6 +37,7 @@
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
 
+#include "android-base/file.h"
 #include "dexopt.h"
 #include "file_parsing.h"
 #include "globals.h"
@@ -195,38 +197,63 @@
         //   export NAME VALUE
         // For simplicity, don't respect string quotation. The values we are interested in can be
         // encoded without them.
-        // init.environ.rc and etc/classpath have the same format for
-        // environment variable exports and can be matched by the same regex.
+        //
+        // init.environ.rc and derive_classpath all have the same format for
+        // environment variable exports (since they are all meant to be read by
+        // init) and can be matched by the same regex.
+
+        std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)");
+        auto parse_results = [&](auto& input) {
+          ParseFile(input, [&](const std::string& line) {
+              std::smatch export_match;
+              if (!std::regex_match(line, export_match, export_regex)) {
+                  return true;
+              }
+
+              if (export_match.size() != 3) {
+                  return true;
+              }
+
+              std::string name = export_match[1].str();
+              std::string value = export_match[2].str();
+
+              system_properties_.SetProperty(name, value);
+
+              return true;
+          });
+        };
+
         // TODO Just like with the system-properties above we really should have
         // common code between init and otapreopt to deal with reading these
         // things. See b/181182967
+        // There have been a variety of places the various env-vars have been
+        // over the years.  Expand or reduce this list as needed.
         static constexpr const char* kEnvironmentVariableSources[] = {
-                "/init.environ.rc", "/etc/classpath"
+                "/init.environ.rc",
         };
-
-        std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)");
+        // First get everything from the static files.
         for (const char* env_vars_file : kEnvironmentVariableSources) {
-            bool parse_result = ParseFile(env_vars_file, [&](const std::string& line) {
-                std::smatch export_match;
-                if (!std::regex_match(line, export_match, export_regex)) {
-                    return true;
-                }
-
-                if (export_match.size() != 3) {
-                    return true;
-                }
-
-                std::string name = export_match[1].str();
-                std::string value = export_match[2].str();
-
-                system_properties_.SetProperty(name, value);
-
-                return true;
-            });
-            if (!parse_result) {
-                return false;
-            }
+          parse_results(env_vars_file);
         }
+
+        // Next get everything from derive_classpath, since we're already in the
+        // chroot it will get the new versions of any dependencies.
+        {
+          android::base::unique_fd fd(memfd_create("derive_classpath_temp", MFD_CLOEXEC));
+          if (!fd.ok()) {
+            LOG(ERROR) << "Unable to create fd for derive_classpath";
+            return false;
+          }
+          std::string memfd_file = StringPrintf("/proc/%d/fd/%d", getpid(), fd.get());
+          std::string error_msg;
+          if (!Exec({"/apex/com.android.sdkext/bin/derive_classpath", memfd_file}, &error_msg)) {
+            PLOG(ERROR) << "Running derive_classpath failed: " << error_msg;
+            return false;
+          }
+          std::ifstream ifs(memfd_file);
+          parse_results(ifs);
+        }
+
         if (system_properties_.GetProperty(kAndroidDataPathPropertyName) == nullptr) {
             return false;
         }
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 83f01de..c62734a 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -275,6 +275,7 @@
     static constexpr const std::string_view kRequiredApexs[] = {
       "com.android.art",
       "com.android.runtime",
+      "com.android.sdkext",  // For derive_classpath
     };
     std::array<bool, arraysize(kRequiredApexs)> found_apexs{ false, false };
     DIR* apex_dir = opendir("/apex");
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index f67ab81..7082017 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -20,6 +20,7 @@
         "libcutils",
     ],
     static_libs: [
+        "libasync_safe",
         "libdiskusage",
         "libinstalld",
         "liblog",
@@ -44,6 +45,7 @@
         "server_configurable_flags",
     ],
     static_libs: [
+        "libasync_safe",
         "libdiskusage",
         "libinstalld",
         "liblog",
@@ -84,6 +86,7 @@
         "server_configurable_flags",
     ],
     static_libs: [
+        "libasync_safe",
         "libdiskusage",
         "libinstalld",
         "liblog",
@@ -124,6 +127,7 @@
         "server_configurable_flags",
     ],
     static_libs: [
+        "libasync_safe",
         "libdiskusage",
         "libinstalld",
         "liblog",
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index b429fb3..90db509 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -481,7 +481,12 @@
           name.c_str());
 
     std::thread([=] {
-        (void)base::SetProperty("ctl.interface_start", "aidl/" + name);
+        if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) {
+            LOG(INFO) << "Tried to start aidl service " << name
+                      << " as a lazy service, but was unable to. Usually this happens when a "
+                         "service is not installed, but if the service is intended to be used as a "
+                         "lazy service, then it may be configured incorrectly.";
+        }
     }).detach();
 }
 
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 8d1bf99..c3d3a4b 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -739,6 +739,9 @@
  * skipping frames in an image with such frames may not produce the correct
  * results.
  *
+ * Only supported by {@link ANDROID_BITMAP_FORMAT_RGBA_8888} and
+ * {@link ANDROID_BITMAP_FORMAT_RGBA_F16}.
+ *
  * @param decoder an {@link AImageDecoder} object.
  * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
  *         indicating the reason for the failure.
@@ -747,6 +750,8 @@
  * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder
  *   represents an image that is not animated (see
  *   {@link AImageDecoder_isAnimated}) or the AImageDecoder is null.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE): The requested
+ *   {@link AndroidBitmapFormat} does not support animation.
  * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The input appears
  *   to be truncated. The client must call {@link AImageDecoder_rewind}
  *   before calling {@link AImageDecoder_decodeImage} again.
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index b7eafcd..f6c2e55 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -534,11 +534,12 @@
  *
  * \param compatibility The frame rate compatibility of this surface. The compatibility value may
  * influence the system's choice of display frame rate. To specify a compatibility use the
- * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum.
+ * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum. This parameter is ignored when frameRate is 0.
  *
- * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
- * A seamless transition is one that doesn't have any visual interruptions, such as a black
- * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this
+ * surface should be seamless. A seamless transition is one that doesn't have any visual
+ * interruptions, such as a black screen for a second or two. See the
+ * ANATIVEWINDOW_CHANGE_FRAME_RATE_* values. This parameter is ignored when frameRate is 0.
  *
  * Available since API level 31.
  */
diff --git a/libs/binder/PermissionCache.cpp b/libs/binder/PermissionCache.cpp
index 6eae5ef..670fd55 100644
--- a/libs/binder/PermissionCache.cpp
+++ b/libs/binder/PermissionCache.cpp
@@ -109,5 +109,10 @@
     return granted;
 }
 
+void PermissionCache::purgeCache() {
+    PermissionCache& pc(PermissionCache::getInstance());
+    pc.purge();
+}
+
 // ---------------------------------------------------------------------------
 } // namespace android
diff --git a/libs/binder/RpcConnection.cpp b/libs/binder/RpcConnection.cpp
index 1388a80..ee5f508 100644
--- a/libs/binder/RpcConnection.cpp
+++ b/libs/binder/RpcConnection.cpp
@@ -19,6 +19,7 @@
 #include <binder/RpcConnection.h>
 
 #include <arpa/inet.h>
+#include <inttypes.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
@@ -45,9 +46,55 @@
 
 namespace android {
 
+using base::borrowed_fd;
 using base::unique_fd;
 using AddrInfo = std::unique_ptr<addrinfo, decltype(&freeaddrinfo)>;
 
+namespace {
+bool checkSockaddrSize(const char* name, size_t actual, size_t expected) {
+    if (actual >= expected) return true;
+    ALOGW("getSockaddrPort: family is %s but size is %zu < %zu", name, actual, expected);
+    return false;
+}
+
+// Get the port number of |storage| for certain families. Requires storage->sa_family to be
+// set to a known family; otherwise, return nullopt.
+std::optional<unsigned int> getSockaddrPort(const sockaddr* storage, socklen_t len) {
+    switch (storage->sa_family) {
+        case AF_INET: {
+            if (!checkSockaddrSize("INET", len, sizeof(sockaddr_in))) return std::nullopt;
+            auto inetStorage = reinterpret_cast<const sockaddr_in*>(storage);
+            return ntohs(inetStorage->sin_port);
+        }
+        default: {
+            uint16_t family = storage->sa_family;
+            ALOGW("Don't know how to infer port for family %" PRIu16, family);
+            return std::nullopt;
+        }
+    }
+}
+
+std::optional<unsigned int> getSocketPort(borrowed_fd socketfd,
+                                          const RpcConnection::SocketAddress& socketAddress) {
+    sockaddr_storage storage{};
+    socklen_t len = sizeof(storage);
+    auto storagePtr = reinterpret_cast<sockaddr*>(&storage);
+    if (0 != getsockname(socketfd.get(), storagePtr, &len)) {
+        int savedErrno = errno;
+        ALOGE("Could not getsockname at %s: %s", socketAddress.toString().c_str(),
+              strerror(savedErrno));
+        return std::nullopt;
+    }
+
+    // getsockname does not fill in family, but getSockaddrPort() needs it.
+    if (storage.ss_family == AF_UNSPEC) {
+        storage.ss_family = socketAddress.addr()->sa_family;
+    }
+    return getSockaddrPort(storagePtr, len);
+}
+
+} // namespace
+
 RpcConnection::SocketAddress::~SocketAddress() {}
 
 RpcConnection::RpcConnection() {
@@ -92,8 +139,8 @@
     return setupSocketServer(UnixSocketAddress(path));
 }
 
-bool RpcConnection::addUnixDomainClient(const char* path) {
-    return addSocketClient(UnixSocketAddress(path));
+bool RpcConnection::setupUnixDomainClient(const char* path) {
+    return setupSocketClient(UnixSocketAddress(path));
 }
 
 #ifdef __BIONIC__
@@ -124,30 +171,27 @@
     return setupSocketServer(VsockSocketAddress(kAnyCid, port));
 }
 
-bool RpcConnection::addVsockClient(unsigned int cid, unsigned int port) {
-    return addSocketClient(VsockSocketAddress(cid, port));
+bool RpcConnection::setupVsockClient(unsigned int cid, unsigned int port) {
+    return setupSocketClient(VsockSocketAddress(cid, port));
 }
 
 #endif // __BIONIC__
 
-class SocketAddressImpl : public RpcConnection::SocketAddress {
+class InetSocketAddress : public RpcConnection::SocketAddress {
 public:
-    SocketAddressImpl(const sockaddr* addr, size_t size, const String8& desc)
-          : mAddr(addr), mSize(size), mDesc(desc) {}
+    InetSocketAddress(const sockaddr* sockAddr, size_t size, const char* addr, unsigned int port)
+          : mSockAddr(sockAddr), mSize(size), mAddr(addr), mPort(port) {}
     [[nodiscard]] std::string toString() const override {
-        return std::string(mDesc.c_str(), mDesc.size());
+        return String8::format("%s:%u", mAddr, mPort).c_str();
     }
-    [[nodiscard]] const sockaddr* addr() const override { return mAddr; }
+    [[nodiscard]] const sockaddr* addr() const override { return mSockAddr; }
     [[nodiscard]] size_t addrSize() const override { return mSize; }
-    void set(const sockaddr* addr, size_t size) {
-        mAddr = addr;
-        mSize = size;
-    }
 
 private:
-    const sockaddr* mAddr = nullptr;
-    size_t mSize = 0;
-    String8 mDesc;
+    const sockaddr* mSockAddr;
+    size_t mSize;
+    const char* mAddr;
+    unsigned int mPort;
 };
 
 AddrInfo GetAddrInfo(const char* addr, unsigned int port) {
@@ -169,26 +213,39 @@
     return AddrInfo(aiStart, &freeaddrinfo);
 }
 
-bool RpcConnection::setupInetServer(unsigned int port) {
-    auto aiStart = GetAddrInfo("127.0.0.1", port);
+bool RpcConnection::setupInetServer(unsigned int port, unsigned int* assignedPort) {
+    const char* kAddr = "127.0.0.1";
+
+    if (assignedPort != nullptr) *assignedPort = 0;
+    auto aiStart = GetAddrInfo(kAddr, port);
     if (aiStart == nullptr) return false;
-    SocketAddressImpl socketAddress(nullptr, 0, String8::format("127.0.0.1:%u", port));
     for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
-        socketAddress.set(ai->ai_addr, ai->ai_addrlen);
-        if (setupSocketServer(socketAddress)) return true;
+        InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, kAddr, port);
+        if (!setupSocketServer(socketAddress)) {
+            continue;
+        }
+        auto realPort = getSocketPort(mServer.get(), socketAddress);
+        LOG_ALWAYS_FATAL_IF(!realPort.has_value(), "Unable to get port number after setting up %s",
+                            socketAddress.toString().c_str());
+        LOG_ALWAYS_FATAL_IF(port != 0 && *realPort != port,
+                            "Requesting inet server on %s but it is set up on %u.",
+                            socketAddress.toString().c_str(), *realPort);
+        if (assignedPort != nullptr) {
+            *assignedPort = *realPort;
+        }
+        return true;
     }
-    ALOGE("None of the socket address resolved for 127.0.0.1:%u can be set up as inet server.",
+    ALOGE("None of the socket address resolved for %s:%u can be set up as inet server.", kAddr,
           port);
     return false;
 }
 
-bool RpcConnection::addInetClient(const char* addr, unsigned int port) {
+bool RpcConnection::setupInetClient(const char* addr, unsigned int port) {
     auto aiStart = GetAddrInfo(addr, port);
     if (aiStart == nullptr) return false;
-    SocketAddressImpl socketAddress(nullptr, 0, String8::format("%s:%u", addr, port));
     for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
-        socketAddress.set(ai->ai_addr, ai->ai_addrlen);
-        if (addSocketClient(socketAddress)) return true;
+        InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, addr, port);
+        if (setupSocketClient(socketAddress)) return true;
     }
     ALOGE("None of the socket address resolved for %s:%u can be added as inet client.", addr, port);
     return false;
@@ -211,6 +268,11 @@
     return state()->getRootObject(socket.fd(), sp<RpcConnection>::fromExisting(this));
 }
 
+status_t RpcConnection::getMaxThreads(size_t* maxThreads) {
+    ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT);
+    return state()->getMaxThreads(socket.fd(), sp<RpcConnection>::fromExisting(this), maxThreads);
+}
+
 status_t RpcConnection::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags) {
     ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this),
@@ -291,7 +353,39 @@
     return true;
 }
 
-bool RpcConnection::addSocketClient(const SocketAddress& addr) {
+bool RpcConnection::setupSocketClient(const SocketAddress& addr) {
+    {
+        std::lock_guard<std::mutex> _l(mSocketMutex);
+        LOG_ALWAYS_FATAL_IF(mClients.size() != 0,
+                            "Must only setup connection once, but already has %zu clients",
+                            mClients.size());
+    }
+
+    if (!setupOneSocketClient(addr)) return false;
+
+    // TODO(b/185167543): we should add additional connections dynamically
+    // instead of all at once.
+    // TODO(b/186470974): first risk of blocking
+    size_t numThreadsAvailable;
+    if (status_t status = getMaxThreads(&numThreadsAvailable); status != OK) {
+        ALOGE("Could not get max threads after initial connection to %s: %s",
+              addr.toString().c_str(), statusToString(status).c_str());
+        return false;
+    }
+
+    // we've already setup one client
+    for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
+        // TODO(b/185167543): avoid race w/ accept4 not being called on server
+        for (size_t tries = 0; tries < 5; tries++) {
+            if (setupOneSocketClient(addr)) break;
+            usleep(10000);
+        }
+    }
+
+    return true;
+}
+
+bool RpcConnection::setupOneSocketClient(const SocketAddress& addr) {
     unique_fd serverFd(
             TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
     if (serverFd == -1) {
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 1fa37ba..8f2805f 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -19,6 +19,7 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
+#include <thread>
 #include <vector>
 
 #include <binder/Parcel.h>
@@ -30,8 +31,6 @@
 
 namespace android {
 
-using base::unique_fd;
-
 RpcServer::RpcServer() {}
 RpcServer::~RpcServer() {}
 
@@ -43,22 +42,60 @@
     mAgreedExperimental = true;
 }
 
+void RpcServer::setMaxThreads(size_t threads) {
+    LOG_ALWAYS_FATAL_IF(threads <= 0, "RpcServer is useless without threads");
+    {
+        // this lock should only ever be needed in the error case
+        std::lock_guard<std::mutex> _l(mLock);
+        LOG_ALWAYS_FATAL_IF(mConnections.size() > 0,
+                            "Must specify max threads before creating a connection");
+    }
+    mMaxThreads = threads;
+}
+
+size_t RpcServer::getMaxThreads() {
+    return mMaxThreads;
+}
+
+void RpcServer::setRootObject(const sp<IBinder>& binder) {
+    std::lock_guard<std::mutex> _l(mLock);
+    mRootObject = binder;
+}
+
+sp<IBinder> RpcServer::getRootObject() {
+    std::lock_guard<std::mutex> _l(mLock);
+    return mRootObject;
+}
+
 sp<RpcConnection> RpcServer::addClientConnection() {
     LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
 
     auto connection = RpcConnection::make();
     connection->setForServer(sp<RpcServer>::fromExisting(this));
-    mConnections.push_back(connection);
+    {
+        std::lock_guard<std::mutex> _l(mLock);
+        LOG_ALWAYS_FATAL_IF(mStarted,
+                            "currently only supports adding client connections at creation time");
+        mConnections.push_back(connection);
+    }
     return connection;
 }
 
-void RpcServer::setRootObject(const sp<IBinder>& binder) {
-    LOG_ALWAYS_FATAL_IF(mRootObject != nullptr, "There can only be one root object");
-    mRootObject = binder;
-}
+void RpcServer::join() {
+    std::vector<std::thread> pool;
+    {
+        std::lock_guard<std::mutex> _l(mLock);
+        mStarted = true;
+        for (const sp<RpcConnection>& connection : mConnections) {
+            for (size_t i = 0; i < mMaxThreads; i++) {
+                pool.push_back(std::thread([=] { connection->join(); }));
+            }
+        }
+    }
 
-sp<IBinder> RpcServer::getRootObject() {
-    return mRootObject;
+    // TODO(b/185167543): don't waste extra thread for join, and combine threads
+    // between clients
+    for (auto& t : pool) t.join();
 }
 
 } // namespace android
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index d934136..6bfcc42 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -248,6 +248,31 @@
     return reply.readStrongBinder();
 }
 
+status_t RpcState::getMaxThreads(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+                                 size_t* maxThreads) {
+    Parcel data;
+    data.markForRpc(connection);
+    Parcel reply;
+
+    status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_MAX_THREADS, data,
+                               connection, &reply, 0);
+    if (status != OK) {
+        ALOGE("Error getting max threads: %s", statusToString(status).c_str());
+        return status;
+    }
+
+    int32_t threads;
+    status = reply.readInt32(&threads);
+    if (status != OK) return status;
+    if (threads <= 0) {
+        ALOGE("Error invalid max threads: %d", threads);
+        return BAD_VALUE;
+    }
+
+    *maxThreads = threads;
+    return OK;
+}
+
 status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address, uint32_t code,
                             const Parcel& data, const sp<RpcConnection>& connection, Parcel* reply,
                             uint32_t flags) {
@@ -516,23 +541,25 @@
             replyStatus = target->transact(transaction->code, data, &reply, transaction->flags);
         } else {
             LOG_RPC_DETAIL("Got special transaction %u", transaction->code);
-            // special case for 'zero' address (special server commands)
-            switch (transaction->code) {
-                case RPC_SPECIAL_TRANSACT_GET_ROOT: {
-                    sp<IBinder> root;
-                    sp<RpcServer> server = connection->server().promote();
-                    if (server) {
-                        root = server->getRootObject();
-                    } else {
-                        ALOGE("Root object requested, but no server attached.");
-                    }
 
-                    replyStatus = reply.writeStrongBinder(root);
-                    break;
+            sp<RpcServer> server = connection->server().promote();
+            if (server) {
+                // special case for 'zero' address (special server commands)
+                switch (transaction->code) {
+                    case RPC_SPECIAL_TRANSACT_GET_ROOT: {
+                        replyStatus = reply.writeStrongBinder(server->getRootObject());
+                        break;
+                    }
+                    case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: {
+                        replyStatus = reply.writeInt32(server->getMaxThreads());
+                        break;
+                    }
+                    default: {
+                        replyStatus = UNKNOWN_TRANSACTION;
+                    }
                 }
-                default: {
-                    replyStatus = UNKNOWN_TRANSACTION;
-                }
+            } else {
+                ALOGE("Special command sent, but no server object attached.");
             }
         }
     }
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index f4f5151..1cfa406 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -51,6 +51,8 @@
     ~RpcState();
 
     sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcConnection>& connection);
+    status_t getMaxThreads(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+                           size_t* maxThreadsOut);
 
     [[nodiscard]] status_t transact(const base::unique_fd& fd, const RpcAddress& address,
                                     uint32_t code, const Parcel& data,
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index 60ec6c9..cc7cacb 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -47,6 +47,7 @@
  */
 enum : uint32_t {
     RPC_SPECIAL_TRANSACT_GET_ROOT = 0,
+    RPC_SPECIAL_TRANSACT_GET_MAX_THREADS = 1,
 };
 
 // serialization is like:
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 31f63c8..52f221d 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -49,39 +49,39 @@
 {
 public:
     enum {
-        FIRST_CALL_TRANSACTION  = 0x00000001,
-        LAST_CALL_TRANSACTION   = 0x00ffffff,
+        FIRST_CALL_TRANSACTION = 0x00000001,
+        LAST_CALL_TRANSACTION = 0x00ffffff,
 
-        PING_TRANSACTION        = B_PACK_CHARS('_','P','N','G'),
-        DUMP_TRANSACTION        = B_PACK_CHARS('_','D','M','P'),
-        SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_','C','M','D'),
-        INTERFACE_TRANSACTION   = B_PACK_CHARS('_', 'N', 'T', 'F'),
-        SYSPROPS_TRANSACTION    = B_PACK_CHARS('_', 'S', 'P', 'R'),
-        EXTENSION_TRANSACTION   = B_PACK_CHARS('_', 'E', 'X', 'T'),
-        DEBUG_PID_TRANSACTION   = B_PACK_CHARS('_', 'P', 'I', 'D'),
+        PING_TRANSACTION = B_PACK_CHARS('_', 'P', 'N', 'G'),
+        DUMP_TRANSACTION = B_PACK_CHARS('_', 'D', 'M', 'P'),
+        SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_', 'C', 'M', 'D'),
+        INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'),
+        SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'),
+        EXTENSION_TRANSACTION = B_PACK_CHARS('_', 'E', 'X', 'T'),
+        DEBUG_PID_TRANSACTION = B_PACK_CHARS('_', 'P', 'I', 'D'),
 
         // See android.os.IBinder.TWEET_TRANSACTION
         // Most importantly, messages can be anything not exceeding 130 UTF-8
         // characters, and callees should exclaim "jolly good message old boy!"
-        TWEET_TRANSACTION       = B_PACK_CHARS('_', 'T', 'W', 'T'),
+        TWEET_TRANSACTION = B_PACK_CHARS('_', 'T', 'W', 'T'),
 
         // See android.os.IBinder.LIKE_TRANSACTION
         // Improve binder self-esteem.
-        LIKE_TRANSACTION        = B_PACK_CHARS('_', 'L', 'I', 'K'),
+        LIKE_TRANSACTION = B_PACK_CHARS('_', 'L', 'I', 'K'),
 
         // Corresponds to TF_ONE_WAY -- an asynchronous call.
-        FLAG_ONEWAY             = 0x00000001,
+        FLAG_ONEWAY = 0x00000001,
 
         // Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call
         // is made
-        FLAG_CLEAR_BUF          = 0x00000020,
+        FLAG_CLEAR_BUF = 0x00000020,
 
         // Private userspace flag for transaction which is being requested from
         // a vendor context.
-        FLAG_PRIVATE_VENDOR     = 0x10000000,
+        FLAG_PRIVATE_VENDOR = 0x10000000,
     };
 
-                          IBinder();
+    IBinder();
 
     /**
      * Check if this IBinder implements the interface named by
diff --git a/libs/binder/include/binder/PermissionCache.h b/libs/binder/include/binder/PermissionCache.h
index 835a3a8..21aa705 100644
--- a/libs/binder/include/binder/PermissionCache.h
+++ b/libs/binder/include/binder/PermissionCache.h
@@ -73,6 +73,8 @@
 
     static bool checkPermission(const String16& permission,
             pid_t pid, uid_t uid);
+
+    static void purgeCache();
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcConnection.h
index 2395e78..3a2d8e5 100644
--- a/libs/binder/include/binder/RpcConnection.h
+++ b/libs/binder/include/binder/RpcConnection.h
@@ -59,7 +59,7 @@
      * This should be called once per thread, matching 'join' in the remote
      * process.
      */
-    [[nodiscard]] bool addUnixDomainClient(const char* path);
+    [[nodiscard]] bool setupUnixDomainClient(const char* path);
 
 #ifdef __BIONIC__
     /**
@@ -70,18 +70,24 @@
     /**
      * Connects to an RPC server at the CVD & port.
      */
-    [[nodiscard]] bool addVsockClient(unsigned int cvd, unsigned int port);
+    [[nodiscard]] bool setupVsockClient(unsigned int cvd, unsigned int port);
 #endif // __BIONIC__
 
     /**
-     * Creates an RPC server at the current port.
+     * Creates an RPC server at the current port using IPv4.
+     *
+     * TODO(b/182914638): IPv6 support
+     *
+     * Set |port| to 0 to pick an ephemeral port; see discussion of
+     * /proc/sys/net/ipv4/ip_local_port_range in ip(7). In this case, |assignedPort|
+     * will be set to the picked port number, if it is not null.
      */
-    [[nodiscard]] bool setupInetServer(unsigned int port);
+    [[nodiscard]] bool setupInetServer(unsigned int port, unsigned int* assignedPort);
 
     /**
      * Connects to an RPC server at the given address and port.
      */
-    [[nodiscard]] bool addInetClient(const char* addr, unsigned int port);
+    [[nodiscard]] bool setupInetClient(const char* addr, unsigned int port);
 
     /**
      * For debugging!
@@ -98,16 +104,16 @@
      */
     sp<IBinder> getRootObject();
 
+    /**
+     * Query the other side of the connection for the maximum number of threads
+     * it supports (maximum number of concurrent non-nested synchronous transactions)
+     */
+    status_t getMaxThreads(size_t* maxThreads);
+
     [[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data,
                                     Parcel* reply, uint32_t flags);
     [[nodiscard]] status_t sendDecStrong(const RpcAddress& address);
 
-    /**
-     * Adds a server thread accepting connections. Must be called after
-     * setup*Server.
-     */
-    void join();
-
     ~RpcConnection();
 
     void setForServer(const wp<RpcServer>& server);
@@ -126,8 +132,11 @@
 
 private:
     friend sp<RpcConnection>;
+    friend RpcServer;
     RpcConnection();
 
+    void join();
+
     struct ConnectionSocket : public RefBase {
         base::unique_fd fd;
 
@@ -137,7 +146,8 @@
     };
 
     bool setupSocketServer(const SocketAddress& address);
-    bool addSocketClient(const SocketAddress& address);
+    bool setupSocketClient(const SocketAddress& address);
+    bool setupOneSocketClient(const SocketAddress& address);
     void addClient(base::unique_fd&& fd);
     sp<ConnectionSocket> assignServerToThisThread(base::unique_fd&& fd);
     bool removeServerSocket(const sp<ConnectionSocket>& socket);
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index d29b651..9247128 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -21,6 +21,8 @@
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 
+#include <mutex>
+
 // WARNING: This is a feature which is still in development, and it is subject
 // to radical change. Any production use of this may subject your code to any
 // number of problems.
@@ -30,9 +32,6 @@
 /**
  * This represents a server of an interface, which may be connected to by any
  * number of clients over sockets.
- *
- * This object is not (currently) thread safe. All calls to it are expected to
- * happen at process startup.
  */
 class RpcServer final : public virtual RefBase {
 public:
@@ -41,6 +40,24 @@
     void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
 
     /**
+     * This must be called before adding a client connection.
+     *
+     * If this is not specified, this will be a single-threaded server.
+     *
+     * TODO(b/185167543): these are currently created per client, but these
+     * should be shared.
+     */
+    void setMaxThreads(size_t threads);
+    size_t getMaxThreads();
+
+    /**
+     * The root object can be retrieved by any client, without any
+     * authentication. TODO(b/183988761)
+     */
+    void setRootObject(const sp<IBinder>& binder);
+    sp<IBinder> getRootObject();
+
+    /**
      * Setup a static connection, when the number of clients are known.
      *
      * Each call to this function corresponds to a different client, and clients
@@ -51,23 +68,9 @@
     sp<RpcConnection> addClientConnection();
 
     /**
-     * Allowing a server to explicitly drop clients would be easy to add here,
-     * but it is not currently implemented, since users of this functionality
-     * could not use similar functionality if they are running under real
-     * binder.
+     * You must have at least one client connection before calling this.
      */
-    // void drop(const sp<RpcConnection>& connection);
-
-    /**
-     * The root object can be retrieved by any client, without any
-     * authentication.
-     */
-    void setRootObject(const sp<IBinder>& binder);
-
-    /**
-     * Root object set with setRootObject
-     */
-    sp<IBinder> getRootObject();
+    void join();
 
     ~RpcServer();
 
@@ -76,9 +79,11 @@
     RpcServer();
 
     bool mAgreedExperimental = false;
+    bool mStarted = false; // TODO(b/185167543): support dynamically added clients
+    size_t mMaxThreads = 1;
 
+    std::mutex mLock; // for below
     sp<IBinder> mRootObject;
-
     std::vector<sp<RpcConnection>> mConnections; // per-client
 };
 
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index eb103d3..b03e24c 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -55,7 +55,9 @@
     defaults: ["libbinder_ndk_host_user"],
     host_supported: true,
 
-    llndk_stubs: "libbinder_ndk.llndk",
+    llndk: {
+        symbol_file: "libbinder_ndk.map.txt",
+    },
 
     export_include_dirs: [
         "include_cpp",
@@ -192,13 +194,3 @@
     symbol_file: "libbinder_ndk.map.txt",
     first_version: "29",
 }
-
-llndk_library {
-    name: "libbinder_ndk.llndk",
-    symbol_file: "libbinder_ndk.map.txt",
-    export_include_dirs: [
-        "include_cpp",
-        "include_ndk",
-        "include_platform",
-    ],
-}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index f303b7c..c0f7c99 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -35,6 +35,7 @@
     name: "binderDriverInterfaceTest_IPC_32",
     defaults: ["binder_test_defaults"],
     srcs: ["binderDriverInterfaceTest.cpp"],
+    header_libs: ["libbinder_headers"],
     compile_multilib: "32",
     multilib: { lib32: { suffix: "" } },
     cflags: ["-DBINDER_IPC_32BIT=1"],
@@ -49,7 +50,7 @@
             cflags: ["-DBINDER_IPC_32BIT=1"],
         },
     },
-
+    header_libs: ["libbinder_headers"],
     srcs: ["binderDriverInterfaceTest.cpp"],
     test_suites: ["device-tests", "vts"],
 }
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index 7c82226..b3282ff 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -127,12 +127,12 @@
         sp<RpcConnection> connection = server->addClientConnection();
         CHECK(connection->setupUnixDomainServer(addr.c_str()));
 
-        connection->join();
+        server->join();
     }).detach();
 
     for (size_t tries = 0; tries < 5; tries++) {
         usleep(10000);
-        if (gConnection->addUnixDomainClient(addr.c_str())) goto success;
+        if (gConnection->setupUnixDomainClient(addr.c_str())) goto success;
     }
     LOG(FATAL) << "Could not connect.";
 success:
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index dd68fdb..f3ec904 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -17,6 +17,7 @@
 #include <BnBinderRpcSession.h>
 #include <BnBinderRpcTest.h>
 #include <aidl/IBinderRpcTest.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android/binder_auto_utils.h>
 #include <android/binder_libbinder.h>
@@ -176,14 +177,27 @@
 };
 sp<IBinder> MyBinderRpcTest::mHeldBinder;
 
+class Pipe {
+public:
+    Pipe() { CHECK(android::base::Pipe(&mRead, &mWrite)); }
+    Pipe(Pipe&&) = default;
+    android::base::borrowed_fd readEnd() { return mRead; }
+    android::base::borrowed_fd writeEnd() { return mWrite; }
+
+private:
+    android::base::unique_fd mRead;
+    android::base::unique_fd mWrite;
+};
+
 class Process {
 public:
-    Process(const std::function<void()>& f) {
+    Process(Process&&) = default;
+    Process(const std::function<void(Pipe*)>& f) {
         if (0 == (mPid = fork())) {
             // racey: assume parent doesn't crash before this is set
             prctl(PR_SET_PDEATHSIG, SIGHUP);
 
-            f();
+            f(&mPipe);
         }
     }
     ~Process() {
@@ -191,9 +205,11 @@
             kill(mPid, SIGKILL);
         }
     }
+    Pipe* getPipe() { return &mPipe; }
 
 private:
     pid_t mPid = 0;
+    Pipe mPipe;
 };
 
 static std::string allocateSocketAddress() {
@@ -215,6 +231,7 @@
     // whether connection should be invalidated by end of run
     bool expectInvalid = false;
 
+    ProcessConnection(ProcessConnection&&) = default;
     ~ProcessConnection() {
         rootBinder = nullptr;
         EXPECT_NE(nullptr, connection);
@@ -238,6 +255,7 @@
     // pre-casted root object
     sp<IBinderRpcTest> rootIface;
 
+    BinderRpcTestProcessConnection(BinderRpcTestProcessConnection&&) = default;
     ~BinderRpcTestProcessConnection() {
         if (!proc.expectInvalid) {
             int32_t remoteBinders = 0;
@@ -280,20 +298,19 @@
     ProcessConnection createRpcTestSocketServerProcess(
             size_t numThreads,
             const std::function<void(const sp<RpcServer>&, const sp<RpcConnection>&)>& configure) {
-        CHECK_GT(numThreads, 0);
-
         SocketType socketType = GetParam();
 
         std::string addr = allocateSocketAddress();
         unlink(addr.c_str());
-        static unsigned int port = 3456;
-        port++;
+        static unsigned int vsockPort = 3456;
+        vsockPort++;
 
         auto ret = ProcessConnection{
-                .host = Process([&] {
+                .host = Process([&](Pipe* pipe) {
                     sp<RpcServer> server = RpcServer::make();
 
                     server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+                    server->setMaxThreads(numThreads);
 
                     // server supporting one client on one socket
                     sp<RpcConnection> connection = server->addClientConnection();
@@ -304,53 +321,57 @@
                             break;
 #ifdef __BIONIC__
                         case SocketType::VSOCK:
-                            CHECK(connection->setupVsockServer(port));
+                            CHECK(connection->setupVsockServer(vsockPort));
                             break;
 #endif // __BIONIC__
-                        case SocketType::INET:
-                            CHECK(connection->setupInetServer(port));
+                        case SocketType::INET: {
+                            unsigned int outPort = 0;
+                            CHECK(connection->setupInetServer(0, &outPort));
+                            CHECK_NE(0, outPort);
+                            CHECK(android::base::WriteFully(pipe->writeEnd(), &outPort,
+                                                            sizeof(outPort)));
                             break;
+                        }
                         default:
                             LOG_ALWAYS_FATAL("Unknown socket type");
                     }
 
                     configure(server, connection);
 
-                    // accept 'numThreads' connections
-                    std::vector<std::thread> pool;
-                    for (size_t i = 0; i + 1 < numThreads; i++) {
-                        pool.push_back(std::thread([=] { connection->join(); }));
-                    }
-                    connection->join();
-                    for (auto& t : pool) t.join();
+                    server->join();
                 }),
                 .connection = RpcConnection::make(),
         };
 
-        // create remainder of connections
-        for (size_t i = 0; i < numThreads; i++) {
-            for (size_t tries = 0; tries < 5; tries++) {
-                usleep(10000);
-                switch (socketType) {
-                    case SocketType::UNIX:
-                        if (ret.connection->addUnixDomainClient(addr.c_str())) goto success;
-                        break;
-#ifdef __BIONIC__
-                    case SocketType::VSOCK:
-                        if (ret.connection->addVsockClient(VMADDR_CID_LOCAL, port)) goto success;
-                        break;
-#endif // __BIONIC__
-                    case SocketType::INET:
-                        if (ret.connection->addInetClient("127.0.0.1", port)) goto success;
-                        break;
-                    default:
-                        LOG_ALWAYS_FATAL("Unknown socket type");
-                }
-            }
-            LOG_ALWAYS_FATAL("Could not connect");
-        success:;
+        unsigned int inetPort = 0;
+        if (socketType == SocketType::INET) {
+            CHECK(android::base::ReadFully(ret.host.getPipe()->readEnd(), &inetPort,
+                                           sizeof(inetPort)));
+            CHECK_NE(0, inetPort);
         }
 
+        // create remainder of connections
+        for (size_t tries = 0; tries < 10; tries++) {
+            usleep(10000);
+            switch (socketType) {
+                case SocketType::UNIX:
+                    if (ret.connection->setupUnixDomainClient(addr.c_str())) goto success;
+                    break;
+#ifdef __BIONIC__
+                case SocketType::VSOCK:
+                    if (ret.connection->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success;
+                    break;
+#endif // __BIONIC__
+                case SocketType::INET:
+                    if (ret.connection->setupInetClient("127.0.0.1", inetPort)) goto success;
+                    break;
+                default:
+                    LOG_ALWAYS_FATAL("Unknown socket type");
+            }
+        }
+        LOG_ALWAYS_FATAL("Could not connect");
+    success:
+
         ret.rootBinder = ret.connection->getRootObject();
         return ret;
     }
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 517b49e..267db76 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -98,7 +98,6 @@
     SAFE_PARCEL(output.write, transparentRegion);
     SAFE_PARCEL(output.writeUint32, transform);
     SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
-    SAFE_PARCEL(output.write, crop);
     SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
 
     if (buffer) {
@@ -167,6 +166,7 @@
     }
 
     SAFE_PARCEL(output.write, stretchEffect);
+    SAFE_PARCEL(output.write, bufferCrop);
 
     return NO_ERROR;
 }
@@ -209,7 +209,6 @@
     SAFE_PARCEL(input.read, transparentRegion);
     SAFE_PARCEL(input.readUint32, &transform);
     SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
-    SAFE_PARCEL(input.read, crop);
     SAFE_PARCEL(input.read, orientedDisplaySpaceRect);
 
     bool tmpBool = false;
@@ -296,6 +295,7 @@
     }
 
     SAFE_PARCEL(input.read, stretchEffect);
+    SAFE_PARCEL(input.read, bufferCrop);
 
     return NO_ERROR;
 }
@@ -539,6 +539,10 @@
         what |= eStretchChanged;
         stretchEffect = other.stretchEffect;
     }
+    if (other.what & eBufferCropChanged) {
+        what |= eBufferCropChanged;
+        bufferCrop = other.bufferCrop;
+    }
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
               "other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 5db0eae..808b731 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1664,6 +1664,21 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferCrop(
+        const sp<SurfaceControl>& sc, const Rect& bufferCrop) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eBufferCropChanged;
+    s->bufferCrop = bufferCrop;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h
index 85ae9cb..ce9716f 100644
--- a/libs/gui/include/gui/JankInfo.h
+++ b/libs/gui/include/gui/JankInfo.h
@@ -42,6 +42,10 @@
     BufferStuffing = 0x40,
     // Jank due to unknown reasons.
     Unknown = 0x80,
+    // SF is said to be stuffed if the previous frame ran longer than expected resulting in the case
+    // where the previous frame was presented in the current frame's expected vsync. This pushes the
+    // current frame to the next vsync. The behavior is similar to BufferStuffing.
+    SurfaceFlingerStuffing = 0x100,
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index b4f62f2..3947f22 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -85,7 +85,7 @@
         eReleaseBufferListenerChanged = 0x00000400,
         eShadowRadiusChanged = 0x00000800,
         eLayerCreated = 0x00001000,
-        /* was eDetachChildren, now available 0x00002000, */
+        eBufferCropChanged = 0x00002000,
         eRelativeLayerChanged = 0x00004000,
         eReparent = 0x00008000,
         eColorChanged = 0x00010000,
@@ -227,6 +227,8 @@
     // Stretch effect to be applied to this layer
     StretchEffect stretchEffect;
 
+    Rect bufferCrop;
+
     // Listens to when the buffer is safe to be released. This is used for blast
     // layers only. The callback includes a release fence as well as the graphic
     // buffer id to identify the buffer.
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 5bbd8e3..f3439c4 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -542,6 +542,8 @@
                                       float right, float bottom, float vecX, float vecY,
                                       float maxAmount);
 
+        Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop);
+
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
 
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 8675439..9286009 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -60,7 +60,13 @@
 
 cc_library {
     name: "libnativewindow",
-    llndk_stubs: "libnativewindow.llndk",
+    llndk: {
+        symbol_file: "libnativewindow.map.txt",
+        unversioned: true,
+        override_export_include_dirs: [
+            "include"
+        ],
+    },
     export_include_dirs: [
         "include",
         "include-private",
@@ -115,11 +121,4 @@
     },
 }
 
-llndk_library {
-    name: "libnativewindow.llndk",
-    symbol_file: "libnativewindow.map.txt",
-    unversioned: true,
-    export_include_dirs: ["include"],
-}
-
 subdirs = ["tests"]
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 61b3f94..3865ba5 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -302,6 +302,8 @@
  *
  * Available since API level 31.
  *
+ * \param window pointer to an ANativeWindow object.
+ *
  * \param frameRate The intended frame rate of this window, in frames per
  * second. 0 is a special value that indicates the app will accept the system's
  * choice for the display frame rate, which is the default behavior if this
@@ -309,15 +311,16 @@
  * valid refresh rate for this device's display - e.g., it's fine to pass 30fps
  * to a device that can only run the display at 60fps.
  *
- * \param window pointer to an ANativeWindow object.
- *
  * \param compatibility The frame rate compatibility of this window. The
  * compatibility value may influence the system's choice of display refresh
  * rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info.
+ * This parameter is ignored when frameRate is 0.
  *
- * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this
+ * window should be seamless.
  * A seamless transition is one that doesn't have any visual interruptions, such as a black
  * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
+ * This parameter is ignored when frameRate is 0.
  *
  * \return 0 for success, -EINVAL if the window, frame rate, or compatibility
  * value are invalid.
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index c54c5ba..e976a5a 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -156,6 +156,10 @@
 
     std::vector<BlurRegion> blurRegions;
 
+    // Transform matrix used to convert the blurRegions geometry into the same
+    // coordinate space as LayerSettings.geometry
+    mat4 blurRegionTransform = mat4();
+
     StretchEffect stretchEffect;
 
     // Name associated with the layer for debugging purposes.
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 9ed759f..8ae69de 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -46,7 +46,7 @@
     ALOGE_IF(!mBackendTexture.isValid(),
              "Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d isWriteable:%d "
              "format:%d",
-             this, desc.width, desc.height, isOutputBuffer, createProtectedImage, desc.format);
+             this, desc.width, desc.height, createProtectedImage, isOutputBuffer, desc.format);
 }
 
 void AutoBackendTexture::unref(bool releaseLocalResources) {
@@ -129,4 +129,4 @@
 
 } // namespace skia
 } // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 1c2b2fc..6ab93df 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -37,12 +37,15 @@
                                      0.f,  0.7f, 0.f, 0.f,
                                      0.f,   0.f, 1.f, 0.f,
                                    67.3f, 52.2f, 0.f, 1.f);
+const auto kScaleYOnly = mat4(1.f,   0.f, 0.f, 0.f,
+                              0.f,  0.7f, 0.f, 0.f,
+                              0.f,   0.f, 1.f, 0.f,
+                              0.f,   0.f, 0.f, 1.f);
 // clang-format on
-// When choosing dataspaces below, whether the match the destination or not determined whether
-// a color correction effect is added to the shader. There may be other additional shader details
-// for particular color spaces.
-// TODO(b/184842383) figure out which color related shaders are necessary
+// When setting layer.sourceDataspace, whether it matches the destination or not determines whether
+// a color correction effect is added to the shader.
 constexpr auto kDestDataSpace = ui::Dataspace::SRGB;
+constexpr auto kOtherDataSpace = ui::Dataspace::DISPLAY_P3;
 } // namespace
 
 static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
@@ -115,20 +118,24 @@
 
     // Test both drawRect and drawRRect
     auto layers = std::vector<const LayerSettings*>{&layer};
-    for (bool identity : {true, false}) {
-        layer.geometry.positionTransform = identity ? mat4() : kScaleAndTranslate;
-        // Corner radii less than 0.5 creates a special shader. This likely occurs in real usage
-        // due to animating corner radius.
-        // For the non-idenity matrix, only the large corner radius will create a new shader.
-        for (float roundedCornersRadius : identity ? threeCornerRadii : oneCornerRadius) {
-            // roundedCornersCrop is always set, but it is this radius that triggers the behavior
-            layer.geometry.roundedCornersRadius = roundedCornersRadius;
-            for (bool isOpaque : {true, false}) {
-                layer.source.buffer.isOpaque = isOpaque;
-                for (auto alpha : {half(.23999f), half(1.0f)}) {
-                    layer.alpha = alpha;
-                    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                             base::unique_fd(), nullptr);
+    for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
+        layer.sourceDataspace = dataspace;
+        for (bool identity : {true, false}) {
+            layer.geometry.positionTransform = identity ? mat4() : kScaleAndTranslate;
+            // Corner radii less than 0.5 creates a special shader. This likely occurs in real usage
+            // due to animating corner radius.
+            // For the non-idenity matrix, only the large corner radius will create a new shader.
+            for (float roundedCornersRadius : identity ? threeCornerRadii : oneCornerRadius) {
+                // roundedCornersCrop is always set, but it is this radius that triggers the
+                // behavior
+                layer.geometry.roundedCornersRadius = roundedCornersRadius;
+                for (bool isOpaque : {true, false}) {
+                    layer.source.buffer.isOpaque = isOpaque;
+                    for (auto alpha : {half(.23999f), half(1.0f)}) {
+                        layer.alpha = alpha;
+                        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                                                 base::unique_fd(), nullptr);
+                    }
                 }
             }
         }
@@ -182,6 +189,37 @@
     }
 }
 
+static void drawTextureScaleLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+                                   const std::shared_ptr<ExternalTexture>& dstTexture,
+                                   const std::shared_ptr<ExternalTexture>& srcTexture) {
+    const Rect& displayRect = display.physicalDisplay;
+    FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+    LayerSettings layer{
+            .geometry =
+                    Geometry{
+                            .boundaries = rect,
+                            .roundedCornersCrop = rect,
+                            .positionTransform = kScaleAndTranslate,
+                            .roundedCornersRadius = 300,
+                    },
+            .source = PixelSource{.buffer =
+                                          Buffer{
+                                                  .buffer = srcTexture,
+                                                  .maxMasteringLuminance = 1000.f,
+                                                  .maxContentLuminance = 1000.f,
+                                                  .textureTransform = kScaleYOnly,
+                                          }},
+            .sourceDataspace = kOtherDataSpace,
+    };
+
+    auto layers = std::vector<const LayerSettings*>{&layer};
+    for (float alpha : {0.5f, 1.f}) {
+        layer.alpha = alpha,
+        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                                 base::unique_fd(), nullptr);
+    }
+}
+
 //
 // The collection of shaders cached here were found by using perfetto to record shader compiles
 // during actions that involve RenderEngine, logging the layer settings, and the shader code
@@ -250,6 +288,9 @@
     // between 6 and 8 will occur in real uses.
     drawImageLayers(renderengine, display, dstTexture, externalTexture);
 
+    // Draw layers for b/185569240.
+    drawTextureScaleLayers(renderengine, display, dstTexture, externalTexture);
+
     const nsecs_t timeAfter = systemTime();
     const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
     const int shadersCompiled = renderengine->reportShadersCompiled();
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 377b6f8..acdb78a 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -834,6 +834,8 @@
                     mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect,
                                                 blurredImage, blurInput);
                 }
+                SkAutoCanvasRestore acr(canvas, true);
+                canvas->concat(getSkM44(layer->blurRegionTransform).asM33());
                 for (auto region : layer->blurRegions) {
                     if (cachedBlurs[region.blurRadius] == nullptr) {
                         ATRACE_NAME("BlurRegion");
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 6dd4161..2028def 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -53,7 +53,7 @@
         }
     )");
 
-    auto [blurEffect, error] = SkRuntimeEffect::Make(blurString);
+    auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
     if (!blurEffect) {
         LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
     }
@@ -65,11 +65,11 @@
         uniform float mixFactor;
 
         half4 main(float2 xy) {
-            return half4(mix(sample(originalInput), sample(blurredInput), mixFactor));
+            return half4(mix(sample(originalInput, xy), sample(blurredInput, xy), mixFactor));
         }
     )");
 
-    auto [mixEffect, mixError] = SkRuntimeEffect::Make(mixString);
+    auto [mixEffect, mixError] = SkRuntimeEffect::MakeForShader(mixString);
     if (!mixEffect) {
         LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str());
     }
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index 8e8e42e..0fbd669 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -438,7 +438,7 @@
     generateOETF(linearEffect.outputDataspace, shaderString);
     generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
 
-    auto [shader, error] = SkRuntimeEffect::Make(shaderString);
+    auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString);
     if (!shader) {
         LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
     }
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 3878cb1..b15694b 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -69,11 +69,9 @@
     host_supported: true,
     vendor_available: true,
     export_include_dirs: ["include"],
-}
-
-llndk_headers {
-    name: "gl_llndk_headers",
-    export_include_dirs: ["include"],
+    llndk: {
+        llndk_headers: true,
+    },
 }
 
 subdirs = [
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 7861d62..c9fce8a 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -137,7 +137,12 @@
 cc_library_shared {
     name: "libEGL",
     defaults: ["egl_libs_defaults"],
-    llndk_stubs: "libEGL.llndk",
+    llndk: {
+        symbol_file: "libEGL.map.txt",
+        export_llndk_headers: ["gl_headers"],
+        // Don't export EGL/include from the LLNDK variant.
+        override_export_include_dirs: [],
+    },
     srcs: [
         "EGL/egl_tls.cpp",
         "EGL/egl_cache.cpp",
@@ -203,7 +208,12 @@
 cc_library_shared {
     name: "libGLESv1_CM",
     defaults: ["gles_libs_defaults"],
-    llndk_stubs: "libGLESv1_CM.llndk",
+    llndk: {
+        symbol_file: "libGLESv1_CM.map.txt",
+        export_llndk_headers: ["gl_headers"],
+        // Don't export EGL/include from the LLNDK variant.
+        override_export_include_dirs: [],
+    },
     srcs: ["GLES_CM/gl.cpp"],
     cflags: ["-DLOG_TAG=\"libGLESv1\""],
     version_script: "libGLESv1_CM.map.txt",
@@ -215,7 +225,12 @@
 cc_library_shared {
     name: "libGLESv2",
     defaults: ["gles_libs_defaults"],
-    llndk_stubs: "libGLESv2.llndk",
+    llndk: {
+        symbol_file: "libGLESv2.map.txt",
+        export_llndk_headers: ["gl_headers"],
+        // Don't export EGL/include from the LLNDK variant.
+        override_export_include_dirs: [],
+    },
     srcs: ["GLES2/gl2.cpp"],
     cflags: ["-DLOG_TAG=\"libGLESv2\""],
 
@@ -230,31 +245,12 @@
 cc_library_shared {
     name: "libGLESv3",
     defaults: ["gles_libs_defaults"],
-    llndk_stubs: "libGLESv3.llndk",
+    llndk: {
+        symbol_file: "libGLESv3.map.txt",
+        export_llndk_headers: ["gl_headers"],
+        // Don't export EGL/include from the LLNDK variant.
+        override_export_include_dirs: [],
+    },
     srcs: ["GLES2/gl2.cpp"],
     cflags: ["-DLOG_TAG=\"libGLESv3\""],
 }
-
-llndk_library {
-    name: "libEGL.llndk",
-    symbol_file: "libEGL.map.txt",
-    export_llndk_headers: ["gl_llndk_headers"],
-}
-
-llndk_library {
-    name: "libGLESv1_CM.llndk",
-    symbol_file: "libGLESv1_CM.map.txt",
-    export_llndk_headers: ["gl_llndk_headers"],
-}
-
-llndk_library {
-    name: "libGLESv2.llndk",
-    symbol_file: "libGLESv2.map.txt",
-    export_llndk_headers: ["gl_llndk_headers"],
-}
-
-llndk_library {
-    name: "libGLESv3.llndk",
-    symbol_file: "libGLESv3.map.txt",
-    export_llndk_headers: ["gl_llndk_headers"],
-}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index edbf21a..27443b0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2967,7 +2967,7 @@
         return; // Not a key or a motion
     }
 
-    std::unordered_set<sp<IBinder>, IBinderHash> newConnectionTokens;
+    std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> newConnectionTokens;
     std::vector<sp<Connection>> newConnections;
     for (const InputTarget& target : targets) {
         if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) ==
@@ -4284,12 +4284,8 @@
     return nullptr;
 }
 
-sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
-    sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
-    return getWindowHandleLocked(focusedToken, displayId);
-}
-
-bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const {
+sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
+        const sp<InputWindowHandle>& windowHandle) const {
     for (auto& it : mWindowHandlesByDisplay) {
         const std::vector<sp<InputWindowHandle>>& windowHandles = it.second;
         for (const sp<InputWindowHandle>& handle : windowHandles) {
@@ -4301,11 +4297,16 @@
                           windowHandle->getName().c_str(), it.first,
                           windowHandle->getInfo()->displayId);
                 }
-                return true;
+                return handle;
             }
         }
     }
-    return false;
+    return nullptr;
+}
+
+sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
+    sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
+    return getWindowHandleLocked(focusedToken, displayId);
 }
 
 bool InputDispatcher::hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const {
@@ -4461,7 +4462,7 @@
         TouchState& state = stateIt->second;
         for (size_t i = 0; i < state.windows.size();) {
             TouchedWindow& touchedWindow = state.windows[i];
-            if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
+            if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) {
                 if (DEBUG_FOCUS) {
                     ALOGD("Touched window was removed: %s in display %" PRId32,
                           touchedWindow.windowHandle->getName().c_str(), displayId);
@@ -4493,7 +4494,7 @@
     // Otherwise, they might stick around until the window handle is destroyed
     // which might not happen until the next GC.
     for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
-        if (!hasWindowHandleLocked(oldWindowHandle)) {
+        if (getWindowHandleLocked(oldWindowHandle) == nullptr) {
             if (DEBUG_FOCUS) {
                 ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
             }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 5708fac..7ab4fd7 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -221,12 +221,11 @@
 
     void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
 
-    struct IBinderHash {
-        std::size_t operator()(const sp<IBinder>& b) const {
-            return std::hash<IBinder*>{}(b.get());
-        }
+    template <typename T>
+    struct StrongPointerHash {
+        std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); }
     };
-    std::unordered_map<sp<IBinder>, std::shared_ptr<InputChannel>, IBinderHash>
+    std::unordered_map<sp<IBinder>, std::shared_ptr<InputChannel>, StrongPointerHash<IBinder>>
             mInputChannelsByToken GUARDED_BY(mLock);
 
     // Finds the display ID of the gesture monitor identified by the provided token.
@@ -327,10 +326,11 @@
     // to loop through all displays.
     sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
                                                 int displayId) const REQUIRES(mLock);
+    sp<InputWindowHandle> getWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const
+            REQUIRES(mLock);
     std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const
             REQUIRES(mLock);
     sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock);
-    bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
     bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock);
 
     /*
@@ -371,7 +371,8 @@
     std::string mLastAnrState GUARDED_BY(mLock);
 
     // The connection tokens of the channels that the user last interacted, for debugging
-    std::unordered_set<sp<IBinder>, IBinderHash> mInteractionConnectionTokens GUARDED_BY(mLock);
+    std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> mInteractionConnectionTokens
+            GUARDED_BY(mLock);
     void updateInteractionTokensLocked(const EventEntry& entry,
                                        const std::vector<InputTarget>& targets) REQUIRES(mLock);
 
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index d8e8b52..5d6f8c7 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -132,8 +132,6 @@
 }
 
 void SensorDevice::initializeSensorList() {
-    float minPowerMa = 0.001; // 1 microAmp
-
     checkReturn(mSensors->getSensorsList(
             [&](const auto &list) {
                 const size_t count = list.size();
@@ -151,13 +149,18 @@
                         // Don't crash in this case since CTS will verify that devices don't go to
                         // production with a resolution of 0.
                         if (sensor.resolution != 0) {
-                            double promotedResolution = sensor.resolution;
-                            double promotedMaxRange = sensor.maxRange;
-                            if (fmod(promotedMaxRange, promotedResolution) != 0) {
-                                ALOGW("%s's max range %f is not a multiple of the resolution %f",
-                                        sensor.name, sensor.maxRange, sensor.resolution);
-                                SensorDeviceUtils::quantizeValue(
-                                        &sensor.maxRange, promotedResolution);
+                            float quantizedRange = sensor.maxRange;
+                            SensorDeviceUtils::quantizeValue(
+                                    &quantizedRange, sensor.resolution, /*factor=*/ 1);
+                            // Only rewrite maxRange if the requantization produced a "significant"
+                            // change, which is fairly arbitrarily defined as resolution / 8.
+                            // Smaller deltas are permitted, as they may simply be due to floating
+                            // point representation error, etc.
+                            if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) {
+                                ALOGW("%s's max range %.12f is not a multiple of the resolution "
+                                      "%.12f - updated to %.12f", sensor.name, sensor.maxRange,
+                                      sensor.resolution, quantizedRange);
+                                sensor.maxRange = quantizedRange;
                             }
                         } else {
                             // Don't crash here or the device will go into a crashloop.
@@ -166,10 +169,11 @@
                     }
 
                     // Sanity check and clamp power if it is 0 (or close)
-                    if (sensor.power < minPowerMa) {
-                        ALOGI("Reported power %f not deemed sane, clamping to %f",
-                              sensor.power, minPowerMa);
-                        sensor.power = minPowerMa;
+                    constexpr float MIN_POWER_MA = 0.001; // 1 microAmp
+                    if (sensor.power < MIN_POWER_MA) {
+                        ALOGI("%s's reported power %f invalid, clamped to %f",
+                              sensor.name, sensor.power, MIN_POWER_MA);
+                        sensor.power = MIN_POWER_MA;
                     }
                     mSensorList.push_back(sensor);
 
diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h
index 1309971..255f7e1 100644
--- a/services/sensorservice/SensorDeviceUtils.h
+++ b/services/sensorservice/SensorDeviceUtils.h
@@ -32,16 +32,15 @@
 namespace android {
 namespace SensorDeviceUtils {
 
-// Quantizes a single value using a sensor's resolution.
-inline void quantizeValue(float *value, double resolution) {
+// Quantizes a single value to (a fractional factor of) a sensor's resolution. Typically we
+// increase the value of the sensor's nominal resolution to ensure that sensor accuracy
+// improvements, like runtime calibration, are not masked during requantization.
+inline void quantizeValue(float *value, double resolution, double factor = 0.125) {
     if (resolution == 0) {
         return;
     }
 
-    // Increase the value of the sensor's nominal resolution to ensure that
-    // sensor accuracy improvements, like runtime calibration, are not masked
-    // during requantization.
-    double incRes = 0.125 * resolution;
+    double incRes = factor * resolution;
     *value = round(static_cast<double>(*value) / incRes) * incRes;
 }
 
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index d243989..4c73b6e 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -186,7 +186,7 @@
         }
     }
     const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
-            (isSecure() && !targetSettings.isSecure);
+            ((isSecure() || isProtected()) && !targetSettings.isSecure);
     const bool bufferCanBeUsedAsHwTexture =
             mBufferInfo.mBuffer->getBuffer()->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
     compositionengine::LayerFE::LayerSettings& layer = *result;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 7a5b20d..24b3599 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -284,6 +284,17 @@
     return true;
 }
 
+bool BufferStateLayer::setBufferCrop(const Rect& bufferCrop) {
+    if (mCurrentState.bufferCrop == bufferCrop) return false;
+
+    mCurrentState.sequence++;
+    mCurrentState.bufferCrop = bufferCrop;
+
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix,
                                  bool allowNonRectPreservingTransforms) {
     if (mCurrentState.transform.dsdx() == matrix.dsdx &&
@@ -809,10 +820,15 @@
 }
 
 Rect BufferStateLayer::computeBufferCrop(const State& s) {
-    if (s.buffer) {
+    if (s.buffer && !s.bufferCrop.isEmpty()) {
+        Rect bufferCrop;
+        s.buffer->getBuffer()->getBounds().intersect(s.bufferCrop, &bufferCrop);
+        return bufferCrop;
+    } else if (s.buffer) {
         return s.buffer->getBuffer()->getBounds();
+    } else {
+        return s.bufferCrop;
     }
-    return Rect::INVALID_RECT;
 }
 
 sp<Layer> BufferStateLayer::createClone() {
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index af4fcae..570a41a 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -88,6 +88,8 @@
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
     void setAutoRefresh(bool autoRefresh) override;
 
+    bool setBufferCrop(const Rect& bufferCrop) override;
+
     // -----------------------------------------------------------------------
 
     // -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/Clock.h b/services/surfaceflinger/Clock.h
new file mode 100644
index 0000000..3f23c6d
--- /dev/null
+++ b/services/surfaceflinger/Clock.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 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 <chrono>
+
+namespace android {
+
+// Abstract interface for timekeeping which can be injected for unit tests.
+class Clock {
+public:
+    Clock() = default;
+    virtual ~Clock() = default;
+
+    // Returns the current time
+    virtual std::chrono::steady_clock::time_point now() const = 0;
+};
+
+class SteadyClock : public Clock {
+public:
+    virtual ~SteadyClock() = default;
+
+    std::chrono::steady_clock::time_point now() const override {
+        return std::chrono::steady_clock::now();
+    }
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
index 7ca91d8..58bb41a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -96,6 +96,11 @@
         // physical translation and finally rotate to the physical orientation.
         return rotationTransform * destTranslation * scale * sourceTranslation;
     }
+
+    bool operator==(const ProjectionSpace& other) const {
+        return bounds == other.bounds && content == other.content &&
+                orientation == other.orientation;
+    }
 };
 
 } // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 430945a..ff7d430 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -78,6 +78,20 @@
         dumpVal(out, "stretchEffect", stretchEffect);
     }
 
+    if (!blurRegions.empty()) {
+        out.append("\n      blurRegions {");
+        for (const auto& region : blurRegions) {
+            out.append("\n           ");
+            base::StringAppendF(&out,
+                                "{radius=%du, cornerRadii=[%f, %f, %f, %f], alpha=%f, rect=[%d, "
+                                "%d, %d, %d]",
+                                region.blurRadius, region.cornerRadiusTL, region.cornerRadiusTR,
+                                region.cornerRadiusBL, region.cornerRadiusBR, region.alpha,
+                                region.left, region.top, region.right, region.bottom);
+        }
+        out.append("\n      }\n      ");
+    }
+
     if (!metadata.empty()) {
         out.append("\n      metadata {");
         for (const auto& [key, entry] : metadata) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index d3e2c25..7f5c01c 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -367,7 +367,12 @@
 
     if (outputDependentState.overrideInfo.buffer != nullptr) {
         displayFrame = outputDependentState.overrideInfo.displayFrame;
-        sourceCrop = displayFrame.toFloatRect();
+        sourceCrop =
+                FloatRect(0.f, 0.f,
+                          static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer()
+                                                     ->getWidth()),
+                          static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer()
+                                                     ->getHeight()));
     }
 
     ALOGV("Writing display frame [%d, %d, %d, %d]", displayFrame.left, displayFrame.top,
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 31662c7..67854cf 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -158,8 +158,12 @@
     const ui::Dataspace& outputDataspace = outputState.dataspace;
     const ui::Transform::RotationFlags orientation =
             ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
+
     renderengine::DisplaySettings displaySettings{
-            .physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
+            .physicalDisplay = Rect(-mBounds.left + outputState.framebufferSpace.content.left,
+                                    -mBounds.top + outputState.framebufferSpace.content.top,
+                                    -mBounds.left + outputState.framebufferSpace.content.right,
+                                    -mBounds.top + outputState.framebufferSpace.content.bottom),
             .clip = viewport,
             .outputDataspace = outputDataspace,
             .orientation = orientation,
@@ -246,9 +250,7 @@
 
     if (result == NO_ERROR) {
         mDrawFence = new Fence(drawFence.release());
-        mOutputSpace = ProjectionSpace(ui::Size(outputState.framebufferSpace.bounds.getWidth(),
-                                                outputState.framebufferSpace.bounds.getHeight()),
-                                       mBounds);
+        mOutputSpace = outputState.framebufferSpace;
         mTexture = std::move(texture);
         mOutputSpace.orientation = outputState.framebufferSpace.orientation;
         mOutputDataspace = outputDataspace;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 818cb1b..e876a61 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -23,10 +23,11 @@
 #include <gtest/gtest.h>
 #include <log/log.h>
 
+#include <renderengine/mock/RenderEngine.h>
+#include <ui/PixelFormat.h>
 #include "MockHWC2.h"
 #include "MockHWComposer.h"
 #include "RegionMatcher.h"
-#include "renderengine/mock/RenderEngine.h"
 
 namespace android::compositionengine {
 namespace {
@@ -707,6 +708,7 @@
     static const half4 kColor;
     static const Rect kDisplayFrame;
     static const Rect kOverrideDisplayFrame;
+    static const FloatRect kOverrideSourceCrop;
     static const Region kOutputSpaceVisibleRegion;
     static const Region kOverrideVisibleRegion;
     static const mat4 kColorTransform;
@@ -715,7 +717,7 @@
     static const HdrMetadata kHdrMetadata;
     static native_handle_t* kSidebandStreamHandle;
     static const sp<GraphicBuffer> kBuffer;
-    std::shared_ptr<renderengine::ExternalTexture> kOverrideBuffer;
+    static const sp<GraphicBuffer> kOverrideBuffer;
     static const sp<Fence> kFence;
     static const sp<Fence> kOverrideFence;
     static const std::string kLayerGenericMetadata1Key;
@@ -724,11 +726,6 @@
     static const std::vector<uint8_t> kLayerGenericMetadata2Value;
 
     OutputLayerWriteStateToHWCTest() {
-        kOverrideBuffer = std::make_shared<
-                renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
-                                               renderengine::ExternalTexture::Usage::READABLE |
-                                                       renderengine::ExternalTexture::Usage::
-                                                               WRITEABLE);
         auto& outputLayerState = mOutputLayer.editState();
         outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer);
 
@@ -768,7 +765,11 @@
     void includeOverrideInfo() {
         auto& overrideInfo = mOutputLayer.editState().overrideInfo;
 
-        overrideInfo.buffer = kOverrideBuffer;
+        overrideInfo.buffer = std::make_shared<
+                renderengine::ExternalTexture>(kOverrideBuffer, mRenderEngine,
+                                               renderengine::ExternalTexture::Usage::READABLE |
+                                                       renderengine::ExternalTexture::Usage::
+                                                               WRITEABLE);
         overrideInfo.acquireFence = kOverrideFence;
         overrideInfo.displayFrame = kOverrideDisplayFrame;
         overrideInfo.dataspace = kOverrideDataspace;
@@ -850,6 +851,7 @@
                                                    84.f / 255.f};
 const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044};
 const Rect OutputLayerWriteStateToHWCTest::kOverrideDisplayFrame{1002, 1003, 1004, 20044};
+const FloatRect OutputLayerWriteStateToHWCTest::kOverrideSourceCrop{0.f, 0.f, 4.f, 5.f};
 const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{
         Rect{1005, 1006, 1007, 1008}};
 const Region OutputLayerWriteStateToHWCTest::kOverrideVisibleRegion{Rect{1006, 1007, 1008, 1009}};
@@ -863,6 +865,10 @@
 native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle =
         reinterpret_cast<native_handle_t*>(1031);
 const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
+const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kOverrideBuffer =
+        new GraphicBuffer(4, 5, PIXEL_FORMAT_RGBA_8888,
+                          AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+                                  AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
 const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
 const sp<Fence> OutputLayerWriteStateToHWCTest::kOverrideFence = new Fence();
 const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key =
@@ -1050,11 +1056,11 @@
     mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
     includeOverrideInfo();
 
-    expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideDisplayFrame.toFloatRect(),
-                              kOverrideBufferTransform, kOverrideBlendMode, kOverrideAlpha);
+    expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
+                              kOverrideBlendMode, kOverrideAlpha);
     expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
                               kOverrideSurfaceDamage);
-    expectSetHdrMetadataAndBufferCalls(kOverrideBuffer->getBuffer(), kOverrideFence);
+    expectSetHdrMetadataAndBufferCalls(kOverrideBuffer, kOverrideFence);
     expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 1d21ca0..a331b53 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -291,10 +291,11 @@
 }
 
 TEST_F(CachedSetTest, render) {
-    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
-    sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
-    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
-    sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE;
+    // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+    CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+    CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
 
     CachedSet cachedSet(layer1);
     cachedSet.append(CachedSet(layer2));
@@ -311,7 +312,7 @@
                                 const std::vector<const renderengine::LayerSettings*>& layers,
                                 const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
                                 base::unique_fd&&, base::unique_fd*) -> size_t {
-        EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.physicalDisplay);
+        EXPECT_EQ(Rect(-1, -1, 9, 4), displaySettings.physicalDisplay);
         EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
         EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
                   displaySettings.orientation);
@@ -328,10 +329,55 @@
     cachedSet.render(mRenderEngine, mOutputState);
     expectReadyBuffer(cachedSet);
 
-    EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getOutputSpace().content);
-    EXPECT_EQ(Rect(mOutputState.framebufferSpace.bounds.getWidth(),
-                   mOutputState.framebufferSpace.bounds.getHeight()),
-              cachedSet.getOutputSpace().bounds);
+    EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+
+    // Now check that appending a new cached set properly cleans up RenderEngine resources.
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+    cachedSet.append(CachedSet(layer3));
+}
+
+TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) {
+    // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+    CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+    CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
+
+    CachedSet cachedSet(layer1);
+    cachedSet.append(CachedSet(layer2));
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+    clientCompList1.push_back({});
+    clientCompList1[0].alpha = 0.5f;
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+    clientCompList2.push_back({});
+    clientCompList2[0].alpha = 0.75f;
+
+    mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5));
+
+    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+                                const std::vector<const renderengine::LayerSettings*>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&, base::unique_fd*) -> size_t {
+        EXPECT_EQ(Rect(1, 2, 9, 4), displaySettings.physicalDisplay);
+        EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
+        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+                  displaySettings.orientation);
+        EXPECT_EQ(0.5f, layers[0]->alpha);
+        EXPECT_EQ(0.75f, layers[1]->alpha);
+        EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
+
+        return NO_ERROR;
+    };
+
+    EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
+    EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    cachedSet.render(mRenderEngine, mOutputState);
+    expectReadyBuffer(cachedSet);
+
+    EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
 
     // Now check that appending a new cached set properly cleans up RenderEngine resources.
     CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index d75b636..71757f6 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -277,11 +277,7 @@
     mTime += 200ms;
     expectAllLayersFlattened(layers);
 
-    EXPECT_EQ(overrideDisplaySpace.bounds,
-              Rect(mOutputState.framebufferSpace.bounds.getWidth(),
-                   mOutputState.framebufferSpace.bounds.getHeight()));
-    EXPECT_EQ(overrideDisplaySpace.content, Rect(0, 0, 2, 2));
-    EXPECT_EQ(overrideDisplaySpace.orientation, mOutputState.framebufferSpace.orientation);
+    EXPECT_EQ(overrideDisplaySpace, mOutputState.framebufferSpace);
 }
 
 TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsDamageRegions) {
diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp
index 0bc2d3e..23db805 100644
--- a/services/surfaceflinger/FpsReporter.cpp
+++ b/services/surfaceflinger/FpsReporter.cpp
@@ -26,10 +26,18 @@
 
 namespace android {
 
-FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger)
-      : mFrameTimeline(frameTimeline), mFlinger(flinger) {}
+FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger,
+                         std::unique_ptr<Clock> clock)
+      : mFrameTimeline(frameTimeline), mFlinger(flinger), mClock(std::move(clock)) {
+    LOG_ALWAYS_FATAL_IF(mClock == nullptr, "Passed in null clock when constructing FpsReporter!");
+}
 
-void FpsReporter::dispatchLayerFps() const {
+void FpsReporter::dispatchLayerFps() {
+    const auto now = mClock->now();
+    if (now - mLastDispatch < kMinDispatchDuration) {
+        return;
+    }
+
     std::vector<TrackedListener> localListeners;
     {
         std::scoped_lock lock(mMutex);
@@ -71,6 +79,8 @@
 
         listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds));
     }
+
+    mLastDispatch = now;
 }
 
 void FpsReporter::binderDied(const wp<IBinder>& who) {
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
index 1cec295..bd7b9a5 100644
--- a/services/surfaceflinger/FpsReporter.h
+++ b/services/surfaceflinger/FpsReporter.h
@@ -22,6 +22,7 @@
 
 #include <unordered_map>
 
+#include "Clock.h"
 #include "FrameTimeline/FrameTimeline.h"
 
 namespace android {
@@ -31,12 +32,13 @@
 
 class FpsReporter : public IBinder::DeathRecipient {
 public:
-    FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger);
+    FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger,
+                std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
 
     // Dispatches updated layer fps values for the registered listeners
     // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
     // must be held when calling this method.
-    void dispatchLayerFps() const EXCLUDES(mMutex);
+    void dispatchLayerFps() EXCLUDES(mMutex);
 
     // Override for IBinder::DeathRecipient
     void binderDied(const wp<IBinder>&) override;
@@ -61,6 +63,10 @@
 
     frametimeline::FrameTimeline& mFrameTimeline;
     SurfaceFlinger& mFlinger;
+    static const constexpr std::chrono::steady_clock::duration kMinDispatchDuration =
+            std::chrono::milliseconds(500);
+    std::unique_ptr<Clock> mClock;
+    std::chrono::steady_clock::time_point mLastDispatch;
     std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
 };
 
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index be552c6..0033dbe 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -256,6 +256,10 @@
         protoJank |= FrameTimelineEvent::JANK_UNKNOWN;
         jankType &= ~JankType::Unknown;
     }
+    if (jankType & JankType::SurfaceFlingerStuffing) {
+        protoJank |= FrameTimelineEvent::JANK_SF_STUFFING;
+        jankType &= ~JankType::SurfaceFlingerStuffing;
+    }
 
     // jankType should be 0 if all types of jank were checked for.
     LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
@@ -875,7 +879,8 @@
     mGpuFence = gpuFence;
 }
 
-void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync) {
+void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
+                                               nsecs_t previousPresentTime) {
     if (mPredictionState == PredictionState::Expired ||
         mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
         // Cannot do jank classification with expired predictions or invalid signal times. Set the
@@ -949,7 +954,15 @@
                 mJankType = JankType::Unknown;
             }
         } else if (mFramePresentMetadata == FramePresentMetadata::LatePresent) {
-            if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+            if (std::abs(mSurfaceFlingerPredictions.presentTime - previousPresentTime) <=
+                        mJankClassificationThresholds.presentThreshold ||
+                previousPresentTime > mSurfaceFlingerPredictions.presentTime) {
+                // The previous frame was either presented in the current frame's expected vsync or
+                // it was presented even later than the current frame's expected vsync.
+                mJankType = JankType::SurfaceFlingerStuffing;
+            }
+            if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish &&
+                !(mJankType & JankType::SurfaceFlingerStuffing)) {
                 // Finish on time, Present late
                 if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
                     deltaToVsync >= (mRefreshRate.getPeriodNsecs() -
@@ -963,11 +976,12 @@
                     mJankType = JankType::PredictionError;
                 }
             } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
-                if (mFrameStartMetadata == FrameStartMetadata::LateStart) {
-                    // Late start, Late finish, Late Present
-                    mJankType = JankType::SurfaceFlingerScheduling;
-                } else {
-                    // OnTime start, Finish late, Present late
+                if (!(mJankType & JankType::SurfaceFlingerStuffing) ||
+                    mSurfaceFlingerActuals.presentTime - previousPresentTime >
+                            mRefreshRate.getPeriodNsecs() +
+                                    mJankClassificationThresholds.presentThreshold) {
+                    // Classify CPU vs GPU if SF wasn't stuffed or if SF was stuffed but this frame
+                    // was presented more than a vsync late.
                     if (mGpuFence != FenceTime::NO_FENCE &&
                         mSurfaceFlingerActuals.endTime - mSurfaceFlingerActuals.startTime <
                                 mRefreshRate.getPeriodNsecs()) {
@@ -989,11 +1003,11 @@
     }
 }
 
-void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) {
+void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime, nsecs_t previousPresentTime) {
     mSurfaceFlingerActuals.presentTime = signalTime;
     nsecs_t deadlineDelta = 0;
     nsecs_t deltaToVsync = 0;
-    classifyJank(deadlineDelta, deltaToVsync);
+    classifyJank(deadlineDelta, deltaToVsync, previousPresentTime);
 
     for (auto& surfaceFrame : mSurfaceFrames) {
         surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate, deadlineDelta, deltaToVsync);
@@ -1158,8 +1172,9 @@
             }
         }
         auto& displayFrame = pendingPresentFence.second;
-        displayFrame->onPresent(signalTime);
+        displayFrame->onPresent(signalTime, mPreviousPresentTime);
         displayFrame->trace(mSurfaceFlingerPid);
+        mPreviousPresentTime = signalTime;
 
         mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
         --i;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 41f4978..0563a53 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -370,7 +370,7 @@
         void onSfWakeUp(int64_t token, Fps refreshRate, std::optional<TimelineItem> predictions,
                         nsecs_t wakeUpTime);
         // Sets the appropriate metadata and classifies the jank.
-        void onPresent(nsecs_t signalTime);
+        void onPresent(nsecs_t signalTime, nsecs_t previousPresentTime);
         // Adds the provided SurfaceFrame to the current display frame.
         void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame);
 
@@ -398,7 +398,8 @@
         void dump(std::string& result, nsecs_t baseTime) const;
         void tracePredictions(pid_t surfaceFlingerPid) const;
         void traceActuals(pid_t surfaceFlingerPid) const;
-        void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync);
+        void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
+                          nsecs_t previousPresentTime);
 
         int64_t mToken = FrameTimelineInfo::INVALID_VSYNC_ID;
 
@@ -480,6 +481,7 @@
     uint32_t mMaxDisplayFrames;
     std::shared_ptr<TimeStats> mTimeStats;
     const pid_t mSurfaceFlingerPid;
+    nsecs_t mPreviousPresentTime = 0;
     const JankClassificationThresholds mJankClassificationThresholds;
     static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
     // The initial container size for the vector<SurfaceFrames> inside display frame. Although
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 4461420..7707aaf 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -632,6 +632,8 @@
     if (!targetSettings.disableBlurs) {
         layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
         layerSettings.blurRegions = getBlurRegions();
+        layerSettings.blurRegionTransform =
+                getActiveTransform(getDrawingState()).inverse().asMatrix4();
     }
     layerSettings.stretchEffect = getDrawingState().stretchEffect;
     // Record the name of the layer for debugging further down the stack.
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 688a2c3..a83408b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -274,6 +274,8 @@
 
         // Stretch effect to apply to this layer
         StretchEffect stretchEffect;
+
+        Rect bufferCrop;
     };
 
     /*
@@ -885,6 +887,7 @@
     bool setStretchEffect(const StretchEffect& effect);
     StretchEffect getStretchEffect() const;
 
+    virtual bool setBufferCrop(const Rect& /* bufferCrop */) { return false; }
     virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; }
     virtual std::string getPendingBufferCounterName() { return ""; }
 
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index d659398..16f041a 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -15,7 +15,6 @@
  */
 
 #include "OneShotTimer.h"
-
 #include <utils/Log.h>
 #include <utils/Timers.h>
 #include <chrono>
@@ -40,14 +39,9 @@
 namespace android {
 namespace scheduler {
 
-std::chrono::steady_clock::time_point OneShotTimer::Clock::now() const {
-    return std::chrono::steady_clock::now();
-}
-
 OneShotTimer::OneShotTimer(std::string name, const Interval& interval,
                            const ResetCallback& resetCallback,
-                           const TimeoutCallback& timeoutCallback,
-                           std::unique_ptr<OneShotTimer::Clock> clock)
+                           const TimeoutCallback& timeoutCallback, std::unique_ptr<Clock> clock)
       : mClock(std::move(clock)),
         mName(std::move(name)),
         mInterval(interval),
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index 7285427..09265bb 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -20,6 +20,7 @@
 #include <chrono>
 #include <condition_variable>
 #include <thread>
+#include "../Clock.h"
 
 #include <android-base/thread_annotations.h>
 
@@ -36,17 +37,9 @@
     using ResetCallback = std::function<void()>;
     using TimeoutCallback = std::function<void()>;
 
-    class Clock {
-    public:
-        Clock() = default;
-        virtual ~Clock() = default;
-
-        virtual std::chrono::steady_clock::time_point now() const;
-    };
-
     OneShotTimer(std::string name, const Interval& interval, const ResetCallback& resetCallback,
                  const TimeoutCallback& timeoutCallback,
-                 std::unique_ptr<OneShotTimer::Clock> = std::make_unique<OneShotTimer::Clock>());
+                 std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
     ~OneShotTimer();
 
     // Initializes and turns on the idle timer.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 57bd045..fac2c65 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -583,13 +583,9 @@
 
     scheduler::LayerHistory::LayerVoteType voteType;
 
-    if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
+    if (!mOptions.useContentDetection ||
+        layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
         voteType = scheduler::LayerHistory::LayerVoteType::NoVote;
-    } else if (!mOptions.useContentDetection) {
-        // If the content detection feature is off, all layers are registered at Max. We still keep
-        // the layer history, since we use it for other features (like Frame Rate API), so layers
-        // still need to be registered.
-        voteType = scheduler::LayerHistory::LayerVoteType::Max;
     } else if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
         // Running Wallpaper at Min is considered as part of content detection.
         voteType = scheduler::LayerHistory::LayerVoteType::Min;
@@ -597,6 +593,9 @@
         voteType = scheduler::LayerHistory::LayerVoteType::Heuristic;
     }
 
+    // If the content detection feature is off, we still keep the layer history,
+    // since we use it for other features (like Frame Rate API), so layers
+    // still need to be registered.
     mLayerHistory->registerLayer(layer, voteType);
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b048682..2dace92 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4033,6 +4033,11 @@
             flags |= eTraversalNeeded;
         }
     }
+    if (what & layer_state_t::eBufferCropChanged) {
+        if (layer->setBufferCrop(s.bufferCrop)) {
+            flags |= eTraversalNeeded;
+        }
+    }
     // This has to happen after we reparent children because when we reparent to null we remove
     // child layers from current state and remove its relative z. If the children are reparented in
     // the same transaction, then we have to make sure we reparent the children first so we do not
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 3d82afa..10d58a6 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -427,8 +427,8 @@
     return true;
 }
 
-static int32_t clampToSmallestBucket(Fps fps, size_t bucketWidth) {
-    return (fps.getIntValue() / bucketWidth) * bucketWidth;
+static int32_t clampToNearestBucket(Fps fps, size_t bucketWidth) {
+    return std::round(fps.getValue() / bucketWidth) * bucketWidth;
 }
 
 void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
@@ -441,10 +441,10 @@
     TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
     std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
     const int32_t refreshRateBucket =
-            clampToSmallestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
+            clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
     const int32_t renderRateBucket =
-            clampToSmallestBucket(renderRate ? *renderRate : displayRefreshRate,
-                                  RENDER_RATE_BUCKET_WIDTH);
+            clampToNearestBucket(renderRate ? *renderRate : displayRefreshRate,
+                                 RENDER_RATE_BUCKET_WIDTH);
     while (!timeRecords.empty()) {
         if (!recordReadyLocked(layerId, &timeRecords[0])) break;
         ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerId,
@@ -799,10 +799,10 @@
     static const std::string kDefaultLayerName = "none";
 
     const int32_t refreshRateBucket =
-            clampToSmallestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
+            clampToNearestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
     const int32_t renderRateBucket =
-            clampToSmallestBucket(info.renderRate ? *info.renderRate : info.refreshRate,
-                                  RENDER_RATE_BUCKET_WIDTH);
+            clampToNearestBucket(info.renderRate ? *info.renderRate : info.refreshRate,
+                                 RENDER_RATE_BUCKET_WIDTH);
     const TimeStatsHelper::TimelineStatsKey timelineKey = {refreshRateBucket, renderRateBucket};
 
     if (!mTimeStats.stats.count(timelineKey)) {
@@ -1021,6 +1021,7 @@
 
 void TimeStats::clearAll() {
     std::lock_guard<std::mutex> lock(mMutex);
+    mTimeStats.stats.clear();
     clearGlobalLocked();
     clearLayersLocked();
 }
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 011ff70..965aac3 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -767,7 +767,8 @@
     EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
 }
 
-TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) {
+// TODO (b/183181768): Fix & re-enable
+TEST_F(LayerCallbackTest, DISABLED_MultipleTransactions_SingleFrame) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
 
@@ -938,7 +939,8 @@
     EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
 }
 
-TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) {
+// TODO (b/183181768): Fix & re-enable
+TEST_F(LayerCallbackTest, DISABLED_DesiredPresentTime_OutOfOrder) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
 
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 7581cd3..c8eeac6 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -1336,7 +1336,8 @@
                                        Color::GREEN, true /* filtered */);
 }
 
-TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) {
+// TODO (b/186543004): Fix & re-enable
+TEST_P(LayerRenderTypeTransactionTest, DISABLED_SetFenceBasic_BufferState) {
     sp<SurfaceControl> layer;
     Transaction transaction;
     ASSERT_NO_FATAL_FAILURE(
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index a2291b2..dec0ff5 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -28,6 +28,7 @@
 #include "FpsReporter.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
+#include "fake/FakeClock.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockFrameTimeline.h"
@@ -91,7 +92,9 @@
     sp<Layer> mUnrelated;
 
     sp<TestableFpsListener> mFpsListener;
-    sp<FpsReporter> mFpsReporter = new FpsReporter(mFrameTimeline, *(mFlinger.flinger()));
+    fake::FakeClock* mClock = new fake::FakeClock();
+    sp<FpsReporter> mFpsReporter =
+            new FpsReporter(mFrameTimeline, *(mFlinger.flinger()), std::unique_ptr<Clock>(mClock));
 };
 
 FpsReporterTest::FpsReporterTest() {
@@ -178,6 +181,7 @@
             .WillOnce(Return(expectedFps));
 
     mFpsReporter->addListener(mFpsListener, kTaskId);
+    mClock->advanceTime(600ms);
     mFpsReporter->dispatchLayerFps();
     EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps);
     mFpsReporter->removeListener(mFpsListener);
@@ -187,5 +191,34 @@
     mFpsReporter->dispatchLayerFps();
 }
 
+TEST_F(FpsReporterTest, rateLimits) {
+    const constexpr int32_t kTaskId = 12;
+    LayerMetadata targetMetadata;
+    targetMetadata.setInt32(METADATA_TASK_ID, kTaskId);
+    mTarget = createBufferStateLayer(targetMetadata);
+    mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget);
+
+    float firstFps = 44.0;
+    float secondFps = 53.0;
+
+    EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(mTarget->getSequence())))
+            .WillOnce(Return(firstFps))
+            .WillOnce(Return(secondFps));
+
+    mFpsReporter->addListener(mFpsListener, kTaskId);
+    mClock->advanceTime(600ms);
+    mFpsReporter->dispatchLayerFps();
+    EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+    mClock->advanceTime(200ms);
+    mFpsReporter->dispatchLayerFps();
+    EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+    mClock->advanceTime(200ms);
+    mFpsReporter->dispatchLayerFps();
+    EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+    mClock->advanceTime(200ms);
+    mFpsReporter->dispatchLayerFps();
+    EXPECT_EQ(secondFps, mFpsListener->lastReportedFps);
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 8a3f561..6ed6148 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -1561,13 +1561,22 @@
     /*
      * Case 1 - cpu time > vsync period but combined time > deadline > deadline -> cpudeadlinemissed
      * Case 2 - cpu time < vsync period but combined time > deadline -> gpudeadlinemissed
+     * Case 3 - previous frame ran longer -> sf_stuffing
+     * Case 4 - Long cpu under SF stuffing -> cpudeadlinemissed
      */
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto gpuFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto gpuFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto gpuFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+    int64_t sfToken3 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+    int64_t sfToken4 = mTokenManager->generateTokenForPredictions({112, 120, 120});
+
     // case 1 - cpu time = 33 - 12 = 21, vsync period = 11
     mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11));
     mFrameTimeline->setSfPresent(33, presentFence1, gpuFence1);
@@ -1578,12 +1587,12 @@
     // Fences haven't been flushed yet, so it should be 0
     EXPECT_EQ(displayFrame0->getActuals().presentTime, 0);
 
-    // case 2 - cpu time = 56 - 52 = 4, vsync period = 11
-    mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+    // case 2 - cpu time = 56 - 52 = 4, vsync period = 30
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(30));
     mFrameTimeline->setSfPresent(56, presentFence2, gpuFence2);
     auto displayFrame1 = getDisplayFrame(1);
-    gpuFence2->signalForTest(66);
-    presentFence2->signalForTest(71);
+    gpuFence2->signalForTest(76);
+    presentFence2->signalForTest(90);
 
     EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
     // Fences have flushed for first displayFrame, so the present timestamps should be updated
@@ -1592,35 +1601,41 @@
     EXPECT_EQ(displayFrame0->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
     EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
 
-    addEmptyDisplayFrame();
+    // case 3 - cpu time = 86 - 82 = 4, vsync period = 30
+    mFrameTimeline->setSfWakeUp(sfToken3, 106, Fps::fromPeriodNsecs(30));
+    mFrameTimeline->setSfPresent(112, presentFence3, gpuFence3);
+    auto displayFrame2 = getDisplayFrame(2);
+    gpuFence3->signalForTest(116);
+    presentFence3->signalForTest(120);
 
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
     // Fences have flushed for second displayFrame, so the present timestamps should be updated
-    EXPECT_EQ(displayFrame1->getActuals().presentTime, 71);
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 90);
     EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
     EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
     EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
-}
 
-TEST_F(FrameTimelineTest, jankClassification_displayFrameLateStartLateFinishLatePresent) {
-    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
-    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
-    mFrameTimeline->setSfWakeUp(sfToken1, 26, Fps::fromPeriodNsecs(11));
-    mFrameTimeline->setSfPresent(36, presentFence1);
-    auto displayFrame = getDisplayFrame(0);
-    presentFence1->signalForTest(52);
+    // case 4 - cpu time = 86 - 82 = 4, vsync period = 30
+    mFrameTimeline->setSfWakeUp(sfToken4, 120, Fps::fromPeriodNsecs(30));
+    mFrameTimeline->setSfPresent(140, presentFence4, gpuFence4);
+    auto displayFrame3 = getDisplayFrame(3);
+    gpuFence4->signalForTest(156);
+    presentFence4->signalForTest(180);
 
-    // Fences haven't been flushed yet, so it should be 0
-    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+    EXPECT_EQ(displayFrame3->getActuals().presentTime, 0);
+    // Fences have flushed for third displayFrame, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 120);
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerStuffing);
 
     addEmptyDisplayFrame();
-    displayFrame = getDisplayFrame(0);
 
-    // Fences have flushed, so the present timestamps should be updated
-    EXPECT_EQ(displayFrame->getActuals().presentTime, 52);
-    EXPECT_EQ(displayFrame->getFrameStartMetadata(), FrameStartMetadata::LateStart);
-    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
-    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
-    EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+    // Fences have flushed for third displayFrame, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame3->getActuals().presentTime, 180);
+    EXPECT_EQ(displayFrame3->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame3->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame3->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
 }
 
 TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) {
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index a1f0588..6916764 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -23,23 +23,13 @@
 
 #include "AsyncCallRecorder.h"
 #include "Scheduler/OneShotTimer.h"
+#include "fake/FakeClock.h"
 
 using namespace std::chrono_literals;
 
 namespace android {
 namespace scheduler {
 
-class FakeClock : public OneShotTimer::Clock {
-public:
-    virtual ~FakeClock() = default;
-    std::chrono::steady_clock::time_point now() const override { return mNow; }
-
-    void advanceTime(std::chrono::nanoseconds delta) { mNow += delta; }
-
-private:
-    std::chrono::steady_clock::time_point mNow;
-};
-
 class OneShotTimerTest : public testing::Test {
 protected:
     OneShotTimerTest() = default;
@@ -58,17 +48,17 @@
 
 namespace {
 TEST_F(OneShotTimerTest, createAndDestroyTest) {
-    FakeClock* clock = new FakeClock();
+    fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>(
-            "TestTimer", 3ms, [] {}, [] {}, std::unique_ptr<FakeClock>(clock));
+            "TestTimer", 3ms, [] {}, [] {}, std::unique_ptr<fake::FakeClock>(clock));
 }
 
 TEST_F(OneShotTimerTest, startStopTest) {
-    FakeClock* clock = new FakeClock();
+    fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
                                                            mExpiredTimerCallback.getInvocable(),
-                                                           std::unique_ptr<FakeClock>(clock));
+                                                           std::unique_ptr<fake::FakeClock>(clock));
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
@@ -83,11 +73,11 @@
 }
 
 TEST_F(OneShotTimerTest, resetTest) {
-    FakeClock* clock = new FakeClock();
+    fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
                                                            mExpiredTimerCallback.getInvocable(),
-                                                           std::unique_ptr<FakeClock>(clock));
+                                                           std::unique_ptr<fake::FakeClock>(clock));
 
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
@@ -105,11 +95,11 @@
 }
 
 TEST_F(OneShotTimerTest, resetBackToBackTest) {
-    FakeClock* clock = new FakeClock();
+    fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
                                                            mExpiredTimerCallback.getInvocable(),
-                                                           std::unique_ptr<FakeClock>(clock));
+                                                           std::unique_ptr<fake::FakeClock>(clock));
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
 
@@ -139,11 +129,11 @@
 }
 
 TEST_F(OneShotTimerTest, startNotCalledTest) {
-    FakeClock* clock = new FakeClock();
+    fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
                                                            mExpiredTimerCallback.getInvocable(),
-                                                           std::unique_ptr<FakeClock>(clock));
+                                                           std::unique_ptr<fake::FakeClock>(clock));
     // The start hasn't happened, so the callback does not happen.
     EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
@@ -155,11 +145,11 @@
 }
 
 TEST_F(OneShotTimerTest, idleTimerIdlesTest) {
-    FakeClock* clock = new FakeClock();
+    fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
                                                            mExpiredTimerCallback.getInvocable(),
-                                                           std::unique_ptr<FakeClock>(clock));
+                                                           std::unique_ptr<fake::FakeClock>(clock));
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     clock->advanceTime(2ms);
@@ -180,11 +170,11 @@
 }
 
 TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) {
-    FakeClock* clock = new FakeClock();
+    fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
                                                            mExpiredTimerCallback.getInvocable(),
-                                                           std::unique_ptr<FakeClock>(clock));
+                                                           std::unique_ptr<fake::FakeClock>(clock));
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
 
@@ -197,11 +187,11 @@
 }
 
 TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) {
-    FakeClock* clock = new FakeClock();
+    fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
                                                            mExpiredTimerCallback.getInvocable(),
-                                                           std::unique_ptr<FakeClock>(clock));
+                                                           std::unique_ptr<fake::FakeClock>(clock));
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     clock->advanceTime(2ms);
@@ -215,11 +205,11 @@
 }
 
 TEST_F(OneShotTimerTest, noCallbacksAfterStopTest) {
-    FakeClock* clock = new FakeClock();
+    fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
                                                            mExpiredTimerCallback.getInvocable(),
-                                                           std::unique_ptr<FakeClock>(clock));
+                                                           std::unique_ptr<fake::FakeClock>(clock));
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 188ea75..ff53a7b 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -897,24 +897,24 @@
     EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0"));
     EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
     EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
-    std::string expectedResult = "totalTimelineFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "jankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "sfLongCpuJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "sfLongGpuJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "sfUnattributedJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "appUnattributedJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "sfSchedulingJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
+    std::string expectedResult = "totalTimelineFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "jankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "sfLongCpuJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "sfLongGpuJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "sfUnattributedJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "appUnattributedJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "sfSchedulingJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "sfPredictionErrorJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "appBufferStuffingJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
 }
 
 TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
@@ -1348,6 +1348,30 @@
     }
 }
 
+TEST_F(TimeStatsTest, refreshRateIsClampedToNearestBucket) {
+    // this stat is not in the proto so verify by checking the string dump
+    const auto verifyRefreshRateBucket = [&](Fps fps, int32_t bucket) {
+        EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
+        EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+        insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+        mTimeStats->incrementJankyFrames(
+                {fps, std::nullopt, UID_0, genLayerName(LAYER_ID_0), JankType::None, 0, 0, 0});
+        const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+        std::string expectedResult = "displayRefreshRate = " + std::to_string(bucket) + " fps";
+        EXPECT_THAT(result, HasSubstr(expectedResult)) << "failed for " << fps;
+    };
+
+    verifyRefreshRateBucket(Fps(91.f), 90);
+    verifyRefreshRateBucket(Fps(89.f), 90);
+
+    verifyRefreshRateBucket(Fps(61.f), 60);
+    verifyRefreshRateBucket(Fps(59.f), 60);
+
+    verifyRefreshRateBucket(Fps(31.f), 30);
+    verifyRefreshRateBucket(Fps(29.f), 30);
+}
+
 } // namespace
 } // namespace android
 
diff --git a/services/surfaceflinger/tests/unittests/fake/FakeClock.h b/services/surfaceflinger/tests/unittests/fake/FakeClock.h
new file mode 100644
index 0000000..6d9c764
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/fake/FakeClock.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 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 "../../Clock.h"
+
+namespace android::fake {
+
+class FakeClock : public Clock {
+public:
+    virtual ~FakeClock() = default;
+    std::chrono::steady_clock::time_point now() const override { return mNow; }
+
+    void advanceTime(std::chrono::nanoseconds delta) { mNow += delta; }
+
+private:
+    std::chrono::steady_clock::time_point mNow;
+};
+
+} // namespace android::fake
\ No newline at end of file
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 67cd875..440c5b1 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -29,17 +29,14 @@
     unversioned_until: "current",
 }
 
-llndk_library {
-    name: "libvulkan.llndk",
-    symbol_file: "libvulkan.map.txt",
-    export_llndk_headers: [
-        "vulkan_headers_llndk",
-    ],
-}
-
 cc_library_shared {
     name: "libvulkan",
-    llndk_stubs: "libvulkan.llndk",
+    llndk: {
+        symbol_file: "libvulkan.map.txt",
+        export_llndk_headers: [
+            "vulkan_headers",
+        ],
+    },
     clang: true,
     sanitize: {
         misc_undefined: ["integer"],