Merge "libui: print more fence information when fence is timeout"
diff --git a/build/phone-xhdpi-2048-dalvik-heap.mk b/build/phone-xhdpi-2048-dalvik-heap.mk
index 042a6e6..7ccfc13 100644
--- a/build/phone-xhdpi-2048-dalvik-heap.mk
+++ b/build/phone-xhdpi-2048-dalvik-heap.mk
@@ -14,7 +14,7 @@
 # limitations under the License.
 #
 
-# Provides overrides to configure the Dalvik heap for a 2G phone
+# Provides overrides to configure the Dalvik heap for a 2GB phone
 # 192m of RAM gives enough space for 5 8 megapixel camera bitmaps in RAM.
 
 PRODUCT_PROPERTY_OVERRIDES += \
diff --git a/build/phone-xhdpi-4096-dalvik-heap.mk b/build/phone-xhdpi-4096-dalvik-heap.mk
new file mode 100644
index 0000000..2b84841
--- /dev/null
+++ b/build/phone-xhdpi-4096-dalvik-heap.mk
@@ -0,0 +1,25 @@
+#
+# Copyright 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Provides overrides to configure the Dalvik heap for a 4GB phone
+
+PRODUCT_PROPERTY_OVERRIDES += \
+    dalvik.vm.heapstartsize=8m \
+    dalvik.vm.heapgrowthlimit=192m \
+    dalvik.vm.heapsize=512m \
+    dalvik.vm.heaptargetutilization=0.6 \
+    dalvik.vm.heapminfree=8m \
+    dalvik.vm.heapmaxfree=16m
diff --git a/build/phone-xhdpi-6144-dalvik-heap.mk b/build/phone-xhdpi-6144-dalvik-heap.mk
new file mode 100644
index 0000000..2bacc4a
--- /dev/null
+++ b/build/phone-xhdpi-6144-dalvik-heap.mk
@@ -0,0 +1,25 @@
+#
+# Copyright 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Provides overrides to configure the Dalvik heap for a 6GB phone
+
+PRODUCT_PROPERTY_OVERRIDES += \
+    dalvik.vm.heapstartsize=16m \
+    dalvik.vm.heapgrowthlimit=256m \
+    dalvik.vm.heapsize=512m \
+    dalvik.vm.heaptargetutilization=0.5 \
+    dalvik.vm.heapminfree=8m \
+    dalvik.vm.heapmaxfree=32m
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 5476319..08a31c1 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -20,15 +20,16 @@
         "android.hardware.vibrator@1.2",
         "android.hardware.vibrator@1.3",
         "libbase",
-        "libbinder",
+        "libbinder_ndk",
         "libhidlbase",
         "liblog",
         "libutils",
-        "vintf-vibrator-cpp",
+        "vintf-vibrator-ndk_platform",
     ],
     cflags: [
         "-DLOG_TAG=\"idlcli\"",
     ],
+    vendor_available: true,
 }
 
 cc_library {
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index 2f11923..ca5142d 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -13,13 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
 #define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
 
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <android/binder_manager.h>
 #include <android/hardware/vibrator/1.3/IVibrator.h>
-#include <android/hardware/vibrator/IVibrator.h>
-#include <binder/IServiceManager.h>
 
 #include "utils.h"
 
@@ -39,22 +38,27 @@
 }
 
 template <>
-inline binder::Status NullptrStatus() {
-    using binder::Status;
-    return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+inline ndk::ScopedAStatus NullptrStatus() {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_NULL_POINTER));
 }
 
 template <typename I>
-inline sp<I> getService() {
+inline auto getService() {
     return I::getService();
 }
 
 template <>
-inline sp<hardware::vibrator::IVibrator> getService() {
-    return waitForVintfService<hardware::vibrator::IVibrator>();
+inline auto getService<aidl::android::hardware::vibrator::IVibrator>() {
+    const auto instance =
+            std::string() + aidl::android::hardware::vibrator::IVibrator::descriptor + "/default";
+    auto vibBinder = ndk::SpAIBinder(AServiceManager_getService(instance.c_str()));
+    return aidl::android::hardware::vibrator::IVibrator::fromBinder(vibBinder);
 }
 
 template <typename I>
+using shared_ptr = std::result_of_t<decltype(getService<I>)&()>;
+
+template <typename I>
 class HalWrapper {
 public:
     static std::unique_ptr<HalWrapper> Create() {
@@ -70,10 +74,10 @@
     }
 
 private:
-    HalWrapper(sp<I>&& hal) : mHal(std::move(hal)) {}
+    HalWrapper(shared_ptr<I>&& hal) : mHal(std::move(hal)) {}
 
 private:
-    sp<I> mHal;
+    shared_ptr<I> mHal;
 };
 
 template <typename I>
@@ -95,7 +99,7 @@
 namespace V1_1 = ::android::hardware::vibrator::V1_1;
 namespace V1_2 = ::android::hardware::vibrator::V1_2;
 namespace V1_3 = ::android::hardware::vibrator::V1_3;
-namespace aidl = ::android::hardware::vibrator;
+namespace aidl = ::aidl::android::hardware::vibrator;
 
 } // namespace vibrator
 } // namespace idlcli
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
index 705e40b..4721a5f 100644
--- a/cmds/idlcli/vibrator/CommandCompose.cpp
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -80,7 +80,7 @@
         Status ret;
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else {
             return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
index 30d8587..303a989 100644
--- a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
@@ -48,7 +48,7 @@
 
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::getCapabilities, &cap);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else {
             return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
index b414307..10508bd 100644
--- a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
@@ -50,7 +50,7 @@
 
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::getCompositionDelayMax, &maxDelayMs);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else {
             return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
index 360fc9d..900cb18 100644
--- a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
@@ -50,7 +50,7 @@
 
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::getCompositionSizeMax, &maxSize);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else {
             return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandOff.cpp b/cmds/idlcli/vibrator/CommandOff.cpp
index 53fada0..cedb9fe 100644
--- a/cmds/idlcli/vibrator/CommandOff.cpp
+++ b/cmds/idlcli/vibrator/CommandOff.cpp
@@ -47,7 +47,7 @@
 
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::off);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else if (auto hal = getHal<V1_0::IVibrator>()) {
             auto status = hal->call(&V1_0::IVibrator::off);
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index ccb3c19..4e7e493 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -55,7 +55,7 @@
 
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else if (auto hal = getHal<V1_0::IVibrator>()) {
             auto status = hal->call(&V1_0::IVibrator::on, mDuration);
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index 58d4e0a..69c7e37 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -99,7 +99,7 @@
             auto status =
                     hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect),
                               static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             lengthMs = static_cast<uint32_t>(aidlLengthMs);
             ret = status.isOk() ? OK : ERROR;
         } else {
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
index 33d7eed..8b8058c 100644
--- a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -56,7 +56,7 @@
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::setAmplitude,
                                     static_cast<float>(mAmplitude) / UINT8_MAX);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else if (auto hal = getHal<V1_0::IVibrator>()) {
             auto status = hal->call(&V1_0::IVibrator::setAmplitude, mAmplitude);
diff --git a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
index 5bc827e..1795793 100644
--- a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
+++ b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
@@ -53,7 +53,7 @@
 
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::setExternalControl, mEnable);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else if (auto hal = getHal<V1_3::IVibrator>()) {
             auto status = hal->call(&V1_3::IVibrator::setExternalControl, mEnable);
diff --git a/cmds/installd/.gitignore b/cmds/installd/.gitignore
new file mode 100644
index 0000000..abc921c
--- /dev/null
+++ b/cmds/installd/.gitignore
@@ -0,0 +1,2 @@
+# ignore the files generated by intellij
+.idea
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 75dec37..8ff4dd8 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -13,6 +13,7 @@
     srcs: [
         "CacheItem.cpp",
         "CacheTracker.cpp",
+        "CrateManager.cpp",
         "InstalldNativeService.cpp",
         "QuotaUtils.cpp",
         "dexopt.cpp",
@@ -163,6 +164,7 @@
     name: "installd_aidl",
     srcs: [
         "binder/android/os/IInstalld.aidl",
+        "binder/android/os/storage/CrateMetadata.aidl",
     ],
     path: "binder",
 }
diff --git a/cmds/installd/CrateManager.cpp b/cmds/installd/CrateManager.cpp
new file mode 100644
index 0000000..344aefb
--- /dev/null
+++ b/cmds/installd/CrateManager.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CrateManager.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android/log.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <string>
+#include <utils.h>
+
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+CrateManager::CrateManager(const char* uuid, userid_t userId, const std::string& packageName) {
+    mPackageName = packageName;
+    mRoot = create_data_user_ce_package_path(uuid, userId, (const char*)packageName.c_str());
+    mCratedFoldersRoot = StringPrintf("%s/crates", mRoot.c_str());
+}
+
+CrateManager::~CrateManager() {}
+
+static std::string getValidatedCratedPath(std::string path) {
+    size_t pos = path.rfind("/");
+    if (pos == std::string::npos) {
+        return path;
+    }
+
+    return path.substr(pos + 1, path.length());
+}
+
+void CrateManager::traverseChildDir(const std::string& targetDir,
+    std::function<void(FTSENT*)>& onVisitChildDir) {
+    char* argv[] = {(char*)targetDir.c_str(), nullptr};
+    FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr);
+    if (fts == nullptr) {
+        PLOG(WARNING) << "Failed to fts_open " << targetDir;
+        return;
+    }
+
+    FTSENT* p;
+    while ((p = fts_read(fts)) != nullptr) {
+        switch (p->fts_info) {
+            case FTS_D:
+                if (p->fts_level == 1) {
+                    onVisitChildDir(p);
+                }
+                break;
+            default:
+                break;
+        }
+
+        if (p->fts_level == 1) {
+            fts_set(fts, p, FTS_SKIP);
+        }
+    }
+    fts_close(fts);
+}
+
+void CrateManager::traverseAllPackagesForUser(
+        const std::unique_ptr<std::string>& uuid, userid_t userId,
+        std::function<void(FTSENT*)>& onHandlingPackage) {
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    auto ce_path = create_data_user_ce_path(uuid_, userId);
+    traverseChildDir(ce_path, onHandlingPackage);
+}
+
+void CrateManager::createCrate(
+        CratedFolder cratedFolder,
+        std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+    const char* path = cratedFolder->fts_path;
+    if (path == nullptr || *path == '\0') {
+        return;
+    }
+
+    std::unique_ptr<CrateMetadata> crateMetadata = std::make_unique<CrateMetadata>();
+    crateMetadata->uid = cratedFolder->fts_statp->st_uid;
+    crateMetadata->packageName = mPackageName;
+    crateMetadata->id = getValidatedCratedPath(path);
+
+    onCreateCrate(cratedFolder, crateMetadata);
+}
+
+void CrateManager::traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+    std::function<void(FTSENT*)> onVisitCrateDir = [&](FTSENT* cratedFolder) -> void {
+        createCrate(cratedFolder, onCreateCrate);
+    };
+    traverseChildDir(mCratedFoldersRoot, onVisitCrateDir);
+}
+
+#if CRATE_DEBUG
+void CrateManager::dump(std::unique_ptr<CrateMetadata>& CrateMetadata) {
+    LOG(DEBUG) << "CrateMetadata = {"
+            << "uid : \"" << CrateMetadata->uid
+            << "\", packageName : \"" << CrateMetadata->packageName
+            << "\", id : \"" << CrateMetadata->id
+            << "\"}";
+}
+#endif
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/CrateManager.h b/cmds/installd/CrateManager.h
new file mode 100644
index 0000000..1776622
--- /dev/null
+++ b/cmds/installd/CrateManager.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INSTALLD_CRATE_INFO_MANAGER_H
+#define ANDROID_INSTALLD_CRATE_INFO_MANAGER_H
+
+#include <android/os/storage/CrateMetadata.h>
+#include <cutils/multiuser.h>
+#include <fts.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#ifndef CRATE_DEBUG
+#define CRATE_DEBUG 1
+#endif
+
+namespace android {
+namespace installd {
+
+using android::os::storage::CrateMetadata;
+
+/**
+ * The crated folder actually is a folder that is the first level child director. In order to
+ * distingish between the crated folder and the other FTSENT*, to define the type "CratedFolder"
+ * make the code easy to identify the difference.
+ */
+typedef FTSENT* CratedFolder;
+
+/**
+ * In order to give the users more fine-grained files controlling, the crate information can help
+ * applications' developers to show the more detail information to the users. The crate information
+ * include the Label, Expiration etc..
+ */
+class CrateManager {
+public:
+    CrateManager(const char* uuid, userid_t userId, const std::string& packageName);
+    ~CrateManager();
+
+    void traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+
+    static void traverseChildDir(const std::string& targetDir,
+            std::function<void(FTSENT*)>& onVisitChildDir);
+
+    static void traverseAllPackagesForUser(
+        const std::unique_ptr<std::string>& uuid,
+        userid_t userId,
+        std::function<void(FTSENT*)>& onHandlingPackage);
+
+#if CRATE_DEBUG
+    static void dump(std::unique_ptr<CrateMetadata>& CrateMetadata);
+#endif
+private:
+    std::string mRoot;
+    std::string mCratedFoldersRoot;
+    std::string mPackageName;
+
+    void createCrate(
+        CratedFolder cratedFolder,
+        std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_CRATE_INFO_MANAGER_H
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 72b9850..f8a68b4 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -66,6 +66,7 @@
 #include "view_compiler.h"
 
 #include "CacheTracker.h"
+#include "CrateManager.h"
 #include "MatchExtensionGen.h"
 #include "QuotaUtils.h"
 
@@ -2045,6 +2046,82 @@
     return ok();
 }
 
+binder::Status InstalldNativeService::getAppCrates(
+        const std::unique_ptr<std::string>& uuid,
+        const std::vector<std::string>& packageNames, int32_t userId,
+        std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    for (const auto& packageName : packageNames) {
+        CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    }
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
+            [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+        if (cratedFolder == nullptr) {
+            return;
+        }
+        retVector->push_back(std::move(crateMetadata));
+    };
+
+    for (const auto& packageName : packageNames) {
+#if CRATE_DEBUG
+        LOG(DEBUG) << "packageName = " << packageName;
+#endif
+        auto crateManager = std::make_unique<CrateManager>(uuid_, userId, packageName);
+        crateManager->traverseAllCrates(onCreateCrate);
+    }
+
+#if CRATE_DEBUG
+    LOG(WARNING) << "retVector->size() =" << retVector->size();
+    for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
+        CrateManager::dump(*iter);
+    }
+#endif
+
+    *_aidl_return = std::move(retVector);
+    return ok();
+}
+
+binder::Status InstalldNativeService::getUserCrates(
+        const std::unique_ptr<std::string>& uuid, int32_t userId,
+        std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+
+    std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
+            [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+        if (cratedFolder == nullptr) {
+            return;
+        }
+        retVector->push_back(std::move(crateMetadata));
+    };
+
+    std::function<void(FTSENT*)> onHandingPackage = [&](FTSENT* packageDir) -> void {
+        auto crateManager = std::make_unique<CrateManager>(uuid_, userId, packageDir->fts_name);
+        crateManager->traverseAllCrates(onCreateCrate);
+    };
+    CrateManager::traverseAllPackagesForUser(uuid, userId, onHandingPackage);
+
+#if CRATE_DEBUG
+    LOG(DEBUG) << "retVector->size() =" << retVector->size();
+    for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
+        CrateManager::dump(*iter);
+    }
+#endif
+
+    *_aidl_return = std::move(retVector);
+    return ok();
+}
+
 binder::Status InstalldNativeService::setAppQuota(const std::unique_ptr<std::string>& uuid,
         int32_t userId, int32_t appId, int64_t cacheQuota) {
     ENFORCE_UID(AID_SYSTEM);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index daacd26..bf11002 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -81,6 +81,16 @@
             int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
             std::vector<int64_t>* _aidl_return);
 
+    binder::Status getAppCrates(const std::unique_ptr<std::string>& uuid,
+            const std::vector<std::string>& packageNames,
+            int32_t userId,
+            std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+                    _aidl_return);
+    binder::Status getUserCrates(
+            const std::unique_ptr<std::string>& uuid, int32_t userId,
+            std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+                    _aidl_return);
+
     binder::Status setAppQuota(const std::unique_ptr<std::string>& uuid,
             int32_t userId, int32_t appId, int64_t cacheQuota);
 
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index fda6559..891b26d 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -40,6 +40,14 @@
     long[] getUserSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
     long[] getExternalSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
 
+    @nullable
+    android.os.storage.CrateMetadata[] getAppCrates(
+            @nullable @utf8InCpp String uuid, in @utf8InCpp String[] packageNames,
+             int userId);
+    @nullable
+    android.os.storage.CrateMetadata[] getUserCrates(
+            @nullable @utf8InCpp String uuid, int userId);
+
     void setAppQuota(@nullable @utf8InCpp String uuid, int userId, int appId, long cacheQuota);
 
     void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid,
diff --git a/cmds/installd/binder/android/os/storage/CrateMetadata.aidl b/cmds/installd/binder/android/os/storage/CrateMetadata.aidl
new file mode 100644
index 0000000..bd6d12d
--- /dev/null
+++ b/cmds/installd/binder/android/os/storage/CrateMetadata.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os.storage;
+
+/** {@hide} */
+parcelable CrateMetadata {
+    /**
+     * To tell which uid the crate belong to.
+     * <p>Because installd query all of crates in specified userId, the install may return the list
+     * whose elements have the same crate id but different uid and package name.
+     * It needs to tell the caller the difference between these elements.
+     */
+    int uid;
+
+    /**
+     * To tell which the package the crate belong to.
+     * <p>Because installd query all of crates in specified uid, the install may return the list
+     * whose elements have the same uid and crate id but different package name.
+     * It needs to tell the caller the difference between these elements.
+     */
+    @utf8InCpp String packageName;
+
+    /**
+     * To tell the crate id that is the child directory/folder name in crates
+     * root.
+     */
+    @utf8InCpp String id;
+}
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 4eb1df0..6012822 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -278,6 +278,15 @@
     return "/data/dalvik-cache";
 }
 
+std::string create_system_user_ce_path(userid_t userId) {
+    return StringPrintf("%s/system_ce/%u", create_data_path(nullptr).c_str(), userId);
+}
+
+std::string create_system_user_ce_package_path(userid_t userId, const char* package_name) {
+    check_package_name(package_name);
+    return StringPrintf("%s/%s", create_system_user_ce_path(userId).c_str(), package_name);
+}
+
 // Keep profile paths in sync with ActivityThread and LoadedApk.
 const std::string PROFILE_EXT = ".prof";
 const std::string CURRENT_PROFILE_EXT = ".cur";
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 6a42026..6a39adc 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -82,6 +82,10 @@
 
 std::string create_data_dalvik_cache_path();
 
+std::string create_system_user_ce_path(userid_t userId);
+
+std::string create_system_user_ce_package_path(userid_t userId, const char* package_name);
+
 std::string create_primary_cur_profile_dir_path(userid_t userid);
 std::string create_primary_current_profile_package_dir_path(
         userid_t user, const std::string& package_name);
diff --git a/data/etc/android.hardware.reboot_escrow.xml b/data/etc/android.hardware.reboot_escrow.xml
new file mode 100644
index 0000000..5db9f4c
--- /dev/null
+++ b/data/etc/android.hardware.reboot_escrow.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!--
+     This is the feature indicating that the device has support for the
+     RebootEscrow HAL.
+-->
+
+<permissions>
+    <feature name="android.hardware.reboot_escrow" />
+</permissions>
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 3310722..05f4340 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -645,10 +645,14 @@
  */
 void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong);
 
+#if __ANDROID_API__ >= 30
 /**
  * Return the current ACONFIGURATION_SCREENROUND_* set in the configuration.
+ *
+ * Available since API level 30.
  */
-int32_t AConfiguration_getScreenRound(AConfiguration* config);
+int32_t AConfiguration_getScreenRound(AConfiguration* config) __INTRODUCED_IN(30);
+#endif
 
 /**
  * Set the current screen round in the configuration.
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 4f0b7d3..ca9c608 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -44,16 +44,16 @@
 }  // namespace
 
 static String16 _appops("appops");
-static pthread_mutex_t gTokenMutex = PTHREAD_MUTEX_INITIALIZER;
-static sp<IBinder> gToken;
+static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER;
+static sp<IBinder> gClientId;
 
-static const sp<IBinder>& getToken(const sp<IAppOpsService>& service) {
-    pthread_mutex_lock(&gTokenMutex);
-    if (gToken == nullptr || gToken->pingBinder() != NO_ERROR) {
-        gToken = service->getToken(new BBinder());
+static const sp<IBinder>& getClientId() {
+    pthread_mutex_lock(&gClientIdMutex);
+    if (gClientId == nullptr) {
+        gClientId = new BBinder();
     }
-    pthread_mutex_unlock(&gTokenMutex);
-    return gToken;
+    pthread_mutex_unlock(&gClientIdMutex);
+    return gClientId;
 }
 
 thread_local uint64_t notedAppOpsInThisBinderTransaction[2];
@@ -144,7 +144,7 @@
         const String16& message) {
     sp<IAppOpsService> service = getService();
     int32_t mode = service != nullptr
-            ? service->startOperation(getToken(service), op, uid, callingPackage,
+            ? service->startOperation(getClientId(), op, uid, callingPackage,
                     featureId, startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE;
 
     if (mode == AppOpsManager::MODE_ALLOWED) {
@@ -162,7 +162,7 @@
         const std::unique_ptr<String16>& callingFeatureId) {
     sp<IAppOpsService> service = getService();
     if (service != nullptr) {
-        service->finishOperation(getToken(service), op, uid, callingPackage, callingFeatureId);
+        service->finishOperation(getClientId(), op, uid, callingPackage, callingFeatureId);
     }
 }
 
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index b85a5f2..7384466 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -106,16 +106,6 @@
         remote()->transact(STOP_WATCHING_MODE_TRANSACTION, data, &reply);
     }
 
-    virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
-        data.writeStrongBinder(clientToken);
-        remote()->transact(GET_TOKEN_TRANSACTION, data, &reply);
-        // fail on exception
-        if (reply.readExceptionCode() != 0) return nullptr;
-        return reply.readStrongBinder();
-    }
-
     virtual int32_t permissionToOpCode(const String16& permission) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -251,14 +241,6 @@
             reply->writeNoException();
             return NO_ERROR;
         } break;
-        case GET_TOKEN_TRANSACTION: {
-            CHECK_INTERFACE(IAppOpsService, data, reply);
-            sp<IBinder> clientToken = data.readStrongBinder();
-            sp<IBinder> token = getToken(clientToken);
-            reply->writeNoException();
-            reply->writeStrongBinder(token);
-            return NO_ERROR;
-        } break;
         case PERMISSION_TO_OP_CODE_TRANSACTION: {
             CHECK_INTERFACE(IAppOpsService, data, reply);
             String16 permission = data.readString16();
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 15ba005..68a917e 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -45,7 +45,6 @@
     virtual void startWatchingMode(int32_t op, const String16& packageName,
             const sp<IAppOpsCallback>& callback) = 0;
     virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
-    virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0;
     virtual int32_t permissionToOpCode(const String16& permission) = 0;
     virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid,
             const String16& packageName) = 0;
@@ -62,12 +61,11 @@
         FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3,
         START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4,
         STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
-        GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
-        PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
-        CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
-        NOTE_ASYNC_OP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
-        SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+10,
-        SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+11,
+        PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
+        CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
+        NOTE_ASYNC_OP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
+        SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
+        SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+10,
     };
 
     enum {
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index b762f1e..d02e839 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -28,6 +28,7 @@
 using aidl::android::hardware::graphics::common::BlendMode;
 using aidl::android::hardware::graphics::common::ChromaSiting;
 using aidl::android::hardware::graphics::common::Compression;
+using aidl::android::hardware::graphics::common::Cta861_3;
 using aidl::android::hardware::graphics::common::Dataspace;
 using aidl::android::hardware::graphics::common::ExtendableType;
 using aidl::android::hardware::graphics::common::Interlaced;
@@ -35,7 +36,9 @@
 using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
 using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
 using aidl::android::hardware::graphics::common::Rect;
+using aidl::android::hardware::graphics::common::Smpte2086;
 using aidl::android::hardware::graphics::common::StandardMetadataType;
+using aidl::android::hardware::graphics::common::XyColor;
 
 using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
 
@@ -154,6 +157,13 @@
         return mVec->size() > mOffset;
     }
 
+    size_t getRemainingSize() {
+        if (!mVec) {
+            return 0;
+        }
+        return mVec->size() - mOffset;
+    }
+
 private:
     const hidl_vec<uint8_t>* mVec;
     size_t mOffset = 0;
@@ -218,6 +228,15 @@
     return encodeHelper(input, &outputHidlVec);
 }
 
+template <class T>
+status_t encodeOptional(const MetadataType& metadataType, const std::optional<T>& input,
+                        hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
+    if (!input) {
+        return NO_ERROR;
+    }
+    return encode(metadataType, *input, output, encodeHelper);
+}
+
 /**
  * decode is the main decode function. It takes in a hidl_vec and uses the decodeHelper function to
  * turn the hidl_vec byte stream into T. If an error occurs, the errorHandler function cleans up
@@ -249,13 +268,32 @@
     return NO_ERROR;
 }
 
+template <class T>
+status_t decodeOptional(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                        std::optional<T>* output, DecodeHelper<T> decodeHelper) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+    if (input.size() <= 0) {
+        output->reset();
+        return NO_ERROR;
+    }
+    T tmp;
+    status_t err = decode(metadataType, input, &tmp, decodeHelper);
+    if (!err) {
+        *output = tmp;
+    }
+    return err;
+}
+
 /**
  * Private helper functions
  */
 template <class T>
 status_t encodeInteger(const T& input, OutputHidlVec* output) {
     static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
-                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value);
+                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+                  std::is_same<T, float>::value || std::is_same<T, double>::value);
     if (!output) {
         return BAD_VALUE;
     }
@@ -267,7 +305,8 @@
 template <class T>
 status_t decodeInteger(InputHidlVec* input, T* output) {
     static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
-                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value);
+                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+                  std::is_same<T, float>::value || std::is_same<T, double>::value);
     if (!output) {
         return BAD_VALUE;
     }
@@ -306,6 +345,38 @@
     return input->decode(output, size);
 }
 
+status_t encodeByteVector(const std::vector<uint8_t>& input, OutputHidlVec* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    status_t err = encodeInteger<int64_t>(input.size(), output);
+    if (err) {
+        return err;
+    }
+
+    return output->encode(input.data(), input.size());
+}
+
+status_t decodeByteVector(InputHidlVec* input, std::vector<uint8_t>* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    int64_t size = 0;
+    status_t err = decodeInteger<int64_t>(input, &size);
+    if (err || size < 0) {
+        return err;
+    }
+
+    if (size > input->getRemainingSize()) {
+        return BAD_VALUE;
+    }
+    output->resize(size);
+
+    return input->decode(output->data(), size);
+}
+
 status_t encodeExtendableType(const ExtendableType& input, OutputHidlVec* output) {
     status_t err = encodeString(input.name, output);
     if (err) {
@@ -391,6 +462,30 @@
     return NO_ERROR;
 }
 
+status_t encodeXyColor(const XyColor& input, OutputHidlVec* output) {
+    status_t err = encodeInteger<float>(input.x, output);
+    if (err) {
+        return err;
+    }
+    return encodeInteger<float>(input.y, output);
+}
+
+status_t decodeXyColor(InputHidlVec* input, XyColor* output) {
+    status_t err = decodeInteger<float>(input, &output->x);
+    if (err) {
+        return err;
+    }
+    return decodeInteger<float>(input, &output->y);
+}
+
+void clearXyColor(XyColor* output) {
+    if (!output) {
+        return;
+    }
+    output->x = 0;
+    output->y = 0;
+}
+
 status_t encodeRect(const Rect& input, OutputHidlVec* output) {
     status_t err = encodeInteger<int32_t>(static_cast<int32_t>(input.left), output);
     if (err) {
@@ -635,6 +730,70 @@
     output->clear();
 }
 
+status_t encodeSmpte2086Helper(const Smpte2086& smpte2086, OutputHidlVec* outOutputHidlVec) {
+    status_t err = encodeXyColor(smpte2086.primaryRed, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    err = encodeXyColor(smpte2086.primaryGreen, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    err = encodeXyColor(smpte2086.primaryBlue, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    err = encodeXyColor(smpte2086.whitePoint, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<float>(smpte2086.maxLuminance, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    return encodeInteger<float>(smpte2086.minLuminance, outOutputHidlVec);
+}
+
+status_t decodeSmpte2086Helper(InputHidlVec* inputHidlVec, Smpte2086* outSmpte2086) {
+    status_t err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryRed);
+    if (err) {
+        return err;
+    }
+    err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryGreen);
+    if (err) {
+        return err;
+    }
+    err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryBlue);
+    if (err) {
+        return err;
+    }
+    err = decodeXyColor(inputHidlVec, &outSmpte2086->whitePoint);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<float>(inputHidlVec, &outSmpte2086->maxLuminance);
+    if (err) {
+        return err;
+    }
+    return decodeInteger<float>(inputHidlVec, &outSmpte2086->minLuminance);
+}
+
+status_t encodeCta861_3Helper(const Cta861_3& cta861_3, OutputHidlVec* outOutputHidlVec) {
+    status_t err = encodeInteger<float>(cta861_3.maxContentLightLevel, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    return encodeInteger<float>(cta861_3.maxFrameAverageLightLevel, outOutputHidlVec);
+}
+
+status_t decodeCta861_3Helper(InputHidlVec* inputHidlVec, Cta861_3* outCta861_3) {
+    status_t err = decodeInteger<float>(inputHidlVec, &outCta861_3->maxContentLightLevel);
+    if (err) {
+        return err;
+    }
+    return decodeInteger<float>(inputHidlVec, &outCta861_3->maxFrameAverageLightLevel);
+}
+
 /**
  * Public API functions
  */
@@ -798,6 +957,36 @@
                   decodeInteger);
 }
 
+status_t encodeSmpte2086(const std::optional<Smpte2086>& smpte2086,
+                         hidl_vec<uint8_t>* outSmpte2086) {
+    return encodeOptional(MetadataType_Smpte2086, smpte2086, outSmpte2086, encodeSmpte2086Helper);
+}
+
+status_t decodeSmpte2086(const hidl_vec<uint8_t>& smpte2086,
+                         std::optional<Smpte2086>* outSmpte2086) {
+    return decodeOptional(MetadataType_Smpte2086, smpte2086, outSmpte2086, decodeSmpte2086Helper);
+}
+
+status_t encodeCta861_3(const std::optional<Cta861_3>& cta861_3, hidl_vec<uint8_t>* outCta861_3) {
+    return encodeOptional(MetadataType_Cta861_3, cta861_3, outCta861_3, encodeCta861_3Helper);
+}
+
+status_t decodeCta861_3(const hidl_vec<uint8_t>& cta861_3, std::optional<Cta861_3>* outCta861_3) {
+    return decodeOptional(MetadataType_Cta861_3, cta861_3, outCta861_3, decodeCta861_3Helper);
+}
+
+status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094_40,
+                            hidl_vec<uint8_t>* outSmpte2094_40) {
+    return encodeOptional(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+                          encodeByteVector);
+}
+
+status_t decodeSmpte2094_40(const hidl_vec<uint8_t>& smpte2094_40,
+                            std::optional<std::vector<uint8_t>>* outSmpte2094_40) {
+    return decodeOptional(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+                          decodeByteVector);
+}
+
 bool isStandardMetadataType(const MetadataType& metadataType) {
     return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE,
                          metadataType.name.size());
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.cpp b/libs/gralloc/types/fuzzer/gralloctypes.cpp
index 23c90b8..da8cf97 100644
--- a/libs/gralloc/types/fuzzer/gralloctypes.cpp
+++ b/libs/gralloc/types/fuzzer/gralloctypes.cpp
@@ -60,5 +60,8 @@
     GRALLOCTYPES_DECODE(std::vector<aidl::android::hardware::graphics::common::PlaneLayout>, ::android::gralloc4::decodePlaneLayouts),
     GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::Dataspace, ::android::gralloc4::decodeDataspace),
     GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::BlendMode, ::android::gralloc4::decodeBlendMode),
+    GRALLOCTYPES_DECODE(std::optional<aidl::android::hardware::graphics::common::Smpte2086>, ::android::gralloc4::decodeSmpte2086),
+    GRALLOCTYPES_DECODE(std::optional<aidl::android::hardware::graphics::common::Cta861_3>, ::android::gralloc4::decodeCta861_3),
+    GRALLOCTYPES_DECODE(std::optional<std::vector<uint8_t>>, ::android::gralloc4::decodeSmpte2094_40),
 };
 // clang-format on
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index a5d3bb2..94de8f1 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -17,16 +17,17 @@
 #pragma once
 
 #include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Cta861_3.h>
 #include <aidl/android/hardware/graphics/common/Dataspace.h>
 #include <aidl/android/hardware/graphics/common/ExtendableType.h>
 #include <aidl/android/hardware/graphics/common/Interlaced.h>
 #include <aidl/android/hardware/graphics/common/PlaneLayout.h>
-#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
-#include <aidl/android/hardware/graphics/common/Compression.h>
-#include <aidl/android/hardware/graphics/common/Interlaced.h>
-#include <aidl/android/hardware/graphics/common/StandardMetadataType.h>
 #include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
-
+#include <aidl/android/hardware/graphics/common/Smpte2086.h>
+#include <aidl/android/hardware/graphics/common/StandardMetadataType.h>
+#include <aidl/android/hardware/graphics/common/XyColor.h>
 #include <android/hardware/graphics/mapper/4.0/IMapper.h>
 
 namespace android {
@@ -113,6 +114,22 @@
         GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::BLEND_MODE)
 };
 
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+        MetadataType_Smpte2086 = {GRALLOC4_STANDARD_METADATA_TYPE,
+                                  static_cast<int64_t>(aidl::android::hardware::graphics::common::
+                                                               StandardMetadataType::SMPTE2086)};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+        MetadataType_Cta861_3 = {GRALLOC4_STANDARD_METADATA_TYPE,
+                                 static_cast<int64_t>(aidl::android::hardware::graphics::common::
+                                                              StandardMetadataType::CTA861_3)};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+        MetadataType_Smpte2094_40 = {GRALLOC4_STANDARD_METADATA_TYPE,
+                                     static_cast<int64_t>(
+                                             aidl::android::hardware::graphics::common::
+                                                     StandardMetadataType::SMPTE2094_40)};
+
 /*---------------------------------------------------------------------------------------------*/
 
 /**
@@ -272,6 +289,25 @@
 status_t encodeBlendMode(const aidl::android::hardware::graphics::common::BlendMode& blendMode, android::hardware::hidl_vec<uint8_t>* outBlendMode);
 status_t decodeBlendMode(const android::hardware::hidl_vec<uint8_t>& blendMode, aidl::android::hardware::graphics::common::BlendMode* outBlendMode);
 
+status_t encodeSmpte2086(
+        const std::optional<aidl::android::hardware::graphics::common::Smpte2086>& smpte2086,
+        android::hardware::hidl_vec<uint8_t>* outSmpte2086);
+status_t decodeSmpte2086(
+        const android::hardware::hidl_vec<uint8_t>& smpte2086,
+        std::optional<aidl::android::hardware::graphics::common::Smpte2086>* outSmpte2086);
+
+status_t encodeCta861_3(
+        const std::optional<aidl::android::hardware::graphics::common::Cta861_3>& cta861_3,
+        android::hardware::hidl_vec<uint8_t>* outCta861_3);
+status_t decodeCta861_3(
+        const android::hardware::hidl_vec<uint8_t>& cta861_3,
+        std::optional<aidl::android::hardware::graphics::common::Cta861_3>* outCta861_3);
+
+status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094_40,
+                            android::hardware::hidl_vec<uint8_t>* outSmpte2094_40);
+status_t decodeSmpte2094_40(const android::hardware::hidl_vec<uint8_t>& smpte2094_40,
+                            std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+
 /**
  * The functions below can be used to parse extendable types.
  */
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index b9cf9e1..a5e5693 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -17,10 +17,14 @@
 #undef LOG_TAG
 #define LOG_TAG "BLASTBufferQueue"
 
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #include <gui/BLASTBufferQueue.h>
 #include <gui/BufferItemConsumer.h>
 #include <gui/GLConsumer.h>
 
+#include <utils/Trace.h>
+
 #include <chrono>
 
 using namespace std::chrono_literals;
@@ -29,23 +33,29 @@
 
 BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height)
       : mSurfaceControl(surface),
-        mPendingCallbacks(0),
         mWidth(width),
         mHeight(height),
         mNextTransaction(nullptr) {
     BufferQueue::createBufferQueue(&mProducer, &mConsumer);
     mConsumer->setMaxBufferCount(MAX_BUFFERS);
     mProducer->setMaxDequeuedBufferCount(MAX_BUFFERS - 1);
+    mConsumer->setMaxAcquiredBufferCount(MAX_ACQUIRED_BUFFERS);
     mBufferItemConsumer =
             new BufferItemConsumer(mConsumer, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, 1, true);
-    mBufferItemConsumer->setName(String8("BLAST Consumer"));
+    static int32_t id = 0;
+    auto name = std::string("BLAST Consumer") + std::to_string(id);
+    id++;
+    mBufferItemConsumer->setName(String8(name.c_str()));
     mBufferItemConsumer->setFrameAvailableListener(this);
     mBufferItemConsumer->setBufferFreedListener(this);
     mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
     mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
     mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint());
 
-    mAcquired = false;
+    mNumAcquired = 0;
+    mNumFrameAvailable = 0;
+    mPendingReleaseItem.item = BufferItem();
+    mPendingReleaseItem.releaseFence = nullptr;
 }
 
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
@@ -70,34 +80,46 @@
 void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
                                            const std::vector<SurfaceControlStats>& stats) {
     std::unique_lock _lock{mMutex};
+    ATRACE_CALL();
 
-    if (stats.size() > 0 && !mShadowQueue.empty()) {
-        mBufferItemConsumer->releaseBuffer(mNextCallbackBufferItem,
-                                           stats[0].previousReleaseFence
-                                                   ? stats[0].previousReleaseFence
+    if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
+        if (stats.size() > 0) {
+            mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
+            mTransformHint = stats[0].transformHint;
+        } else {
+            ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
+            mPendingReleaseItem.releaseFence = nullptr;
+        }
+        mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
+                                           mPendingReleaseItem.releaseFence
+                                                   ? mPendingReleaseItem.releaseFence
                                                    : Fence::NO_FENCE);
-        mAcquired = false;
-        mNextCallbackBufferItem = BufferItem();
-        mBufferItemConsumer->setTransformHint(stats[0].transformHint);
+        mNumAcquired--;
+        mPendingReleaseItem.item = BufferItem();
+        mPendingReleaseItem.releaseFence = nullptr;
     }
-    mPendingCallbacks--;
+
+    if (mSubmitted.empty()) {
+        ALOGE("ERROR: callback with no corresponding submitted buffer item");
+    }
+    mPendingReleaseItem.item = std::move(mSubmitted.front());
+    mSubmitted.pop();
     processNextBufferLocked();
     mCallbackCV.notify_all();
     decStrong((void*)transactionCallbackThunk);
 }
 
 void BLASTBufferQueue::processNextBufferLocked() {
-    if (mShadowQueue.empty()) {
+    ATRACE_CALL();
+    if (mNumFrameAvailable == 0) {
         return;
     }
 
-    if (mAcquired) {
+    if (mSurfaceControl == nullptr) {
+        ALOGE("ERROR : surface control is null");
         return;
     }
 
-    BufferItem item = std::move(mShadowQueue.front());
-    mShadowQueue.pop();
-
     SurfaceComposerClient::Transaction localTransaction;
     bool applyTransaction = true;
     SurfaceComposerClient::Transaction* t = &localTransaction;
@@ -107,33 +129,34 @@
         applyTransaction = false;
     }
 
-    mNextCallbackBufferItem = mLastSubmittedBufferItem;
-    mLastSubmittedBufferItem = BufferItem();
+    BufferItem bufferItem;
 
-    status_t status = mBufferItemConsumer->acquireBuffer(&mLastSubmittedBufferItem, -1, false);
-    mAcquired = true;
+    status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, -1, false);
     if (status != OK) {
-        ALOGE("Failed to acquire?");
-    }
-
-    auto buffer = mLastSubmittedBufferItem.mGraphicBuffer;
-
-    if (buffer == nullptr) {
-        ALOGE("Null buffer");
         return;
     }
+    auto buffer = bufferItem.mGraphicBuffer;
+    mNumFrameAvailable--;
+
+    if (buffer == nullptr) {
+        mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
+        return;
+    }
+
+    mNumAcquired++;
+    mSubmitted.push(bufferItem);
 
     // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
     incStrong((void*)transactionCallbackThunk);
 
     t->setBuffer(mSurfaceControl, buffer);
     t->setAcquireFence(mSurfaceControl,
-                       item.mFence ? new Fence(item.mFence->dup()) : Fence::NO_FENCE);
+                       bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
     t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
 
     t->setFrame(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
-    t->setCrop(mSurfaceControl, computeCrop(mLastSubmittedBufferItem));
-    t->setTransform(mSurfaceControl, mLastSubmittedBufferItem.mTransform);
+    t->setCrop(mSurfaceControl, computeCrop(bufferItem));
+    t->setTransform(mSurfaceControl, bufferItem.mTransform);
 
     if (applyTransaction) {
         t->apply();
@@ -147,13 +170,13 @@
     return item.mCrop;
 }
 
-void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
+void BLASTBufferQueue::onFrameAvailable(const BufferItem& /*item*/) {
+    ATRACE_CALL();
     std::lock_guard _lock{mMutex};
 
     // add to shadow queue
-    mShadowQueue.push(item);
+    mNumFrameAvailable++;
     processNextBufferLocked();
-    mPendingCallbacks++;
 }
 
 void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index dd0b470..0a0a03c 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -68,23 +68,25 @@
 
     std::mutex mMutex;
     std::condition_variable mCallbackCV;
-    uint64_t mPendingCallbacks GUARDED_BY(mMutex);
 
     static const int MAX_BUFFERS = 3;
-    struct BufferInfo {
-        sp<GraphicBuffer> buffer;
-        int fence;
+    static const int MAX_ACQUIRED_BUFFERS = 2;
+
+    int32_t mNumFrameAvailable GUARDED_BY(mMutex);
+    int32_t mNumAcquired GUARDED_BY(mMutex);
+
+    struct PendingReleaseItem {
+        BufferItem item;
+        sp<Fence> releaseFence;
     };
 
-    std::queue<const BufferItem> mShadowQueue GUARDED_BY(mMutex);
-    bool mAcquired GUARDED_BY(mMutex);
+    std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
+    PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
 
     int mWidth GUARDED_BY(mMutex);
     int mHeight GUARDED_BY(mMutex);
 
-    BufferItem mLastSubmittedBufferItem GUARDED_BY(mMutex);
-    BufferItem mNextCallbackBufferItem GUARDED_BY(mMutex);
-    sp<Fence> mLastFence GUARDED_BY(mMutex);
+    uint32_t mTransformHint GUARDED_BY(mMutex);
 
     sp<IGraphicBufferConsumer> mConsumer;
     sp<IGraphicBufferProducer> mProducer;
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 48a5cb6..0f618f1 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -69,7 +69,7 @@
 
     void waitForCallbacks() {
         std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
-        while (mBlastBufferQueueAdapter->mPendingCallbacks > 0) {
+        while (mBlastBufferQueueAdapter->mSubmitted.size() > 0) {
             mBlastBufferQueueAdapter->mCallbackCV.wait(lock);
         }
     }
@@ -302,7 +302,7 @@
         igbProducer->cancelBuffer(allocated[i].first, allocated[i].second);
     }
 
-    for (int i = 0; i < 10; i++) {
+    for (int i = 0; i < 100; i++) {
         int slot;
         sp<Fence> fence;
         sp<GraphicBuffer> buf;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 200e1f3..d53a557 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -301,8 +301,8 @@
     if (nWrite < 0) {
         int error = errno;
 #if DEBUG_CHANNEL_MESSAGES
-        ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.c_str(),
-                msg->header.type, error);
+        ALOGD("channel '%s' ~ error sending message of type %d, %s", mName.c_str(),
+              msg->header.type, strerror(error));
 #endif
         if (error == EAGAIN || error == EWOULDBLOCK) {
             return WOULD_BLOCK;
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 2c897cf..30c48c8 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -614,6 +614,24 @@
                outBlendMode);
 }
 
+status_t Gralloc4Mapper::getSmpte2086(buffer_handle_t bufferHandle,
+                                      std::optional<ui::Smpte2086>* outSmpte2086) const {
+    return get(bufferHandle, gralloc4::MetadataType_Smpte2086, gralloc4::decodeSmpte2086,
+               outSmpte2086);
+}
+
+status_t Gralloc4Mapper::getCta861_3(buffer_handle_t bufferHandle,
+                                     std::optional<ui::Cta861_3>* outCta861_3) const {
+    return get(bufferHandle, gralloc4::MetadataType_Cta861_3, gralloc4::decodeCta861_3,
+               outCta861_3);
+}
+
+status_t Gralloc4Mapper::getSmpte2094_40(
+        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_40) const {
+    return get(bufferHandle, gralloc4::MetadataType_Smpte2094_40, gralloc4::decodeSmpte2094_40,
+               outSmpte2094_40);
+}
+
 template <class T>
 status_t Gralloc4Mapper::getDefault(uint32_t width, uint32_t height, PixelFormat format,
                                     uint32_t layerCount, uint64_t usage,
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 8540fd3..d20bd7a 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -286,6 +286,21 @@
     return mMapper->getBlendMode(bufferHandle, outBlendMode);
 }
 
+status_t GraphicBufferMapper::getSmpte2086(buffer_handle_t bufferHandle,
+                                           std::optional<ui::Smpte2086>* outSmpte2086) {
+    return mMapper->getSmpte2086(bufferHandle, outSmpte2086);
+}
+
+status_t GraphicBufferMapper::getCta861_3(buffer_handle_t bufferHandle,
+                                          std::optional<ui::Cta861_3>* outCta861_3) {
+    return mMapper->getCta861_3(bufferHandle, outCta861_3);
+}
+
+status_t GraphicBufferMapper::getSmpte2094_40(
+        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_40) {
+    return mMapper->getSmpte2094_40(bufferHandle, outSmpte2094_40);
+}
+
 status_t GraphicBufferMapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
                                                           PixelFormat format, uint32_t layerCount,
                                                           uint64_t usage,
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index fcd959c..e199648 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -165,6 +165,19 @@
                                   ui::BlendMode* /*outBlendMode*/) const {
         return INVALID_OPERATION;
     }
+    virtual status_t getSmpte2086(buffer_handle_t /*bufferHandle*/,
+                                  std::optional<ui::Smpte2086>* /*outSmpte2086*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getCta861_3(buffer_handle_t /*bufferHandle*/,
+                                 std::optional<ui::Cta861_3>* /*outCta861_3*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getSmpte2094_40(
+            buffer_handle_t /*bufferHandle*/,
+            std::optional<std::vector<uint8_t>>* /*outSmpte2094_40*/) const {
+        return INVALID_OPERATION;
+    }
 
     virtual status_t getDefaultPixelFormatFourCC(uint32_t /*width*/, uint32_t /*height*/,
                                                  PixelFormat /*format*/, uint32_t /*layerCount*/,
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index af7c076..4729cba 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -102,6 +102,12 @@
                              std::vector<ui::PlaneLayout>* outPlaneLayouts) const override;
     status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace) const override;
     status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode) const override;
+    status_t getSmpte2086(buffer_handle_t bufferHandle,
+                          std::optional<ui::Smpte2086>* outSmpte2086) const override;
+    status_t getCta861_3(buffer_handle_t bufferHandle,
+                         std::optional<ui::Cta861_3>* outCta861_3) const override;
+    status_t getSmpte2094_40(buffer_handle_t bufferHandle,
+                             std::optional<std::vector<uint8_t>>* outSmpte2094_40) const override;
 
     status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
                                          uint32_t layerCount, uint64_t usage,
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 77c00ae..837e3d8 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -122,6 +122,10 @@
                              std::vector<ui::PlaneLayout>* outPlaneLayouts);
     status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace);
     status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode);
+    status_t getSmpte2086(buffer_handle_t bufferHandle, std::optional<ui::Smpte2086>* outSmpte2086);
+    status_t getCta861_3(buffer_handle_t bufferHandle, std::optional<ui::Cta861_3>* outCta861_3);
+    status_t getSmpte2094_40(buffer_handle_t bufferHandle,
+                             std::optional<std::vector<uint8_t>>* outSmpte2094_40);
 
     /**
      * Gets the default metadata for a gralloc buffer allocated with the given parameters.
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index ad5ee80..4bdacb0 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -19,8 +19,10 @@
 #include <aidl/android/hardware/graphics/common/BlendMode.h>
 #include <aidl/android/hardware/graphics/common/ChromaSiting.h>
 #include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Cta861_3.h>
 #include <aidl/android/hardware/graphics/common/Interlaced.h>
 #include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <aidl/android/hardware/graphics/common/Smpte2086.h>
 #include <android/hardware/graphics/common/1.1/types.h>
 #include <android/hardware/graphics/common/1.2/types.h>
 #include <system/graphics.h>
@@ -47,7 +49,9 @@
  * Stable AIDL types
  */
 using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::Cta861_3;
 using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::Smpte2086;
 
 /**
  * The following stable AIDL types below have standard platform definitions
diff --git a/libs/vr/OWNERS b/libs/vr/OWNERS
index ec2d712..098c80e 100644
--- a/libs/vr/OWNERS
+++ b/libs/vr/OWNERS
@@ -1,4 +1,4 @@
 hendrikw@google.com
 jwcai@google.com
 steventhomas@google.com
-
+patplunkett@google.com
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
index 1a9d727..23a4224 100644
--- a/libs/vr/libpdx/Android.bp
+++ b/libs/vr/libpdx/Android.bp
@@ -53,6 +53,8 @@
         "libpdx",
         "liblog",
         "libutils",
+    ],
+    shared_libs: [
         "libvndksupport",
     ],
 }
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index ada2266..09ecb13 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -4,6 +4,7 @@
     name: "inputflinger_tests",
     srcs: [
         "BlockingQueue_test.cpp",
+        "EventHub_test.cpp",
         "TestInputListener.cpp",
         "InputClassifier_test.cpp",
         "InputClassifierConverter_test.cpp",
@@ -36,4 +37,5 @@
     header_libs: [
         "libinputreader_headers",
     ],
+    require_root: true,
 }
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
new file mode 100644
index 0000000..6504738
--- /dev/null
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EventHub.h"
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+#include <chrono>
+
+#define TAG "EventHub_test"
+
+using android::EventHub;
+using android::EventHubInterface;
+using android::InputDeviceIdentifier;
+using android::RawEvent;
+using android::sp;
+using android::base::StringPrintf;
+using std::chrono_literals::operator""ms;
+
+static constexpr bool DEBUG = false;
+static const char* DEVICE_NAME = "EventHub Test Device";
+
+static void dumpEvents(const std::vector<RawEvent>& events) {
+    for (const RawEvent& event : events) {
+        if (event.type >= EventHubInterface::FIRST_SYNTHETIC_EVENT) {
+            switch (event.type) {
+                case EventHubInterface::DEVICE_ADDED:
+                    ALOGI("Device added: %i", event.deviceId);
+                    break;
+                case EventHubInterface::DEVICE_REMOVED:
+                    ALOGI("Device removed: %i", event.deviceId);
+                    break;
+                case EventHubInterface::FINISHED_DEVICE_SCAN:
+                    ALOGI("Finished device scan.");
+                    break;
+            }
+        } else {
+            ALOGI("Device %" PRId32 " : time = %" PRId64 ", type %i, code %i, value %i",
+                  event.deviceId, event.when, event.type, event.code, event.value);
+        }
+    }
+}
+
+// --- EventHubTest ---
+class EventHubTest : public testing::Test {
+protected:
+    std::unique_ptr<EventHubInterface> mEventHub;
+    // We are only going to emulate a single input device currently.
+    android::base::unique_fd mDeviceFd;
+    int32_t mDeviceId;
+    virtual void SetUp() override {
+        mEventHub = std::make_unique<EventHub>();
+        consumeInitialDeviceAddedEvents();
+        createDevice();
+        mDeviceId = waitForDeviceCreation();
+    }
+    virtual void TearDown() override {
+        mDeviceFd.reset();
+        waitForDeviceClose(mDeviceId);
+    }
+
+    void createDevice();
+    /**
+     * Return the device id of the created device.
+     */
+    int32_t waitForDeviceCreation();
+    void waitForDeviceClose(int32_t deviceId);
+    void consumeInitialDeviceAddedEvents();
+    void sendEvent(uint16_t type, uint16_t code, int32_t value);
+    std::vector<RawEvent> getEvents(std::chrono::milliseconds timeout = 5ms);
+};
+
+std::vector<RawEvent> EventHubTest::getEvents(std::chrono::milliseconds timeout) {
+    static constexpr size_t EVENT_BUFFER_SIZE = 256;
+    std::array<RawEvent, EVENT_BUFFER_SIZE> eventBuffer;
+    std::vector<RawEvent> events;
+
+    while (true) {
+        size_t count =
+                mEventHub->getEvents(timeout.count(), eventBuffer.data(), eventBuffer.size());
+        if (count == 0) {
+            break;
+        }
+        events.insert(events.end(), eventBuffer.begin(), eventBuffer.begin() + count);
+    }
+    if (DEBUG) {
+        dumpEvents(events);
+    }
+    return events;
+}
+
+void EventHubTest::createDevice() {
+    mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
+    if (mDeviceFd < 0) {
+        FAIL() << "Can't open /dev/uinput :" << strerror(errno);
+    }
+
+    /**
+     * Signal which type of events this input device supports.
+     * We will emulate a keyboard here.
+     */
+    // enable key press/release event
+    if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_KEY)) {
+        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno);
+    }
+
+    // enable set of KEY events
+    if (ioctl(mDeviceFd, UI_SET_KEYBIT, KEY_HOME)) {
+        ADD_FAILURE() << "Error in ioctl : UI_SET_KEYBIT : KEY_HOME: " << strerror(errno);
+    }
+
+    // enable synchronization event
+    if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_SYN)) {
+        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno);
+    }
+
+    struct uinput_user_dev keyboard = {};
+    strlcpy(keyboard.name, DEVICE_NAME, UINPUT_MAX_NAME_SIZE);
+    keyboard.id.bustype = BUS_USB;
+    keyboard.id.vendor = 0x01;
+    keyboard.id.product = 0x01;
+    keyboard.id.version = 1;
+
+    if (write(mDeviceFd, &keyboard, sizeof(keyboard)) < 0) {
+        FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: "
+               << strerror(errno);
+    }
+
+    if (ioctl(mDeviceFd, UI_DEV_CREATE)) {
+        FAIL() << "Error in ioctl : UI_DEV_CREATE: " << strerror(errno);
+    }
+}
+
+/**
+ * Since the test runs on a real platform, there will be existing devices
+ * in addition to the test devices being added. Therefore, when EventHub is first created,
+ * it will return a lot of "device added" type of events.
+ */
+void EventHubTest::consumeInitialDeviceAddedEvents() {
+    std::vector<RawEvent> events = getEvents(0ms);
+    std::set<int32_t /*deviceId*/> existingDevices;
+    // All of the events should be DEVICE_ADDED type, except the last one.
+    for (size_t i = 0; i < events.size() - 1; i++) {
+        const RawEvent& event = events[i];
+        EXPECT_EQ(EventHubInterface::DEVICE_ADDED, event.type);
+        existingDevices.insert(event.deviceId);
+    }
+    // None of the existing system devices should be changing while this test is run.
+    // Check that the returned device ids are unique for all of the existing devices.
+    EXPECT_EQ(existingDevices.size(), events.size() - 1);
+    // The last event should be "finished device scan"
+    EXPECT_EQ(EventHubInterface::FINISHED_DEVICE_SCAN, events[events.size() - 1].type);
+}
+
+int32_t EventHubTest::waitForDeviceCreation() {
+    // Wait a little longer than usual, to ensure input device has time to be created
+    std::vector<RawEvent> events = getEvents(20ms);
+    EXPECT_EQ(2U, events.size()); // Using "expect" because the function is non-void.
+    const RawEvent& deviceAddedEvent = events[0];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_ADDED), deviceAddedEvent.type);
+    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId);
+    const int32_t deviceId = deviceAddedEvent.deviceId;
+    EXPECT_EQ(identifier.name, DEVICE_NAME);
+    const RawEvent& finishedDeviceScanEvent = events[1];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
+              finishedDeviceScanEvent.type);
+    return deviceId;
+}
+
+void EventHubTest::waitForDeviceClose(int32_t deviceId) {
+    std::vector<RawEvent> events = getEvents(20ms);
+    ASSERT_EQ(2U, events.size());
+    const RawEvent& deviceRemovedEvent = events[0];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_REMOVED), deviceRemovedEvent.type);
+    EXPECT_EQ(deviceId, deviceRemovedEvent.deviceId);
+    const RawEvent& finishedDeviceScanEvent = events[1];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
+              finishedDeviceScanEvent.type);
+}
+
+void EventHubTest::sendEvent(uint16_t type, uint16_t code, int32_t value) {
+    struct input_event event = {};
+    event.type = type;
+    event.code = code;
+    event.value = value;
+    event.time = {}; // uinput ignores the timestamp
+
+    if (write(mDeviceFd, &event, sizeof(input_event)) < 0) {
+        std::string msg = StringPrintf("Could not write event %" PRIu16 " %" PRIu16
+                                       " with value %" PRId32 " : %s",
+                                       type, code, value, strerror(errno));
+        ALOGE("%s", msg.c_str());
+        ADD_FAILURE() << msg.c_str();
+    }
+}
+
+/**
+ * Ensure that input_events are generated with monotonic clock.
+ * That means input_event should receive a timestamp that is in the future of the time
+ * before the event was sent.
+ * Input system uses CLOCK_MONOTONIC everywhere in the code base.
+ */
+TEST_F(EventHubTest, InputEvent_TimestampIsMonotonic) {
+    nsecs_t lastEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    // key press
+    sendEvent(EV_KEY, KEY_HOME, 1);
+    sendEvent(EV_SYN, SYN_REPORT, 0);
+
+    // key release
+    sendEvent(EV_KEY, KEY_HOME, 0);
+    sendEvent(EV_SYN, SYN_REPORT, 0);
+
+    std::vector<RawEvent> events = getEvents();
+    ASSERT_EQ(4U, events.size()) << "Expected to receive 2 keys and 2 syncs, total of 4 events";
+    for (const RawEvent& event : events) {
+        // Cannot use strict comparison because the events may happen too quickly
+        ASSERT_LE(lastEventTime, event.when) << "Event must have occurred after the key was sent";
+        ASSERT_LT(std::chrono::nanoseconds(event.when - lastEventTime), 100ms)
+                << "Event times are too far apart";
+        lastEventTime = event.when; // Ensure all returned events are monotonic
+    }
+}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 0701f3b..994010b 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -418,7 +418,7 @@
         std::chrono::time_point start = std::chrono::steady_clock::now();
         status_t status = WOULD_BLOCK;
         while (status == WOULD_BLOCK) {
-            status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, &consumeSeq,
+            status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq,
                                         &event);
             std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
             if (elapsed > 100ms) {
@@ -932,6 +932,32 @@
     monitor->consumeMotionUp(ADISPLAY_ID_DEFAULT);
 }
 
+TEST_F(InputDispatcherTest, TestMoveEvent) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->notifyMotion(&motionArgs);
+    // Window should receive motion down event.
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    motionArgs.action = AMOTION_EVENT_ACTION_MOVE;
+    motionArgs.sequenceNum += 1;
+    motionArgs.eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    motionArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
+                                             motionArgs.pointerCoords[0].getX() - 10);
+
+    mDispatcher->notifyMotion(&motionArgs);
+    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, ADISPLAY_ID_DEFAULT,
+                         0 /*expectedFlags*/);
+}
+
 /* Test InputDispatcher for MultiDisplay */
 class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
 public:
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 34254e0..4f96ad3 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -62,8 +62,11 @@
 
 class ComposerCallbackBridge : public Hwc2::IComposerCallback {
 public:
-    ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId)
-            : mCallback(callback), mSequenceId(sequenceId) {}
+    ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId,
+                           bool vsyncSwitchingSupported)
+          : mCallback(callback),
+            mSequenceId(sequenceId),
+            mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
 
     Return<void> onHotplug(Hwc2::Display display,
                            IComposerCallback::Connection conn) override
@@ -81,15 +84,23 @@
 
     Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
     {
-        mCallback->onVsyncReceived(mSequenceId, display, timestamp, std::nullopt);
+        if (!mVsyncSwitchingSupported) {
+            mCallback->onVsyncReceived(mSequenceId, display, timestamp, std::nullopt);
+        } else {
+            ALOGW("Unexpected onVsync callback on composer >= 2.4, ignoring.");
+        }
         return Void();
     }
 
     Return<void> onVsync_2_4(Hwc2::Display display, int64_t timestamp,
                              Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override {
-        // TODO(b/140201379): use vsyncPeriodNanos in the new DispSync
-        mCallback->onVsyncReceived(mSequenceId, display, timestamp,
-                                   std::make_optional(vsyncPeriodNanos));
+        if (mVsyncSwitchingSupported) {
+            // TODO(b/140201379): use vsyncPeriodNanos in the new DispSync
+            mCallback->onVsyncReceived(mSequenceId, display, timestamp,
+                                       std::make_optional(vsyncPeriodNanos));
+        } else {
+            ALOGW("Unexpected onVsync_2_4 callback on composer <= 2.3, ignoring.");
+        }
         return Void();
     }
 
@@ -107,6 +118,7 @@
 private:
     ComposerCallback* mCallback;
     int32_t mSequenceId;
+    const bool mVsyncSwitchingSupported;
 };
 
 } // namespace anonymous
@@ -126,7 +138,8 @@
     }
     mRegisteredCallback = true;
     sp<ComposerCallbackBridge> callbackBridge(
-            new ComposerCallbackBridge(callback, sequenceId));
+            new ComposerCallbackBridge(callback, sequenceId,
+                                       mComposer->isVsyncPeriodSwitchSupported()));
     mComposer->registerCallback(callbackBridge);
 }
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index e001080..56b3252 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -26,7 +26,7 @@
 class TimeKeeper;
 class VSyncTracker;
 
-enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error };
+enum class ScheduleResult { Scheduled, CannotSchedule, Error };
 enum class CancelResult { Cancelled, TooLate, Error };
 
 /*
@@ -83,7 +83,6 @@
      * \param [in] earliestVsync   The targeted display time. This will be snapped to the closest
      *                             predicted vsync time after earliestVsync.
      * \return                     A ScheduleResult::Scheduled if callback was scheduled.
-     *                             A ScheduleResult::ReScheduled if callback was rescheduled.
      *                             A ScheduleResult::CannotSchedule
      *                             if (workDuration - earliestVsync) is in the past, or
      *                             if a callback was dispatched for the predictedVsync already.
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index a79fe98..48f2abb 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -29,8 +29,13 @@
 TimeKeeper::~TimeKeeper() = default;
 
 VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name,
-                                                           std::function<void(nsecs_t)> const& cb)
-      : mName(name), mCallback(cb), mWorkDuration(0), mEarliestVsync(0) {}
+                                                           std::function<void(nsecs_t)> const& cb,
+                                                           nsecs_t minVsyncDistance)
+      : mName(name),
+        mCallback(cb),
+        mWorkDuration(0),
+        mEarliestVsync(0),
+        mMinVsyncDistance(minVsyncDistance) {}
 
 std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
     return mLastDispatchTime;
@@ -49,18 +54,28 @@
 
 ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
                                                       VSyncTracker& tracker, nsecs_t now) {
-    auto const nextVsyncTime =
+    auto nextVsyncTime =
             tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
-    if (mLastDispatchTime >= nextVsyncTime) { // already dispatched a callback for this vsync
-        return ScheduleResult::CannotSchedule;
+
+    bool const wouldSkipAVsyncTarget =
+            mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
+    if (wouldSkipAVsyncTarget) {
+        return ScheduleResult::Scheduled;
+    }
+
+    bool const alreadyDispatchedForVsync = mLastDispatchTime &&
+            ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
+             (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
+    if (alreadyDispatchedForVsync) {
+        nextVsyncTime =
+                tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
     }
 
     auto const nextWakeupTime = nextVsyncTime - workDuration;
-    auto result = mArmedInfo ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
     mWorkDuration = workDuration;
     mEarliestVsync = earliestVsync;
     mArmedInfo = {nextWakeupTime, nextVsyncTime};
-    return result;
+    return ScheduleResult::Scheduled;
 }
 
 void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
@@ -101,8 +116,12 @@
 }
 
 VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
-                                                 VSyncTracker& tracker, nsecs_t timerSlack)
-      : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {}
+                                                 VSyncTracker& tracker, nsecs_t timerSlack,
+                                                 nsecs_t minVsyncDistance)
+      : mTimeKeeper(std::move(tk)),
+        mTracker(tracker),
+        mTimerSlack(timerSlack),
+        mMinVsyncDistance(minVsyncDistance) {}
 
 VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
     std::lock_guard<decltype(mMutex)> lk(mMutex);
@@ -187,7 +206,8 @@
             mCallbacks
                     .emplace(++mCallbackToken,
                              std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
-                                                                            callbackFn))
+                                                                            callbackFn,
+                                                                            mMinVsyncDistance))
                     .first->first};
 }
 
@@ -277,12 +297,16 @@
 }
 
 ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
-    if (!mValidToken) return ScheduleResult::Error;
+    if (!mValidToken) {
+        return ScheduleResult::Error;
+    }
     return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
 }
 
 CancelResult VSyncCallbackRegistration::cancel() {
-    if (!mValidToken) return CancelResult::Error;
+    if (!mValidToken) {
+        return CancelResult::Error;
+    }
     return mDispatch.get().cancel(mToken);
 }
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index fc78da3..0c9b4fe 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -36,7 +36,8 @@
     // Valid transition: disarmed -> armed ( when scheduled )
     // Valid transition: armed -> running -> disarmed ( when timer is called)
     // Valid transition: armed -> disarmed ( when cancelled )
-    VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn);
+    VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn,
+                                 nsecs_t minVsyncDistance);
     std::string_view name() const;
 
     // Start: functions that are not threadsafe.
@@ -72,6 +73,7 @@
 
     nsecs_t mWorkDuration;
     nsecs_t mEarliestVsync;
+    nsecs_t const mMinVsyncDistance;
 
     struct ArmingInfo {
         nsecs_t mActualWakeupTime;
@@ -91,8 +93,15 @@
  */
 class VSyncDispatchTimerQueue : public VSyncDispatch {
 public:
+    // Constructs a VSyncDispatchTimerQueue.
+    // \param[in] tk                    A timekeeper.
+    // \param[in] tracker               A tracker.
+    // \param[in] timerSlack            The threshold at which different similarly timed callbacks
+    //                                  should be grouped into one wakeup.
+    // \param[in] minVsyncDistance      The minimum distance between two vsync estimates before the
+    //                                  vsyncs are considered the same vsync event.
     explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
-                                     nsecs_t timerSlack);
+                                     nsecs_t timerSlack, nsecs_t minVsyncDistance);
     ~VSyncDispatchTimerQueue();
 
     CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
@@ -119,6 +128,7 @@
     std::unique_ptr<TimeKeeper> const mTimeKeeper;
     VSyncTracker& mTracker;
     nsecs_t const mTimerSlack;
+    nsecs_t const mMinVsyncDistance;
 
     std::mutex mutable mMutex;
     size_t mCallbackToken GUARDED_BY(mMutex) = 0;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index f2a7791..47e3f4f 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
+#undef LOG_TAG
+#define LOG_TAG "VSyncReactor"
+//#define LOG_NDEBUG 0
 #include "VSyncReactor.h"
+#include <log/log.h>
 #include "TimeKeeper.h"
 #include "VSyncDispatch.h"
 #include "VSyncTracker.h"
@@ -30,6 +34,87 @@
         mTracker(std::move(tracker)),
         mPendingLimit(pendingFenceLimit) {}
 
+VSyncReactor::~VSyncReactor() = default;
+
+// The DispSync interface has a 'repeat this callback at rate' semantic. This object adapts
+// VSyncDispatch's individually-scheduled callbacks so as to meet DispSync's existing semantic
+// for now.
+class CallbackRepeater {
+public:
+    CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
+                     nsecs_t period, nsecs_t offset, nsecs_t notBefore)
+          : mCallback(cb),
+            mRegistration(dispatch,
+                          std::bind(&CallbackRepeater::callback, this, std::placeholders::_1),
+                          std::string(name)),
+            mPeriod(period),
+            mOffset(offset),
+            mLastCallTime(notBefore) {}
+
+    ~CallbackRepeater() {
+        std::lock_guard<std::mutex> lk(mMutex);
+        mRegistration.cancel();
+    }
+
+    void start(nsecs_t offset) {
+        std::lock_guard<std::mutex> lk(mMutex);
+        mStopped = false;
+        mOffset = offset;
+
+        auto const schedule_result = mRegistration.schedule(calculateWorkload(), mLastCallTime);
+        LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
+                            "Error scheduling callback: rc %X", schedule_result);
+    }
+
+    void setPeriod(nsecs_t period) {
+        std::lock_guard<std::mutex> lk(mMutex);
+        if (period == mPeriod) {
+            return;
+        }
+        mPeriod = period;
+    }
+
+    void stop() {
+        std::lock_guard<std::mutex> lk(mMutex);
+        LOG_ALWAYS_FATAL_IF(mStopped, "DispSyncInterface misuse: callback already stopped");
+        mStopped = true;
+        mRegistration.cancel();
+    }
+
+private:
+    void callback(nsecs_t vsynctime) {
+        nsecs_t period = 0;
+        {
+            std::lock_guard<std::mutex> lk(mMutex);
+            period = mPeriod;
+            mLastCallTime = vsynctime;
+        }
+
+        mCallback->onDispSyncEvent(vsynctime - period);
+
+        {
+            std::lock_guard<std::mutex> lk(mMutex);
+            auto const schedule_result = mRegistration.schedule(calculateWorkload(), vsynctime);
+            LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
+                                "Error rescheduling callback: rc %X", schedule_result);
+        }
+    }
+
+    // DispSync offsets are defined as time after the vsync before presentation.
+    // VSyncReactor workloads are defined as time before the intended presentation vsync.
+    // Note change in sign between the two defnitions.
+    nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
+
+    DispSync::Callback* const mCallback;
+
+    std::mutex mutable mMutex;
+    VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
+    bool mStopped GUARDED_BY(mMutex) = false;
+    nsecs_t mPeriod GUARDED_BY(mMutex);
+    nsecs_t mOffset GUARDED_BY(mMutex);
+    nsecs_t mLastCallTime GUARDED_BY(mMutex);
+};
+
 bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
     if (!fence) {
         return false;
@@ -92,6 +177,9 @@
     {
         std::lock_guard<std::mutex> lk(mMutex);
         mPeriodChangeInProgress = true;
+        for (auto& entry : mCallbacks) {
+            entry.second->setPeriod(period);
+        }
     }
 }
 
@@ -114,4 +202,47 @@
     return false;
 }
 
+status_t VSyncReactor::addEventListener(const char* name, nsecs_t phase,
+                                        DispSync::Callback* callback,
+                                        nsecs_t /* lastCallbackTime */) {
+    std::lock_guard<std::mutex> lk(mMutex);
+    auto it = mCallbacks.find(callback);
+    if (it == mCallbacks.end()) {
+        // TODO (b/146557561): resolve lastCallbackTime semantics in DispSync i/f.
+        static auto constexpr maxListeners = 3;
+        if (mCallbacks.size() >= maxListeners) {
+            ALOGE("callback %s not added, exceeded callback limit of %i (currently %zu)", name,
+                  maxListeners, mCallbacks.size());
+            return NO_MEMORY;
+        }
+
+        auto const period = mTracker->currentPeriod();
+        auto repeater = std::make_unique<CallbackRepeater>(*mDispatch, callback, name, period,
+                                                           phase, mClock->now());
+        it = mCallbacks.emplace(std::pair(callback, std::move(repeater))).first;
+    }
+
+    it->second->start(phase);
+    return NO_ERROR;
+}
+
+status_t VSyncReactor::removeEventListener(DispSync::Callback* callback,
+                                           nsecs_t* /* outLastCallback */) {
+    std::lock_guard<std::mutex> lk(mMutex);
+    auto const it = mCallbacks.find(callback);
+    LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback %p not registered", callback);
+
+    it->second->stop();
+    return NO_ERROR;
+}
+
+status_t VSyncReactor::changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
+    std::lock_guard<std::mutex> lk(mMutex);
+    auto const it = mCallbacks.find(callback);
+    LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback was %p not registered", callback);
+
+    it->second->start(phase);
+    return NO_ERROR;
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 786ee98..837eb75 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -20,19 +20,23 @@
 #include <ui/FenceTime.h>
 #include <memory>
 #include <mutex>
+#include <unordered_map>
 #include <vector>
+#include "DispSync.h"
 
 namespace android::scheduler {
 
 class Clock;
 class VSyncDispatch;
 class VSyncTracker;
+class CallbackRepeater;
 
 // TODO (b/145217110): consider renaming.
 class VSyncReactor /* TODO (b/140201379): : public android::DispSync */ {
 public:
     VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
                  std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit);
+    ~VSyncReactor();
 
     bool addPresentFence(const std::shared_ptr<FenceTime>& fence);
     void setIgnorePresentFences(bool ignoration);
@@ -48,6 +52,11 @@
     bool addResyncSample(nsecs_t timestamp, bool* periodFlushed);
     void endResync();
 
+    status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
+                              nsecs_t lastCallbackTime);
+    status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback);
+    status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase);
+
 private:
     std::unique_ptr<Clock> const mClock;
     std::unique_ptr<VSyncDispatch> const mDispatch;
@@ -58,6 +67,8 @@
     bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
     std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
     bool mPeriodChangeInProgress GUARDED_BY(mMutex) = false;
+    std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks
+            GUARDED_BY(mMutex);
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 484947d..5846c77 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -92,6 +92,7 @@
 
 struct VSyncDispatchRealtimeTest : testing::Test {
     static nsecs_t constexpr mDispatchGroupThreshold = toNs(100us);
+    static nsecs_t constexpr mVsyncMoveThreshold = toNs(500us);
     static size_t constexpr mIterations = 20;
 };
 
@@ -148,7 +149,8 @@
 
 TEST_F(VSyncDispatchRealtimeTest, triple_alarm) {
     FixedRateIdealStubTracker tracker;
-    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+                                     mVsyncMoveThreshold);
 
     static size_t constexpr num_clients = 3;
     std::array<RepeatingCallbackReceiver, num_clients>
@@ -176,7 +178,8 @@
 TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) {
     auto next_vsync_interval = toNs(3ms);
     VRRStubTracker tracker(next_vsync_interval);
-    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+                                     mVsyncMoveThreshold);
 
     RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
 
@@ -193,7 +196,8 @@
 // starts at 333hz, jumps to 200hz at frame 10
 TEST_F(VSyncDispatchRealtimeTest, fixed_jump) {
     VRRStubTracker tracker(toNs(3ms));
-    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+                                     mVsyncMoveThreshold);
 
     RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
 
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index d668a41..5aff429 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -196,16 +196,18 @@
     NiceMock<ControllableClock> mMockClock;
     static nsecs_t constexpr mDispatchGroupThreshold = 5;
     nsecs_t const mPeriod = 1000;
+    nsecs_t const mVsyncMoveThreshold = 300;
     NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
-    VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold};
+    VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
+                                      mVsyncMoveThreshold};
 };
 
 TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
     EXPECT_CALL(mMockClock, alarmIn(_, 900));
     EXPECT_CALL(mMockClock, alarmCancel());
     {
-        VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker,
-                                          mDispatchGroupThreshold};
+        VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
+                                          mVsyncMoveThreshold};
         CountingCallback cb(mDispatch);
         EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled);
     }
@@ -468,14 +470,24 @@
 
 TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) {
     VSyncDispatch::CallbackToken tmp;
+    std::optional<nsecs_t> lastTarget;
     tmp = mDispatch.registerCallback(
-            [&](auto) {
-                EXPECT_EQ(mDispatch.schedule(tmp, 400, 1000), ScheduleResult::CannotSchedule);
+            [&](auto timestamp) {
+                EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp - mVsyncMoveThreshold),
+                          ScheduleResult::Scheduled);
+                EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp), ScheduleResult::Scheduled);
+                EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp + mVsyncMoveThreshold),
+                          ScheduleResult::Scheduled);
+                lastTarget = timestamp;
             },
             "oo");
 
     mDispatch.schedule(tmp, 999, 1000);
     advanceToNextCallback();
+    EXPECT_THAT(lastTarget, Eq(1000));
+
+    advanceToNextCallback();
+    EXPECT_THAT(lastTarget, Eq(2000));
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) {
@@ -547,10 +559,33 @@
     EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
 }
 
-TEST_F(VSyncDispatchTimerQueueTest, distinguishesScheduleAndReschedule) {
+TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
     CountingCallback cb0(mDispatch);
     EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::ReScheduled);
+    EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::Scheduled);
+}
+
+// b/1450138150
+TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 500));
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+    mMockClock.advanceBy(400);
+
+    EXPECT_EQ(mDispatch.schedule(cb, 800, 1000), ScheduleResult::Scheduled);
+    advanceToNextCallback();
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+            .Times(2)
+            .WillOnce(Return(1000))
+            .WillOnce(Return(1002));
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+    mMockClock.advanceBy(400);
+    EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
@@ -570,13 +605,15 @@
     EXPECT_EQ(mDispatch.schedule(cb0, 1900, 2000), ScheduleResult::Scheduled);
 }
 
-TEST_F(VSyncDispatchTimerQueueTest, cannotScheduleDoesNotAffectSchedulingState) {
+TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
     EXPECT_CALL(mMockClock, alarmIn(_, 600));
 
     CountingCallback cb(mDispatch);
     EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+
+    EXPECT_EQ(mDispatch.schedule(cb, 1400, 1000), ScheduleResult::Scheduled);
+
     advanceToNextCallback();
-    EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::CannotSchedule);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
@@ -612,19 +649,22 @@
 class VSyncDispatchTimerQueueEntryTest : public testing::Test {
 protected:
     nsecs_t const mPeriod = 1000;
+    nsecs_t const mVsyncMoveThreshold = 200;
     NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
 };
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
     std::string name("basicname");
-    VSyncDispatchTimerQueueEntry entry(name, [](auto) {});
+    VSyncDispatchTimerQueueEntry entry(
+            name, [](auto) {}, mVsyncMoveThreshold);
     EXPECT_THAT(entry.name(), Eq(name));
     EXPECT_FALSE(entry.lastExecutedVsyncTarget());
     EXPECT_FALSE(entry.wakeupTime());
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) {
-    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
     EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
@@ -643,7 +683,8 @@
     EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration))
             .Times(1)
             .WillOnce(Return(10000));
-    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
     EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled));
@@ -655,10 +696,13 @@
 TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) {
     auto callCount = 0;
     auto calledTime = 0;
-    VSyncDispatchTimerQueueEntry entry("test", [&](auto time) {
-        callCount++;
-        calledTime = time;
-    });
+    VSyncDispatchTimerQueueEntry entry(
+            "test",
+            [&](auto time) {
+                callCount++;
+                calledTime = time;
+            },
+            mVsyncMoveThreshold);
 
     EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
     auto const wakeup = entry.wakeupTime();
@@ -681,7 +725,8 @@
             .WillOnce(Return(1000))
             .WillOnce(Return(1020));
 
-    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
     entry.update(mStubTracker, 0);
@@ -699,7 +744,8 @@
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
-    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto) {}, mVsyncMoveThreshold);
     EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
     entry.update(mStubTracker, 0);
 
@@ -708,19 +754,52 @@
     EXPECT_THAT(*wakeup, Eq(wakeup));
 }
 
-TEST_F(VSyncDispatchTimerQueueEntryTest, reportsCannotScheduleIfMissedOpportunity) {
-    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto) {}, mVsyncMoveThreshold);
     EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
-    entry.executing();
-    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::CannotSchedule));
-    EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::CannotSchedule));
+    entry.executing(); // 1000 is executing
+    // had 1000 not been executing, this could have been scheduled for time 800.
+    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
+
+    EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
+
     EXPECT_THAT(entry.schedule(200, 1001, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
 }
 
-TEST_F(VSyncDispatchTimerQueueEntryTest, reportsReScheduleIfStillTime) {
-    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+TEST_F(VSyncDispatchTimerQueueEntryTest,
+       willRequestNextEstimateWhenSnappingToNextTargettableVSync) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto) {}, mVsyncMoveThreshold);
+
+    Sequence seq;
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+            .InSequence(seq)
+            .WillOnce(Return(1000));
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+            .InSequence(seq)
+            .WillOnce(Return(1000));
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold))
+            .InSequence(seq)
+            .WillOnce(Return(2000));
+
     EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
-    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::ReScheduled));
+
+    entry.executing(); // 1000 is executing
+
+    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto) {}, mVsyncMoveThreshold);
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 84df019..537cc80 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -122,21 +122,53 @@
     return ft;
 }
 
+class StubCallback : public DispSync::Callback {
+public:
+    void onDispSyncEvent(nsecs_t when) final {
+        std::lock_guard<std::mutex> lk(mMutex);
+        mLastCallTime = when;
+    }
+    std::optional<nsecs_t> lastCallTime() const {
+        std::lock_guard<std::mutex> lk(mMutex);
+        return mLastCallTime;
+    }
+
+private:
+    std::mutex mutable mMutex;
+    std::optional<nsecs_t> mLastCallTime GUARDED_BY(mMutex);
+};
+
 class VSyncReactorTest : public testing::Test {
 protected:
     VSyncReactorTest()
-          : mMockDispatch(std::make_shared<MockVSyncDispatch>()),
+          : mMockDispatch(std::make_shared<NiceMock<MockVSyncDispatch>>()),
             mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
             mMockClock(std::make_shared<NiceMock<MockClock>>()),
             mReactor(std::make_unique<ClockWrapper>(mMockClock),
                      std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
-                     std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {}
+                     std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {
+        ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
+        ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
+    }
 
     std::shared_ptr<MockVSyncDispatch> mMockDispatch;
     std::shared_ptr<MockVSyncTracker> mMockTracker;
     std::shared_ptr<MockClock> mMockClock;
     static constexpr size_t kPendingLimit = 3;
-    static constexpr nsecs_t dummyTime = 47;
+    static constexpr nsecs_t mDummyTime = 47;
+    static constexpr nsecs_t mPhase = 3000;
+    static constexpr nsecs_t mAnotherPhase = 5200;
+    static constexpr nsecs_t period = 10000;
+    static constexpr nsecs_t mAnotherPeriod = 23333;
+    static constexpr nsecs_t mFakeCbTime = 2093;
+    static constexpr nsecs_t mFakeNow = 2214;
+    static constexpr const char mName[] = "callbacky";
+    VSyncDispatch::CallbackToken const mFakeToken{2398};
+
+    nsecs_t lastCallbackTime = 0;
+    StubCallback outerCb;
+    std::function<void(nsecs_t)> innerCb;
+
     VSyncReactor mReactor;
 };
 
@@ -149,8 +181,8 @@
 }
 
 TEST_F(VSyncReactorTest, addingSignalledFenceAddsToTracker) {
-    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
-    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
 }
 
 TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) {
@@ -161,9 +193,9 @@
     EXPECT_FALSE(mReactor.addPresentFence(pendingFence));
     Mock::VerifyAndClearExpectations(mMockTracker.get());
 
-    signalFenceWithTime(pendingFence, dummyTime);
+    signalFenceWithTime(pendingFence, mDummyTime);
 
-    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
     EXPECT_CALL(*mMockTracker, addVsyncTimestamp(anotherDummyTime));
     EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(anotherDummyTime)));
 }
@@ -193,15 +225,15 @@
 
 TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) {
     static constexpr size_t aFewTimes = 8;
-    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime)).Times(1);
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime)).Times(1);
 
     mReactor.setIgnorePresentFences(true);
     for (auto i = 0; i < aFewTimes; i++) {
-        mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime));
+        mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime));
     }
 
     mReactor.setIgnorePresentFences(false);
-    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
 }
 
 TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
@@ -227,11 +259,11 @@
 TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
     nsecs_t const fakeTimestamp = 4839;
     nsecs_t const fakePeriod = 1010;
-    nsecs_t const fakeNow = 2214;
+    nsecs_t const mFakeNow = 2214;
     int const numPeriodsOut = 3;
-    EXPECT_CALL(*mMockClock, now()).WillOnce(Return(fakeNow));
+    EXPECT_CALL(*mMockClock, now()).WillOnce(Return(mFakeNow));
     EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
-    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(fakeNow + numPeriodsOut * fakePeriod))
+    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(mFakeNow + numPeriodsOut * fakePeriod))
             .WillOnce(Return(fakeTimestamp));
     EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut), Eq(fakeTimestamp));
 }
@@ -267,4 +299,194 @@
     EXPECT_TRUE(periodFlushed);
 }
 
+static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) {
+    return period - phase;
+}
+
+TEST_F(VSyncReactorTest, addEventListener) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, addEventListenerTwiceChangesPhase) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _)) // mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, eventListenerGetsACallbackAndReschedules) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeCbTime))
+            .Times(2)
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    ASSERT_TRUE(innerCb);
+    innerCb(mFakeCbTime);
+    innerCb(mFakeCbTime);
+}
+
+TEST_F(VSyncReactorTest, callbackTimestampReadapted) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, _))
+            .InSequence(seq)
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeCbTime))
+            .InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    ASSERT_TRUE(innerCb);
+    innerCb(mFakeCbTime);
+    EXPECT_THAT(outerCb.lastCallTime(), Optional(mFakeCbTime - period));
+}
+
+TEST_F(VSyncReactorTest, eventListenersRemovedOnDestruction) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, addEventListenerChangePeriod) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(period, mAnotherPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, changingPeriodChangesOfsetsOnNextCb) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockTracker, setPeriod(mAnotherPeriod));
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(mAnotherPeriod, mPhase), mFakeNow))
+            .InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.setPeriod(mAnotherPeriod);
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, offsetsAppliedOnNextOpportunity) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), _))
+            .InSequence(seq)
+            .WillOnce(Return(ScheduleResult::Scheduled));
+
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
+            .InSequence(seq)
+            .WillOnce(Return(ScheduleResult::Scheduled));
+
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
+            .InSequence(seq)
+            .WillOnce(Return(ScheduleResult::Scheduled));
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.changePhaseOffset(&outerCb, mAnotherPhase);
+    ASSERT_TRUE(innerCb);
+    innerCb(mFakeCbTime);
+}
+
+TEST_F(VSyncReactorTest, negativeOffsetsApplied) {
+    nsecs_t const negativePhase = -4000;
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(period, negativePhase), mFakeNow))
+            .InSequence(seq);
+    mReactor.addEventListener(mName, negativePhase, &outerCb, lastCallbackTime);
+}
+
+using VSyncReactorDeathTest = VSyncReactorTest;
+TEST_F(VSyncReactorDeathTest, invalidRemoval) {
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+    EXPECT_DEATH(mReactor.removeEventListener(&outerCb, &lastCallbackTime), ".*");
+}
+
+TEST_F(VSyncReactorDeathTest, invalidChange) {
+    EXPECT_DEATH(mReactor.changePhaseOffset(&outerCb, mPhase), ".*");
+
+    // the current DispSync-interface usage pattern has evolved around an implementation quirk,
+    // which is a callback is assumed to always exist, and it is valid api usage to change the
+    // offset of an object that is in the removed state.
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+    mReactor.changePhaseOffset(&outerCb, mPhase);
+}
+
+TEST_F(VSyncReactorDeathTest, cannotScheduleOnRegistration) {
+    ON_CALL(*mMockDispatch, schedule(_, _, _))
+            .WillByDefault(Return(ScheduleResult::CannotSchedule));
+    EXPECT_DEATH(mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime), ".*");
+}
+
+TEST_F(VSyncReactorDeathTest, cannotScheduleOnCallback) {
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(_, _, _)).WillOnce(Return(ScheduleResult::Scheduled));
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    ASSERT_TRUE(innerCb);
+    Mock::VerifyAndClearExpectations(mMockDispatch.get());
+
+    ON_CALL(*mMockDispatch, schedule(_, _, _))
+            .WillByDefault(Return(ScheduleResult::CannotSchedule));
+    EXPECT_DEATH(innerCb(mFakeCbTime), ".*");
+}
+
 } // namespace android::scheduler
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index aa7c19a..a44b9e7 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -24,6 +24,7 @@
 #include <system/window.h>
 #include <ui/BufferQueueDefs.h>
 #include <utils/StrongPointer.h>
+#include <utils/Timers.h>
 #include <utils/Trace.h>
 
 #include <algorithm>
@@ -228,8 +229,10 @@
           mailbox_mode(present_mode == VK_PRESENT_MODE_MAILBOX_KHR),
           pre_transform(pre_transform_),
           frame_timestamps_enabled(false),
+          acquire_next_image_timeout(-1),
           shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
-                 present_mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+                 present_mode ==
+                     VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
         ANativeWindow* window = surface.window.get();
         native_window_get_refresh_cycle_duration(
             window,
@@ -251,6 +254,7 @@
     int pre_transform;
     bool frame_timestamps_enabled;
     int64_t refresh_duration;
+    nsecs_t acquire_next_image_timeout;
     bool shared;
 
     struct Image {
@@ -363,7 +367,7 @@
         int64_t composition_latch_time = 0;
         int64_t actual_present_time = 0;
         // Obtain timestamps:
-        int ret = native_window_get_frame_timestamps(
+        int err = native_window_get_frame_timestamps(
             swapchain.surface.window.get(), ti.native_frame_id_,
             &desired_present_time, &render_complete_time,
             &composition_latch_time,
@@ -374,7 +378,7 @@
             nullptr,  //&dequeue_ready_time,
             nullptr /*&reads_done_time*/);
 
-        if (ret != android::NO_ERROR) {
+        if (err != android::OK) {
             continue;
         }
 
@@ -530,7 +534,7 @@
     surface->swapchain_handle = VK_NULL_HANDLE;
     int err = native_window_get_consumer_usage(surface->window.get(),
                                                &surface->consumer_usage);
-    if (err != android::NO_ERROR) {
+    if (err != android::OK) {
         ALOGE("native_window_get_consumer_usage() failed: %s (%d)",
               strerror(-err), err);
         surface->~Surface();
@@ -540,7 +544,7 @@
 
     err =
         native_window_api_connect(surface->window.get(), NATIVE_WINDOW_API_EGL);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("native_window_api_connect() failed: %s (%d)", strerror(-err),
               err);
         surface->~Surface();
@@ -588,7 +592,7 @@
 
     int query_value;
     int err = window->query(window, NATIVE_WINDOW_FORMAT, &query_value);
-    if (err != 0 || query_value < 0) {
+    if (err != android::OK || query_value < 0) {
         ALOGE("NATIVE_WINDOW_FORMAT query failed: %s (%d) value=%d",
               strerror(-err), err, query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -629,13 +633,13 @@
 
     int width, height;
     err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
     err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -643,7 +647,7 @@
 
     int transform_hint;
     err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -651,7 +655,7 @@
 
     int max_buffer_count;
     err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &max_buffer_count);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -856,7 +860,7 @@
     ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
 
     err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
-    if (err != 0 || query_value < 0) {
+    if (err != android::OK || query_value < 0) {
         ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) value=%d",
               strerror(-err), err, query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -864,7 +868,7 @@
     uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
 
     err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
-    if (err != 0 || query_value < 0) {
+    if (err != android::OK || query_value < 0) {
         ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d",
               strerror(-err), err, query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -956,12 +960,12 @@
 
         int width = 0, height = 0;
         err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
-        if (err != 0) {
+        if (err != android::OK) {
             ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
                   strerror(-err), err);
         }
         err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
-        if (err != 0) {
+        if (err != android::OK) {
             ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
                   strerror(-err), err);
         }
@@ -1085,17 +1089,23 @@
     // dequeue all buffers.
     //
     // TODO(http://b/134186185) recycle swapchain images more efficiently
-    err = native_window_api_disconnect(surface.window.get(),
-                                       NATIVE_WINDOW_API_EGL);
-    ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)",
+    ANativeWindow* window = surface.window.get();
+    err = native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+    ALOGW_IF(err != android::OK, "native_window_api_disconnect failed: %s (%d)",
              strerror(-err), err);
-    err =
-        native_window_api_connect(surface.window.get(), NATIVE_WINDOW_API_EGL);
-    ALOGW_IF(err != 0, "native_window_api_connect failed: %s (%d)",
+    err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+    ALOGW_IF(err != android::OK, "native_window_api_connect failed: %s (%d)",
              strerror(-err), err);
 
-    err = native_window_set_buffer_count(surface.window.get(), 0);
-    if (err != 0) {
+    err = window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, -1);
+    if (err != android::OK) {
+        ALOGE("window->perform(SET_DEQUEUE_TIMEOUT) failed: %s (%d)",
+              strerror(-err), err);
+        return VK_ERROR_SURFACE_LOST_KHR;
+    }
+
+    err = native_window_set_buffer_count(window, 0);
+    if (err != android::OK) {
         ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1103,22 +1113,22 @@
 
     int swap_interval =
         create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
-    err = surface.window->setSwapInterval(surface.window.get(), swap_interval);
-    if (err != 0) {
+    err = window->setSwapInterval(window, swap_interval);
+    if (err != android::OK) {
         ALOGE("native_window->setSwapInterval(1) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
-    err = native_window_set_shared_buffer_mode(surface.window.get(), false);
-    if (err != 0) {
+    err = native_window_set_shared_buffer_mode(window, false);
+    if (err != android::OK) {
         ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
-    err = native_window_set_auto_refresh(surface.window.get(), false);
-    if (err != 0) {
+    err = native_window_set_auto_refresh(window, false);
+    if (err != android::OK) {
         ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1128,25 +1138,23 @@
 
     const auto& dispatch = GetData(device).driver;
 
-    err = native_window_set_buffers_format(surface.window.get(),
-                                           native_pixel_format);
-    if (err != 0) {
+    err = native_window_set_buffers_format(window, native_pixel_format);
+    if (err != android::OK) {
         ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)",
               native_pixel_format, strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
-    err = native_window_set_buffers_data_space(surface.window.get(),
-                                               native_dataspace);
-    if (err != 0) {
+    err = native_window_set_buffers_data_space(window, native_dataspace);
+    if (err != android::OK) {
         ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)",
               native_dataspace, strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     err = native_window_set_buffers_dimensions(
-        surface.window.get(), static_cast<int>(create_info->imageExtent.width),
+        window, static_cast<int>(create_info->imageExtent.width),
         static_cast<int>(create_info->imageExtent.height));
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
               create_info->imageExtent.width, create_info->imageExtent.height,
               strerror(-err), err);
@@ -1162,9 +1170,8 @@
     // it's job the two transforms cancel each other out and the compositor ends
     // up applying an identity transform to the app's buffer.
     err = native_window_set_buffers_transform(
-        surface.window.get(),
-        InvertTransformToNative(create_info->preTransform));
-    if (err != 0) {
+        window, InvertTransformToNative(create_info->preTransform));
+    if (err != android::OK) {
         ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
               InvertTransformToNative(create_info->preTransform),
               strerror(-err), err);
@@ -1172,8 +1179,8 @@
     }
 
     err = native_window_set_scaling_mode(
-        surface.window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-    if (err != 0) {
+        window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+    if (err != android::OK) {
         ALOGE("native_window_set_scaling_mode(SCALE_TO_WINDOW) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1183,26 +1190,25 @@
     if (create_info->presentMode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
         create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
         swapchain_image_usage |= VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID;
-        err = native_window_set_shared_buffer_mode(surface.window.get(), true);
-        if (err != 0) {
+        err = native_window_set_shared_buffer_mode(window, true);
+        if (err != android::OK) {
             ALOGE("native_window_set_shared_buffer_mode failed: %s (%d)", strerror(-err), err);
             return VK_ERROR_SURFACE_LOST_KHR;
         }
     }
 
     if (create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
-        err = native_window_set_auto_refresh(surface.window.get(), true);
-        if (err != 0) {
+        err = native_window_set_auto_refresh(window, true);
+        if (err != android::OK) {
             ALOGE("native_window_set_auto_refresh failed: %s (%d)", strerror(-err), err);
             return VK_ERROR_SURFACE_LOST_KHR;
         }
     }
 
     int query_value;
-    err = surface.window->query(surface.window.get(),
-                                NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
-                                &query_value);
-    if (err != 0 || query_value < 0) {
+    err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                        &query_value);
+    if (err != android::OK || query_value < 0) {
         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
               query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1218,8 +1224,8 @@
     // in place for that to work yet. Note we only lie to the lower layer-- we
     // don't want to give the app back a swapchain with extra images (which they
     // can't actually use!).
-    err = native_window_set_buffer_count(surface.window.get(), std::max(2u, num_images));
-    if (err != 0) {
+    err = native_window_set_buffer_count(window, std::max(2u, num_images));
+    if (err != android::OK) {
         ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1257,16 +1263,15 @@
         createProtectedSwapchain = true;
         native_usage |= BufferUsage::PROTECTED;
     }
-    err = native_window_set_usage(surface.window.get(), native_usage);
-    if (err != 0) {
+    err = native_window_set_usage(window, native_usage);
+    if (err != android::OK) {
         ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     int transform_hint;
-    err = surface.window->query(surface.window.get(),
-                                NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
-    if (err != 0) {
+    err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1322,9 +1327,8 @@
         Swapchain::Image& img = swapchain->images[i];
 
         ANativeWindowBuffer* buffer;
-        err = surface.window->dequeueBuffer(surface.window.get(), &buffer,
-                                            &img.dequeue_fence);
-        if (err != 0) {
+        err = window->dequeueBuffer(window, &buffer, &img.dequeue_fence);
+        if (err != android::OK) {
             ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err);
             switch (-err) {
                 case ENOMEM:
@@ -1368,8 +1372,8 @@
         Swapchain::Image& img = swapchain->images[i];
         if (img.dequeued) {
             if (!swapchain->shared) {
-                surface.window->cancelBuffer(surface.window.get(), img.buffer.get(),
-                                             img.dequeue_fence);
+                window->cancelBuffer(window, img.buffer.get(),
+                                     img.dequeue_fence);
                 img.dequeue_fence = -1;
                 img.dequeued = false;
             }
@@ -1447,10 +1451,6 @@
     if (swapchain.surface.swapchain_handle != swapchain_handle)
         return VK_ERROR_OUT_OF_DATE_KHR;
 
-    ALOGW_IF(
-        timeout != UINT64_MAX,
-        "vkAcquireNextImageKHR: non-infinite timeouts not yet implemented");
-
     if (swapchain.shared) {
         // In shared mode, we keep the buffer dequeued all the time, so we don't
         // want to dequeue a buffer here. Instead, just ask the driver to ensure
@@ -1461,10 +1461,27 @@
         return result;
     }
 
+    const nsecs_t acquire_next_image_timeout =
+        timeout > (uint64_t)std::numeric_limits<nsecs_t>::max() ? -1 : timeout;
+    if (acquire_next_image_timeout != swapchain.acquire_next_image_timeout) {
+        // Cache the timeout to avoid the duplicate binder cost.
+        err = window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT,
+                              acquire_next_image_timeout);
+        if (err != android::OK) {
+            ALOGE("window->perform(SET_DEQUEUE_TIMEOUT) failed: %s (%d)",
+                  strerror(-err), err);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+        swapchain.acquire_next_image_timeout = acquire_next_image_timeout;
+    }
+
     ANativeWindowBuffer* buffer;
     int fence_fd;
     err = window->dequeueBuffer(window, &buffer, &fence_fd);
-    if (err != 0) {
+    if (err == android::TIMED_OUT) {
+        ALOGW("dequeueBuffer timed out: %s (%d)", strerror(-err), err);
+        return timeout ? VK_TIMEOUT : VK_NOT_READY;
+    } else if (err != android::OK) {
         ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
@@ -1671,7 +1688,7 @@
                     uint64_t nativeFrameId = 0;
                     err = native_window_get_next_frame_id(
                             window, &nativeFrameId);
-                    if (err != android::NO_ERROR) {
+                    if (err != android::OK) {
                         ALOGE("Failed to get next native frame ID.");
                     }
 
@@ -1695,7 +1712,7 @@
 
                 err = window->queueBuffer(window, img.buffer.get(), fence);
                 // queueBuffer always closes fence, even on error
-                if (err != 0) {
+                if (err != android::OK) {
                     ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
                     swapchain_result = WorstPresentResult(
                         swapchain_result, VK_ERROR_OUT_OF_DATE_KHR);
@@ -1715,17 +1732,15 @@
                     ANativeWindowBuffer* buffer;
                     int fence_fd;
                     err = window->dequeueBuffer(window, &buffer, &fence_fd);
-                    if (err != 0) {
+                    if (err != android::OK) {
                         ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
                         swapchain_result = WorstPresentResult(swapchain_result,
                             VK_ERROR_SURFACE_LOST_KHR);
-                    }
-                    else if (img.buffer != buffer) {
+                    } else if (img.buffer != buffer) {
                         ALOGE("got wrong image back for shared swapchain");
                         swapchain_result = WorstPresentResult(swapchain_result,
                             VK_ERROR_SURFACE_LOST_KHR);
-                    }
-                    else {
+                    } else {
                         img.dequeue_fence = fence_fd;
                         img.dequeued = true;
                     }
@@ -1737,7 +1752,7 @@
             int window_transform_hint;
             err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
                                 &window_transform_hint);
-            if (err != 0) {
+            if (err != android::OK) {
                 ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
                       strerror(-err), err);
                 swapchain_result = WorstPresentResult(