Merge "SF: Define DisplayId as struct type"
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 90cadb4..aad9939 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -201,23 +201,53 @@
     }
 }
 
-// Automatically adds binary and null terminator arg.
-static inline void ExecVWithArgs(const char* bin, const std::vector<std::string>& args) {
-    std::vector<const char*> argv = {bin};
-    for (const std::string& arg : args) {
-        argv.push_back(arg.c_str());
-    }
-    // Add null terminator.
-    argv.push_back(nullptr);
-    execv(bin, (char * const *)&argv[0]);
-}
+// ExecVHelper prepares and holds pointers to parsed command line arguments so that no allocations
+// need to be performed between the fork and exec.
+class ExecVHelper {
+  public:
+    // Store a placeholder for the binary name.
+    ExecVHelper() : args_(1u, std::string()) {}
 
-static inline void AddArgIfNonEmpty(const std::string& arg, std::vector<std::string>* args) {
-    DCHECK(args != nullptr);
-    if (!arg.empty()) {
-        args->push_back(arg);
+    void PrepareArgs(const std::string& bin) {
+        CHECK(!args_.empty());
+        CHECK(args_[0].empty());
+        args_[0] = bin;
+        // Write char* into array.
+        for (const std::string& arg : args_) {
+            argv_.push_back(arg.c_str());
+        }
+        argv_.push_back(nullptr);  // Add null terminator.
     }
-}
+
+    [[ noreturn ]]
+    void Exec(int exit_code) {
+        execv(argv_[0], (char * const *)&argv_[0]);
+        PLOG(ERROR) << "execv(" << argv_[0] << ") failed";
+        exit(exit_code);
+    }
+
+    // Add an arg if it's not empty.
+    void AddArg(const std::string& arg) {
+        if (!arg.empty()) {
+            args_.push_back(arg);
+        }
+    }
+
+    // Add a runtime arg if it's not empty.
+    void AddRuntimeArg(const std::string& arg) {
+        if (!arg.empty()) {
+            args_.push_back("--runtime-arg");
+            args_.push_back(arg);
+        }
+    }
+
+  protected:
+    // Holder arrays for backing arg storage.
+    std::vector<std::string> args_;
+
+    // Argument poiners.
+    std::vector<const char*> argv_;
+};
 
 static std::string MapPropertyToArg(const std::string& property,
                                     const std::string& format,
@@ -229,212 +259,220 @@
   return "";
 }
 
-[[ noreturn ]]
-static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vdex_fd, int image_fd,
-        const char* input_file_name, const char* output_file_name, int swap_fd,
-        const char* instruction_set, const char* compiler_filter,
-        bool debuggable, bool post_bootcomplete, bool background_job_compile, int profile_fd,
-        const char* class_loader_context, int target_sdk_version, bool enable_hidden_api_checks,
-        bool generate_compact_dex, int dex_metadata_fd, const char* compilation_reason) {
-    // Get the relative path to the input file.
-    const char* relative_input_file_name = get_location_from_path(input_file_name);
+class RunDex2Oat : public ExecVHelper {
+  public:
+    RunDex2Oat(int zip_fd,
+               int oat_fd,
+               int input_vdex_fd,
+               int output_vdex_fd,
+               int image_fd,
+               const char* input_file_name,
+               const char* output_file_name,
+               int swap_fd,
+               const char* instruction_set,
+               const char* compiler_filter,
+               bool debuggable,
+               bool post_bootcomplete,
+               bool background_job_compile,
+               int profile_fd,
+               const char* class_loader_context,
+               int target_sdk_version,
+               bool enable_hidden_api_checks,
+               bool generate_compact_dex,
+               int dex_metadata_fd,
+               const char* compilation_reason) {
+        // Get the relative path to the input file.
+        const char* relative_input_file_name = get_location_from_path(input_file_name);
 
-    std::string dex2oat_Xms_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s");
-    std::string dex2oat_Xmx_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s");
+        std::string dex2oat_Xms_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s");
+        std::string dex2oat_Xmx_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s");
 
-    const char* threads_property = post_bootcomplete
-            ? "dalvik.vm.dex2oat-threads"
-            : "dalvik.vm.boot-dex2oat-threads";
-    std::string dex2oat_threads_arg = MapPropertyToArg(threads_property, "-j%s");
+        const char* threads_property = post_bootcomplete
+                ? "dalvik.vm.dex2oat-threads"
+                : "dalvik.vm.boot-dex2oat-threads";
+        std::string dex2oat_threads_arg = MapPropertyToArg(threads_property, "-j%s");
 
-    const std::string dex2oat_isa_features_key =
-            StringPrintf("dalvik.vm.isa.%s.features", instruction_set);
-    std::string instruction_set_features_arg =
-        MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s");
+        const std::string dex2oat_isa_features_key =
+                StringPrintf("dalvik.vm.isa.%s.features", instruction_set);
+        std::string instruction_set_features_arg =
+            MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s");
 
-    const std::string dex2oat_isa_variant_key =
-            StringPrintf("dalvik.vm.isa.%s.variant", instruction_set);
-    std::string instruction_set_variant_arg =
-        MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s");
+        const std::string dex2oat_isa_variant_key =
+                StringPrintf("dalvik.vm.isa.%s.variant", instruction_set);
+        std::string instruction_set_variant_arg =
+            MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s");
 
-    const char *dex2oat_norelocation = "-Xnorelocate";
+        const char* dex2oat_norelocation = "-Xnorelocate";
 
-    const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", "");
-    std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags);
-    ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str());
+        const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", "");
+        std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags);
+        ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str());
 
-    // If we are booting without the real /data, don't spend time compiling.
-    std::string vold_decrypt = GetProperty("vold.decrypt", "");
-    bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
-                            vold_decrypt == "1";
+        // If we are booting without the real /data, don't spend time compiling.
+        std::string vold_decrypt = GetProperty("vold.decrypt", "");
+        bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
+                                vold_decrypt == "1";
 
-    const std::string resolve_startup_string_arg  =
-            MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",
-                             "--resolve-startup-const-strings=%s");
-    const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false);
+        const std::string resolve_startup_string_arg  =
+                MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",
+                                 "--resolve-startup-const-strings=%s");
+        const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false);
 
-    std::string image_format_arg;
-    if (image_fd >= 0) {
-        image_format_arg = MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s");
-    }
-
-    std::string dex2oat_large_app_threshold_arg =
-        MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");
-
-    // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat.
-    const char* dex2oat_bin = "/system/bin/dex2oat";
-    constexpr const char* kDex2oatDebugPath = "/system/bin/dex2oatd";
-    // Do not use dex2oatd for release candidates (give dex2oat more soak time).
-    bool is_release = android::base::GetProperty("ro.build.version.codename", "") == "REL";
-    if (is_debug_runtime() || (background_job_compile && is_debuggable_build() && !is_release)) {
-        if (access(kDex2oatDebugPath, X_OK) == 0) {
-            dex2oat_bin = kDex2oatDebugPath;
+        std::string image_format_arg;
+        if (image_fd >= 0) {
+            image_format_arg = MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s");
         }
-    }
 
-    bool generate_minidebug_info = kEnableMinidebugInfo &&
-            android::base::GetBoolProperty(kMinidebugInfoSystemProperty,
-                                           kMinidebugInfoSystemPropertyDefault);
+        std::string dex2oat_large_app_threshold_arg =
+            MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");
 
-    // clang FORTIFY doesn't let us use strlen in constant array bounds, so we
-    // use arraysize instead.
-    std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd);
-    std::string zip_location_arg = StringPrintf("--zip-location=%s", relative_input_file_name);
-    std::string input_vdex_fd_arg = StringPrintf("--input-vdex-fd=%d", input_vdex_fd);
-    std::string output_vdex_fd_arg = StringPrintf("--output-vdex-fd=%d", output_vdex_fd);
-    std::string oat_fd_arg = StringPrintf("--oat-fd=%d", oat_fd);
-    std::string oat_location_arg = StringPrintf("--oat-location=%s", output_file_name);
-    std::string instruction_set_arg = StringPrintf("--instruction-set=%s", instruction_set);
-    std::string dex2oat_compiler_filter_arg;
-    std::string dex2oat_swap_fd;
-    std::string dex2oat_image_fd;
-    std::string target_sdk_version_arg;
-    if (target_sdk_version != 0) {
-        StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version);
-    }
-    std::string class_loader_context_arg;
-    if (class_loader_context != nullptr) {
-        class_loader_context_arg = StringPrintf("--class-loader-context=%s", class_loader_context);
-    }
+        // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat.
+        const char* dex2oat_bin = "/system/bin/dex2oat";
+        constexpr const char* kDex2oatDebugPath = "/system/bin/dex2oatd";
+        // Do not use dex2oatd for release candidates (give dex2oat more soak time).
+        bool is_release = android::base::GetProperty("ro.build.version.codename", "") == "REL";
+        if (is_debug_runtime() ||
+                (background_job_compile && is_debuggable_build() && !is_release)) {
+            if (access(kDex2oatDebugPath, X_OK) == 0) {
+                dex2oat_bin = kDex2oatDebugPath;
+            }
+        }
 
-    if (swap_fd >= 0) {
-        dex2oat_swap_fd = StringPrintf("--swap-fd=%d", swap_fd);
-    }
-    if (image_fd >= 0) {
-        dex2oat_image_fd = StringPrintf("--app-image-fd=%d", image_fd);
-    }
+        bool generate_minidebug_info = kEnableMinidebugInfo &&
+                GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault);
 
-    // Compute compiler filter.
-    bool have_dex2oat_relocation_skip_flag = false;
-    if (skip_compilation) {
-        dex2oat_compiler_filter_arg = "--compiler-filter=extract";
-        have_dex2oat_relocation_skip_flag = true;
-    } else if (compiler_filter != nullptr) {
-        dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", compiler_filter);
-    }
+        // clang FORTIFY doesn't let us use strlen in constant array bounds, so we
+        // use arraysize instead.
+        std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd);
+        std::string zip_location_arg = StringPrintf("--zip-location=%s", relative_input_file_name);
+        std::string input_vdex_fd_arg = StringPrintf("--input-vdex-fd=%d", input_vdex_fd);
+        std::string output_vdex_fd_arg = StringPrintf("--output-vdex-fd=%d", output_vdex_fd);
+        std::string oat_fd_arg = StringPrintf("--oat-fd=%d", oat_fd);
+        std::string oat_location_arg = StringPrintf("--oat-location=%s", output_file_name);
+        std::string instruction_set_arg = StringPrintf("--instruction-set=%s", instruction_set);
+        std::string dex2oat_compiler_filter_arg;
+        std::string dex2oat_swap_fd;
+        std::string dex2oat_image_fd;
+        std::string target_sdk_version_arg;
+        if (target_sdk_version != 0) {
+            StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version);
+        }
+        std::string class_loader_context_arg;
+        if (class_loader_context != nullptr) {
+            class_loader_context_arg = StringPrintf("--class-loader-context=%s",
+                                                    class_loader_context);
+        }
 
-    if (dex2oat_compiler_filter_arg.empty()) {
-        dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",
-                                                       "--compiler-filter=%s");
-    }
+        if (swap_fd >= 0) {
+            dex2oat_swap_fd = StringPrintf("--swap-fd=%d", swap_fd);
+        }
+        if (image_fd >= 0) {
+            dex2oat_image_fd = StringPrintf("--app-image-fd=%d", image_fd);
+        }
 
-    // Check whether all apps should be compiled debuggable.
-    if (!debuggable) {
-        debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1";
-    }
-    std::string profile_arg;
-    if (profile_fd != -1) {
-        profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd);
-    }
+        // Compute compiler filter.
+        bool have_dex2oat_relocation_skip_flag = false;
+        if (skip_compilation) {
+            dex2oat_compiler_filter_arg = "--compiler-filter=extract";
+            have_dex2oat_relocation_skip_flag = true;
+        } else if (compiler_filter != nullptr) {
+            dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", compiler_filter);
+        }
 
-    // Get the directory of the apk to pass as a base classpath directory.
-    std::string base_dir;
-    std::string apk_dir(input_file_name);
-    unsigned long dir_index = apk_dir.rfind('/');
-    bool has_base_dir = dir_index != std::string::npos;
-    if (has_base_dir) {
-        apk_dir = apk_dir.substr(0, dir_index);
-        base_dir = StringPrintf("--classpath-dir=%s", apk_dir.c_str());
+        if (dex2oat_compiler_filter_arg.empty()) {
+            dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",
+                                                           "--compiler-filter=%s");
+        }
+
+        // Check whether all apps should be compiled debuggable.
+        if (!debuggable) {
+            debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1";
+        }
+        std::string profile_arg;
+        if (profile_fd != -1) {
+            profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd);
+        }
+
+        // Get the directory of the apk to pass as a base classpath directory.
+        std::string base_dir;
+        std::string apk_dir(input_file_name);
+        unsigned long dir_index = apk_dir.rfind('/');
+        bool has_base_dir = dir_index != std::string::npos;
+        if (has_base_dir) {
+            apk_dir = apk_dir.substr(0, dir_index);
+            base_dir = StringPrintf("--classpath-dir=%s", apk_dir.c_str());
+        }
+
+        std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd);
+
+        std::string compilation_reason_arg = compilation_reason == nullptr
+                ? ""
+                : std::string("--compilation-reason=") + compilation_reason;
+
+        ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name);
+
+        // Disable cdex if update input vdex is true since this combination of options is not
+        // supported.
+        const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd);
+
+        AddArg(zip_fd_arg);
+        AddArg(zip_location_arg);
+        AddArg(input_vdex_fd_arg);
+        AddArg(output_vdex_fd_arg);
+        AddArg(oat_fd_arg);
+        AddArg(oat_location_arg);
+        AddArg(instruction_set_arg);
+
+        AddArg(instruction_set_variant_arg);
+        AddArg(instruction_set_features_arg);
+
+        AddRuntimeArg(dex2oat_Xms_arg);
+        AddRuntimeArg(dex2oat_Xmx_arg);
+
+        AddArg(resolve_startup_string_arg);
+        AddArg(dex2oat_compiler_filter_arg);
+        AddArg(dex2oat_threads_arg);
+        AddArg(dex2oat_swap_fd);
+        AddArg(dex2oat_image_fd);
+
+        if (generate_debug_info) {
+            AddArg("--generate-debug-info");
+        }
+        if (debuggable) {
+            AddArg("--debuggable");
+        }
+        AddArg(image_format_arg);
+        AddArg(dex2oat_large_app_threshold_arg);
+
+        if (have_dex2oat_relocation_skip_flag) {
+            AddRuntimeArg(dex2oat_norelocation);
+        }
+        AddArg(profile_arg);
+        AddArg(base_dir);
+        AddArg(class_loader_context_arg);
+        if (generate_minidebug_info) {
+            AddArg(kMinidebugDex2oatFlag);
+        }
+        if (disable_cdex) {
+            AddArg(kDisableCompactDexFlag);
+        }
+        AddArg(target_sdk_version_arg);
+        if (enable_hidden_api_checks) {
+            AddRuntimeArg("-Xhidden-api-checks");
+        }
+
+        if (dex_metadata_fd > -1) {
+            AddArg(dex_metadata_fd_arg);
+        }
+
+        AddArg(compilation_reason_arg);
+
+        // Do not add args after dex2oat_flags, they should override others for debugging.
+        args_.insert(args_.end(), dex2oat_flags_args.begin(), dex2oat_flags_args.end());
+
+        PrepareArgs(dex2oat_bin);
     }
-
-    std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd);
-
-    std::string compilation_reason_arg = compilation_reason == nullptr
-            ? ""
-            : std::string("--compilation-reason=") + compilation_reason;
-
-    ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name);
-
-    // Disable cdex if update input vdex is true since this combination of options is not
-    // supported.
-    const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd);
-
-    std::vector<std::string> args = {
-        zip_fd_arg,
-        zip_location_arg,
-        input_vdex_fd_arg,
-        output_vdex_fd_arg,
-        oat_fd_arg,
-        oat_location_arg,
-        instruction_set_arg,
-    };
-    auto add_runtime_arg = [&](const std::string& arg) {
-        args.push_back("--runtime-arg");
-        args.push_back(arg);
-    };
-
-    AddArgIfNonEmpty(instruction_set_variant_arg, &args);
-    AddArgIfNonEmpty(instruction_set_features_arg, &args);
-    if (!dex2oat_Xms_arg.empty()) {
-        add_runtime_arg(dex2oat_Xms_arg);
-    }
-    if (!dex2oat_Xmx_arg.empty()) {
-        add_runtime_arg(dex2oat_Xmx_arg);
-    }
-    AddArgIfNonEmpty(resolve_startup_string_arg, &args);
-    AddArgIfNonEmpty(dex2oat_compiler_filter_arg, &args);
-    AddArgIfNonEmpty(dex2oat_threads_arg, &args);
-    AddArgIfNonEmpty(dex2oat_swap_fd, &args);
-    AddArgIfNonEmpty(dex2oat_image_fd, &args);
-
-    if (generate_debug_info) {
-        args.push_back("--generate-debug-info");
-    }
-    if (debuggable) {
-        args.push_back("--debuggable");
-    }
-    AddArgIfNonEmpty(image_format_arg, &args);
-    AddArgIfNonEmpty(dex2oat_large_app_threshold_arg, &args);
-    args.insert(args.end(), dex2oat_flags_args.begin(), dex2oat_flags_args.end());
-    if (have_dex2oat_relocation_skip_flag) {
-        add_runtime_arg(dex2oat_norelocation);
-    }
-    AddArgIfNonEmpty(profile_arg, &args);
-    AddArgIfNonEmpty(base_dir, &args);
-    AddArgIfNonEmpty(class_loader_context_arg, &args);
-    if (generate_minidebug_info) {
-        args.push_back(kMinidebugDex2oatFlag);
-    }
-    if (disable_cdex) {
-        args.push_back(kDisableCompactDexFlag);
-    }
-    AddArgIfNonEmpty(target_sdk_version_arg, &args);
-    if (enable_hidden_api_checks) {
-        add_runtime_arg("-Xhidden-api-checks");
-    }
-
-    if (dex_metadata_fd > -1) {
-        args.push_back(dex_metadata_fd_arg);
-    }
-
-    AddArgIfNonEmpty(compilation_reason_arg, &args);
-
-    // Do not add after dex2oat_flags, they should override others for debugging.
-
-    ExecVWithArgs(dex2oat_bin, args);
-    PLOG(ERROR) << "execv(" << dex2oat_bin << ") failed";
-    exit(DexoptReturnCodes::kDex2oatExec);
-}
+};
 
 /*
  * Whether dexopt should use a swap file when compiling an APK.
@@ -610,74 +648,91 @@
 static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
 static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
 
-[[ noreturn ]]
-static void run_profman(const std::vector<unique_fd>& profile_fds,
-                        const unique_fd& reference_profile_fd,
-                        const std::vector<unique_fd>* apk_fds,
-                        const std::vector<std::string>* dex_locations,
-                        bool copy_and_update) {
-    const char* profman_bin = is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman";
+class RunProfman : public ExecVHelper {
+  public:
+   void SetupArgs(const std::vector<unique_fd>& profile_fds,
+                  const unique_fd& reference_profile_fd,
+                  const std::vector<unique_fd>& apk_fds,
+                  const std::vector<std::string>& dex_locations,
+                  bool copy_and_update) {
+        const char* profman_bin =
+                is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman";
 
-    if (copy_and_update) {
-        CHECK_EQ(1u, profile_fds.size());
-        CHECK(apk_fds != nullptr);
-        CHECK_EQ(1u, apk_fds->size());
-    }
-    std::vector<std::string> args;
-    args.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile_fd.get()));
-
-    for (const unique_fd& fd : profile_fds) {
-        args.push_back("--profile-file-fd=" + std::to_string(fd.get()));
-    }
-
-    if (apk_fds != nullptr) {
-        for (const unique_fd& fd : *apk_fds) {
-            args.push_back("--apk-fd=" + std::to_string(fd.get()));
+        if (copy_and_update) {
+            CHECK_EQ(1u, profile_fds.size());
+            CHECK_EQ(1u, apk_fds.size());
         }
-    }
-
-    std::vector<std::string> dex_location_args;
-    if (dex_locations != nullptr) {
-        for (const std::string& dex_location : *dex_locations) {
-            args.push_back("--dex-location=" + dex_location);
+        if (reference_profile_fd != -1) {
+            AddArg("--reference-profile-file-fd=" + std::to_string(reference_profile_fd.get()));
         }
+
+        for (const unique_fd& fd : profile_fds) {
+            AddArg("--profile-file-fd=" + std::to_string(fd.get()));
+        }
+
+        for (const unique_fd& fd : apk_fds) {
+            AddArg("--apk-fd=" + std::to_string(fd.get()));
+        }
+
+        for (const std::string& dex_location : dex_locations) {
+            AddArg("--dex-location=" + dex_location);
+        }
+
+        if (copy_and_update) {
+            AddArg("--copy-and-update-profile-key");
+        }
+
+        // Do not add after dex2oat_flags, they should override others for debugging.
+        PrepareArgs(profman_bin);
     }
 
-    if (copy_and_update) {
-        args.push_back("--copy-and-update-profile-key");
+    void SetupMerge(const std::vector<unique_fd>& profiles_fd,
+                    const unique_fd& reference_profile_fd,
+                    const std::vector<unique_fd>& apk_fds = std::vector<unique_fd>(),
+                    const std::vector<std::string>& dex_locations = std::vector<std::string>()) {
+        SetupArgs(profiles_fd,
+                    reference_profile_fd,
+                    apk_fds,
+                    dex_locations,
+                    /*copy_and_update=*/false);
     }
 
-    // Do not add after dex2oat_flags, they should override others for debugging.
+    void SetupCopyAndUpdate(unique_fd&& profile_fd,
+                            unique_fd&& reference_profile_fd,
+                            unique_fd&& apk_fd,
+                            const std::string& dex_location) {
+        // The fds need to stay open longer than the scope of the function, so put them into a local
+        // variable vector.
+        profiles_fd_.push_back(std::move(profile_fd));
+        apk_fds_.push_back(std::move(apk_fd));
+        reference_profile_fd_ = std::move(reference_profile_fd);
+        std::vector<std::string> dex_locations = {dex_location};
+        SetupArgs(profiles_fd_, reference_profile_fd_, apk_fds_, dex_locations,
+                  /*copy_and_update=*/true);
+    }
 
-    ExecVWithArgs(profman_bin, args);
-    PLOG(ERROR) << "execv(" << profman_bin << ") failed";
-    exit(DexoptReturnCodes::kProfmanExec);   /* only get here on exec failure */
-}
+    void SetupDump(const std::vector<unique_fd>& profiles_fd,
+                   const unique_fd& reference_profile_fd,
+                   const std::vector<std::string>& dex_locations,
+                   const std::vector<unique_fd>& apk_fds,
+                   const unique_fd& output_fd) {
+        AddArg("--dump-only");
+        AddArg(StringPrintf("--dump-output-to-fd=%d", output_fd.get()));
+        SetupArgs(profiles_fd, reference_profile_fd, apk_fds, dex_locations,
+                  /*copy_and_update=*/false);
+    }
 
-[[ noreturn ]]
-static void run_profman_merge(const std::vector<unique_fd>& profiles_fd,
-                              const unique_fd& reference_profile_fd,
-                              const std::vector<unique_fd>* apk_fds = nullptr,
-                              const std::vector<std::string>* dex_locations = nullptr) {
-    run_profman(profiles_fd, reference_profile_fd, apk_fds, dex_locations,
-            /*copy_and_update*/false);
-}
+    void Exec() {
+        ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec);
+    }
 
-[[ noreturn ]]
-static void run_profman_copy_and_update(unique_fd&& profile_fd,
-                                        unique_fd&& reference_profile_fd,
-                                        unique_fd&& apk_fd,
-                                        const std::string& dex_location) {
-    std::vector<unique_fd> profiles_fd;
-    profiles_fd.push_back(std::move(profile_fd));
-    std::vector<unique_fd> apk_fds;
-    apk_fds.push_back(std::move(apk_fd));
-    std::vector<std::string> dex_locations;
-    dex_locations.push_back(dex_location);
+  private:
+    unique_fd reference_profile_fd_;
+    std::vector<unique_fd> profiles_fd_;
+    std::vector<unique_fd> apk_fds_;
+};
 
-    run_profman(profiles_fd, reference_profile_fd, &apk_fds, &dex_locations,
-            /*copy_and_update*/true);
-}
+
 
 // Decides if profile guided compilation is needed or not based on existing profiles.
 // The location is the package name for primary apks or the dex path for secondary dex files.
@@ -697,11 +752,13 @@
         return false;
     }
 
+    RunProfman profman_merge;
+    profman_merge.SetupMerge(profiles_fd, reference_profile_fd);
     pid_t pid = fork();
     if (pid == 0) {
         /* child -- drop privileges before continuing */
         drop_capabilities(uid);
-        run_profman_merge(profiles_fd, reference_profile_fd);
+        profman_merge.Exec();
     }
     /* parent */
     int return_code = wait_child(pid);
@@ -774,35 +831,6 @@
     return analyze_profiles(uid, package_name, profile_name, /*is_secondary_dex*/false);
 }
 
-[[ noreturn ]]
-static void run_profman_dump(const std::vector<unique_fd>& profile_fds,
-                             const unique_fd& reference_profile_fd,
-                             const std::vector<std::string>& dex_locations,
-                             const std::vector<unique_fd>& apk_fds,
-                             const unique_fd& output_fd) {
-    std::vector<std::string> profman_args;
-    static const char* PROFMAN_BIN = "/system/bin/profman";
-    profman_args.push_back("--dump-only");
-    profman_args.push_back(StringPrintf("--dump-output-to-fd=%d", output_fd.get()));
-    if (reference_profile_fd != -1) {
-        profman_args.push_back(StringPrintf("--reference-profile-file-fd=%d",
-                                            reference_profile_fd.get()));
-    }
-    for (size_t i = 0; i < profile_fds.size(); i++) {
-        profman_args.push_back(StringPrintf("--profile-file-fd=%d", profile_fds[i].get()));
-    }
-    for (const std::string& dex_location : dex_locations) {
-        profman_args.push_back(StringPrintf("--dex-location=%s", dex_location.c_str()));
-    }
-    for (size_t i = 0; i < apk_fds.size(); i++) {
-        profman_args.push_back(StringPrintf("--apk-fd=%d", apk_fds[i].get()));
-    }
-
-    ExecVWithArgs(PROFMAN_BIN, profman_args);
-    PLOG(ERROR) << "execv(" << PROFMAN_BIN << ") failed";
-    exit(DexoptReturnCodes::kProfmanExec);   /* only get here on exec failure */
-}
-
 bool dump_profiles(int32_t uid, const std::string& pkgname, const std::string& profile_name,
         const std::string& code_path) {
     std::vector<unique_fd> profile_fds;
@@ -839,12 +867,13 @@
     apk_fds.push_back(std::move(apk_fd));
 
 
+    RunProfman profman_dump;
+    profman_dump.SetupDump(profile_fds, reference_profile_fd, dex_locations, apk_fds, output_fd);
     pid_t pid = fork();
     if (pid == 0) {
         /* child -- drop privileges before continuing */
         drop_capabilities(uid);
-        run_profman_dump(profile_fds, reference_profile_fd, dex_locations,
-                         apk_fds, output_fd);
+        profman_dump.Exec();
     }
     /* parent */
     int return_code = wait_child(pid);
@@ -1416,55 +1445,60 @@
 // The analyzer will check if the dex_file needs to be (re)compiled to match the compiler_filter.
 // If this is for a profile guided compilation, profile_was_updated will tell whether or not
 // the profile has changed.
-static void exec_dexoptanalyzer(const std::string& dex_file, int vdex_fd, int oat_fd,
-        int zip_fd, const std::string& instruction_set, const std::string& compiler_filter,
-        bool profile_was_updated, bool downgrade,
-        const char* class_loader_context) {
-    CHECK_GE(zip_fd, 0);
-    const char* dexoptanalyzer_bin =
-            is_debug_runtime()
-                    ? "/system/bin/dexoptanalyzerd"
-                    : "/system/bin/dexoptanalyzer";
+class RunDexoptAnalyzer : public ExecVHelper {
+ public:
+    RunDexoptAnalyzer(const std::string& dex_file,
+                    int vdex_fd,
+                    int oat_fd,
+                    int zip_fd,
+                    const std::string& instruction_set,
+                    const std::string& compiler_filter,
+                    bool profile_was_updated,
+                    bool downgrade,
+                    const char* class_loader_context) {
+        CHECK_GE(zip_fd, 0);
+        const char* dexoptanalyzer_bin =
+                is_debug_runtime()
+                        ? "/system/bin/dexoptanalyzerd"
+                        : "/system/bin/dexoptanalyzer";
 
-    std::string dex_file_arg = "--dex-file=" + dex_file;
-    std::string oat_fd_arg = "--oat-fd=" + std::to_string(oat_fd);
-    std::string vdex_fd_arg = "--vdex-fd=" + std::to_string(vdex_fd);
-    std::string zip_fd_arg = "--zip-fd=" + std::to_string(zip_fd);
-    std::string isa_arg = "--isa=" + instruction_set;
-    std::string compiler_filter_arg = "--compiler-filter=" + compiler_filter;
-    const char* assume_profile_changed = "--assume-profile-changed";
-    const char* downgrade_flag = "--downgrade";
-    std::string class_loader_context_arg = "--class-loader-context=";
-    if (class_loader_context != nullptr) {
-        class_loader_context_arg += class_loader_context;
-    }
+        std::string dex_file_arg = "--dex-file=" + dex_file;
+        std::string oat_fd_arg = "--oat-fd=" + std::to_string(oat_fd);
+        std::string vdex_fd_arg = "--vdex-fd=" + std::to_string(vdex_fd);
+        std::string zip_fd_arg = "--zip-fd=" + std::to_string(zip_fd);
+        std::string isa_arg = "--isa=" + instruction_set;
+        std::string compiler_filter_arg = "--compiler-filter=" + compiler_filter;
+        const char* assume_profile_changed = "--assume-profile-changed";
+        const char* downgrade_flag = "--downgrade";
+        std::string class_loader_context_arg = "--class-loader-context=";
+        if (class_loader_context != nullptr) {
+            class_loader_context_arg += class_loader_context;
+        }
 
-    // program name, dex file, isa, filter
-    std::vector<std::string> args = {
-      dex_file_arg,
-      isa_arg,
-      compiler_filter_arg,
-    };
-    if (oat_fd >= 0) {
-        args.push_back(oat_fd_arg);
-    }
-    if (vdex_fd >= 0) {
-        args.push_back(vdex_fd_arg);
-    }
-    args.push_back(zip_fd_arg.c_str());
-    if (profile_was_updated) {
-        args.push_back(assume_profile_changed);
-    }
-    if (downgrade) {
-        args.push_back(downgrade_flag);
-    }
-    if (class_loader_context != nullptr) {
-        args.push_back(class_loader_context_arg.c_str());
-    }
+        // program name, dex file, isa, filter
+        AddArg(dex_file_arg);
+        AddArg(isa_arg);
+        AddArg(compiler_filter_arg);
+        if (oat_fd >= 0) {
+            AddArg(oat_fd_arg);
+        }
+        if (vdex_fd >= 0) {
+            AddArg(vdex_fd_arg);
+        }
+        AddArg(zip_fd_arg.c_str());
+        if (profile_was_updated) {
+            AddArg(assume_profile_changed);
+        }
+        if (downgrade) {
+            AddArg(downgrade_flag);
+        }
+        if (class_loader_context != nullptr) {
+            AddArg(class_loader_context_arg.c_str());
+        }
 
-    ExecVWithArgs(dexoptanalyzer_bin, args);
-    ALOGE("execv(%s) failed: %s\n", dexoptanalyzer_bin, strerror(errno));
-}
+        PrepareArgs(dexoptanalyzer_bin);
+    }
+};
 
 // Prepares the oat dir for the secondary dex files.
 static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid,
@@ -1716,16 +1750,17 @@
                 /*is_secondary_dex*/true);
 
         // Run dexoptanalyzer to get dexopt_needed code. This is not expected to return.
-        exec_dexoptanalyzer(dex_path,
-                            vdex_file_fd.get(),
-                            oat_file_fd.get(),
-                            zip_fd.get(),
-                            instruction_set,
-                            compiler_filter, profile_was_updated,
-                            downgrade,
-                            class_loader_context);
-        PLOG(ERROR) << "Failed to exec dexoptanalyzer";
-        _exit(kSecondaryDexDexoptAnalyzerSkippedFailExec);
+        // Note that we do not do it before the fork since opening the files is required to happen
+        // after forking.
+        RunDexoptAnalyzer run_dexopt_analyzer(dex_path,
+                                              vdex_file_fd.get(),
+                                              oat_file_fd.get(),
+                                              zip_fd.get(),
+                                              instruction_set,
+                                              compiler_filter, profile_was_updated,
+                                              downgrade,
+                                              class_loader_context);
+        run_dexopt_analyzer.Exec(kSecondaryDexDexoptAnalyzerSkippedFailExec);
     }
 
     /* parent */
@@ -1894,6 +1929,27 @@
 
     LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---";
 
+    RunDex2Oat runner(input_fd.get(),
+                      out_oat_fd.get(),
+                      in_vdex_fd.get(),
+                      out_vdex_fd.get(),
+                      image_fd.get(),
+                      dex_path,
+                      out_oat_path,
+                      swap_fd.get(),
+                      instruction_set,
+                      compiler_filter,
+                      debuggable,
+                      boot_complete,
+                      background_job_compile,
+                      reference_profile_fd.get(),
+                      class_loader_context,
+                      target_sdk_version,
+                      enable_hidden_api_checks,
+                      generate_compact_dex,
+                      dex_metadata_fd.get(),
+                      compilation_reason);
+
     pid_t pid = fork();
     if (pid == 0) {
         /* child -- drop privileges before continuing */
@@ -1905,26 +1961,7 @@
             _exit(DexoptReturnCodes::kFlock);
         }
 
-        run_dex2oat(input_fd.get(),
-                    out_oat_fd.get(),
-                    in_vdex_fd.get(),
-                    out_vdex_fd.get(),
-                    image_fd.get(),
-                    dex_path,
-                    out_oat_path,
-                    swap_fd.get(),
-                    instruction_set,
-                    compiler_filter,
-                    debuggable,
-                    boot_complete,
-                    background_job_compile,
-                    reference_profile_fd.get(),
-                    class_loader_context,
-                    target_sdk_version,
-                    enable_hidden_api_checks,
-                    generate_compact_dex,
-                    dex_metadata_fd.get(),
-                    compilation_reason);
+        runner.Exec(DexoptReturnCodes::kDex2oatExec);
     } else {
         int res = wait_child(pid);
         if (res == 0) {
@@ -2488,11 +2525,13 @@
         return false;
     }
 
+    RunProfman args;
+    args.SetupMerge(profiles_fd, snapshot_fd, apk_fds, dex_locations);
     pid_t pid = fork();
     if (pid == 0) {
         /* child -- drop privileges before continuing */
         drop_capabilities(app_shared_gid);
-        run_profman_merge(profiles_fd, snapshot_fd, &apk_fds, &dex_locations);
+        args.Exec();
     }
 
     /* parent */
@@ -2572,6 +2611,8 @@
                 profiles_fd.push_back(std::move(fd));
             }
         }
+        RunProfman args;
+        args.SetupMerge(profiles_fd, snapshot_fd, apk_fds, dex_locations);
         pid_t pid = fork();
         if (pid == 0) {
             /* child -- drop privileges before continuing */
@@ -2579,7 +2620,7 @@
 
             // The introduction of new access flags into boot jars causes them to
             // fail dex file verification.
-            run_profman_merge(profiles_fd, snapshot_fd, &apk_fds, &dex_locations);
+            args.Exec();
         }
 
         /* parent */
@@ -2633,6 +2674,11 @@
         return false;
     }
 
+    RunProfman args;
+    args.SetupCopyAndUpdate(std::move(dex_metadata_fd),
+                            std::move(ref_profile_fd),
+                            std::move(apk_fd),
+                            code_path);
     pid_t pid = fork();
     if (pid == 0) {
         /* child -- drop privileges before continuing */
@@ -2640,10 +2686,7 @@
         drop_capabilities(app_shared_gid);
 
         // The copy and update takes ownership over the fds.
-        run_profman_copy_and_update(std::move(dex_metadata_fd),
-                                    std::move(ref_profile_fd),
-                                    std::move(apk_fd),
-                                    code_path);
+        args.Exec();
     }
 
     /* parent */
diff --git a/headers/media_plugin/media/drm/DrmAPI.h b/headers/media_plugin/media/drm/DrmAPI.h
index c44a1f6..aa8bd3d 100644
--- a/headers/media_plugin/media/drm/DrmAPI.h
+++ b/headers/media_plugin/media/drm/DrmAPI.h
@@ -167,6 +167,25 @@
             kSecurityLevelHwSecureAll
         };
 
+        // An offline license may be usable or inactive. The keys in a
+        // usable offline license are available for decryption. When
+        // the offline license state is inactive, the keys have been
+        // marked for release using getKeyRequest with
+        // kKeyType_Release but the key response has not been
+        // received. The keys in an inactive offline license are not
+        // usable for decryption.
+
+        enum OfflineLicenseState {
+            // The offline license state is unknown due to an error
+            kOfflineLicenseStateUnknown,
+            // Offline license state is usable, the keys may be used for decryption.
+            kOfflineLicenseStateUsable,
+            // Offline license state is inactive, the keys have been marked for
+            // release using getKeyRequest() with kKeyType_Release but the
+            // key response has not been received.
+            kOfflineLicenseStateInactive
+        };
+
         DrmPlugin() {}
         virtual ~DrmPlugin() {}
 
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index a5842f7..4ee1b67 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -109,11 +109,11 @@
  *
  * \param arrayData some external representation of an array.
  * \param index the index at which a string should be allocated.
- * \param outLength an out parameter for the length of the string (not including the
- * null-terminator)
+ * \param outLength an out parameter for the length of the string at the specified index. This
+ * should not include the length for a null-terminator if there is one.
  *
- * \param a null-terminated buffer of size 'outLength + 1' representing the string at the provided
- * index including the null-terminator.
+ * \param a buffer of size outLength or more representing the string at the provided index. This is
+ * not required to be null-terminated.
  */
 typedef const char* (*AParcel_stringArrayElementGetter)(const void* arrayData, size_t index,
                                                         size_t* outLength);
@@ -382,8 +382,7 @@
  * Writes utf-8 string value to the next location in a non-null parcel.
  *
  * \param parcel the parcel to write to.
- * \param string the null-terminated string to write to the parcel. The buffer including the null
- * terminator should be of size 'length' + 1.
+ * \param string the null-terminated string to write to the parcel, at least of size 'length'.
  * \param length the length of the string to be written.
  *
  * \return STATUS_OK on successful write.
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 98264ac..7677c3a 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -107,6 +107,7 @@
         "IProducerListener.cpp",
         "ISurfaceComposer.cpp",
         "ISurfaceComposerClient.cpp",
+        "ITransactionCompletedListener.cpp",
         "LayerDebugInfo.cpp",
         "LayerState.cpp",
         "OccupancyTracker.cpp",
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index accf72c..69e5379 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -634,10 +634,10 @@
             if (count > data.dataSize()) {
                 return BAD_VALUE;
             }
-            ComposerState s;
             Vector<ComposerState> state;
             state.setCapacity(count);
             for (size_t i = 0; i < count; i++) {
+                ComposerState s;
                 if (s.read(data) == BAD_VALUE) {
                     return BAD_VALUE;
                 }
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
new file mode 100644
index 0000000..95b1038
--- /dev/null
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ITransactionCompletedListener"
+//#define LOG_NDEBUG 0
+
+#include <gui/ITransactionCompletedListener.h>
+
+namespace android {
+
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+    ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
+    LAST = ON_TRANSACTION_COMPLETED,
+};
+
+} // Anonymous namespace
+
+status_t SurfaceStats::writeToParcel(Parcel* output) const {
+    status_t err = output->writeStrongBinder(surfaceControl);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = output->writeInt64(acquireTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    return output->writeBool(releasePreviousBuffer);
+}
+
+status_t SurfaceStats::readFromParcel(const Parcel* input) {
+    status_t err = input->readStrongBinder(&surfaceControl);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = input->readInt64(&acquireTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    return input->readBool(&releasePreviousBuffer);
+}
+
+status_t TransactionStats::writeToParcel(Parcel* output) const {
+    status_t err = output->writeInt64(latchTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = output->writeInt64(presentTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    return output->writeParcelableVector(surfaceStats);
+}
+
+status_t TransactionStats::readFromParcel(const Parcel* input) {
+    status_t err = input->readInt64(&latchTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = input->readInt64(&presentTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    return input->readParcelableVector(&surfaceStats);
+}
+
+status_t ListenerStats::writeToParcel(Parcel* output) const {
+    status_t err = output->writeInt32(static_cast<int32_t>(transactionStats.size()));
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    for (const auto& [callbackIds, stats] : transactionStats) {
+        err = output->writeParcelable(stats);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        err = output->writeInt64Vector(callbackIds);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t ListenerStats::readFromParcel(const Parcel* input) {
+    int32_t transactionStats_size = input->readInt32();
+
+    for (int i = 0; i < transactionStats_size; i++) {
+        TransactionStats stats;
+        std::vector<CallbackId> callbackIds;
+
+        status_t err = input->readParcelable(&stats);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        err = input->readInt64Vector(&callbackIds);
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        transactionStats.emplace(callbackIds, stats);
+    }
+    return NO_ERROR;
+}
+
+ListenerStats ListenerStats::createEmpty(const sp<ITransactionCompletedListener>& listener,
+                                         const std::unordered_set<CallbackId>& callbackIds) {
+    ListenerStats listenerStats;
+    listenerStats.listener = listener;
+    TransactionStats transactionStats;
+    listenerStats.transactionStats.emplace(std::piecewise_construct,
+                                           std::forward_as_tuple(callbackIds.begin(),
+                                                                 callbackIds.end()),
+                                           std::forward_as_tuple(transactionStats));
+    return listenerStats;
+}
+
+class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> {
+public:
+    explicit BpTransactionCompletedListener(const sp<IBinder>& impl)
+          : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") {
+    }
+
+    ~BpTransactionCompletedListener() override;
+
+    void onTransactionCompleted(ListenerStats stats) override {
+        callRemoteAsync<decltype(&ITransactionCompletedListener::
+                                         onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
+                                                                  stats);
+    }
+};
+
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpTransactionCompletedListener::~BpTransactionCompletedListener() = default;
+
+IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener");
+
+status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data,
+                                                    Parcel* reply, uint32_t flags) {
+    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+    auto tag = static_cast<Tag>(code);
+    switch (tag) {
+        case Tag::ON_TRANSACTION_COMPLETED:
+            return callLocalAsync(data, reply,
+                                  &ITransactionCompletedListener::onTransactionCompleted);
+    }
+}
+
+}; // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 2b0a461..7b71b39 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -80,6 +80,13 @@
     memcpy(output.writeInplace(16 * sizeof(float)),
            colorTransform.asArray(), 16 * sizeof(float));
 
+    if (output.writeVectorSize(listenerCallbacks) == NO_ERROR) {
+        for (const auto& [listener, callbackIds] : listenerCallbacks) {
+            output.writeStrongBinder(IInterface::asBinder(listener));
+            output.writeInt64Vector(callbackIds);
+        }
+    }
+
     return NO_ERROR;
 }
 
@@ -135,6 +142,14 @@
 
     colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
 
+    int32_t listenersSize = input.readInt32();
+    for (int32_t i = 0; i < listenersSize; i++) {
+        auto listener = interface_cast<ITransactionCompletedListener>(input.readStrongBinder());
+        std::vector<CallbackId> callbackIds;
+        input.readInt64Vector(&callbackIds);
+        listenerCallbacks.emplace_back(listener, callbackIds);
+    }
+
     return NO_ERROR;
 }
 
@@ -323,6 +338,10 @@
         what |= eColorTransformChanged;
         colorTransform = other.colorTransform;
     }
+    if (other.what & eListenerCallbacksChanged) {
+        what |= eListenerCallbacksChanged;
+        listenerCallbacks = other.listenerCallbacks;
+    }
 
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index e5a2454..e10bda4 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -25,7 +25,9 @@
 #include <utils/String8.h>
 #include <utils/threads.h>
 
+#include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
 
 #include <system/graphics.h>
 
@@ -98,6 +100,61 @@
 
 // ---------------------------------------------------------------------------
 
+// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs
+// to be able to return a sp<> to its instance to pass to SurfaceFlinger.
+// ANDROID_SINGLETON_STATIC_INSTANCE only allows a reference to an instance.
+
+// 0 is an invalid callback id
+TransactionCompletedListener::TransactionCompletedListener() : mCallbackIdCounter(1) {}
+
+CallbackId TransactionCompletedListener::getNextIdLocked() {
+    return mCallbackIdCounter++;
+}
+
+sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() {
+    static sp<TransactionCompletedListener> sInstance = new TransactionCompletedListener;
+    return sInstance;
+}
+
+sp<ITransactionCompletedListener> TransactionCompletedListener::getIInstance() {
+    return static_cast<sp<ITransactionCompletedListener>>(getInstance());
+}
+
+void TransactionCompletedListener::startListeningLocked() {
+    if (mListening) {
+        return;
+    }
+    ProcessState::self()->startThreadPool();
+    mListening = true;
+}
+
+CallbackId TransactionCompletedListener::addCallback(const TransactionCompletedCallback& callback) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    startListeningLocked();
+
+    CallbackId callbackId = getNextIdLocked();
+    mCallbacks.emplace(callbackId, callback);
+    return callbackId;
+}
+
+void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
+    std::lock_guard lock(mMutex);
+
+    for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
+        for (auto callbackId : callbackIds) {
+            const auto& callback = mCallbacks[callbackId];
+            if (!callback) {
+                ALOGE("cannot call null callback function, skipping");
+                continue;
+            }
+            callback(transactionStats);
+            mCallbacks.erase(callbackId);
+        }
+    }
+}
+
+// ---------------------------------------------------------------------------
+
 SurfaceComposerClient::Transaction::Transaction(const Transaction& other) :
     mForceSynchronous(other.mForceSynchronous),
     mTransactionNestCount(other.mTransactionNestCount),
@@ -127,6 +184,17 @@
     }
     other.mDisplayStates.clear();
 
+    for (const auto& [listener, callbackInfo] : other.mListenerCallbacks) {
+        auto& [callbackIds, surfaceControls] = callbackInfo;
+        mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator(
+                                                                callbackIds.begin()),
+                                                        std::make_move_iterator(callbackIds.end()));
+        mListenerCallbacks[listener]
+                .surfaceControls.insert(std::make_move_iterator(surfaceControls.begin()),
+                                        std::make_move_iterator(surfaceControls.end()));
+    }
+    other.mListenerCallbacks.clear();
+
     return *this;
 }
 
@@ -137,6 +205,32 @@
 
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
 
+    // For every listener with registered callbacks
+    for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
+        auto& [callbackIds, surfaceControls] = callbackInfo;
+        if (callbackIds.empty()) {
+            continue;
+        }
+
+        // If the listener does not have any SurfaceControls set on this Transaction, send the
+        // callback now
+        if (surfaceControls.empty()) {
+            listener->onTransactionCompleted(ListenerStats::createEmpty(listener, callbackIds));
+        }
+
+        // If the listener has any SurfaceControls set on this Transaction update the surface state
+        for (const auto& surfaceControl : surfaceControls) {
+            layer_state_t* s = getLayerState(surfaceControl);
+            if (!s) {
+                ALOGE("failed to get layer state");
+                continue;
+            }
+            s->what |= layer_state_t::eListenerCallbacksChanged;
+            s->listenerCallbacks.emplace_back(listener, std::move(callbackIds));
+        }
+    }
+    mListenerCallbacks.clear();
+
     Vector<ComposerState> composerStates;
     Vector<DisplayState> displayStates;
     uint32_t flags = 0;
@@ -206,6 +300,11 @@
     return &(mComposerStates[sc].state);
 }
 
+void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
+        const sp<SurfaceControl>& sc) {
+    mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls.insert(sc);
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
         const sp<SurfaceControl>& sc, float x, float y) {
     layer_state_t* s = getLayerState(sc);
@@ -216,6 +315,8 @@
     s->what |= layer_state_t::ePositionChanged;
     s->x = x;
     s->y = y;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -240,6 +341,7 @@
     s->w = w;
     s->h = h;
 
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -252,6 +354,8 @@
     }
     s->what |= layer_state_t::eLayerChanged;
     s->z = z;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -264,6 +368,8 @@
     s->what |= layer_state_t::eRelativeLayerChanged;
     s->relativeLayerHandle = relativeTo;
     s->z = z;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -283,6 +389,8 @@
     s->flags &= ~mask;
     s->flags |= (flags & mask);
     s->mask |= mask;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -296,6 +404,8 @@
     }
     s->what |= layer_state_t::eTransparentRegionChanged;
     s->transparentRegion = transparentRegion;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -308,6 +418,8 @@
     }
     s->what |= layer_state_t::eAlphaChanged;
     s->alpha = alpha;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -320,6 +432,8 @@
     }
     s->what |= layer_state_t::eLayerStackChanged;
     s->layerStack = layerStack;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -338,6 +452,8 @@
     matrix.dsdy = dsdy;
     matrix.dtdy = dtdy;
     s->matrix = matrix;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -350,6 +466,8 @@
     }
     s->what |= layer_state_t::eCropChanged_legacy;
     s->crop_legacy = crop;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -365,6 +483,8 @@
     s->what |= layer_state_t::eDeferTransaction_legacy;
     s->barrierHandle_legacy = handle;
     s->frameNumber_legacy = frameNumber;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -380,6 +500,8 @@
     s->what |= layer_state_t::eDeferTransaction_legacy;
     s->barrierGbp_legacy = barrierSurface->getIGraphicBufferProducer();
     s->frameNumber_legacy = frameNumber;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -393,6 +515,8 @@
     }
     s->what |= layer_state_t::eReparentChildren;
     s->reparentHandle = newParentHandle;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -406,6 +530,8 @@
     }
     s->what |= layer_state_t::eReparent;
     s->parentHandleForChild = newParentHandle;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -419,6 +545,8 @@
     }
     s->what |= layer_state_t::eColorChanged;
     s->color = color;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -431,6 +559,8 @@
     }
     s->what |= layer_state_t::eTransformChanged;
     s->transform = transform;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -444,6 +574,8 @@
     }
     s->what |= layer_state_t::eTransformToDisplayInverseChanged;
     s->transformToDisplayInverse = transformToDisplayInverse;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -456,6 +588,8 @@
     }
     s->what |= layer_state_t::eCropChanged;
     s->crop = crop;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -468,6 +602,8 @@
     }
     s->what |= layer_state_t::eBufferChanged;
     s->buffer = buffer;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -480,6 +616,8 @@
     }
     s->what |= layer_state_t::eAcquireFenceChanged;
     s->acquireFence = fence;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -492,6 +630,8 @@
     }
     s->what |= layer_state_t::eDataspaceChanged;
     s->dataspace = dataspace;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -504,6 +644,8 @@
     }
     s->what |= layer_state_t::eHdrMetadataChanged;
     s->hdrMetadata = hdrMetadata;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -516,6 +658,8 @@
     }
     s->what |= layer_state_t::eSurfaceDamageRegionChanged;
     s->surfaceDamageRegion = surfaceDamageRegion;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -528,6 +672,8 @@
     }
     s->what |= layer_state_t::eApiChanged;
     s->api = api;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -540,6 +686,22 @@
     }
     s->what |= layer_state_t::eSidebandStreamChanged;
     s->sidebandStream = sidebandStream;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
+        TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+    auto listener = TransactionCompletedListener::getInstance();
+
+    auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1);
+
+    CallbackId callbackId = listener->addCallback(callbackWithContext);
+
+    mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace(
+            callbackId);
     return *this;
 }
 
@@ -550,6 +712,8 @@
         mStatus = BAD_INDEX;
     }
     s->what |= layer_state_t::eDetachChildren;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -577,6 +741,8 @@
 
     s->what |= layer_state_t::eOverrideScalingModeChanged;
     s->overrideScalingMode = overrideScalingMode;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -588,6 +754,8 @@
         return *this;
     }
     s->what |= layer_state_t::eGeometryAppliesWithResize;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -611,6 +779,8 @@
     }
     s->what |= layer_state_t::eColorTransformChanged;
     s->colorTransform = mat4(matrix, translation);
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
new file mode 100644
index 0000000..5c41c21
--- /dev/null
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/SafeInterface.h>
+
+#include <utils/Timers.h>
+
+#include <cstdint>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace android {
+
+class ITransactionCompletedListener;
+
+using CallbackId = int64_t;
+
+struct CallbackIdsHash {
+    // CallbackId vectors have several properties that let us get away with this simple hash.
+    // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is
+    // empty we can still hash 0.
+    // 2) CallbackId vectors for the same listener either are identical or contain none of the
+    // same members. It is sufficient to just check the first CallbackId in the vectors. If
+    // they match, they are the same. If they do not match, they are not the same.
+    std::size_t operator()(const std::vector<CallbackId> callbackIds) const {
+        return std::hash<CallbackId>{}((callbackIds.size() == 0) ? 0 : callbackIds.front());
+    }
+};
+
+class SurfaceStats : public Parcelable {
+public:
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    SurfaceStats() = default;
+    SurfaceStats(const sp<IBinder>& sc, nsecs_t time, bool releasePrevBuffer)
+          : surfaceControl(sc), acquireTime(time), releasePreviousBuffer(releasePrevBuffer) {}
+
+    sp<IBinder> surfaceControl;
+    nsecs_t acquireTime = -1;
+    bool releasePreviousBuffer = false;
+};
+
+class TransactionStats : public Parcelable {
+public:
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    nsecs_t latchTime = -1;
+    nsecs_t presentTime = -1;
+    std::vector<SurfaceStats> surfaceStats;
+};
+
+class ListenerStats : public Parcelable {
+public:
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    static ListenerStats createEmpty(const sp<ITransactionCompletedListener>& listener,
+                                     const std::unordered_set<CallbackId>& callbackIds);
+
+    sp<ITransactionCompletedListener> listener;
+    std::unordered_map<std::vector<CallbackId>, TransactionStats, CallbackIdsHash> transactionStats;
+};
+
+class ITransactionCompletedListener : public IInterface {
+public:
+    DECLARE_META_INTERFACE(TransactionCompletedListener)
+
+    virtual void onTransactionCompleted(ListenerStats stats) = 0;
+};
+
+class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
+public:
+    BnTransactionCompletedListener()
+          : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {}
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t flags = 0) override;
+};
+
+class ListenerCallbacks {
+public:
+    ListenerCallbacks(const sp<ITransactionCompletedListener>& listener,
+                      const std::unordered_set<CallbackId>& callbacks)
+          : transactionCompletedListener(listener),
+            callbackIds(callbacks.begin(), callbacks.end()) {}
+
+    ListenerCallbacks(const sp<ITransactionCompletedListener>& listener,
+                      const std::vector<CallbackId>& ids)
+          : transactionCompletedListener(listener), callbackIds(ids) {}
+
+    sp<ITransactionCompletedListener> transactionCompletedListener;
+    std::vector<CallbackId> callbackIds;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index e06e2b1..ddbac7b 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -23,6 +23,7 @@
 #include <utils/Errors.h>
 
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/ITransactionCompletedListener.h>
 #include <math/mat4.h>
 #include <math/vec3.h>
 #include <ui/GraphicTypes.h>
@@ -74,6 +75,7 @@
         eApiChanged = 0x04000000,
         eSidebandStreamChanged = 0x08000000,
         eColorTransformChanged = 0x10000000,
+        eListenerCallbacksChanged = 0x20000000,
     };
 
     layer_state_t()
@@ -154,6 +156,8 @@
     int32_t api;
     sp<NativeHandle> sidebandStream;
     mat4 colorTransform;
+
+    std::vector<ListenerCallbacks> listenerCallbacks;
 };
 
 struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 8ccee05..1cafb77 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -19,7 +19,9 @@
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <set>
 #include <unordered_map>
+#include <unordered_set>
 
 #include <binder/IBinder.h>
 
@@ -33,9 +35,10 @@
 #include <ui/PixelFormat.h>
 
 #include <gui/CpuConsumer.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <gui/LayerState.h>
 #include <gui/SurfaceControl.h>
 #include <math/vec3.h>
-#include <gui/LayerState.h>
 
 namespace android {
 
@@ -49,6 +52,37 @@
 
 // ---------------------------------------------------------------------------
 
+using TransactionCompletedCallbackTakesContext =
+        std::function<void(void* /*context*/, const TransactionStats&)>;
+using TransactionCompletedCallback = std::function<void(const TransactionStats&)>;
+
+class TransactionCompletedListener : public BnTransactionCompletedListener {
+    TransactionCompletedListener();
+
+    CallbackId getNextIdLocked() REQUIRES(mMutex);
+
+    std::mutex mMutex;
+
+    bool mListening GUARDED_BY(mMutex) = false;
+
+    CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
+
+    std::map<CallbackId, TransactionCompletedCallback> mCallbacks GUARDED_BY(mMutex);
+
+public:
+    static sp<TransactionCompletedListener> getInstance();
+    static sp<ITransactionCompletedListener> getIInstance();
+
+    void startListeningLocked() REQUIRES(mMutex);
+
+    CallbackId addCallback(const TransactionCompletedCallback& callback);
+
+    // Overrides BnTransactionCompletedListener's onTransactionCompleted
+    void onTransactionCompleted(ListenerStats stats) override;
+};
+
+// ---------------------------------------------------------------------------
+
 class SurfaceComposerClient : public RefBase
 {
     friend class Composer;
@@ -158,9 +192,27 @@
         }
     };
 
+    struct TCLHash {
+        std::size_t operator()(const sp<ITransactionCompletedListener>& tcl) const {
+            return std::hash<IBinder*>{}((tcl) ? IInterface::asBinder(tcl).get() : nullptr);
+        }
+    };
+
+    struct CallbackInfo {
+        // All the callbacks that have been requested for a TransactionCompletedListener in the
+        // Transaction
+        std::unordered_set<CallbackId> callbackIds;
+        // All the SurfaceControls that have been modified in this TransactionCompletedListener's
+        // process that require a callback if there is one or more callbackIds set.
+        std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls;
+    };
+
     class Transaction {
         std::unordered_map<sp<SurfaceControl>, ComposerState, SCHash> mComposerStates;
         SortedVector<DisplayState > mDisplayStates;
+        std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
+                mListenerCallbacks;
+
         uint32_t                    mForceSynchronous = 0;
         uint32_t                    mTransactionNestCount = 0;
         bool                        mAnimation = false;
@@ -171,6 +223,8 @@
         layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
         DisplayState& getDisplayState(const sp<IBinder>& token);
 
+        void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
+
     public:
         Transaction() = default;
         virtual ~Transaction() = default;
@@ -250,6 +304,9 @@
         Transaction& setSidebandStream(const sp<SurfaceControl>& sc,
                                        const sp<NativeHandle>& sidebandStream);
 
+        Transaction& addTransactionCompletedCallback(
+                TransactionCompletedCallbackTakesContext callback, void* callbackContext);
+
         // Detaches all child surfaces (and their children recursively)
         // from their SurfaceControl.
         // The child SurfaceControls will not throw exceptions or return errors,
@@ -349,6 +406,7 @@
 };
 
 // ---------------------------------------------------------------------------
+
 }; // namespace android
 
 #endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 1bbd82b..42d774e 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -327,8 +327,8 @@
         eventTime = event->getHistoricalEventTime(h);
         for (size_t i = 0; i < pointerCount; i++) {
             uint32_t index = pointerIndex[i];
-            positions[index].x = event->getHistoricalRawX(i, h);
-            positions[index].y = event->getHistoricalRawY(i, h);
+            positions[index].x = event->getHistoricalX(i, h);
+            positions[index].y = event->getHistoricalY(i, h);
         }
         addMovement(eventTime, idBits, positions);
     }
@@ -336,8 +336,8 @@
     eventTime = event->getEventTime();
     for (size_t i = 0; i < pointerCount; i++) {
         uint32_t index = pointerIndex[i];
-        positions[index].x = event->getRawX(i);
-        positions[index].y = event->getRawY(i);
+        positions[index].x = event->getX(i);
+        positions[index].y = event->getY(i);
     }
     addMovement(eventTime, idBits, positions);
 }
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 647fe86..fba319d 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -47,6 +47,7 @@
     cflags: [
         "-Wall",
         "-Werror",
+        "-Wno-enum-compare",
         "-Wno-unused-function",
     ],
 
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 9ca4941..23ac60b 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -15,12 +15,31 @@
  */
 
 /**
- * @addtogroup NativeActivity Native Activity
- * @{
- */
-
-/**
  * @file hardware_buffer.h
+ * @brief API for native hardware buffers.
+ */
+/**
+ * @defgroup AHardwareBuffer Native Hardware Buffer
+ *
+ * AHardwareBuffer objects represent chunks of memory that can be
+ * accessed by various hardware components in the system. It can be
+ * easily converted to the Java counterpart
+ * android.hardware.HardwareBuffer and passed between processes using
+ * Binder. All operations involving AHardwareBuffer and HardwareBuffer
+ * are zero-copy, i.e., passing AHardwareBuffer to another process
+ * creates a shared view of the same region of memory.
+ *
+ * AHardwareBuffers can be bound to EGL/OpenGL and Vulkan primitives.
+ * For EGL, use the extension function eglGetNativeClientBufferANDROID
+ * to obtain an EGLClientBuffer and pass it directly to
+ * eglCreateImageKHR. Refer to the EGL extensions
+ * EGL_ANDROID_get_native_client_buffer and
+ * EGL_ANDROID_image_native_buffer for more information. In Vulkan,
+ * the contents of the AHardwareBuffer can be accessed as external
+ * memory. See the VK_ANDROID_external_memory_android_hardware_buffer
+ * extension for details.
+ *
+ * @{
  */
 
 #ifndef ANDROID_HARDWARE_BUFFER_H
@@ -37,7 +56,7 @@
 /**
  * Buffer pixel formats.
  */
-enum {
+enum AHardwareBuffer_Format {
     /**
      * Corresponding formats:
      *   Vulkan: VK_FORMAT_R8G8B8A8_UNORM
@@ -134,27 +153,47 @@
 /**
  * Buffer usage flags, specifying how the buffer will be accessed.
  */
-enum {
-    /// The buffer will never be read by the CPU.
+enum AHardwareBuffer_UsageFlags {
+    /// The buffer will never be locked for direct CPU reads using the
+    /// AHardwareBuffer_lock() function. Note that reading the buffer
+    /// using OpenGL or Vulkan functions or memory mappings is still
+    /// allowed.
     AHARDWAREBUFFER_USAGE_CPU_READ_NEVER        = 0UL,
-    /// The buffer will sometimes be read by the CPU.
+    /// The buffer will sometimes be locked for direct CPU reads using
+    /// the AHardwareBuffer_lock() function. Note that reading the
+    /// buffer using OpenGL or Vulkan functions or memory mappings
+    /// does not require the presence of this flag.
     AHARDWAREBUFFER_USAGE_CPU_READ_RARELY       = 2UL,
-    /// The buffer will often be read by the CPU.
+    /// The buffer will often be locked for direct CPU reads using
+    /// the AHardwareBuffer_lock() function. Note that reading the
+    /// buffer using OpenGL or Vulkan functions or memory mappings
+    /// does not require the presence of this flag.
     AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN        = 3UL,
     /// CPU read value mask.
     AHARDWAREBUFFER_USAGE_CPU_READ_MASK         = 0xFUL,
 
-    /// The buffer will never be written by the CPU.
+    /// The buffer will never be locked for direct CPU writes using the
+    /// AHardwareBuffer_lock() function. Note that writing the buffer
+    /// using OpenGL or Vulkan functions or memory mappings is still
+    /// allowed.
     AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER       = 0UL << 4,
-    /// The buffer will sometimes be written to by the CPU.
+    /// The buffer will sometimes be locked for direct CPU writes using
+    /// the AHardwareBuffer_lock() function. Note that writing the
+    /// buffer using OpenGL or Vulkan functions or memory mappings
+    /// does not require the presence of this flag.
     AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY      = 2UL << 4,
-    /// The buffer will often be written to by the CPU.
+    /// The buffer will often be locked for direct CPU writes using
+    /// the AHardwareBuffer_lock() function. Note that writing the
+    /// buffer using OpenGL or Vulkan functions or memory mappings
+    /// does not require the presence of this flag.
     AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN       = 3UL << 4,
     /// CPU write value mask.
     AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK        = 0xFUL << 4,
 
     /// The buffer will be read from by the GPU as a texture.
     AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE      = 1UL << 8,
+    /// The buffer will be written to by the GPU as a framebuffer attachment.
+    AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER        = 1UL << 9,
     /**
      * The buffer will be written to by the GPU as a framebuffer attachment.
      * Note that the name of this flag is somewhat misleading: it does not imply
@@ -162,16 +201,33 @@
      * format that will be used as a framebuffer attachment should also have
      * this flag.
      */
-    AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT       = 1UL << 9,
-    /// The buffer must not be used outside of a protected hardware path.
+    AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT       = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER,
+    /**
+     * The buffer is protected from direct CPU access or being read by non-secure
+     * hardware, such as video encoders. This flag is incompatible with CPU
+     * read and write flags. It is mainly used when handling DRM video.
+     * Refer to the EGL extension EGL_EXT_protected_content and GL extension
+     * EXT_protected_textures for more information on how these buffers are expected
+     * to behave.
+     */
     AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT      = 1UL << 14,
     /// The buffer will be read by a hardware video encoder.
     AHARDWAREBUFFER_USAGE_VIDEO_ENCODE           = 1UL << 16,
-    /// The buffer will be used for direct writes from sensors.
+    /**
+     * The buffer will be used for direct writes from sensors.
+     * When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB.
+     */
     AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA     = 1UL << 23,
-    /// The buffer will be used as a shader storage or uniform buffer object.
+    /**
+     * The buffer will be used as a shader storage or uniform buffer object.
+     * When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB.
+     */
     AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER        = 1UL << 24,
-    /// The buffer will be used as a cube map texture.
+    /**
+     * The buffer will be used as a cube map texture.
+     * When this flag is present, the buffer must have a layer count that is
+     * a multiple of 6.
+     */
     AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP               = 1UL << 25,
     /// The buffer contains a complete mipmap hierarchy.
     AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE        = 1UL << 26,
@@ -206,20 +262,27 @@
     uint32_t    width;      ///< Width in pixels.
     uint32_t    height;     ///< Height in pixels.
     uint32_t    layers;     ///< Number of images in an image array.
-    uint32_t    format;     ///< One of AHARDWAREBUFFER_FORMAT_*
-    uint64_t    usage;      ///< Combination of AHARDWAREBUFFER_USAGE_*
+    uint32_t    format;     ///< One of AHardwareBuffer_Format.
+    uint64_t    usage;      ///< Combination of AHardwareBuffer_UsageFlags.
     uint32_t    stride;     ///< Row stride in pixels, ignored for AHardwareBuffer_allocate()
     uint32_t    rfu0;       ///< Initialize to zero, reserved for future use.
     uint64_t    rfu1;       ///< Initialize to zero, reserved for future use.
 } AHardwareBuffer_Desc;
 
+/**
+ * Opaque handle for a native hardware buffer.
+ */
 typedef struct AHardwareBuffer AHardwareBuffer;
 
 #if __ANDROID_API__ >= 26
 
 /**
- * Allocates a buffer that backs an AHardwareBuffer using the passed
- * AHardwareBuffer_Desc.
+ * Allocates a buffer that matches the passed AHardwareBuffer_Desc.
+ *
+ * If allocation succeeds, the buffer can be used according to the
+ * usage flags specified in its description. If a buffer is used in ways
+ * not compatible with its usage flags, the results are undefined and
+ * may include program termination.
  *
  * \return 0 on success, or an error number of the allocation fails for
  * any reason. The returned buffer has a reference count of 1.
@@ -234,7 +297,7 @@
 
 /**
  * Remove a reference that was previously acquired with
- * AHardwareBuffer_acquire().
+ * AHardwareBuffer_acquire() or AHardwareBuffer_allocate().
  */
 void AHardwareBuffer_release(AHardwareBuffer* buffer) __INTRODUCED_IN(26);
 
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 6831f91..6730596 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -15,7 +15,13 @@
  */
 
 /**
- * @addtogroup NativeActivity Native Activity
+ * @defgroup ANativeWindow Native Window
+ *
+ * ANativeWindow represents the producer end of an image queue.
+ * It is the C counterpart of the android.view.Surface object in Java,
+ * and can be converted both ways. Depending on the consumer, images
+ * submitted to ANativeWindow can be shown on the display or sent to
+ * other consumers, such as video encoders.
  * @{
  */
 
@@ -41,7 +47,7 @@
  * Legacy window pixel format names, kept for backwards compatibility.
  * New code and APIs should use AHARDWAREBUFFER_FORMAT_*.
  */
-enum {
+enum ANativeWindow_LegacyFormat {
     // NOTE: these values must match the values from graphics/common/x.x/types.hal
 
     /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/
@@ -95,7 +101,7 @@
     /// memory. This may be >= width.
     int32_t stride;
 
-    /// The format of the buffer. One of AHARDWAREBUFFER_FORMAT_*
+    /// The format of the buffer. One of AHardwareBuffer_Format.
     int32_t format;
 
     /// The actual bits.
@@ -151,7 +157,7 @@
  *
  * \param width width of the buffers in pixels.
  * \param height height of the buffers in pixels.
- * \param format one of AHARDWAREBUFFER_FORMAT_* constants.
+ * \param format one of the AHardwareBuffer_Format constants.
  * \return 0 for success, or a negative value on error.
  */
 int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window,
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 7efc8bd..beaf9ee 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -50,7 +50,6 @@
         "gl/GLExtensions.cpp",
         "gl/GLFramebuffer.cpp",
         "gl/GLImage.cpp",
-        "gl/GLSurface.cpp",
         "gl/Program.cpp",
         "gl/ProgramCache.cpp",
     ],
diff --git a/libs/renderengine/gl/GLES20RenderEngine.cpp b/libs/renderengine/gl/GLES20RenderEngine.cpp
index 70a4322..d35762d 100644
--- a/libs/renderengine/gl/GLES20RenderEngine.cpp
+++ b/libs/renderengine/gl/GLES20RenderEngine.cpp
@@ -41,7 +41,6 @@
 #include "GLExtensions.h"
 #include "GLFramebuffer.h"
 #include "GLImage.h"
-#include "GLSurface.h"
 #include "Program.h"
 #include "ProgramCache.h"
 
@@ -275,6 +274,8 @@
 
     // now figure out what version of GL did we actually get
     // NOTE: a dummy surface is not needed if KHR_create_context is supported
+    // TODO(alecmouri): don't create this surface if EGL_KHR_surfaceless_context
+    // is supported.
 
     EGLConfig dummyConfig = config;
     if (dummyConfig == EGL_NO_CONFIG) {
@@ -301,10 +302,10 @@
             break;
         case GLES_VERSION_2_0:
         case GLES_VERSION_3_0:
-            engine = std::make_unique<GLES20RenderEngine>(featureFlags);
+            engine = std::make_unique<GLES20RenderEngine>(featureFlags, display, config, ctxt,
+                                                          dummy);
             break;
     }
-    engine->setEGLHandles(display, config, ctxt);
 
     ALOGI("OpenGL ES informations:");
     ALOGI("vendor    : %s", extensions.getVendor());
@@ -314,9 +315,6 @@
     ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
     ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
 
-    eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    eglDestroySurface(display, dummy);
-
     return engine;
 }
 
@@ -359,11 +357,13 @@
     return config;
 }
 
-GLES20RenderEngine::GLES20RenderEngine(uint32_t featureFlags)
+GLES20RenderEngine::GLES20RenderEngine(uint32_t featureFlags, EGLDisplay display, EGLConfig config,
+                                       EGLContext ctxt, EGLSurface dummy)
       : renderengine::impl::RenderEngine(featureFlags),
-        mEGLDisplay(EGL_NO_DISPLAY),
-        mEGLConfig(nullptr),
-        mEGLContext(EGL_NO_CONTEXT),
+        mEGLDisplay(display),
+        mEGLConfig(config),
+        mEGLContext(ctxt),
+        mDummySurface(dummy),
         mVpWidth(0),
         mVpHeight(0),
         mUseColorManagement(featureFlags & USE_COLOR_MANAGEMENT) {
@@ -422,10 +422,6 @@
     return std::make_unique<GLFramebuffer>(*this);
 }
 
-std::unique_ptr<Surface> GLES20RenderEngine::createSurface() {
-    return std::make_unique<GLSurface>(*this);
-}
-
 std::unique_ptr<Image> GLES20RenderEngine::createImage() {
     return std::make_unique<GLImage>(*this);
 }
@@ -438,31 +434,6 @@
     return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext();
 }
 
-bool GLES20RenderEngine::setCurrentSurface(const Surface& surface) {
-    // Surface is an abstract interface. GLES20RenderEngine only ever
-    // creates GLSurface's, so it is safe to just cast to the actual
-    // type.
-    bool success = true;
-    const GLSurface& glSurface = static_cast<const GLSurface&>(surface);
-    EGLSurface eglSurface = glSurface.getEGLSurface();
-    if (eglSurface != eglGetCurrentSurface(EGL_DRAW)) {
-        success = eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext) == EGL_TRUE;
-        if (success && glSurface.getAsync()) {
-            eglSwapInterval(mEGLDisplay, 0);
-        }
-        if (success) {
-            mSurfaceHeight = glSurface.getHeight();
-        }
-    }
-
-    return success;
-}
-
-void GLES20RenderEngine::resetCurrentSurface() {
-    eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    mSurfaceHeight = 0;
-}
-
 base::unique_fd GLES20RenderEngine::flush() {
     if (!GLExtensions::getInstance().hasNativeFenceSync()) {
         return base::unique_fd();
@@ -576,7 +547,7 @@
 
 void GLES20RenderEngine::setScissor(const Rect& region) {
     // Invert y-coordinate to map to GL-space.
-    int32_t canvasHeight = mRenderToFbo ? mFboHeight : mSurfaceHeight;
+    int32_t canvasHeight = mFboHeight;
     int32_t glBottom = canvasHeight - region.bottom;
 
     glScissor(region.left, glBottom, region.getWidth(), region.getHeight());
@@ -619,7 +590,6 @@
     glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
 
-    mRenderToFbo = true;
     mFboHeight = glFramebuffer->getBufferHeight();
 
     uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
@@ -631,17 +601,10 @@
 }
 
 void GLES20RenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) {
-    mRenderToFbo = false;
     mFboHeight = 0;
 
     // back to main framebuffer
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
-    // Workaround for b/77935566 to force the EGL driver to release the
-    // screenshot buffer
-    setScissor(Rect::EMPTY_RECT);
-    clearWithColor(0.0, 0.0, 0.0, 0.0);
-    disableScissor();
 }
 
 void GLES20RenderEngine::checkErrors() const {
@@ -666,9 +629,7 @@
     int32_t r = sourceCrop.right;
     int32_t b = sourceCrop.bottom;
     int32_t t = sourceCrop.top;
-    if (mRenderToFbo) {
-        std::swap(t, b);
-    }
+    std::swap(t, b);
     mat4 m = mat4::ortho(l, r, b, t, 0, 1);
 
     // Apply custom rotation to the projection.
@@ -974,12 +935,6 @@
     return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
 }
 
-void GLES20RenderEngine::setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt) {
-    mEGLDisplay = display;
-    mEGLConfig = config;
-    mEGLContext = ctxt;
-}
-
 } // namespace gl
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/gl/GLES20RenderEngine.h b/libs/renderengine/gl/GLES20RenderEngine.h
index 148df2f..6c50938 100644
--- a/libs/renderengine/gl/GLES20RenderEngine.h
+++ b/libs/renderengine/gl/GLES20RenderEngine.h
@@ -40,24 +40,21 @@
 namespace gl {
 
 class GLImage;
-class GLSurface;
 
 class GLES20RenderEngine : public impl::RenderEngine {
 public:
     static std::unique_ptr<GLES20RenderEngine> create(int hwcFormat, uint32_t featureFlags);
     static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
 
-    GLES20RenderEngine(uint32_t featureFlags); // See RenderEngine::FeatureFlag
+    GLES20RenderEngine(uint32_t featureFlags, // See RenderEngine::FeatureFlag
+                       EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy);
     ~GLES20RenderEngine() override;
 
     std::unique_ptr<Framebuffer> createFramebuffer() override;
-    std::unique_ptr<Surface> createSurface() override;
     std::unique_ptr<Image> createImage() override;
 
     void primeCache() const override;
     bool isCurrent() const override;
-    bool setCurrentSurface(const Surface& surface) override;
-    void resetCurrentSurface() override;
     base::unique_fd flush() override;
     bool finish() override;
     bool waitFence(base::unique_fd fenceFd) override;
@@ -120,11 +117,12 @@
     // with PQ or HLG transfer function.
     bool isHdrDataSpace(const ui::Dataspace dataSpace) const;
     bool needsXYZTransformMatrix() const;
-    void setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt);
+    void setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy);
 
     EGLDisplay mEGLDisplay;
     EGLConfig mEGLConfig;
     EGLContext mEGLContext;
+    EGLSurface mDummySurface;
     GLuint mProtectedTexName;
     GLint mMaxViewportDims[2];
     GLint mMaxTextureSize;
@@ -145,8 +143,6 @@
     mat4 mBt2020ToSrgb;
     mat4 mBt2020ToDisplayP3;
 
-    bool mRenderToFbo = false;
-    int32_t mSurfaceHeight = 0;
     int32_t mFboHeight = 0;
 
     // Current dataspace of layer being rendered
diff --git a/libs/renderengine/gl/GLSurface.cpp b/libs/renderengine/gl/GLSurface.cpp
deleted file mode 100644
index 2d694e9..0000000
--- a/libs/renderengine/gl/GLSurface.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2018 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 "GLSurface.h"
-
-#include <android/native_window.h>
-#include <log/log.h>
-#include <ui/PixelFormat.h>
-#include "GLES20RenderEngine.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GLSurface::GLSurface(const GLES20RenderEngine& engine)
-      : mEGLDisplay(engine.getEGLDisplay()), mEGLConfig(engine.getEGLConfig()) {
-    // RE does not assume any config when EGL_KHR_no_config_context is supported
-    if (mEGLConfig == EGL_NO_CONFIG_KHR) {
-        mEGLConfig =
-                GLES20RenderEngine::chooseEglConfig(mEGLDisplay, PIXEL_FORMAT_RGBA_8888, false);
-    }
-}
-
-GLSurface::~GLSurface() {
-    setNativeWindow(nullptr);
-}
-
-void GLSurface::setNativeWindow(ANativeWindow* window) {
-    if (mEGLSurface != EGL_NO_SURFACE) {
-        eglDestroySurface(mEGLDisplay, mEGLSurface);
-        mEGLSurface = EGL_NO_SURFACE;
-        mSurfaceWidth = 0;
-        mSurfaceHeight = 0;
-    }
-
-    mWindow = window;
-    if (mWindow) {
-        mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mWindow, nullptr);
-        mSurfaceWidth = ANativeWindow_getWidth(window);
-        mSurfaceHeight = ANativeWindow_getHeight(window);
-    }
-}
-
-void GLSurface::swapBuffers() const {
-    if (!eglSwapBuffers(mEGLDisplay, mEGLSurface)) {
-        EGLint error = eglGetError();
-
-        const char format[] = "eglSwapBuffers(%p, %p) failed with 0x%08x";
-        if (mCritical || error == EGL_CONTEXT_LOST) {
-            LOG_ALWAYS_FATAL(format, mEGLDisplay, mEGLSurface, error);
-        } else {
-            ALOGE(format, mEGLDisplay, mEGLSurface, error);
-        }
-    }
-}
-
-EGLint GLSurface::queryConfig(EGLint attrib) const {
-    EGLint value;
-    if (!eglGetConfigAttrib(mEGLDisplay, mEGLConfig, attrib, &value)) {
-        value = 0;
-    }
-
-    return value;
-}
-
-int32_t GLSurface::queryRedSize() const {
-    return queryConfig(EGL_RED_SIZE);
-}
-
-int32_t GLSurface::queryGreenSize() const {
-    return queryConfig(EGL_GREEN_SIZE);
-}
-
-int32_t GLSurface::queryBlueSize() const {
-    return queryConfig(EGL_BLUE_SIZE);
-}
-
-int32_t GLSurface::queryAlphaSize() const {
-    return queryConfig(EGL_ALPHA_SIZE);
-}
-
-int32_t GLSurface::getWidth() const {
-    return mSurfaceWidth;
-}
-
-int32_t GLSurface::getHeight() const {
-    return mSurfaceHeight;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLSurface.h b/libs/renderengine/gl/GLSurface.h
deleted file mode 100644
index 092d371..0000000
--- a/libs/renderengine/gl/GLSurface.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <EGL/egl.h>
-#include <android-base/macros.h>
-#include <renderengine/Surface.h>
-
-struct ANativeWindow;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GLES20RenderEngine;
-
-class GLSurface final : public renderengine::Surface {
-public:
-    GLSurface(const GLES20RenderEngine& engine);
-    ~GLSurface() override;
-
-    // renderengine::Surface implementation
-    void setCritical(bool enable) override { mCritical = enable; }
-    void setAsync(bool enable) override { mAsync = enable; }
-
-    void setNativeWindow(ANativeWindow* window) override;
-    void swapBuffers() const override;
-
-    int32_t queryRedSize() const override;
-    int32_t queryGreenSize() const override;
-    int32_t queryBlueSize() const override;
-    int32_t queryAlphaSize() const override;
-
-    bool getAsync() const { return mAsync; }
-    EGLSurface getEGLSurface() const { return mEGLSurface; }
-
-    int32_t getWidth() const override;
-    int32_t getHeight() const override;
-
-private:
-    EGLint queryConfig(EGLint attrib) const;
-
-    EGLDisplay mEGLDisplay;
-    EGLConfig mEGLConfig;
-
-    bool mCritical = false;
-    bool mAsync = false;
-
-    int32_t mSurfaceWidth = 0;
-    int32_t mSurfaceHeight = 0;
-
-    ANativeWindow* mWindow = nullptr;
-    EGLSurface mEGLSurface = EGL_NO_SURFACE;
-
-    DISALLOW_COPY_AND_ASSIGN(GLSurface);
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index becb3c3..22891c4 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -48,7 +48,6 @@
 class BindNativeBufferAsFramebuffer;
 class Image;
 class Mesh;
-class Surface;
 class Texture;
 
 namespace impl {
@@ -72,7 +71,6 @@
     // used to support legacy behavior.
 
     virtual std::unique_ptr<Framebuffer> createFramebuffer() = 0;
-    virtual std::unique_ptr<Surface> createSurface() = 0;
     virtual std::unique_ptr<Image> createImage() = 0;
 
     virtual void primeCache() const = 0;
@@ -84,8 +82,6 @@
     virtual bool useWaitSync() const = 0;
 
     virtual bool isCurrent() const = 0;
-    virtual bool setCurrentSurface(const Surface& surface) = 0;
-    virtual void resetCurrentSurface() = 0;
 
     // helpers
     // flush submits RenderEngine command stream for execution and returns a
diff --git a/libs/renderengine/include/renderengine/Surface.h b/libs/renderengine/include/renderengine/Surface.h
deleted file mode 100644
index ba7331d..0000000
--- a/libs/renderengine/include/renderengine/Surface.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-struct ANativeWindow;
-
-namespace android {
-namespace renderengine {
-
-class Surface {
-public:
-    virtual ~Surface() = default;
-
-    virtual void setCritical(bool enable) = 0;
-    virtual void setAsync(bool enable) = 0;
-
-    virtual void setNativeWindow(ANativeWindow* window) = 0;
-    virtual void swapBuffers() const = 0;
-
-    virtual int32_t queryRedSize() const = 0;
-    virtual int32_t queryGreenSize() const = 0;
-    virtual int32_t queryBlueSize() const = 0;
-    virtual int32_t queryAlphaSize() const = 0;
-
-    virtual int32_t getWidth() const = 0;
-    virtual int32_t getHeight() const = 0;
-};
-
-} // namespace renderengine
-} // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 5a1ddee..29deb01 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -64,12 +64,11 @@
 {
 }
 
-GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
-        PixelFormat inFormat, uint32_t inLayerCount, uint64_t usage, std::string requestorName)
-    : GraphicBuffer()
-{
-    mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount,
-            usage, std::move(requestorName));
+GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+                             uint32_t inLayerCount, uint64_t inUsage, std::string requestorName)
+      : GraphicBuffer() {
+    mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount, inUsage,
+                              std::move(requestorName));
 }
 
 // deprecated
@@ -82,15 +81,12 @@
 {
 }
 
-GraphicBuffer::GraphicBuffer(const native_handle_t* handle,
-        HandleWrapMethod method, uint32_t width, uint32_t height,
-        PixelFormat format, uint32_t layerCount,
-        uint64_t usage,
-        uint32_t stride)
-    : GraphicBuffer()
-{
-    mInitCheck = initWithHandle(handle, method, width, height, format,
-            layerCount, usage, stride);
+GraphicBuffer::GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod method,
+                             uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+                             uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride)
+      : GraphicBuffer() {
+    mInitCheck = initWithHandle(inHandle, method, inWidth, inHeight, inFormat, inLayerCount,
+                                inUsage, inStride);
 }
 
 GraphicBuffer::~GraphicBuffer()
@@ -182,26 +178,24 @@
     return err;
 }
 
-status_t GraphicBuffer::initWithHandle(const native_handle_t* handle,
-        HandleWrapMethod method, uint32_t width, uint32_t height,
-        PixelFormat format, uint32_t layerCount, uint64_t usage,
-        uint32_t stride)
-{
-    ANativeWindowBuffer::width  = static_cast<int>(width);
-    ANativeWindowBuffer::height = static_cast<int>(height);
-    ANativeWindowBuffer::stride = static_cast<int>(stride);
-    ANativeWindowBuffer::format = format;
-    ANativeWindowBuffer::usage  = usage;
-    ANativeWindowBuffer::usage_deprecated = int(usage);
+status_t GraphicBuffer::initWithHandle(const native_handle_t* inHandle, HandleWrapMethod method,
+                                       uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+                                       uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride) {
+    ANativeWindowBuffer::width = static_cast<int>(inWidth);
+    ANativeWindowBuffer::height = static_cast<int>(inHeight);
+    ANativeWindowBuffer::stride = static_cast<int>(inStride);
+    ANativeWindowBuffer::format = inFormat;
+    ANativeWindowBuffer::usage = inUsage;
+    ANativeWindowBuffer::usage_deprecated = int(inUsage);
 
-    ANativeWindowBuffer::layerCount = layerCount;
+    ANativeWindowBuffer::layerCount = inLayerCount;
 
     mOwner = (method == WRAP_HANDLE) ? ownNone : ownHandle;
 
     if (method == TAKE_UNREGISTERED_HANDLE || method == CLONE_HANDLE) {
         buffer_handle_t importedHandle;
-        status_t err = mBufferMapper.importBuffer(handle, width, height,
-                layerCount, format, usage, stride, &importedHandle);
+        status_t err = mBufferMapper.importBuffer(inHandle, inWidth, inHeight, inLayerCount,
+                                                  inFormat, inUsage, inStride, &importedHandle);
         if (err != NO_ERROR) {
             initWithHandle(nullptr, WRAP_HANDLE, 0, 0, 0, 0, 0, 0);
 
@@ -209,15 +203,15 @@
         }
 
         if (method == TAKE_UNREGISTERED_HANDLE) {
-            native_handle_close(handle);
-            native_handle_delete(const_cast<native_handle_t*>(handle));
+            native_handle_close(inHandle);
+            native_handle_delete(const_cast<native_handle_t*>(inHandle));
         }
 
-        handle = importedHandle;
-        mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
+        inHandle = importedHandle;
+        mBufferMapper.getTransportSize(inHandle, &mTransportNumFds, &mTransportNumInts);
     }
 
-    ANativeWindowBuffer::handle = handle;
+    ANativeWindowBuffer::handle = inHandle;
 
     return NO_ERROR;
 }
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index e794462..fe6229a 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -117,18 +117,16 @@
         // cannot be used directly, such as one from hidl_handle.
         CLONE_HANDLE,
     };
-    GraphicBuffer(const native_handle_t* handle, HandleWrapMethod method,
-            uint32_t width, uint32_t height,
-            PixelFormat format, uint32_t layerCount,
-            uint64_t usage, uint32_t stride);
+    GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod method, uint32_t inWidth,
+                  uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage,
+                  uint32_t inStride);
 
     // These functions are deprecated because they only take 32 bits of usage
-    GraphicBuffer(const native_handle_t* handle, HandleWrapMethod method,
-            uint32_t width, uint32_t height,
-            PixelFormat format, uint32_t layerCount,
-            uint32_t usage, uint32_t stride)
-        : GraphicBuffer(handle, method, width, height, format, layerCount,
-                static_cast<uint64_t>(usage), stride) {}
+    GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod method, uint32_t inWidth,
+                  uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage,
+                  uint32_t inStride)
+          : GraphicBuffer(inHandle, method, inWidth, inHeight, inFormat, inLayerCount,
+                          static_cast<uint64_t>(inUsage), inStride) {}
     GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
             uint32_t inLayerCount, uint32_t inUsage, uint32_t inStride,
             native_handle_t* inHandle, bool keepOwnership);
@@ -220,10 +218,9 @@
             PixelFormat inFormat, uint32_t inLayerCount,
             uint64_t inUsage, std::string requestorName);
 
-    status_t initWithHandle(const native_handle_t* handle,
-            HandleWrapMethod method, uint32_t width, uint32_t height,
-            PixelFormat format, uint32_t layerCount,
-            uint64_t usage, uint32_t stride);
+    status_t initWithHandle(const native_handle_t* inHandle, HandleWrapMethod method,
+                            uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+                            uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride);
 
     void free_handle();
 
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 1521e1d..4c9c176 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -32,7 +32,8 @@
     name: "BufferHubBuffer_test",
     header_libs: [
         "libbufferhub_headers",
-        "libdvr_headers"
+        "libdvr_headers",
+        "libnativewindow_headers",
     ],
     shared_libs: [
         "android.frameworks.bufferhub@1.0",
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index 606aee60..143335a 100644
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -18,6 +18,7 @@
 
 #include <android/frameworks/bufferhub/1.0/IBufferClient.h>
 #include <android/frameworks/bufferhub/1.0/IBufferHub.h>
+#include <android/hardware_buffer.h>
 #include <gtest/gtest.h>
 #include <hidl/ServiceManagement.h>
 #include <hwbinder/IPCThreadState.h>
@@ -41,6 +42,7 @@
 using frameworks::bufferhub::V1_0::IBufferClient;
 using frameworks::bufferhub::V1_0::IBufferHub;
 using hardware::hidl_handle;
+using hardware::graphics::common::V1_2::HardwareBufferDescription;
 using hidl::base::V1_0::IBase;
 using pdx::LocalChannelHandle;
 
@@ -124,19 +126,22 @@
     return;
 }
 
-TEST_F(BufferHubBufferTest, ConnectHidlServer) {
-    sp<IBufferHub> bufferhub = IBufferHub::getService();
-    ASSERT_NE(nullptr, bufferhub.get());
+TEST_F(BufferHubBufferTest, AllocateBuffer) {
+    // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
+    sp<IBufferHub> bufferHub = IBufferHub::getService();
+    ASSERT_NE(nullptr, bufferHub.get());
 
-    // TODO(b/116681016): Fill in real test once the interface gets implemented..
-    hidl_handle handle;
-    EXPECT_TRUE(bufferhub
-                        ->importBuffer(handle,
-                                       [](const auto& client, const auto& ret) {
-                                           EXPECT_EQ(client, nullptr);
-                                           EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
-                                       })
-                        .isOk());
+    // Stride is an output, rfu0 and rfu1 are reserved data slot for future use.
+    AHardwareBuffer_Desc aDesc = {kWidth, kHeight,        kLayerCount,  kFormat,
+                                  kUsage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL};
+    HardwareBufferDescription desc;
+    memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription));
+
+    IBufferHub::allocateBuffer_cb callback = [](const auto& client, const auto& status) {
+        EXPECT_EQ(status, BufferHubStatus::NO_ERROR);
+        EXPECT_NE(nullptr, client.get());
+    };
+    EXPECT_TRUE(bufferHub->allocateBuffer(desc, kUserMetadataSize, callback).isOk());
 }
 
 } // namespace
diff --git a/libs/vr/libpdx/service.cpp b/libs/vr/libpdx/service.cpp
index 68b8dd7..3769162 100644
--- a/libs/vr/libpdx/service.cpp
+++ b/libs/vr/libpdx/service.cpp
@@ -318,13 +318,7 @@
   PDX_TRACE_NAME("Message::ReplyFileHandle");
   auto svc = service_.lock();
   if (!replied_ && svc) {
-    Status<void> ret;
-
-    if (handle)
-      ret = svc->endpoint()->MessageReply(this, handle.Get());
-    else
-      ret = svc->endpoint()->MessageReply(this, handle.Get());
-
+    Status<void> ret = svc->endpoint()->MessageReply(this, handle.Get());
     replied_ = ret.ok();
     return ret;
   } else {
diff --git a/services/bufferhub/Android.bp b/services/bufferhub/Android.bp
index ca65e02..871561f 100644
--- a/services/bufferhub/Android.bp
+++ b/services/bufferhub/Android.bp
@@ -52,6 +52,12 @@
     srcs: [
         "main_bufferhub.cpp"
     ],
+    header_libs: [
+        "libbufferhub_headers",
+        "libdvr_headers",
+        "libnativewindow_headers",
+        "libpdx_headers",
+    ],
     shared_libs: [
         "android.frameworks.bufferhub@1.0",
         "libbufferhubservice",
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
index 86598e0..b72b556 100644
--- a/services/bufferhub/BufferHubService.cpp
+++ b/services/bufferhub/BufferHubService.cpp
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
+#include <android/hardware_buffer.h>
 #include <bufferhub/BufferHubService.h>
+#include <log/log.h>
 
 namespace android {
 namespace frameworks {
@@ -24,11 +26,27 @@
 
 using hardware::Void;
 
-Return<void> BufferHubService::allocateBuffer(const HardwareBufferDescription& /*description*/,
-                                              const uint32_t /*userMetadataSize*/,
+Return<void> BufferHubService::allocateBuffer(const HardwareBufferDescription& description,
+                                              const uint32_t userMetadataSize,
                                               allocateBuffer_cb _hidl_cb) {
-    // TODO(b/118614333): implement buffer allocation
-    _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::NO_ERROR);
+    AHardwareBuffer_Desc desc;
+    memcpy(&desc, &description, sizeof(AHardwareBuffer_Desc));
+
+    std::shared_ptr<BufferNode> node =
+            std::make_shared<BufferNode>(desc.width, desc.height, desc.layers, desc.format,
+                                         desc.usage, userMetadataSize);
+    if (node == nullptr || !node->IsValid()) {
+        ALOGE("%s: creating BufferNode failed.", __FUNCTION__);
+        _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::ALLOCATION_FAILED);
+        return Void();
+    }
+
+    sp<BufferClient> client = new BufferClient(node);
+    // Add it to list for bookkeeping and dumpsys.
+    std::lock_guard<std::mutex> lock(mClientListMutex);
+    mClientList.push_back(client);
+
+    _hidl_cb(/*bufferClient=*/client, /*status=*/BufferHubStatus::NO_ERROR);
     return Void();
 }
 
diff --git a/services/bufferhub/BufferNode.cpp b/services/bufferhub/BufferNode.cpp
index 62583a6..53dd702 100644
--- a/services/bufferhub/BufferNode.cpp
+++ b/services/bufferhub/BufferNode.cpp
@@ -35,7 +35,7 @@
                                                      /*requestor=*/"bufferhub");
 
     if (ret != OK || buffer_handle_ == nullptr) {
-        ALOGE("BufferNode::BufferNode: Failed to allocate buffer: %s", strerror(-ret));
+        ALOGE("%s: Failed to allocate buffer: %s", __FUNCTION__, strerror(-ret));
         return;
     }
 
@@ -48,7 +48,7 @@
 
     metadata_ = BufferHubMetadata::Create(user_metadata_size);
     if (!metadata_.IsValid()) {
-        ALOGE("BufferNode::BufferNode: Failed to allocate metadata.");
+        ALOGE("%s: Failed to allocate metadata.", __FUNCTION__);
         return;
     }
     InitializeMetadata();
@@ -59,7 +59,7 @@
     if (buffer_handle_ != nullptr) {
         status_t ret = GraphicBufferAllocator::get().free(buffer_handle_);
         if (ret != OK) {
-            ALOGE("BufferNode::~BufferNode: Failed to free handle; Got error: %d", ret);
+            ALOGE("%s: Failed to free handle; Got error: %d", __FUNCTION__, ret);
         }
     }
 }
@@ -76,8 +76,7 @@
         client_state_mask = dvr::BufferHubDefs::FindNextAvailableClientStateMask(
                 current_active_clients_bit_mask);
         if (client_state_mask == 0ULL) {
-            ALOGE("BufferNode::AddNewActiveClientsBitToMask: reached the maximum "
-                  "mumber of channels per buffer node: 32.");
+            ALOGE("%s: reached the maximum number of channels per buffer node: 32.", __FUNCTION__);
             errno = E2BIG;
             return 0ULL;
         }
diff --git a/services/bufferhub/include/bufferhub/BufferClient.h b/services/bufferhub/include/bufferhub/BufferClient.h
index 14ea95c..c6eb791 100644
--- a/services/bufferhub/include/bufferhub/BufferClient.h
+++ b/services/bufferhub/include/bufferhub/BufferClient.h
@@ -18,6 +18,7 @@
 #define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_CLIENT_H
 
 #include <android/frameworks/bufferhub/1.0/IBufferClient.h>
+#include <bufferhub/BufferNode.h>
 
 namespace android {
 namespace frameworks {
@@ -27,9 +28,19 @@
 
 using hardware::Return;
 
+// Forward declaration to avoid circular dependency
+class BufferHubService;
+
 class BufferClient : public IBufferClient {
 public:
+    // Creates a server-side buffer client from an existing BufferNode. Note that
+    // this funciton takes ownership of the shared_ptr.
+    explicit BufferClient(const std::shared_ptr<BufferNode>& node) : mBufferNode(node){};
+
     Return<void> duplicate(duplicate_cb _hidl_cb) override;
+
+private:
+    std::shared_ptr<BufferNode> mBufferNode;
 };
 
 } // namespace implementation
diff --git a/services/bufferhub/include/bufferhub/BufferHubService.h b/services/bufferhub/include/bufferhub/BufferHubService.h
index 5e0cff0..1bbd807 100644
--- a/services/bufferhub/include/bufferhub/BufferHubService.h
+++ b/services/bufferhub/include/bufferhub/BufferHubService.h
@@ -17,8 +17,11 @@
 #ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
 #define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
 
-#include <android/frameworks/bufferhub/1.0/IBufferClient.h>
+#include <mutex>
+
 #include <android/frameworks/bufferhub/1.0/IBufferHub.h>
+#include <bufferhub/BufferClient.h>
+#include <utils/Mutex.h>
 
 namespace android {
 namespace frameworks {
@@ -36,6 +39,11 @@
                                 const uint32_t userMetadataSize,
                                 allocateBuffer_cb _hidl_cb) override;
     Return<void> importBuffer(const hidl_handle& nativeHandle, importBuffer_cb _hidl_cb) override;
+
+private:
+    // List of active BufferClient for bookkeeping.
+    std::mutex mClientListMutex;
+    std::vector<sp<BufferClient>> mClientList GUARDED_BY(mClientListMutex);
 };
 
 } // namespace implementation
diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp
index a964d29..31057f6 100644
--- a/services/inputflinger/EventHub.cpp
+++ b/services/inputflinger/EventHub.cpp
@@ -147,8 +147,7 @@
         fd(fd), id(id), path(path), identifier(identifier),
         classes(0), configuration(nullptr), virtualKeyMap(nullptr),
         ffEffectPlaying(false), ffEffectId(-1), controllerNumber(0),
-        timestampOverrideSec(0), timestampOverrideUsec(0), enabled(true),
-        isVirtual(fd < 0) {
+        enabled(true), isVirtual(fd < 0) {
     memset(keyBitmask, 0, sizeof(keyBitmask));
     memset(absBitmask, 0, sizeof(absBitmask));
     memset(relBitmask, 0, sizeof(relBitmask));
@@ -193,8 +192,6 @@
 
 // --- EventHub ---
 
-const uint32_t EventHub::EPOLL_ID_INOTIFY;
-const uint32_t EventHub::EPOLL_ID_WAKE;
 const int EventHub::EPOLL_SIZE_HINT;
 const int EventHub::EPOLL_MAX_EVENTS;
 
@@ -217,7 +214,7 @@
     struct epoll_event eventItem;
     memset(&eventItem, 0, sizeof(eventItem));
     eventItem.events = EPOLLIN;
-    eventItem.data.u32 = EPOLL_ID_INOTIFY;
+    eventItem.data.fd = mINotifyFd;
     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
 
@@ -236,7 +233,7 @@
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
             errno);
 
-    eventItem.data.u32 = EPOLL_ID_WAKE;
+    eventItem.data.fd = mWakeReadPipeFd;
     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
             errno);
@@ -735,6 +732,16 @@
     return nullptr;
 }
 
+EventHub::Device* EventHub::getDeviceByFdLocked(int fd) const {
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        Device* device = mDevices.valueAt(i);
+        if (device->fd == fd) {
+            return device;
+        }
+    }
+    return nullptr;
+}
+
 size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
     ALOG_ASSERT(bufferSize >= 1);
 
@@ -811,7 +818,7 @@
         bool deviceChanged = false;
         while (mPendingEventIndex < mPendingEventCount) {
             const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
-            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
+            if (eventItem.data.fd == mINotifyFd) {
                 if (eventItem.events & EPOLLIN) {
                     mPendingINotify = true;
                 } else {
@@ -820,7 +827,7 @@
                 continue;
             }
 
-            if (eventItem.data.u32 == EPOLL_ID_WAKE) {
+            if (eventItem.data.fd == mWakeReadPipeFd) {
                 if (eventItem.events & EPOLLIN) {
                     ALOGV("awoken after wake()");
                     awoken = true;
@@ -836,14 +843,13 @@
                 continue;
             }
 
-            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
-            if (deviceIndex < 0) {
-                ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
-                        eventItem.events, eventItem.data.u32);
+            Device* device = getDeviceByFdLocked(eventItem.data.fd);
+            if (device == nullptr) {
+                ALOGW("Received unexpected epoll event 0x%08x for unknown device fd %d.",
+                        eventItem.events, eventItem.data.fd);
                 continue;
             }
 
-            Device* device = mDevices.valueAt(deviceIndex);
             if (eventItem.events & EPOLLIN) {
                 int32_t readSize = read(device->fd, readBuffer,
                         sizeof(struct input_event) * capacity);
@@ -871,31 +877,6 @@
                                 (int) iev.time.tv_sec, (int) iev.time.tv_usec,
                                 iev.type, iev.code, iev.value);
 
-                        // Some input devices may have a better concept of the time
-                        // when an input event was actually generated than the kernel
-                        // which simply timestamps all events on entry to evdev.
-                        // This is a custom Android extension of the input protocol
-                        // mainly intended for use with uinput based device drivers.
-                        if (iev.type == EV_MSC) {
-                            if (iev.code == MSC_ANDROID_TIME_SEC) {
-                                device->timestampOverrideSec = iev.value;
-                                continue;
-                            } else if (iev.code == MSC_ANDROID_TIME_USEC) {
-                                device->timestampOverrideUsec = iev.value;
-                                continue;
-                            }
-                        }
-                        if (device->timestampOverrideSec || device->timestampOverrideUsec) {
-                            iev.time.tv_sec = device->timestampOverrideSec;
-                            iev.time.tv_usec = device->timestampOverrideUsec;
-                            if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
-                                device->timestampOverrideSec = 0;
-                                device->timestampOverrideUsec = 0;
-                            }
-                            ALOGV("applied override time %d.%06d",
-                                    int(iev.time.tv_sec), int(iev.time.tv_usec));
-                        }
-
                         // Use the time specified in the event instead of the current time
                         // so that downstream code can get more accurate estimates of
                         // event dispatch latency from the time the event is enqueued onto
@@ -1089,7 +1070,7 @@
     if (mUsingEpollWakeup) {
         eventItem.events |= EPOLLWAKEUP;
     }
-    eventItem.data.u32 = device->id;
+    eventItem.data.fd = device->fd;
     if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, device->fd, &eventItem)) {
         ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);
         return -errno;
diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/EventHub.h
index ea663b7..e2c7e82 100644
--- a/services/inputflinger/EventHub.h
+++ b/services/inputflinger/EventHub.h
@@ -42,20 +42,6 @@
 #define BTN_FIRST 0x100  // first button code
 #define BTN_LAST 0x15f   // last button code
 
-/*
- * These constants are used privately in Android to pass raw timestamps
- * through evdev from uinput device drivers because there is currently no
- * other way to transfer this information.  The evdev driver automatically
- * timestamps all input events with the time they were posted and clobbers
- * whatever information was passed in.
- *
- * For the purposes of this hack, the timestamp is specified in the
- * CLOCK_MONOTONIC timebase and is split into two EV_MSC events specifying
- * seconds and microseconds.
- */
-#define MSC_ANDROID_TIME_SEC 0x6
-#define MSC_ANDROID_TIME_USEC 0x7
-
 namespace android {
 
 enum {
@@ -371,9 +357,6 @@
 
         int32_t controllerNumber;
 
-        int32_t timestampOverrideSec;
-        int32_t timestampOverrideUsec;
-
         Device(int fd, int32_t id, const std::string& path,
                 const InputDeviceIdentifier& identifier);
         ~Device();
@@ -418,6 +401,7 @@
     Device* getDeviceByDescriptorLocked(const std::string& descriptor) const;
     Device* getDeviceLocked(int32_t deviceId) const;
     Device* getDeviceByPathLocked(const char* devicePath) const;
+    Device* getDeviceByFdLocked(int fd) const;
 
     bool hasKeycodeLocked(Device* device, int keycode) const;
 
@@ -466,10 +450,6 @@
     int mWakeReadPipeFd;
     int mWakeWritePipeFd;
 
-    // Ids used for epoll notifications not associated with devices.
-    static const uint32_t EPOLL_ID_INOTIFY = 0x80000001;
-    static const uint32_t EPOLL_ID_WAKE = 0x80000002;
-
     // Epoll FD list size hint.
     static const int EPOLL_SIZE_HINT = 8;
 
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 869bd71..e85e6ef 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -3480,8 +3480,8 @@
     }
 
     // Raw width and height in the natural orientation.
-    int32_t rawWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
-    int32_t rawHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
+    int32_t rawWidth = mRawPointerAxes.getRawWidth();
+    int32_t rawHeight = mRawPointerAxes.getRawHeight();
 
     // Get associated display dimensions.
     DisplayViewport newViewport;
@@ -3913,8 +3913,8 @@
 
     int32_t touchScreenLeft = mRawPointerAxes.x.minValue;
     int32_t touchScreenTop = mRawPointerAxes.y.minValue;
-    int32_t touchScreenWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
-    int32_t touchScreenHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
+    int32_t touchScreenWidth = mRawPointerAxes.getRawWidth();
+    int32_t touchScreenHeight = mRawPointerAxes.getRawHeight();
 
     for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
         const VirtualKeyDefinition& virtualKeyDefinition =
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 3410bc9..9b0007a 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -772,6 +772,8 @@
     RawAbsoluteAxisInfo slot;
 
     RawPointerAxes();
+    inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
+    inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
     void clear();
 };
 
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index e8092a9..9abb040 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -146,6 +146,7 @@
         "SurfaceInterceptor.cpp",
         "SurfaceTracing.cpp",
         "TimeStats/TimeStats.cpp",
+        "TransactionCompletedThread.cpp",
     ],
 }
 
@@ -183,6 +184,7 @@
         "libhidltransport",
         "liblayers_proto",
         "liblog",
+        "libsync",
         "libtimestats_proto",
         "libutils",
     ],
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 6ef3aef..ade62bf 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -517,7 +517,7 @@
 }
 
 bool BufferLayer::hasReadyFrame() const {
-    return hasDrawingBuffer() || getSidebandStreamChanged() || getAutoRefresh();
+    return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
 }
 
 uint32_t BufferLayer::getEffectiveScalingMode() const {
@@ -655,13 +655,43 @@
 }
 
 uint64_t BufferLayer::getHeadFrameNumber() const {
-    if (hasDrawingBuffer()) {
+    if (hasFrameUpdate()) {
         return getFrameNumber();
     } else {
         return mCurrentFrameNumber;
     }
 }
 
+Rect BufferLayer::getBufferSize(const State& s) const {
+    // If we have a sideband stream, or we are scaling the buffer then return the layer size since
+    // we cannot determine the buffer size.
+    if ((s.sidebandStream != nullptr) ||
+        (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) {
+        return Rect(getActiveWidth(s), getActiveHeight(s));
+    }
+
+    if (mActiveBuffer == nullptr) {
+        return Rect::INVALID_RECT;
+    }
+
+    uint32_t bufWidth = mActiveBuffer->getWidth();
+    uint32_t bufHeight = mActiveBuffer->getHeight();
+
+    // Undo any transformations on the buffer and return the result.
+    if (mCurrentTransform & ui::Transform::ROT_90) {
+        std::swap(bufWidth, bufHeight);
+    }
+
+    if (getTransformToDisplayInverse()) {
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufWidth, bufHeight);
+        }
+    }
+
+    return Rect(bufWidth, bufHeight);
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index b3ea7e6..690a4e5 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -136,7 +136,7 @@
 
     virtual std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) = 0;
 
-    virtual bool hasDrawingBuffer() const = 0;
+    virtual bool hasFrameUpdate() const = 0;
 
     virtual void setFilteringEnabled(bool enabled) = 0;
 
@@ -188,6 +188,8 @@
     mutable renderengine::Texture mTexture;
 
     bool mRefreshPending{false};
+
+    Rect getBufferSize(const State& s) const override;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 5dea3c8..78ab23a 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -82,7 +82,7 @@
         return true;
     }
 
-    if (!hasDrawingBuffer()) {
+    if (!hasFrameUpdate()) {
         return false;
     }
 
@@ -110,7 +110,7 @@
         return true;
     }
 
-    if (!hasDrawingBuffer()) {
+    if (!hasFrameUpdate()) {
         return true;
     }
 
@@ -206,7 +206,7 @@
     return {};
 }
 
-bool BufferQueueLayer::hasDrawingBuffer() const {
+bool BufferQueueLayer::hasFrameUpdate() const {
     return mQueuedFrames > 0;
 }
 
@@ -407,7 +407,7 @@
             }
         }
 
-        if (!hasDrawingBuffer()) {
+        if (!hasFrameUpdate()) {
             ALOGE("Can't replace a frame on an empty queue");
             return;
         }
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 74dd21e..ae0b705 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -83,7 +83,7 @@
 
     std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) override;
 
-    bool hasDrawingBuffer() const override;
+    bool hasFrameUpdate() const override;
 
     void setFilteringEnabled(bool enabled) override;
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 8d59841..425f5c7 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -35,15 +35,19 @@
 };
 // clang-format on
 
-BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
+BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer(args) {
+    mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+}
 BufferStateLayer::~BufferStateLayer() = default;
 
 // -----------------------------------------------------------------------
 // Interface implementation for Layer
 // -----------------------------------------------------------------------
-void BufferStateLayer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) {
-    // TODO(marissaw): send the release fence back to buffer owner
-    return;
+void BufferStateLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
+    // The transaction completed callback can only be sent if the release fence from the PREVIOUS
+    // frame has fired. In practice, we should never actually wait on the previous release fence
+    // but we should store it just in case.
+    mPreviousReleaseFence = releaseFence;
 }
 
 void BufferStateLayer::setTransformHint(uint32_t /*orientation*/) const {
@@ -52,7 +56,6 @@
 }
 
 void BufferStateLayer::releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) {
-    // TODO(marissaw): use this to signal the buffer owner
     return;
 }
 
@@ -61,7 +64,13 @@
         return true;
     }
 
-    return hasDrawingBuffer();
+    return hasFrameUpdate();
+}
+
+bool BufferStateLayer::willPresentCurrentTransaction() const {
+    // Returns true if the most recent Transaction applied to CurrentState will be presented.
+    return getSidebandStreamChanged() || getAutoRefresh() ||
+            (mCurrentState.modified && mCurrentState.buffer != nullptr);
 }
 
 bool BufferStateLayer::getTransformToDisplayInverse() const {
@@ -81,6 +90,7 @@
     while (!mPendingStates.empty()) {
         popPendingState(stateToCommit);
     }
+    mCurrentStateModified = stateUpdateAvailable && mCurrentState.modified;
     mCurrentState.modified = false;
     return stateUpdateAvailable;
 }
@@ -118,7 +128,11 @@
     return true;
 }
 
-bool BufferStateLayer::setBuffer(sp<GraphicBuffer> buffer) {
+bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer) {
+    if (mCurrentState.buffer) {
+        mReleasePreviousBuffer = true;
+    }
+
     mCurrentState.sequence++;
     mCurrentState.buffer = buffer;
     mCurrentState.modified = true;
@@ -127,6 +141,9 @@
 }
 
 bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) {
+    // The acquire fences of BufferStateLayers have already signaled before they are set
+    mCallbackHandleAcquireTime = fence->getSignalTime();
+
     mCurrentState.acquireFence = fence;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
@@ -182,6 +199,44 @@
     return true;
 }
 
+bool BufferStateLayer::setTransactionCompletedListeners(
+        const std::vector<sp<CallbackHandle>>& handles) {
+    // If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return
+    if (handles.empty()) {
+        mReleasePreviousBuffer = false;
+        return false;
+    }
+
+    const bool willPresent = willPresentCurrentTransaction();
+
+    for (const auto& handle : handles) {
+        // If this transaction set a buffer on this layer, release its previous buffer
+        handle->releasePreviousBuffer = mReleasePreviousBuffer;
+
+        // If this layer will be presented in this frame
+        if (willPresent) {
+            // If this transaction set an acquire fence on this layer, set its acquire time
+            handle->acquireTime = mCallbackHandleAcquireTime;
+
+            // Notify the transaction completed thread that there is a pending latched callback
+            // handle
+            mFlinger->getTransactionCompletedThread().registerPendingLatchedCallbackHandle(handle);
+
+            // Store so latched time and release fence can be set
+            mCurrentState.callbackHandles.push_back(handle);
+
+        } else { // If this layer will NOT need to be relatched and presented this frame
+            // Notify the transaction completed thread this handle is done
+            mFlinger->getTransactionCompletedThread().addUnlatchedCallbackHandle(handle);
+        }
+    }
+
+    mReleasePreviousBuffer = false;
+    mCallbackHandleAcquireTime = -1;
+
+    return willPresent;
+}
+
 bool BufferStateLayer::setSize(uint32_t w, uint32_t h) {
     if (mCurrentState.active.w == w && mCurrentState.active.h == h) return false;
     mCurrentState.active.w = w;
@@ -265,7 +320,7 @@
 }
 
 uint32_t BufferStateLayer::getDrawingScalingMode() const {
-    return NATIVE_WINDOW_SCALING_MODE_FREEZE;
+    return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
 }
 
 Region BufferStateLayer::getDrawingSurfaceDamage() const {
@@ -314,8 +369,8 @@
     return {};
 }
 
-bool BufferStateLayer::hasDrawingBuffer() const {
-    return getDrawingState().buffer != nullptr;
+bool BufferStateLayer::hasFrameUpdate() const {
+    return mCurrentStateModified && getCurrentState().buffer != nullptr;
 }
 
 void BufferStateLayer::setFilteringEnabled(bool enabled) {
@@ -394,7 +449,7 @@
         }
     }
 
-    if (mOverrideScalingMode == NATIVE_WINDOW_SCALING_MODE_FREEZE &&
+    if (getEffectiveScalingMode() == NATIVE_WINDOW_SCALING_MODE_FREEZE &&
         (s.active.w != bufferWidth || s.active.h != bufferHeight)) {
         ALOGE("[%s] rejecting buffer: "
               "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
@@ -403,6 +458,10 @@
         return BAD_VALUE;
     }
 
+    mFlinger->getTransactionCompletedThread()
+            .addLatchedCallbackHandles(getDrawingState().callbackHandles, latchTime,
+                                       mPreviousReleaseFence);
+
     // Handle sync fences
     if (SyncFeatures::getInstance().useNativeFenceSync() && releaseFence != Fence::NO_FENCE) {
         // TODO(alecmouri): Fail somewhere upstream if the fence is invalid.
@@ -512,6 +571,7 @@
               s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
+    mCurrentStateModified = false;
     mFrameNumber++;
 }
 
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 381cd28..315d5af 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -62,13 +62,14 @@
     bool setTransform(uint32_t transform) override;
     bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
     bool setCrop(const Rect& crop) override;
-    bool setBuffer(sp<GraphicBuffer> buffer) override;
+    bool setBuffer(const sp<GraphicBuffer>& buffer) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
     bool setSurfaceDamageRegion(const Region& surfaceDamage) override;
     bool setApi(int32_t api) override;
     bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
+    bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
 
     bool setSize(uint32_t w, uint32_t h) override;
     bool setPosition(float x, float y, bool immediate) override;
@@ -110,7 +111,7 @@
 
     std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) override;
 
-    bool hasDrawingBuffer() const override;
+    bool hasFrameUpdate() const override;
 
     void setFilteringEnabled(bool enabled) override;
 
@@ -125,6 +126,7 @@
 
 private:
     void onFirstRef() override;
+    bool willPresentCurrentTransaction() const;
 
     static const std::array<float, 16> IDENTITY_MATRIX;
 
@@ -136,6 +138,12 @@
 
     uint32_t mFrameNumber{0};
 
+    sp<Fence> mPreviousReleaseFence;
+
+    bool mCurrentStateModified = false;
+    bool mReleasePreviousBuffer = false;
+    nsecs_t mCallbackHandleAcquireTime = -1;
+
     // TODO(marissaw): support sticky transform for LEGACY camera mode
 };
 
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 071f07d..d16febf 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -35,6 +35,8 @@
 #include <gui/Surface.h>
 #include <hardware/gralloc.h>
 #include <renderengine/RenderEngine.h>
+#include <sync/sync.h>
+#include <system/window.h>
 #include <ui/DebugUtils.h>
 #include <ui/DisplayInfo.h>
 #include <ui/PixelFormat.h>
@@ -221,10 +223,8 @@
         mDisplayToken(args.displayToken),
         mId(args.displayId),
         mNativeWindow(args.nativeWindow),
+        mGraphicBuffer(nullptr),
         mDisplaySurface(args.displaySurface),
-        mSurface{std::move(args.renderSurface)},
-        mDisplayWidth(args.displayWidth),
-        mDisplayHeight(args.displayHeight),
         mDisplayInstallOrientation(args.displayInstallOrientation),
         mPageFlipCount(0),
         mIsVirtual(args.isVirtual),
@@ -246,9 +246,6 @@
 
     ALOGE_IF(!mNativeWindow, "No native window was set for display");
     ALOGE_IF(!mDisplaySurface, "No display surface was set for display");
-    ALOGE_IF(!mSurface, "No render surface was set for display");
-    ALOGE_IF(mDisplayWidth <= 0 || mDisplayHeight <= 0,
-             "Invalid dimensions of %d x %d were set for display", mDisplayWidth, mDisplayHeight);
 
     std::vector<Hdr> types = args.hdrCapabilities.getSupportedHdrTypes();
     for (Hdr hdrType : types) {
@@ -287,6 +284,14 @@
     }
     mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance);
 
+    ANativeWindow* const window = mNativeWindow.get();
+
+    int status = native_window_api_connect(mNativeWindow.get(), NATIVE_WINDOW_API_EGL);
+    ALOGE_IF(status != NO_ERROR, "Unable to connect BQ producer: %d", status);
+
+    mDisplayWidth = ANativeWindow_getWidth(window);
+    mDisplayHeight = ANativeWindow_getHeight(window);
+
     // initialize the display orientation transform.
     setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
 }
@@ -321,7 +326,6 @@
 
 void DisplayDevice::flip() const
 {
-    mFlinger->getRenderEngine().checkErrors();
     mPageFlipCount++;
 }
 
@@ -356,9 +360,71 @@
     return mDisplaySurface->prepareFrame(compositionType);
 }
 
-void DisplayDevice::swapBuffers(HWComposer& hwc) const {
+sp<GraphicBuffer> DisplayDevice::dequeueBuffer() {
+    int fd;
+    ANativeWindowBuffer* buffer;
+
+    status_t res = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fd);
+
+    if (res != NO_ERROR) {
+        ALOGE("ANativeWindow::dequeueBuffer failed for display [%s] with error: %d",
+              getDisplayName().c_str(), res);
+        // Return fast here as we can't do much more - any rendering we do
+        // now will just be wrong.
+        return mGraphicBuffer;
+    }
+
+    ALOGW_IF(mGraphicBuffer != nullptr, "Clobbering a non-null pointer to a buffer [%p].",
+             mGraphicBuffer->getNativeBuffer()->handle);
+    mGraphicBuffer = GraphicBuffer::from(buffer);
+
+    // Block until the buffer is ready
+    // TODO(alecmouri): it's perhaps more appropriate to block renderengine so
+    // that the gl driver can block instead.
+    if (fd >= 0) {
+        sync_wait(fd, -1);
+        close(fd);
+    }
+
+    return mGraphicBuffer;
+}
+
+void DisplayDevice::queueBuffer(HWComposer& hwc) {
     if (hwc.hasClientComposition(mId) || hwc.hasFlipClientTargetRequest(mId)) {
-        mSurface->swapBuffers();
+        // hasFlipClientTargetRequest could return true even if we haven't
+        // dequeued a buffer before. Try dequeueing one if we don't have a
+        // buffer ready.
+        if (mGraphicBuffer == nullptr) {
+            ALOGI("Attempting to queue a client composited buffer without one "
+                  "previously dequeued for display [%s]. Attempting to dequeue "
+                  "a scratch buffer now",
+                  mDisplayName.c_str());
+            // We shouldn't deadlock here, since mGraphicBuffer == nullptr only
+            // after a successful call to queueBuffer, or if dequeueBuffer has
+            // never been called.
+            dequeueBuffer();
+        }
+
+        if (mGraphicBuffer == nullptr) {
+            ALOGE("No buffer is ready for display [%s]", mDisplayName.c_str());
+        } else {
+            int fd = mBufferReady.release();
+
+            status_t res = mNativeWindow->queueBuffer(mNativeWindow.get(),
+                                                      mGraphicBuffer->getNativeBuffer(), fd);
+            if (res != NO_ERROR) {
+                ALOGE("Error when queueing buffer for display [%s]: %d", mDisplayName.c_str(), res);
+                // We risk blocking on dequeueBuffer if the primary display failed
+                // to queue up its buffer, so crash here.
+                if (isPrimary()) {
+                    LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", res);
+                } else {
+                    mNativeWindow->cancelBuffer(mNativeWindow.get(),
+                                                mGraphicBuffer->getNativeBuffer(), fd);
+                }
+            }
+            mGraphicBuffer = nullptr;
+        }
     }
 
     status_t result = mDisplaySurface->advanceFrame();
@@ -367,16 +433,10 @@
     }
 }
 
-void DisplayDevice::onSwapBuffersCompleted() const {
+void DisplayDevice::onPresentDisplayCompleted() {
     mDisplaySurface->onFrameCommitted();
 }
 
-bool DisplayDevice::makeCurrent() const {
-    bool success = mFlinger->getRenderEngine().setCurrentSurface(*mSurface);
-    setViewportAndProjection();
-    return success;
-}
-
 void DisplayDevice::setViewportAndProjection() const {
     size_t w = mDisplayWidth;
     size_t h = mDisplayHeight;
@@ -384,6 +444,13 @@
     mFlinger->getRenderEngine().setViewportAndProjection(w, h, sourceCrop, ui::Transform::ROT_0);
 }
 
+void DisplayDevice::finishBuffer() {
+    mBufferReady = mFlinger->getRenderEngine().flush();
+    if (mBufferReady.get() < 0) {
+        mFlinger->getRenderEngine().finish();
+    }
+}
+
 const sp<Fence>& DisplayDevice::getClientTargetAcquireFence() const {
     return mDisplaySurface->getClientTargetAcquireFence();
 }
@@ -532,19 +599,10 @@
 void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) {
     dirtyRegion.set(getBounds());
 
-    mSurface->setNativeWindow(nullptr);
-
     mDisplaySurface->resizeBuffers(newWidth, newHeight);
 
-    ANativeWindow* const window = mNativeWindow.get();
-    mSurface->setNativeWindow(window);
-    mDisplayWidth = mSurface->getWidth();
-    mDisplayHeight = mSurface->getHeight();
-
-    LOG_FATAL_IF(mDisplayWidth != newWidth,
-                "Unable to set new width to %d", newWidth);
-    LOG_FATAL_IF(mDisplayHeight != newHeight,
-                "Unable to set new height to %d", newHeight);
+    mDisplayWidth = newWidth;
+    mDisplayHeight = newHeight;
 }
 
 void DisplayDevice::setProjection(int orientation,
@@ -658,12 +716,11 @@
     ANativeWindow* const window = mNativeWindow.get();
     result.appendFormat("+ %s\n", getDebugName().c_str());
     result.appendFormat("  layerStack=%u, (%4dx%4d), ANativeWindow=%p "
-                        "(%d:%d:%d:%d), orient=%2d (type=%08x), "
-                        "flips=%u, isSecure=%d, powerMode=%d, activeConfig=%d, numLayers=%zu\n",
+                        "format=%d, orient=%2d (type=%08x), flips=%u, isSecure=%d, "
+                        "powerMode=%d, activeConfig=%d, numLayers=%zu\n",
                         mLayerStack, mDisplayWidth, mDisplayHeight, window,
-                        mSurface->queryRedSize(), mSurface->queryGreenSize(),
-                        mSurface->queryBlueSize(), mSurface->queryAlphaSize(), mOrientation,
-                        tr.getType(), getPageFlipCount(), mIsSecure, mPowerMode, mActiveConfig,
+                        ANativeWindow_getFormat(window), mOrientation, tr.getType(),
+                        getPageFlipCount(), mIsSecure, mPowerMode, mActiveConfig,
                         mVisibleLayersSortedByZ.size());
     result.appendFormat("   v:[%d,%d,%d,%d], f:[%d,%d,%d,%d], s:[%d,%d,%d,%d],"
                         "transform:[[%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f]]\n",
@@ -674,9 +731,9 @@
     auto const surface = static_cast<Surface*>(window);
     ui::Dataspace dataspace = surface->getBuffersDataSpace();
     result.appendFormat("   wideColorGamut=%d, hdr10=%d, colorMode=%s, dataspace: %s (%d)\n",
-                        mHasWideColorGamut, mHasHdr10,
-                        decodeColorMode(mActiveColorMode).c_str(),
-                        dataspaceDetails(static_cast<android_dataspace>(dataspace)).c_str(), dataspace);
+                        mHasWideColorGamut, mHasHdr10, decodeColorMode(mActiveColorMode).c_str(),
+                        dataspaceDetails(static_cast<android_dataspace>(dataspace)).c_str(),
+                        dataspace);
 
     String8 surfaceDump;
     mDisplaySurface->dumpAsString(surfaceDump);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 9ec7666..eb2c5c3 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -24,25 +24,25 @@
 #include <string>
 #include <unordered_map>
 
+#include <android/native_window.h>
 #include <binder/IBinder.h>
 #include <gui/LayerState.h>
 #include <hardware/hwcomposer_defs.h>
 #include <math/mat4.h>
-#include <renderengine/Surface.h>
+#include <renderengine/RenderEngine.h>
+#include <system/window.h>
 #include <ui/GraphicTypes.h>
 #include <ui/HdrCapabilities.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
-#include <utils/RefBase.h>
 #include <utils/Mutex.h>
+#include <utils/RefBase.h>
 #include <utils/String8.h>
 #include <utils/Timers.h>
 
 #include "DisplayHardware/DisplayIdentification.h"
 #include "RenderArea.h"
 
-struct ANativeWindow;
-
 namespace android {
 
 class DisplaySurface;
@@ -146,10 +146,13 @@
                           ui::Dataspace* outDataspace, ui::ColorMode* outMode,
                           ui::RenderIntent* outIntent) const;
 
-    void swapBuffers(HWComposer& hwc) const;
+    // Queues the drawn buffer for consumption by HWC.
+    void queueBuffer(HWComposer& hwc);
+    // Allocates a buffer as scratch space for GPU composition
+    sp<GraphicBuffer> dequeueBuffer();
 
     // called after h/w composer has completed its set() call
-    void onSwapBuffersCompleted() const;
+    void onPresentDisplayCompleted();
 
     Rect getBounds() const {
         return Rect(mDisplayWidth, mDisplayHeight);
@@ -159,7 +162,11 @@
     void setDisplayName(const std::string& displayName);
     const std::string& getDisplayName() const { return mDisplayName; }
 
-    bool makeCurrent() const;
+    // Acquires a new buffer for GPU composition.
+    void readyNewBuffer();
+    // Marks the current buffer has finished, so that it can be presented and
+    // swapped out.
+    void finishBuffer();
     void setViewportAndProjection() const;
 
     const sp<Fence>& getClientTargetAcquireFence() const;
@@ -204,9 +211,13 @@
 
     // ANativeWindow this display is rendering into
     sp<ANativeWindow> mNativeWindow;
+    // Current buffer that this display can render to.
+    sp<GraphicBuffer> mGraphicBuffer;
     sp<DisplaySurface> mDisplaySurface;
+    // File descriptor indicating that mGraphicBuffer is ready for display, i.e.
+    // that drawing to the buffer is now complete.
+    base::unique_fd mBufferReady;
 
-    std::unique_ptr<renderengine::Surface> mSurface;
     int             mDisplayWidth;
     int             mDisplayHeight;
     const int       mDisplayInstallOrientation;
@@ -326,9 +337,6 @@
     bool isSecure{false};
     sp<ANativeWindow> nativeWindow;
     sp<DisplaySurface> displaySurface;
-    std::unique_ptr<renderengine::Surface> renderSurface;
-    int displayWidth{0};
-    int displayHeight{0};
     int displayInstallOrientation{DisplayState::eOrientationDefault};
     bool hasWideColorGamut{false};
     HdrCapabilities hdrCapabilities;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 27d3dc5..5f3fcd6 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -107,8 +107,6 @@
     mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
     mConsumer->setDefaultBufferSize(sinkWidth, sinkHeight);
     sink->setAsyncMode(true);
-    IGraphicBufferProducer::QueueBufferOutput output;
-    mSource[SOURCE_SCRATCH]->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &output);
 }
 
 VirtualDisplaySurface::~VirtualDisplaySurface() {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 010a339..038bc58 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -275,39 +275,12 @@
     return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
 }
 
-Rect Layer::computeScreenBounds(bool reduceTransparentRegion) const {
-    const State& s(getDrawingState());
-    Rect win(getActiveWidth(s), getActiveHeight(s));
-
-    Rect crop = getCrop(s);
-    if (!crop.isEmpty()) {
-        win.intersect(crop, &win);
-    }
-
+Rect Layer::computeScreenBounds() const {
+    FloatRect bounds = computeBounds();
     ui::Transform t = getTransform();
-    win = t.transform(win);
-
-    const sp<Layer>& p = mDrawingParent.promote();
-    // Now we need to calculate the parent bounds, so we can clip ourselves to those.
-    // When calculating the parent bounds for purposes of clipping,
-    // we don't need to constrain the parent to its transparent region.
-    // The transparent region is an optimization based on the
-    // buffer contents of the layer, but does not affect the space allocated to
-    // it by policy, and thus children should be allowed to extend into the
-    // parent's transparent region. In fact one of the main uses, is to reduce
-    // buffer allocation size in cases where a child window sits behind a main window
-    // (by marking the hole in the parent window as a transparent region)
-    if (p != nullptr) {
-        Rect bounds = p->computeScreenBounds(false);
-        bounds.intersect(win, &win);
-    }
-
-    if (reduceTransparentRegion) {
-        auto const screenTransparentRegion = t.transform(getActiveTransparentRegion(s));
-        win = reduce(win, screenTransparentRegion);
-    }
-
-    return win;
+    // Transform to screen space.
+    bounds = t.transform(bounds);
+    return Rect{bounds};
 }
 
 FloatRect Layer::computeBounds() const {
@@ -317,32 +290,72 @@
 
 FloatRect Layer::computeBounds(const Region& activeTransparentRegion) const {
     const State& s(getDrawingState());
-    Rect win(getActiveWidth(s), getActiveHeight(s));
+    Rect bounds = getCroppedBufferSize(s);
+    FloatRect floatBounds = bounds.toFloatRect();
+    if (bounds.isValid()) {
+        // Layer has bounds. Pass in our bounds as a special case. Then pass on to our parents so
+        // that they can clip it.
+        floatBounds = cropChildBounds(floatBounds);
+    } else {
+        // Layer does not have bounds, so we fill to our parent bounds. This is done by getting our
+        // parent bounds and inverting the transform to get the maximum bounds we can have that
+        // will fit within our parent bounds.
+        const auto& p = mDrawingParent.promote();
+        if (p != nullptr) {
+            ui::Transform t = s.active_legacy.transform;
+            // When calculating the parent bounds for purposes of clipping, we don't need to
+            // constrain the parent to its transparent region. The transparent region is an
+            // optimization based on the buffer contents of the layer, but does not affect the
+            // space allocated to it by policy, and thus children should be allowed to extend into
+            // the parent's transparent region.
+            // One of the main uses is a parent window with a child sitting behind the parent
+            // window, marked by a transparent region. When computing the parent bounds from the
+            // parent's perspective we pass in the transparent region to reduce buffer allocation
+            // size. When computing the parent bounds from the child's perspective, we pass in an
+            // empty transparent region in order to extend into the the parent bounds.
+            floatBounds = p->computeBounds(Region());
+            // Transform back to layer space.
+            floatBounds = t.inverse().transform(floatBounds);
+        }
+    }
 
-    Rect crop = getCrop(s);
-    if (!crop.isEmpty()) {
-        win.intersect(crop, &win);
+    // Subtract the transparent region and snap to the bounds.
+    return reduce(floatBounds, activeTransparentRegion);
+}
+
+FloatRect Layer::cropChildBounds(const FloatRect& childBounds) const {
+    const State& s(getDrawingState());
+    Rect bounds = getCroppedBufferSize(s);
+    FloatRect croppedBounds = childBounds;
+
+    // If the layer has bounds, then crop the passed in child bounds and pass
+    // it to our parents so they can crop it as well. If the layer has no bounds,
+    // then pass on the child bounds.
+    if (bounds.isValid()) {
+        croppedBounds = croppedBounds.intersect(bounds.toFloatRect());
     }
 
     const auto& p = mDrawingParent.promote();
-    FloatRect floatWin = win.toFloatRect();
-    FloatRect parentBounds = floatWin;
     if (p != nullptr) {
-        // We pass an empty Region here for reasons mirroring that of the case described in
-        // the computeScreenBounds reduceTransparentRegion=false case.
-        parentBounds = p->computeBounds(Region());
+        // Transform to parent space and allow parent layer to crop the
+        // child bounds as well.
+        ui::Transform t = s.active_legacy.transform;
+        croppedBounds = t.transform(croppedBounds);
+        croppedBounds = p->cropChildBounds(croppedBounds);
+        croppedBounds = t.inverse().transform(croppedBounds);
     }
+    return croppedBounds;
+}
 
-    ui::Transform t = s.active_legacy.transform;
-
-    if (p != nullptr) {
-        floatWin = t.transform(floatWin);
-        floatWin = floatWin.intersect(parentBounds);
-        floatWin = t.inverse().transform(floatWin);
+Rect Layer::getCroppedBufferSize(const State& s) const {
+    Rect size = getBufferSize(s);
+    Rect crop = getCrop(s);
+    if (!crop.isEmpty() && size.isValid()) {
+        size.intersect(crop, &size);
+    } else if (!crop.isEmpty()) {
+        size = crop;
     }
-
-    // subtract the transparent region and snap to the bounds
-    return reduce(floatWin, activeTransparentRegion);
+    return size;
 }
 
 Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& display) const {
@@ -1042,6 +1055,7 @@
 
     // Commit the transaction
     commitTransaction(c);
+    mCurrentState.callbackHandles = {};
     return flags;
 }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8ce67c3..40ebe1e 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -48,6 +48,7 @@
 #include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
 #include "TimeStats/TimeStats.h"
+#include "TransactionCompletedThread.h"
 
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/HWComposerBufferCache.h"
@@ -184,6 +185,10 @@
         sp<NativeHandle> sidebandStream;
         mat4 colorTransform;
         bool hasColorTransform;
+
+        // The deque of callback handles for this frame. The back of the deque contains the most
+        // recent callback handle.
+        std::deque<sp<CallbackHandle>> callbackHandles;
     };
 
     explicit Layer(const LayerCreationArgs& args);
@@ -266,13 +271,17 @@
     virtual bool setTransform(uint32_t /*transform*/) { return false; };
     virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
     virtual bool setCrop(const Rect& /*crop*/) { return false; };
-    virtual bool setBuffer(sp<GraphicBuffer> /*buffer*/) { return false; };
+    virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/) { return false; };
     virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
     virtual bool setDataspace(ui::Dataspace /*dataspace*/) { return false; };
     virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) { return false; };
     virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) { return false; };
     virtual bool setApi(int32_t /*api*/) { return false; };
     virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; };
+    virtual bool setTransactionCompletedListeners(
+            const std::vector<sp<CallbackHandle>>& /*handles*/) {
+        return false;
+    };
 
     ui::Dataspace getDataSpace() const { return mCurrentDataSpace; }
 
@@ -570,7 +579,7 @@
     ssize_t removeChild(const sp<Layer>& layer);
     sp<Layer> getParent() const { return mCurrentParent.promote(); }
     bool hasParent() const { return getParent() != nullptr; }
-    Rect computeScreenBounds(bool reduceTransparentRegion = true) const;
+    Rect computeScreenBounds() const;
     bool setChildLayer(const sp<Layer>& childLayer, int32_t z);
     bool setChildRelativeLayer(const sp<Layer>& childLayer,
             const sp<IBinder>& relativeToHandle, int32_t relativeZ);
@@ -782,6 +791,27 @@
                                        const LayerVector::Visitor& visitor);
     LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                           const std::vector<Layer*>& layersInTree);
+
+    /**
+     * Retuns the child bounds in layer space cropped to its bounds as well all its parent bounds.
+     * The cropped bounds must be transformed back from parent layer space to child layer space by
+     * applying the inverse of the child's transformation.
+     */
+    FloatRect cropChildBounds(const FloatRect& childBounds) const;
+
+    /**
+     * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
+     * INVALID_RECT if the layer has no buffer and no crop.
+     * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
+     * bounds are constrained by its parent bounds.
+     */
+    Rect getCroppedBufferSize(const Layer::State& s) const;
+
+    /**
+     * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
+     * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
+     */
+    virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 26de754..e84e21a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -45,6 +45,7 @@
 #include <gui/BufferQueue.h>
 #include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
+#include <gui/IProducerListener.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 #include <renderengine/RenderEngine.h>
@@ -676,10 +677,6 @@
     LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(*display->getId()),
                         "Internal display is disconnected.");
 
-    // make the default display GLContext current so that we can create textures
-    // when creating Layers (which may happens before we render something)
-    display->makeCurrent();
-
     if (useVrFlinger) {
         auto vrFlingerRequestDisplayCallback = [this](bool requestDisplay) {
             // This callback is called from the vr flinger dispatch thread. We
@@ -1409,7 +1406,6 @@
     // mCurrentState and mDrawingState and re-apply all changes when we make the
     // transition.
     mDrawingState.displays.clear();
-    getRenderEngine().resetCurrentSurface();
     mDisplays.clear();
 }
 
@@ -1719,7 +1715,7 @@
             auto& engine(getRenderEngine());
             engine.fillRegionWithColor(dirtyRegion, 1, 0, 1, 1);
 
-            display->swapBuffers(getHwComposer());
+            display->queueBuffer(getHwComposer());
         }
     }
 
@@ -1959,6 +1955,9 @@
             ATRACE_INT("TexturePoolSize", mTexturePool.size());
         }
     }
+
+    mTransactionCompletedThread.addPresentFence(mPreviousPresentFence);
+    mTransactionCompletedThread.sendCallbacks();
 }
 
 void SurfaceFlinger::rebuildLayerStacks() {
@@ -2191,8 +2190,7 @@
         if (displayId) {
             getHwComposer().presentAndGetReleaseFences(*displayId);
         }
-        display->onSwapBuffersCompleted();
-        display->makeCurrent();
+        display->onPresentDisplayCompleted();
         for (auto& layer : display->getVisibleLayersSortedByZ()) {
             sp<Fence> releaseFence = Fence::NO_FENCE;
 
@@ -2339,24 +2337,9 @@
     auto nativeWindow = nativeWindowSurface->getNativeWindow();
     creationArgs.nativeWindow = nativeWindow;
 
-    /*
-     * Create our display's surface
-     */
-    std::unique_ptr<renderengine::Surface> renderSurface = getRenderEngine().createSurface();
-    renderSurface->setCritical(isInternalDisplay);
-    renderSurface->setAsync(state.isVirtual());
-    renderSurface->setNativeWindow(nativeWindow.get());
-    creationArgs.displayWidth = renderSurface->getWidth();
-    creationArgs.displayHeight = renderSurface->getHeight();
-    creationArgs.renderSurface = std::move(renderSurface);
-
     // Make sure that composition can never be stalled by a virtual display
     // consumer that isn't processing buffers fast enough. We have to do this
-    // in two places:
-    // * Here, in case the display is composed entirely by HWC.
-    // * In makeCurrent(), using eglSwapInterval. Some EGL drivers set the
-    //   window's swap interval in eglMakeCurrent, so they'll override the
-    //   interval we set here.
+    // here, in case the display is composed entirely by HWC.
     if (state.isVirtual()) {
         nativeWindow->setSwapInterval(nativeWindow.get(), 0);
     }
@@ -2416,12 +2399,6 @@
                 const auto externalDisplayId = getExternalDisplayId();
 
                 // in drawing state but not in current state
-                // Call makeCurrent() on the primary display so we can
-                // be sure that nothing associated with this display
-                // is current.
-                if (const auto defaultDisplay = getDefaultDisplayDeviceLocked()) {
-                    defaultDisplay->makeCurrent();
-                }
                 if (const auto display = getDisplayDeviceLocked(draw.keyAt(i))) {
                     display->disconnect(getHwComposer());
                 }
@@ -2972,7 +2949,7 @@
     mGeometryInvalid = true;
 }
 
-void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& display,
+void SurfaceFlinger::doDisplayComposition(const sp<DisplayDevice>& display,
                                           const Region& inDirtyRegion) {
     // We only need to actually compose the display if:
     // 1) It is being handled by hardware composer, which may need this to
@@ -2987,10 +2964,10 @@
     if (!doComposeSurfaces(display)) return;
 
     // swap buffers (presentation)
-    display->swapBuffers(getHwComposer());
+    display->queueBuffer(getHwComposer());
 }
 
-bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& display) {
+bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& display) {
     ALOGV("doComposeSurfaces");
 
     const Region bounds(display->bounds());
@@ -3003,9 +2980,31 @@
     bool applyColorMatrix = false;
     bool needsEnhancedColorMatrix = false;
 
+    // Framebuffer will live in this scope for GPU composition.
+    std::unique_ptr<renderengine::BindNativeBufferAsFramebuffer> fbo;
+
     if (hasClientComposition) {
         ALOGV("hasClientComposition");
 
+        sp<GraphicBuffer> buf = display->dequeueBuffer();
+
+        if (buf == nullptr) {
+            ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
+                  "client composition for this frame",
+                  display->getDisplayName().c_str());
+            return false;
+        }
+
+        // Bind the framebuffer in this scope.
+        fbo = std::make_unique<renderengine::BindNativeBufferAsFramebuffer>(getRenderEngine(),
+                                                                            buf->getNativeBuffer());
+
+        if (fbo->getStatus() != NO_ERROR) {
+            ALOGW("Binding buffer for display [%s] failed with status: %d",
+                  display->getDisplayName().c_str(), fbo->getStatus());
+            return false;
+        }
+
         Dataspace outputDataspace = Dataspace::UNKNOWN;
         if (display->hasWideColorGamut()) {
             outputDataspace = display->getCompositionDataSpace();
@@ -3034,18 +3033,7 @@
             colorMatrix *= mEnhancedSaturationMatrix;
         }
 
-        if (!display->makeCurrent()) {
-            ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
-                  display->getDisplayName().c_str());
-            getRenderEngine().resetCurrentSurface();
-
-            // |mStateLock| not needed as we are on the main thread
-            const auto defaultDisplay = getDefaultDisplayDeviceLocked();
-            if (!defaultDisplay || !defaultDisplay->makeCurrent()) {
-                ALOGE("DisplayDevice::makeCurrent on default display failed. Aborting.");
-            }
-            return false;
-        }
+        display->setViewportAndProjection();
 
         // Never touch the framebuffer if we don't have any framebuffer layers
         if (hasDeviceComposition) {
@@ -3138,11 +3126,15 @@
         firstLayer = false;
     }
 
-    // Clear color transform matrix at the end of the frame.
-    getRenderEngine().setColorTransform(mat4());
-
-    // disable scissor at the end of the frame
-    getBE().mRenderEngine->disableScissor();
+    // Perform some cleanup steps if we used client composition.
+    if (hasClientComposition) {
+        getRenderEngine().setColorTransform(mat4());
+        getBE().mRenderEngine->disableScissor();
+        display->finishBuffer();
+        // Clear out error flags here so that we don't wait until next
+        // composition to log.
+        getRenderEngine().checkErrors();
+    }
     return true;
 }
 
@@ -3293,9 +3285,15 @@
         transactionFlags |= setDisplayStateLocked(display);
     }
 
+    uint32_t clientStateFlags = 0;
     for (const ComposerState& state : states) {
-        transactionFlags |= setClientStateLocked(state);
+        clientStateFlags |= setClientStateLocked(state);
     }
+    // If the state doesn't require a traversal and there are callbacks, send them now
+    if (!(clientStateFlags & eTraversalNeeded)) {
+        mTransactionCompletedThread.sendCallbacks();
+    }
+    transactionFlags |= clientStateFlags;
 
     // Iterate through all layers again to determine if any need to be destroyed. Marking layers
     // as destroyed should only occur after setting all other states. This is to allow for a
@@ -3604,6 +3602,17 @@
     if (what & layer_state_t::eSidebandStreamChanged) {
         if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
     }
+
+    std::vector<sp<CallbackHandle>> callbackHandles;
+    if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) {
+        mTransactionCompletedThread.run();
+        for (const auto& [listener, callbackIds] : s.listenerCallbacks) {
+            callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
+        }
+    }
+    if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
+    // Do not put anything that updates layer state or modifies flags after
+    // setTransactionCompletedListener
     return flags;
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e449e20..f5218b1 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -62,6 +62,7 @@
 #include "SurfaceFlingerFactory.h"
 #include "SurfaceInterceptor.h"
 #include "SurfaceTracing.h"
+#include "TransactionCompletedThread.h"
 
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/HWComposer.h"
@@ -368,6 +369,10 @@
     inline void onLayerCreated() { mNumLayers++; }
     inline void onLayerDestroyed() { mNumLayers--; }
 
+    TransactionCompletedThread& getTransactionCompletedThread() {
+        return mTransactionCompletedThread;
+    }
+
 private:
     friend class Client;
     friend class DisplayEventConnection;
@@ -716,10 +721,10 @@
     void doDebugFlashRegions(const sp<DisplayDevice>& display, bool repaintEverything);
     void doTracing(const char* where);
     void logLayerStats();
-    void doDisplayComposition(const sp<const DisplayDevice>& display, const Region& dirtyRegion);
+    void doDisplayComposition(const sp<DisplayDevice>& display, const Region& dirtyRegion);
 
     // This fails if using GL and the surface has been destroyed.
-    bool doComposeSurfaces(const sp<const DisplayDevice>& display);
+    bool doComposeSurfaces(const sp<DisplayDevice>& display);
 
     void postFramebuffer(const sp<DisplayDevice>& display);
     void postFrame();
@@ -919,6 +924,8 @@
     bool mUseHwcVirtualDisplays = false;
     std::atomic<uint32_t> mFrameMissedCount{0};
 
+    TransactionCompletedThread mTransactionCompletedThread;
+
     // Restrict layers to use two buffers in their bufferqueues.
     bool mLayerTripleBufferingDisabled = false;
 
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
new file mode 100644
index 0000000..9b9dc57
--- /dev/null
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "TransactionCompletedThread"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "TransactionCompletedThread.h"
+
+#include <cinttypes>
+
+#include <binder/IInterface.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+TransactionCompletedThread::~TransactionCompletedThread() {
+    {
+        std::lock_guard lock(mMutex);
+        mKeepRunning = false;
+        mConditionVariable.notify_all();
+    }
+
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+
+    {
+        std::lock_guard lock(mMutex);
+        for (const auto& [listener, listenerStats] : mListenerStats) {
+            listener->unlinkToDeath(mDeathRecipient);
+        }
+    }
+}
+
+void TransactionCompletedThread::run() {
+    std::lock_guard lock(mMutex);
+    if (mRunning) {
+        return;
+    }
+    mDeathRecipient = new ThreadDeathRecipient();
+    mRunning = true;
+    mThread = std::thread(&TransactionCompletedThread::threadMain, this);
+}
+
+void TransactionCompletedThread::registerPendingLatchedCallbackHandle(
+        const sp<CallbackHandle>& handle) {
+    std::lock_guard lock(mMutex);
+
+    sp<IBinder> listener = IInterface::asBinder(handle->listener);
+    const auto& callbackIds = handle->callbackIds;
+
+    mPendingTransactions[listener][callbackIds]++;
+}
+
+void TransactionCompletedThread::addLatchedCallbackHandles(
+        const std::deque<sp<CallbackHandle>>& handles, nsecs_t latchTime,
+        const sp<Fence>& previousReleaseFence) {
+    std::lock_guard lock(mMutex);
+
+    // If the previous release fences have not signaled, something as probably gone wrong.
+    // Store the fences and check them again before sending a callback.
+    if (previousReleaseFence &&
+        previousReleaseFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+        ALOGD("release fence from the previous frame has not signaled");
+        mPreviousReleaseFences.push_back(previousReleaseFence);
+    }
+
+    for (const auto& handle : handles) {
+        auto listener = mPendingTransactions.find(IInterface::asBinder(handle->listener));
+        auto& pendingCallbacks = listener->second;
+        auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
+
+        if (pendingCallback != pendingCallbacks.end()) {
+            auto& pendingCount = pendingCallback->second;
+
+            // Decrease the pending count for this listener
+            if (--pendingCount == 0) {
+                pendingCallbacks.erase(pendingCallback);
+            }
+        } else {
+            ALOGE("there are more latched callbacks than there were registered callbacks");
+        }
+
+        addCallbackHandle(handle, latchTime);
+    }
+}
+
+void TransactionCompletedThread::addUnlatchedCallbackHandle(const sp<CallbackHandle>& handle) {
+    std::lock_guard lock(mMutex);
+    addCallbackHandle(handle);
+}
+
+void TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle,
+                                                   nsecs_t latchTime) {
+    const sp<IBinder> listener = IInterface::asBinder(handle->listener);
+
+    // If we don't already have a reference to this listener, linkToDeath so we get a notification
+    // if it dies.
+    if (mListenerStats.count(listener) == 0) {
+        status_t error = listener->linkToDeath(mDeathRecipient);
+        if (error != NO_ERROR) {
+            ALOGE("cannot add callback handle because linkToDeath failed, err: %d", error);
+            return;
+        }
+    }
+
+    auto& listenerStats = mListenerStats[listener];
+    listenerStats.listener = handle->listener;
+
+    auto& transactionStats = listenerStats.transactionStats[handle->callbackIds];
+    transactionStats.latchTime = latchTime;
+    transactionStats.surfaceStats.emplace_back(handle->surfaceControl, handle->acquireTime,
+                                               handle->releasePreviousBuffer);
+}
+
+void TransactionCompletedThread::addPresentFence(const sp<Fence>& presentFence) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mPresentFence = presentFence;
+}
+
+void TransactionCompletedThread::sendCallbacks() {
+    std::lock_guard lock(mMutex);
+    if (mRunning) {
+        mConditionVariable.notify_all();
+    }
+}
+
+void TransactionCompletedThread::threadMain() {
+    std::lock_guard lock(mMutex);
+
+    while (mKeepRunning) {
+        mConditionVariable.wait(mMutex);
+
+        // Present fence should fire almost immediately. If the fence has not signaled in 100ms,
+        // there is a major problem and it will probably never fire.
+        nsecs_t presentTime = -1;
+        if (mPresentFence) {
+            status_t status = mPresentFence->wait(100);
+            if (status == NO_ERROR) {
+                presentTime = mPresentFence->getSignalTime();
+            } else {
+                ALOGE("present fence has not signaled, err %d", status);
+            }
+        }
+
+        // We should never hit this case. The release fences from the previous frame should have
+        // signaled long before the current frame is presented.
+        for (const auto& fence : mPreviousReleaseFences) {
+            status_t status = fence->wait(100);
+            if (status != NO_ERROR) {
+                ALOGE("previous release fence has not signaled, err %d", status);
+            }
+        }
+
+        // For each listener
+        auto it = mListenerStats.begin();
+        while (it != mListenerStats.end()) {
+            auto& [listener, listenerStats] = *it;
+
+            // For each transaction
+            bool sendCallback = true;
+            for (auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
+                // If we are still waiting on the callback handles for this transaction, skip it
+                if (mPendingTransactions[listener].count(callbackIds) != 0) {
+                    sendCallback = false;
+                    break;
+                }
+
+                // If the transaction has been latched
+                if (transactionStats.latchTime >= 0) {
+                    // If the present time is < 0, this transaction has been latched but not
+                    // presented. Skip it for now. This can happen when a new transaction comes
+                    // in between the latch and present steps. sendCallbacks is called by
+                    // SurfaceFlinger when the transaction is received to ensure that if the
+                    // transaction that didn't update state it still got a callback.
+                    if (presentTime < 0) {
+                        sendCallback = false;
+                        break;
+                    }
+
+                    transactionStats.presentTime = presentTime;
+                }
+            }
+            // If the listener has no pending transactions and all latched transactions have been
+            // presented
+            if (sendCallback) {
+                // If the listener is still alive
+                if (listener->isBinderAlive()) {
+                    // Send callback
+                    listenerStats.listener->onTransactionCompleted(listenerStats);
+                    listener->unlinkToDeath(mDeathRecipient);
+                }
+                it = mListenerStats.erase(it);
+            } else {
+                it++;
+            }
+        }
+
+        if (mPresentFence) {
+            mPresentFence.clear();
+            mPreviousReleaseFences.clear();
+        }
+    }
+}
+
+// -----------------------------------------------------------------------
+
+CallbackHandle::CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
+                               const std::vector<CallbackId>& ids, const sp<IBinder>& sc)
+      : listener(transactionListener), callbackIds(ids), surfaceControl(sc) {}
+
+} // namespace android
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
new file mode 100644
index 0000000..5af4097
--- /dev/null
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <deque>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+
+#include <android-base/thread_annotations.h>
+
+#include <binder/IBinder.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <ui/Fence.h>
+
+namespace android {
+
+class CallbackHandle : public RefBase {
+public:
+    CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
+                   const std::vector<CallbackId>& ids, const sp<IBinder>& sc);
+
+    sp<ITransactionCompletedListener> listener;
+    std::vector<CallbackId> callbackIds;
+    sp<IBinder> surfaceControl;
+
+    bool releasePreviousBuffer = false;
+    nsecs_t acquireTime = -1;
+};
+
+class TransactionCompletedThread {
+public:
+    ~TransactionCompletedThread();
+
+    void run();
+
+    // Informs the TransactionCompletedThread that there is a Transaction with a CallbackHandle
+    // that needs to be latched and presented this frame. This function should be called once the
+    // layer has received the CallbackHandle so the TransactionCompletedThread knows not to send
+    // a callback for that Listener/Transaction pair until that CallbackHandle has been latched and
+    // presented.
+    void registerPendingLatchedCallbackHandle(const sp<CallbackHandle>& handle);
+    // Notifies the TransactionCompletedThread that a pending CallbackHandle has been latched.
+    void addLatchedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles, nsecs_t latchTime,
+                                   const sp<Fence>& previousReleaseFence);
+
+    // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
+    // presented this frame.
+    void addUnlatchedCallbackHandle(const sp<CallbackHandle>& handle);
+
+    void addPresentFence(const sp<Fence>& presentFence);
+
+    void sendCallbacks();
+
+private:
+    void threadMain();
+
+    void addCallbackHandle(const sp<CallbackHandle>& handle, nsecs_t latchTime = -1)
+            REQUIRES(mMutex);
+
+    class ThreadDeathRecipient : public IBinder::DeathRecipient {
+    public:
+        // This function is a no-op. isBinderAlive needs a linked DeathRecipient to work.
+        // Death recipients needs a binderDied function.
+        //
+        // (isBinderAlive checks if BpBinder's mAlive is 0. mAlive is only set to 0 in sendObituary.
+        // sendObituary is only called if linkToDeath was called with a DeathRecipient.)
+        void binderDied(const wp<IBinder>& /*who*/) override {}
+    };
+    sp<ThreadDeathRecipient> mDeathRecipient;
+
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& strongPointer) const {
+            return std::hash<IBinder*>{}(strongPointer.get());
+        }
+    };
+
+    std::thread mThread;
+
+    std::mutex mMutex;
+    std::condition_variable_any mConditionVariable;
+
+    std::unordered_map<
+            sp<IBinder /*listener*/>,
+            std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>,
+            IBinderHash>
+            mPendingTransactions GUARDED_BY(mMutex);
+    std::unordered_map<sp<IBinder /*listener*/>, ListenerStats, IBinderHash> mListenerStats
+            GUARDED_BY(mMutex);
+
+    bool mRunning GUARDED_BY(mMutex) = false;
+    bool mKeepRunning GUARDED_BY(mMutex) = true;
+
+    sp<Fence> mPresentFence GUARDED_BY(mMutex);
+    std::vector<sp<Fence>> mPreviousReleaseFences GUARDED_BY(mMutex);
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index 8bd5fcb..91999ae 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
 {
         "presubmit": {
-            "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*"
+            "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*"
         }
 }
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 94b33ac..988454a 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -15,9 +15,12 @@
  */
 
 #include <algorithm>
+#include <chrono>
+#include <cinttypes>
 #include <functional>
 #include <limits>
 #include <ostream>
+#include <thread>
 
 #include <gtest/gtest.h>
 
@@ -64,6 +67,7 @@
 const Color Color::TRANSPARENT{0, 0, 0, 0};
 
 using android::hardware::graphics::common::V1_1::BufferUsage;
+using namespace std::chrono_literals;
 
 std::ostream& operator<<(std::ostream& os, const Color& color) {
     os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a);
@@ -327,16 +331,21 @@
         mClient = 0;
     }
 
-    virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
+    virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
+                                           const char* name, uint32_t width, uint32_t height,
                                            uint32_t flags = 0) {
         auto layer =
-                mClient->createSurface(String8(name), width, height, PIXEL_FORMAT_RGBA_8888, flags);
+                client->createSurface(String8(name), width, height, PIXEL_FORMAT_RGBA_8888, flags);
         EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
 
-        status_t error = Transaction()
-                                 .setLayerStack(layer, mDisplayLayerStack)
-                                 .setLayer(layer, mLayerZBase)
-                                 .apply();
+        Transaction t;
+        t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
+        // If we are creating a color layer, set its crop since its size will be ignored.
+        if (flags == ISurfaceComposerClient::eFXSurfaceColor) {
+            t.setCrop_legacy(layer, Rect(0, 0, width, height));
+        }
+
+        status_t error = t.apply();
         if (error != NO_ERROR) {
             ADD_FAILURE() << "failed to initialize SurfaceControl";
             layer.clear();
@@ -345,6 +354,11 @@
         return layer;
     }
 
+    virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
+                                           uint32_t flags = 0) {
+        return createLayer(mClient, name, width, height, flags);
+    }
+
     ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
         // wait for previous transactions (such as setSize) to complete
         Transaction().apply(true);
@@ -502,6 +516,7 @@
         // set layer stack (b/68888219)
         Transaction t;
         t.setDisplayLayerStack(mDisplay, mDisplayLayerStack);
+        t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
         t.setLayerStack(mBlackBgSurface, mDisplayLayerStack);
         t.setColor(mBlackBgSurface, half3{0, 0, 0});
         t.setLayer(mBlackBgSurface, mLayerZBase);
@@ -1953,7 +1968,7 @@
                               "test");
     fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
-    sp<Fence> fence = new Fence(-1);
+    sp<Fence> fence = Fence::NO_FENCE;
 
     Transaction()
             .setBuffer(layer, buffer)
@@ -2156,6 +2171,627 @@
     }
 }
 
+class ExpectedResult {
+public:
+    enum Transaction {
+        NOT_PRESENTED = 0,
+        PRESENTED,
+    };
+
+    enum Buffer {
+        NOT_ACQUIRED = 0,
+        ACQUIRED,
+    };
+
+    enum PreviousBuffer {
+        NOT_RELEASED = 0,
+        RELEASED,
+    };
+
+    void reset() {
+        mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+        mExpectedSurfaceResults.clear();
+    }
+
+    void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer,
+                    ExpectedResult::Buffer bufferResult = NOT_ACQUIRED,
+                    ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
+        mTransactionResult = transactionResult;
+        mExpectedSurfaceResults.emplace(std::piecewise_construct,
+                                        std::forward_as_tuple(layer->getHandle()),
+                                        std::forward_as_tuple(bufferResult, previousBufferResult));
+    }
+
+    void addSurfaces(ExpectedResult::Transaction transactionResult,
+                     const std::vector<sp<SurfaceControl>>& layers,
+                     ExpectedResult::Buffer bufferResult = NOT_ACQUIRED,
+                     ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
+        for (const auto& layer : layers) {
+            addSurface(transactionResult, layer, bufferResult, previousBufferResult);
+        }
+    }
+
+    void verifyTransactionStats(const TransactionStats& transactionStats) const {
+        const auto& [latchTime, presentTime, surfaceStats] = transactionStats;
+        if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
+            ASSERT_GE(latchTime, 0) << "bad latch time";
+            ASSERT_GE(presentTime, 0) << "bad present time";
+        } else {
+            ASSERT_EQ(presentTime, -1) << "transaction shouldn't have been presented";
+            ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
+        }
+
+        ASSERT_EQ(surfaceStats.size(), mExpectedSurfaceResults.size())
+                << "wrong number of surfaces";
+
+        for (const auto& stats : surfaceStats) {
+            const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl);
+            ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end())
+                    << "unexpected surface control";
+            expectedSurfaceResult->second.verifySurfaceStats(stats, latchTime);
+        }
+    }
+
+private:
+    class ExpectedSurfaceResult {
+    public:
+        ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult,
+                              ExpectedResult::PreviousBuffer previousBufferResult)
+              : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {}
+
+        void verifySurfaceStats(const SurfaceStats& surfaceStats, nsecs_t latchTime) const {
+            const auto& [surfaceControl, acquireTime, releasePreviousBuffer] = surfaceStats;
+
+            ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
+                    << "bad acquire time";
+            ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time";
+            ASSERT_EQ(releasePreviousBuffer,
+                      mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED)
+                    << "bad previous buffer released";
+        }
+
+    private:
+        ExpectedResult::Buffer mBufferResult;
+        ExpectedResult::PreviousBuffer mPreviousBufferResult;
+    };
+
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& strongPointer) const {
+            return std::hash<IBinder*>{}(strongPointer.get());
+        }
+    };
+    ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+    std::unordered_map<sp<IBinder>, ExpectedSurfaceResult, IBinderHash> mExpectedSurfaceResults;
+};
+
+class CallbackHelper {
+public:
+    static void function(void* callbackContext, const TransactionStats& transactionStats) {
+        if (!callbackContext) {
+            ALOGE("failed to get callback context");
+        }
+        CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext);
+        std::lock_guard lock(helper->mMutex);
+        helper->mTransactionStatsQueue.push(transactionStats);
+        helper->mConditionVariable.notify_all();
+    }
+
+    void getTransactionStats(TransactionStats* outStats) {
+        std::unique_lock lock(mMutex);
+
+        if (mTransactionStatsQueue.empty()) {
+            ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)),
+                      std::cv_status::timeout)
+                    << "did not receive callback";
+        }
+
+        *outStats = std::move(mTransactionStatsQueue.front());
+        mTransactionStatsQueue.pop();
+    }
+
+    void verifyFinalState() {
+        // Wait to see if there are extra callbacks
+        std::this_thread::sleep_for(500ms);
+
+        std::lock_guard lock(mMutex);
+        EXPECT_EQ(mTransactionStatsQueue.size(), 0) << "extra callbacks received";
+        mTransactionStatsQueue = {};
+    }
+
+    void* getContext() { return static_cast<void*>(this); }
+
+    std::mutex mMutex;
+    std::condition_variable mConditionVariable;
+    std::queue<TransactionStats> mTransactionStatsQueue;
+};
+
+class LayerCallbackTest : public LayerTransactionTest {
+protected:
+    virtual sp<SurfaceControl> createBufferStateLayer() {
+        return createLayer(mClient, "test", mWidth, mHeight,
+                           ISurfaceComposerClient::eFXSurfaceBufferState);
+    }
+
+    virtual void fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper,
+                                 const sp<SurfaceControl>& layer = nullptr) {
+        if (layer) {
+            sp<GraphicBuffer> buffer =
+                    new GraphicBuffer(mWidth, mHeight, PIXEL_FORMAT_RGBA_8888, 1,
+                                      BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                              BufferUsage::COMPOSER_OVERLAY |
+                                              BufferUsage::GPU_TEXTURE,
+                                      "test");
+            fillGraphicBufferColor(buffer, Rect(0, 0, mWidth, mHeight), Color::RED);
+
+            sp<Fence> fence = new Fence(-1);
+
+            transaction.setBuffer(layer, buffer)
+                    .setAcquireFence(layer, fence)
+                    .setSize(layer, mWidth, mHeight);
+        }
+
+        transaction.addTransactionCompletedCallback(callbackHelper->function,
+                                                    callbackHelper->getContext());
+    }
+
+    void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+                         bool finalState = false) {
+        TransactionStats transactionStats;
+        ASSERT_NO_FATAL_FAILURE(helper.getTransactionStats(&transactionStats));
+        EXPECT_NO_FATAL_FAILURE(expectedResult.verifyTransactionStats(transactionStats));
+
+        if (finalState) {
+            ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+        }
+    }
+
+    void waitForCallbacks(CallbackHelper& helper,
+                          const std::vector<ExpectedResult>& expectedResults,
+                          bool finalState = false) {
+        for (const auto& expectedResult : expectedResults) {
+            waitForCallback(helper, expectedResult);
+        }
+        if (finalState) {
+            ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+        }
+    }
+
+    uint32_t mWidth = 32;
+    uint32_t mHeight = 32;
+};
+
+TEST_F(LayerCallbackTest, Basic) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    fillTransaction(transaction, &callback, layer);
+
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoBuffer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    fillTransaction(transaction, &callback);
+
+    transaction.setPosition(layer, mWidth, mHeight).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoStateChange) {
+    Transaction transaction;
+    CallbackHelper callback;
+    fillTransaction(transaction, &callback);
+
+    transaction.apply();
+
+    ExpectedResult expected;
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, OffScreen) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    fillTransaction(transaction, &callback, layer);
+
+    transaction.setPosition(layer, -100, -100).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    fillTransaction(transaction1, &callback1, layer1);
+    fillTransaction(transaction2, &callback2, layer2);
+
+    transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_SameCallback) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback;
+    fillTransaction(transaction1, &callback, layer1);
+    fillTransaction(transaction2, &callback, layer2);
+
+    transaction2.merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_SameLayer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    fillTransaction(transaction1, &callback1, layer);
+    fillTransaction(transaction2, &callback2, layer);
+
+    transaction2.merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_SingleBuffer) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    fillTransaction(transaction1, &callback1, layer1);
+    fillTransaction(transaction2, &callback2);
+
+    transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_DifferentClients) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    fillTransaction(transaction1, &callback1, layer1);
+    fillTransaction(transaction2, &callback2, layer2);
+
+    transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    for (size_t i = 0; i < 10; i++) {
+        fillTransaction(transaction, &callback, layer);
+
+        transaction.apply();
+
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED,
+                            (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+                                     : ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    for (size_t i = 0; i < 10; i++) {
+        ExpectedResult expected;
+
+        if (i == 0) {
+            fillTransaction(transaction, &callback, layer);
+            expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+        } else {
+            fillTransaction(transaction, &callback);
+        }
+
+        transaction.apply();
+
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    for (size_t i = 0; i < 10; i++) {
+        if (i == 0) {
+            fillTransaction(transaction, &callback, layer);
+        } else {
+            fillTransaction(transaction, &callback);
+        }
+
+        transaction.setPosition(layer, mWidth, mHeight).apply();
+
+        ExpectedResult expected;
+        expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED
+                                     : ExpectedResult::Transaction::NOT_PRESENTED,
+                            layer);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, i == 0));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    for (size_t i = 0; i < 10; i++) {
+        fillTransaction(transaction1, &callback1, layer1);
+        fillTransaction(transaction2, &callback2, layer2);
+
+        transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+        ExpectedResult expected;
+        expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+                             ExpectedResult::Buffer::NOT_ACQUIRED,
+                             (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+                                      : ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
+    ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    for (size_t i = 0; i < 10; i++) {
+        fillTransaction(transaction1, &callback1, layer1);
+        fillTransaction(transaction2, &callback2, layer2);
+
+        transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+        ExpectedResult expected;
+        expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+                             ExpectedResult::Buffer::NOT_ACQUIRED,
+                             (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+                                      : ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
+    ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_NoStateChange) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+
+    // Normal call to set up test
+    fillTransaction(transaction1, &callback1, layer1);
+    fillTransaction(transaction2, &callback2, layer2);
+
+    transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+    expected.reset();
+
+    // Test
+    fillTransaction(transaction1, &callback1);
+    fillTransaction(transaction2, &callback2);
+
+    transaction2.merge(std::move(transaction1)).apply();
+
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateChange) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+
+    // Normal call to set up test
+    fillTransaction(transaction1, &callback1, layer1);
+    fillTransaction(transaction2, &callback2, layer2);
+
+    transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+    expected.reset();
+
+    // Test
+    fillTransaction(transaction1, &callback1);
+    fillTransaction(transaction2, &callback2);
+
+    transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    std::vector<ExpectedResult> expectedResults(50);
+    ExpectedResult::PreviousBuffer previousBufferResult =
+            ExpectedResult::PreviousBuffer::NOT_RELEASED;
+    for (auto& expected : expectedResults) {
+        expected.reset();
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED, previousBufferResult);
+        previousBufferResult = ExpectedResult::PreviousBuffer::RELEASED;
+
+        fillTransaction(transaction, &callback, layer);
+
+        transaction.apply();
+        std::this_thread::sleep_for(200ms);
+    }
+    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    std::vector<ExpectedResult> expectedResults(50);
+    bool first = true;
+    for (auto& expected : expectedResults) {
+        expected.reset();
+
+        if (first) {
+            fillTransaction(transaction, &callback, layer);
+            expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+            first = false;
+        } else {
+            fillTransaction(transaction, &callback);
+        }
+
+        transaction.apply();
+        std::this_thread::sleep_for(200ms);
+    }
+    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    // Normal call to set up test
+    Transaction transaction;
+    CallbackHelper callback;
+    fillTransaction(transaction, &callback, layer);
+
+    transaction.setPosition(layer, mWidth, mHeight).apply();
+
+    ExpectedResult expectedResult;
+    expectedResult.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expectedResult, true));
+
+    // Test
+    std::vector<ExpectedResult> expectedResults(50);
+    for (auto& expected : expectedResults) {
+        expected.reset();
+        expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer);
+
+        fillTransaction(transaction, &callback);
+
+        transaction.setPosition(layer, mWidth, mHeight).apply();
+
+        std::this_thread::sleep_for(200ms);
+    }
+    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
 class LayerUpdateTest : public LayerTransactionTest {
 protected:
     virtual void SetUp() {
@@ -2394,17 +3030,19 @@
             mClient->createSurface(String8("Buffered child"), 20, 20,
                                            PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
     fillSurfaceRGBA8(childBuffer, 200, 200, 200);
-
-    SurfaceComposerClient::Transaction{}.show(childNoBuffer).show(childBuffer).apply(true);
-
+    SurfaceComposerClient::Transaction{}
+            .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
+            .show(childNoBuffer)
+            .show(childBuffer)
+            .apply(true);
     {
         ScreenCapture::captureScreen(&sc);
         sc->expectChildColor(73, 73);
         sc->expectFGColor(74, 74);
     }
-
-    SurfaceComposerClient::Transaction{}.setSize(childNoBuffer, 20, 20).apply(true);
-
+    SurfaceComposerClient::Transaction{}
+            .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
+            .apply(true);
     {
         ScreenCapture::captureScreen(&sc);
         sc->expectChildColor(73, 73);
@@ -2939,6 +3577,221 @@
         mCapture->checkPixel(10, 10, 255, 255, 255);
     }
 }
+class BoundlessLayerTest : public LayerUpdateTest {
+protected:
+    std::unique_ptr<ScreenCapture> mCapture;
+};
+
+// Verify setting a size on a buffer layer has no effect.
+TEST_F(BoundlessLayerTest, BufferLayerIgnoresSize) {
+    sp<SurfaceControl> bufferLayer =
+            mClient->createSurface(String8("BufferLayer"), 45, 45, PIXEL_FORMAT_RGBA_8888, 0,
+                                   mFGSurfaceControl.get());
+    ASSERT_TRUE(bufferLayer != nullptr);
+    ASSERT_TRUE(bufferLayer->isValid());
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::BLACK, 30, 30));
+    asTransaction([&](Transaction& t) { t.show(bufferLayer); });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 94, 94), Color::BLACK);
+        // Buffer layer should not extend past buffer bounds
+        mCapture->expectFGColor(95, 95);
+    }
+}
+
+// Verify a boundless color layer will fill its parent bounds. The parent has a buffer size
+// which will crop the color layer.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentBufferBounds) {
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceColor,
+                                   mFGSurfaceControl.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify a boundless color layer will fill its parent bounds. The parent has no buffer but has
+// a crop which will be used to crop the color layer.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentCropBounds) {
+    sp<SurfaceControl> cropLayer =
+            mClient->createSurface(String8("CropLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   0 /* flags */, mFGSurfaceControl.get());
+    ASSERT_TRUE(cropLayer != nullptr);
+    ASSERT_TRUE(cropLayer->isValid());
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceColor, cropLayer.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(cropLayer);
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // 5 pixels from the foreground we should see the child surface
+        mCapture->expectColor(Rect(69, 69, 74, 74), Color::BLACK);
+        // 10 pixels from the foreground we should be back to the foreground surface
+        mCapture->expectFGColor(74, 74);
+    }
+}
+
+// Verify for boundless layer with no children, their transforms have no effect.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerTransformHasNoEffect) {
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceColor,
+                                   mFGSurfaceControl.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setPosition(colorLayer, 320, 320);
+        t.setMatrix(colorLayer, 2, 0, 0, 2);
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify for boundless layer with children, their transforms have an effect.
+TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerCanSetTransform) {
+    sp<SurfaceControl> boundlessLayerRightShift =
+            mClient->createSurface(String8("BoundlessLayerRightShift"), 0, 0,
+                                   PIXEL_FORMAT_RGBA_8888, 0 /* flags */, mFGSurfaceControl.get());
+    ASSERT_TRUE(boundlessLayerRightShift != nullptr);
+    ASSERT_TRUE(boundlessLayerRightShift->isValid());
+    sp<SurfaceControl> boundlessLayerDownShift =
+            mClient->createSurface(String8("BoundlessLayerLeftShift"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   0 /* flags */, boundlessLayerRightShift.get());
+    ASSERT_TRUE(boundlessLayerDownShift != nullptr);
+    ASSERT_TRUE(boundlessLayerDownShift->isValid());
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceColor,
+                                   boundlessLayerDownShift.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setPosition(boundlessLayerRightShift, 32, 0);
+        t.show(boundlessLayerRightShift);
+        t.setPosition(boundlessLayerDownShift, 0, 32);
+        t.show(boundlessLayerDownShift);
+        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(96, 96, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify child layers do not get clipped if they temporarily move into the negative
+// coordinate space as the result of an intermediate transformation.
+TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) {
+    sp<SurfaceControl> boundlessLayer =
+            mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   0 /* flags */, mFGSurfaceControl.get());
+    ASSERT_TRUE(boundlessLayer != nullptr);
+    ASSERT_TRUE(boundlessLayer->isValid());
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceColor, boundlessLayer.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        // shift child layer off bounds. If this layer was not boundless, we will
+        // expect the child layer to be cropped.
+        t.setPosition(boundlessLayer, 32, 32);
+        t.show(boundlessLayer);
+        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        // undo shift by parent
+        t.setPosition(colorLayer, -32, -32);
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify for boundless root layers with children, their transforms have an effect.
+TEST_F(BoundlessLayerTest, RootBoundlessLayerCanSetTransform) {
+    sp<SurfaceControl> rootBoundlessLayer =
+            mClient->createSurface(String8("RootBoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   0 /* flags */);
+    ASSERT_TRUE(rootBoundlessLayer != nullptr);
+    ASSERT_TRUE(rootBoundlessLayer->isValid());
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceColor,
+                                   rootBoundlessLayer.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
+        t.setPosition(rootBoundlessLayer, 32, 32);
+        t.show(rootBoundlessLayer);
+        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+        t.hide(mFGSurfaceControl);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Top left of foreground must now be visible
+        mCapture->expectBGColor(31, 31);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(32, 32, 96, 96), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(97, 97);
+    }
+}
 
 class ScreenCaptureTest : public LayerUpdateTest {
 protected:
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index de6420e..cfaf495 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -37,6 +37,7 @@
 #include "mock/MockEventThread.h"
 #include "mock/MockMessageQueue.h"
 #include "mock/RenderEngine/MockRenderEngine.h"
+#include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
 namespace {
@@ -45,6 +46,7 @@
 using testing::AtLeast;
 using testing::ByMove;
 using testing::DoAll;
+using testing::Invoke;
 using testing::IsNull;
 using testing::Mock;
 using testing::NotNull;
@@ -92,6 +94,10 @@
         EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
         EXPECT_CALL(*mPrimaryDispSync, getPeriod())
                 .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
 
         mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
         setupComposer(0);
@@ -138,7 +144,10 @@
     sp<DisplayDevice> mDisplay;
     sp<DisplayDevice> mExternalDisplay;
     sp<mock::DisplaySurface> mDisplaySurface = new mock::DisplaySurface();
-    renderengine::mock::Surface* mRenderSurface = new renderengine::mock::Surface();
+    mock::NativeWindow* mNativeWindow = new mock::NativeWindow();
+
+    sp<GraphicBuffer> mBuffer = new GraphicBuffer();
+    ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer();
 
     mock::EventThread* mEventThread = new mock::EventThread();
     mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
@@ -227,6 +236,11 @@
     static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_NORMAL;
 
     static void setupPreconditions(CompositionTest* test) {
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+
         FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, HWC2::DisplayType::Physical,
                                true /* isPrimary */)
                 .setCapabilities(&test->mDefaultCapabilities)
@@ -234,10 +248,8 @@
 
         test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, DEFAULT_DISPLAY_ID,
                                                    false /* isVirtual */, true /* isPrimary */)
-                                 .setDisplaySize(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT)
                                  .setDisplaySurface(test->mDisplaySurface)
-                                 .setRenderSurface(std::unique_ptr<renderengine::Surface>(
-                                         test->mRenderSurface))
+                                 .setNativeWindow(test->mNativeWindow)
                                  .setSecure(Derived::IS_SECURE)
                                  .setPowerMode(Derived::INIT_POWER_MODE)
                                  .inject();
@@ -259,13 +271,9 @@
         EXPECT_CALL(*test->mRenderEngine, checkErrors()).WillRepeatedly(Return());
         EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true));
 
-        EXPECT_CALL(*test->mRenderEngine, setCurrentSurface(Ref(*test->mRenderSurface)))
-                .WillOnce(Return(true));
-        EXPECT_CALL(*test->mRenderEngine,
-                    setViewportAndProjection(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
-                                             Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                             ui::Transform::ROT_0))
-                .Times(1);
+        EXPECT_CALL(*test->mRenderEngine, flush()).WillRepeatedly(Invoke([]() {
+            return base::unique_fd(0);
+        }));
 
         EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1);
         EXPECT_CALL(*test->mDisplaySurface, advanceFrame()).Times(1);
@@ -316,8 +324,6 @@
 
     static void setupHwcCompositionCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC)).Times(1);
-
-        EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1);
     }
 
     static void setupRECompositionCallExpectations(CompositionTest* test) {
@@ -337,12 +343,18 @@
                     setViewportAndProjection(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
                                              Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                                              ui::Transform::ROT_0))
-                .Times(1)
-                .RetiresOnSaturation();
-        EXPECT_CALL(*test->mRenderEngine, setCurrentSurface(Ref(*test->mRenderSurface)))
-                .WillOnce(Return(true))
-                .RetiresOnSaturation();
-        EXPECT_CALL(*test->mRenderSurface, swapBuffers()).Times(1);
+                .Times(1);
+        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull())).WillOnce(Return(true));
+        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull())).WillOnce(Return(true));
+        EXPECT_CALL(*test->mRenderEngine, createFramebuffer())
+                .WillOnce(Return(
+                        ByMove(std::unique_ptr<renderengine::Framebuffer>(test->mReFrameBuffer))));
+        EXPECT_CALL(*test->mRenderEngine, bindFrameBuffer(test->mReFrameBuffer)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, unbindFrameBuffer(test->mReFrameBuffer)).Times(1);
+        EXPECT_CALL(*test->mNativeWindow, queueBuffer(_, _)).WillOnce(Return(0));
+        EXPECT_CALL(*test->mNativeWindow, dequeueBuffer(_, _))
+                .WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1),
+                                Return(0)));
     }
 
     template <typename Case>
@@ -783,6 +795,9 @@
                                                     LayerProperties::HEIGHT,
                                                     LayerProperties::LAYER_FLAGS));
         });
+
+        auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
+        layerDrawingState.crop_legacy = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
         return layer;
     }
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 9d17ef4..ac6a78b 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -119,6 +119,8 @@
     TestableSurfaceFlinger mFlinger;
     mock::EventThread* mEventThread = new mock::EventThread();
     mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
+    sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
+    sp<GraphicBuffer> mBuffer = new GraphicBuffer();
 
     // These mocks are created by the test, but are destroyed by SurfaceFlinger
     // by virtue of being stored into a std::unique_ptr. However we still need
@@ -133,8 +135,6 @@
     sp<mock::GraphicBufferConsumer> mConsumer;
     sp<mock::GraphicBufferProducer> mProducer;
     surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
-    sp<mock::NativeWindow> mNativeWindow;
-    renderengine::mock::Surface* mRenderSurface = nullptr;
 };
 
 DisplayTransactionTest::DisplayTransactionTest() {
@@ -203,7 +203,6 @@
     ASSERT_TRUE(mNativeWindowSurface == nullptr);
 
     mNativeWindowSurface = new surfaceflinger::mock::NativeWindowSurface();
-    mNativeWindow = new mock::NativeWindow();
 
     mFlinger.setCreateNativeWindowSurface([this](auto) {
         return std::unique_ptr<surfaceflinger::NativeWindowSurface>(mNativeWindowSurface);
@@ -326,6 +325,14 @@
                                           static_cast<bool>(VIRTUAL), static_cast<bool>(PRIMARY));
 
         injector.setSecure(static_cast<bool>(SECURE));
+        injector.setNativeWindow(test->mNativeWindow);
+
+        // Creating a DisplayDevice requires getting default dimensions from the
+        // native window.
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
         return injector;
     }
 
@@ -333,20 +340,11 @@
     static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow())
                 .WillOnce(Return(test->mNativeWindow));
-        EXPECT_CALL(*test->mNativeWindow, perform(19)).WillRepeatedly(Return(NO_ERROR));
 
-        // For simplicity, we only expect to create a single render surface for
-        // each test.
-        ASSERT_TRUE(test->mRenderSurface == nullptr);
-        test->mRenderSurface = new renderengine::mock::Surface();
-        EXPECT_CALL(*test->mRenderEngine, createSurface())
-                .WillOnce(Return(ByMove(
-                        std::unique_ptr<renderengine::Surface>(test->mRenderSurface))));
-        EXPECT_CALL(*test->mRenderSurface, setAsync(static_cast<bool>(ASYNC))).Times(1);
-        EXPECT_CALL(*test->mRenderSurface, setCritical(static_cast<bool>(CRITICAL))).Times(1);
-        EXPECT_CALL(*test->mRenderSurface, setNativeWindow(test->mNativeWindow.get())).Times(1);
-        EXPECT_CALL(*test->mRenderSurface, getWidth()).WillOnce(Return(WIDTH));
-        EXPECT_CALL(*test->mRenderSurface, getHeight()).WillOnce(Return(HEIGHT));
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
     }
 
     static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
@@ -1057,9 +1055,6 @@
     // The call disable vsyncs
     EXPECT_CALL(*mEventControlThread, setVsyncEnabled(false)).Times(1);
 
-    // The call clears the current render engine surface
-    EXPECT_CALL(*mRenderEngine, resetCurrentSurface());
-
     // The call ends any display resyncs
     EXPECT_CALL(*mPrimaryDispSync, endResync()).Times(1);
 
@@ -1111,6 +1106,15 @@
     void getBestColorMode() {
         mInjector.setHwcColorModes(mHwcColorModes);
         mInjector.setHasWideColorGamut(mHasWideColorGamut);
+        mInjector.setNativeWindow(mNativeWindow);
+
+        // Creating a DisplayDevice requires getting default dimensions from the
+        // native window.
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(1080 /* arbitrary */), Return(0)));
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(1920 /* arbitrary */), Return(0)));
+        EXPECT_CALL(*mNativeWindow, perform(13)).Times(1);
         auto displayDevice = mInjector.inject();
 
         displayDevice->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
@@ -1704,7 +1708,6 @@
 
     EXPECT_CALL(*surface, setAsyncMode(true)).Times(1);
 
-    EXPECT_CALL(*mProducer, connect(_, _, _, _)).Times(1);
     EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1);
 
     Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
@@ -1928,11 +1931,16 @@
     // A display is set up
     auto nativeWindow = new mock::NativeWindow();
     auto displaySurface = new mock::DisplaySurface();
-    auto renderSurface = new renderengine::mock::Surface();
+    sp<GraphicBuffer> buf = new GraphicBuffer();
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.setNativeWindow(nativeWindow);
     display.setDisplaySurface(displaySurface);
-    display.setRenderSurface(std::unique_ptr<renderengine::Surface>(renderSurface));
+    // Setup injection expections
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+            .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+            .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+    EXPECT_CALL(*nativeWindow, perform(13)).Times(1);
     display.inject();
 
     // There is a change to the viewport state
@@ -1944,11 +1952,7 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    EXPECT_CALL(*renderSurface, setNativeWindow(nullptr)).Times(1);
     EXPECT_CALL(*displaySurface, resizeBuffers(newWidth, oldHeight)).Times(1);
-    EXPECT_CALL(*renderSurface, setNativeWindow(nativeWindow)).Times(1);
-    EXPECT_CALL(*renderSurface, getWidth()).WillOnce(Return(newWidth));
-    EXPECT_CALL(*renderSurface, getHeight()).WillOnce(Return(oldHeight));
 
     // --------------------------------------------------------------------
     // Invocation
@@ -1969,11 +1973,16 @@
     // A display is set up
     auto nativeWindow = new mock::NativeWindow();
     auto displaySurface = new mock::DisplaySurface();
-    auto renderSurface = new renderengine::mock::Surface();
+    sp<GraphicBuffer> buf = new GraphicBuffer();
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.setNativeWindow(nativeWindow);
     display.setDisplaySurface(displaySurface);
-    display.setRenderSurface(std::unique_ptr<renderengine::Surface>(renderSurface));
+    // Setup injection expections
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+            .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+            .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+    EXPECT_CALL(*nativeWindow, perform(13)).Times(1);
     display.inject();
 
     // There is a change to the viewport state
@@ -1985,11 +1994,7 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    EXPECT_CALL(*renderSurface, setNativeWindow(nullptr)).Times(1);
     EXPECT_CALL(*displaySurface, resizeBuffers(oldWidth, newHeight)).Times(1);
-    EXPECT_CALL(*renderSurface, setNativeWindow(nativeWindow)).Times(1);
-    EXPECT_CALL(*renderSurface, getWidth()).WillOnce(Return(oldWidth));
-    EXPECT_CALL(*renderSurface, getHeight()).WillOnce(Return(newHeight));
 
     // --------------------------------------------------------------------
     // Invocation
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index b046e4a..85c835e 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -175,6 +175,7 @@
     auto& mutableLayerDrawingState(sp<Layer> layer) { return layer->mDrawingState; }
 
     void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) {
+        layer->mDrawingState.sidebandStream = sidebandStream;
         layer->getBE().compositionInfo.hwc.sidebandStream = sidebandStream;
     }
 
@@ -473,22 +474,11 @@
             return *this;
         }
 
-        auto& setRenderSurface(std::unique_ptr<renderengine::Surface> renderSurface) {
-            mCreationArgs.renderSurface = std::move(renderSurface);
-            return *this;
-        }
-
         auto& setSecure(bool secure) {
             mCreationArgs.isSecure = secure;
             return *this;
         }
 
-        auto& setDisplaySize(int width, int height) {
-            mCreationArgs.displayWidth = width;
-            mCreationArgs.displayHeight = height;
-            return *this;
-        }
-
         auto& setPowerMode(int mode) {
             mCreationArgs.initialPowerMode = mode;
             return *this;
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
index af54df6..fbfbc3f 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
@@ -26,9 +26,6 @@
 RenderEngine::RenderEngine() = default;
 RenderEngine::~RenderEngine() = default;
 
-Surface::Surface() = default;
-Surface::~Surface() = default;
-
 Image::Image() = default;
 Image::~Image() = default;
 
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
index afca63a..90c3c20 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
+++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
@@ -23,7 +23,6 @@
 #include <renderengine/LayerSettings.h>
 #include <renderengine/Mesh.h>
 #include <renderengine/RenderEngine.h>
-#include <renderengine/Surface.h>
 #include <renderengine/Texture.h>
 #include <ui/GraphicBuffer.h>
 
@@ -37,15 +36,12 @@
     ~RenderEngine() override;
 
     MOCK_METHOD0(createFramebuffer, std::unique_ptr<Framebuffer>());
-    MOCK_METHOD0(createSurface, std::unique_ptr<renderengine::Surface>());
     MOCK_METHOD0(createImage, std::unique_ptr<renderengine::Image>());
     MOCK_CONST_METHOD0(primeCache, void());
     MOCK_METHOD1(dump, void(String8&));
     MOCK_CONST_METHOD0(useNativeFenceSync, bool());
     MOCK_CONST_METHOD0(useWaitSync, bool());
     MOCK_CONST_METHOD0(isCurrent, bool());
-    MOCK_METHOD1(setCurrentSurface, bool(const renderengine::Surface&));
-    MOCK_METHOD0(resetCurrentSurface, void());
     MOCK_METHOD0(flush, base::unique_fd());
     MOCK_METHOD0(finish, bool());
     MOCK_METHOD1(waitFence, bool(base::unique_fd*));
@@ -82,23 +78,6 @@
                                 ANativeWindowBuffer* const, base::unique_fd*));
 };
 
-class Surface : public renderengine::Surface {
-public:
-    Surface();
-    ~Surface() override;
-
-    MOCK_METHOD1(setCritical, void(bool));
-    MOCK_METHOD1(setAsync, void(bool));
-    MOCK_METHOD1(setNativeWindow, void(ANativeWindow*));
-    MOCK_CONST_METHOD0(swapBuffers, void());
-    MOCK_CONST_METHOD0(queryRedSize, int32_t());
-    MOCK_CONST_METHOD0(queryGreenSize, int32_t());
-    MOCK_CONST_METHOD0(queryBlueSize, int32_t());
-    MOCK_CONST_METHOD0(queryAlphaSize, int32_t());
-    MOCK_CONST_METHOD0(getWidth, int32_t());
-    MOCK_CONST_METHOD0(getHeight, int32_t());
-};
-
 class Image : public renderengine::Image {
 public:
     Image();
diff --git a/services/surfaceflinger/tests/unittests/mock/system/window/MockNativeWindow.h b/services/surfaceflinger/tests/unittests/mock/system/window/MockNativeWindow.h
index 561fd58..4950a4b 100644
--- a/services/surfaceflinger/tests/unittests/mock/system/window/MockNativeWindow.h
+++ b/services/surfaceflinger/tests/unittests/mock/system/window/MockNativeWindow.h
@@ -36,6 +36,7 @@
     MOCK_METHOD1(queueBuffer_DEPRECATED, int(struct ANativeWindowBuffer*));
     MOCK_CONST_METHOD2(query, int(int, int*));
     MOCK_METHOD1(perform, int(int));
+    MOCK_METHOD2(perform, int(int, int));
     MOCK_METHOD1(cancelBuffer_DEPRECATED, int(struct ANativeWindowBuffer*));
     MOCK_METHOD2(dequeueBuffer, int(struct ANativeWindowBuffer**, int*));
     MOCK_METHOD2(queueBuffer, int(struct ANativeWindowBuffer*, int));