Merge "Create SessionManager in ADPF PowerAdvisor" into main
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 50c2cd8..36dcbca 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -50,6 +50,7 @@
         "vibrator/CommandAlwaysOnEnable.cpp",
         "vibrator/CommandCompose.cpp",
         "vibrator/CommandComposePwle.cpp",
+        "vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp",
         "vibrator/CommandGetBandwidthAmplitudeMap.cpp",
         "vibrator/CommandGetCapabilities.cpp",
         "vibrator/CommandGetCompositionDelayMax.cpp",
@@ -72,6 +73,11 @@
         "vibrator/CommandSetExternalControl.cpp",
         "vibrator/CommandSupportsAmplitudeControl.cpp",
         "vibrator/CommandSupportsExternalControl.cpp",
+        "vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp",
+        "vibrator/CommandGetPwleV2CompositionSizeMax.cpp",
+        "vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp",
+        "vibrator/CommandComposePwleV2.cpp",
+        "vibrator/CommandPerformVendorEffect.cpp",
     ],
     visibility: [":__subpackages__"],
 }
diff --git a/cmds/idlcli/vibrator/CommandComposePwleV2.cpp b/cmds/idlcli/vibrator/CommandComposePwleV2.cpp
new file mode 100644
index 0000000..6d3cf84
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandComposePwleV2.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2024 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 <stdlib.h>
+
+#include <charconv>
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositePwleV2;
+using aidl::PwleV2Primitive;
+
+class CommandComposePwleV2 : public Command {
+    std::string getDescription() const override { return "Compose normalized PWLE vibration."; }
+
+    std::string getUsageSummary() const override {
+        return "[options] <time> <frequency> <amplitude> ...";
+    }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"-b", {"Block for duration of vibration."}},
+                {"<time>", {"Segment duration in milliseconds"}},
+                {"<frequency>", {"Target frequency in Hz"}},
+                {"<amplitude>", {"Target amplitude in [0.0, 1.0]"}},
+                {"...", {"May repeat multiple times."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args& args) override {
+        while (args.get<std::string>().value_or("").find("-") == 0) {
+            auto opt = *args.pop<std::string>();
+            if (opt == "--") {
+                break;
+            } else if (opt == "-b") {
+                mBlocking = true;
+            } else {
+                std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+                return USAGE;
+            }
+        }
+
+        if (args.empty()) {
+            std::cerr << "Missing arguments! Please see usage" << std::endl;
+            return USAGE;
+        }
+
+        while (!args.empty()) {
+            PwleV2Primitive segment;
+
+            if (auto timeMs = args.pop<decltype(segment.timeMillis)>();
+                timeMs && *timeMs >= 0 && *timeMs <= 0x7ffff) {
+                segment.timeMillis = *timeMs;
+                std::cout << "Time: " << segment.timeMillis << std::endl;
+            } else {
+                std::cerr << "Missing or Invalid Time!" << std::endl;
+                return USAGE;
+            }
+
+            if (auto frequencyHz = args.pop<decltype(segment.frequencyHz)>();
+                frequencyHz && *frequencyHz >= 30 && *frequencyHz <= 300) {
+                segment.frequencyHz = *frequencyHz;
+                std::cout << "Frequency: " << segment.frequencyHz << std::endl;
+            } else {
+                std::cerr << "Missing or Invalid Frequency!" << std::endl;
+                return USAGE;
+            }
+
+            if (auto amplitude = args.pop<decltype(segment.amplitude)>();
+                amplitude && *amplitude >= 0 && *amplitude <= 1.0) {
+                segment.amplitude = *amplitude;
+                std::cout << "Amplitude: " << segment.amplitude << std::endl;
+            } else {
+                std::cerr << "Missing or Invalid Amplitude!" << std::endl;
+                return USAGE;
+            }
+
+            mCompositePwle.pwlePrimitives.emplace_back(std::move(segment));
+        }
+
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+
+        return OK;
+    }
+
+    Status doMain(Args&& /*args*/) override {
+        auto hal = getHal<aidl::IVibrator>();
+
+        if (!hal) {
+            return UNAVAILABLE;
+        }
+
+        ABinderProcess_setThreadPoolMaxThreadCount(1);
+        ABinderProcess_startThreadPool();
+
+        std::shared_ptr<VibratorCallback> callback;
+
+        if (mBlocking) {
+            callback = ndk::SharedRefBase::make<VibratorCallback>();
+        }
+
+        auto status = hal->call(&aidl::IVibrator::composePwleV2, mCompositePwle, callback);
+
+        if (status.isOk() && callback) {
+            callback->waitForComplete();
+        }
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
+
+        return status.isOk() ? OK : ERROR;
+    }
+
+    bool mBlocking;
+    CompositePwleV2 mCompositePwle;
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandComposePwleV2>("composePwleV2");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp
new file mode 100644
index 0000000..2edd0ca
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::FrequencyAccelerationMapEntry;
+
+class CommandGetFrequencyToOutputAccelerationMap : public Command {
+    std::string getDescription() const override {
+        return "Retrieves vibrator frequency to output acceleration map.";
+    }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args& args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args&& /*args*/) override {
+        std::string statusStr;
+        std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getFrequencyToOutputAccelerationMap,
+                                    &frequencyToOutputAccelerationMap);
+            statusStr = status.getDescription();
+            ret = (status.isOk() ? OK : ERROR);
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Frequency to Output Amplitude Map: " << std::endl;
+        for (auto& entry : frequencyToOutputAccelerationMap) {
+            std::cout << entry.frequencyHz << " " << entry.maxOutputAccelerationGs << std::endl;
+        }
+
+        return ret;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandGetFrequencyToOutputAccelerationMap>(
+                "getFrequencyToOutputAccelerationMap");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp
new file mode 100644
index 0000000..cca072c
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetPwleV2CompositionSizeMax : public Command {
+    std::string getDescription() const override {
+        return "Retrieves vibrator PwleV2 max composition size.";
+    }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args& args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args&& /*args*/) override {
+        std::string statusStr;
+        int32_t maxSize;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getPwleV2CompositionSizeMax, &maxSize);
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Max Size: " << maxSize << std::endl;
+
+        return ret;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandGetPwleV2CompositionSizeMax>(
+                "getPwleV2CompositionSizeMax");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp
new file mode 100644
index 0000000..dbbfe1a
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetPwleV2PrimitiveDurationMaxMillis : public Command {
+    std::string getDescription() const override {
+        return "Retrieves vibrator PwleV2 max primitive duration in milliseconds.";
+    }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args& args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args&& /*args*/) override {
+        std::string statusStr;
+        int32_t maxDurationMs;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getPwleV2PrimitiveDurationMaxMillis,
+                                    &maxDurationMs);
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Primitive duration max: " << maxDurationMs << " ms" << std::endl;
+
+        return ret;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandGetPwleV2PrimitiveDurationMaxMillis>(
+                "getPwleV2PrimitiveDurationMaxMillis");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp
new file mode 100644
index 0000000..09225c4
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetPwleV2PrimitiveDurationMinMillis : public Command {
+    std::string getDescription() const override {
+        return "Retrieves vibrator PwleV2 minimum primitive duration in milliseconds.";
+    }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args& args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args&& /*args*/) override {
+        std::string statusStr;
+        int32_t minDurationMs;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getPwleV2PrimitiveDurationMinMillis,
+                                    &minDurationMs);
+            statusStr = status.getDescription();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Primitive duration min: " << minDurationMs << " ms" << std::endl;
+
+        return ret;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandGetPwleV2PrimitiveDurationMinMillis>(
+                "getPwleV2PrimitiveDurationMinMillis");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandPerformVendorEffect.cpp b/cmds/idlcli/vibrator/CommandPerformVendorEffect.cpp
new file mode 100644
index 0000000..9c25bd2
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandPerformVendorEffect.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 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 <thread>
+
+#include "utils.h"
+#include "vibrator.h"
+
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::VendorEffect;
+
+class CommandPerformVendorEffect : public Command {
+    std::string getDescription() const override { return "Perform vendor vibration effect."; }
+
+    std::string getUsageSummary() const override { return "[options] <none>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"-b", {"Block for duration of vibration."}},
+                {"<none>", {"No valid input."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args& args) override {
+        while (args.get<std::string>().value_or("").find("-") == 0) {
+            auto opt = *args.pop<std::string>();
+            if (opt == "--") {
+                break;
+            } else if (opt == "-b") {
+                mBlocking = true;
+            } else {
+                std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+                return USAGE;
+            }
+        }
+
+        return OK;
+    }
+
+    Status doMain(Args&& /*args*/) override { return UNAVAILABLE; }
+
+    bool mBlocking;
+    VendorEffect mEffect;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandPerformVendorEffect>(
+        "performVendorEffect");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp b/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp
index b1c6940..50ea0c7 100644
--- a/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp
+++ b/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp
@@ -47,6 +47,8 @@
 } // namespace android
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    // TODO(b/183141167): need to rewrite 'dump' to avoid SIGPIPE.
+    signal(SIGPIPE, SIG_IGN);
     auto service = sp<InstalldNativeService>::make();
     fuzzService(service, FuzzedDataProvider(data, size));
     return 0;
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index fa7cb64..38a125b 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -396,7 +396,7 @@
     SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
             PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
 
-    *outBinder = tryGetBinder(name, true);
+    *outBinder = tryGetBinder(name, true).service;
     // returns ok regardless of result for legacy reasons
     return Status::ok();
 }
@@ -430,13 +430,15 @@
             return os::Service::make<os::Service::Tag::accessor>(nullptr);
         }
         return os::Service::make<os::Service::Tag::accessor>(
-                tryGetBinder(*accessorName, startIfNotFound));
+                tryGetBinder(*accessorName, startIfNotFound).service);
     } else {
-        return os::Service::make<os::Service::Tag::binder>(tryGetBinder(name, startIfNotFound));
+        return os::Service::make<os::Service::Tag::serviceWithMetadata>(
+                tryGetBinder(name, startIfNotFound));
     }
 }
 
-sp<IBinder> ServiceManager::tryGetBinder(const std::string& name, bool startIfNotFound) {
+os::ServiceWithMetadata ServiceManager::tryGetBinder(const std::string& name,
+                                                     bool startIfNotFound) {
     SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
             PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
 
@@ -450,13 +452,13 @@
         if (!service->allowIsolated && is_multiuser_uid_isolated(ctx.uid)) {
             LOG(WARNING) << "Isolated app with UID " << ctx.uid << " requested '" << name
                          << "', but the service is not allowed for isolated apps.";
-            return nullptr;
+            return os::ServiceWithMetadata();
         }
         out = service->binder;
     }
 
     if (!mAccess->canFind(ctx, name)) {
-        return nullptr;
+        return os::ServiceWithMetadata();
     }
 
     if (!out && startIfNotFound) {
@@ -473,8 +475,11 @@
         CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false));
         service->guaranteeClient = true;
     }
-
-    return out;
+    os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata();
+    serviceWithMetadata.service = out;
+    serviceWithMetadata.isLazyService =
+            service ? service->dumpPriority & FLAG_IS_LAZY_SERVICE : false;
+    return serviceWithMetadata;
 }
 
 bool isValidServiceName(const std::string& name) {
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index c92141b..964abee 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -114,7 +114,7 @@
     void removeClientCallback(const wp<IBinder>& who, ClientCallbackMap::iterator* it);
 
     os::Service tryGetService(const std::string& name, bool startIfNotFound);
-    sp<IBinder> tryGetBinder(const std::string& name, bool startIfNotFound);
+    os::ServiceWithMetadata tryGetBinder(const std::string& name, bool startIfNotFound);
     binder::Status canAddService(const Access::CallingContext& ctx, const std::string& name,
                                  std::optional<std::string>* accessor);
     binder::Status canFindService(const Access::CallingContext& ctx, const std::string& name,
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index c126e91..df5a8ed 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -165,6 +165,7 @@
     IPCThreadState::self()->disableBackgroundScheduling(true);
 
     sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());
+    manager->setRequestingSid(true);
     if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
         LOG(ERROR) << "Could not self register servicemanager";
     }
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 95f459f..e620770 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -92,6 +92,11 @@
     auto sm = getPermissiveServiceManager();
     EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+    EXPECT_TRUE(sm->addService("lazyfoo", getBinder(), false /*allowIsolated*/,
+                               IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT |
+                                       IServiceManager::FLAG_IS_LAZY_SERVICE)
+                        .isOk());
 }
 
 TEST(AddService, EmptyNameDisallowed) {
@@ -156,7 +161,7 @@
 
     Service outA;
     EXPECT_TRUE(sm->getService2("foo", &outA).isOk());
-    EXPECT_EQ(serviceA, outA.get<Service::Tag::binder>());
+    EXPECT_EQ(serviceA, outA.get<Service::Tag::serviceWithMetadata>().service);
     sp<IBinder> outBinderA;
     EXPECT_TRUE(sm->getService("foo", &outBinderA).isOk());
     EXPECT_EQ(serviceA, outBinderA);
@@ -168,7 +173,7 @@
 
     Service outB;
     EXPECT_TRUE(sm->getService2("foo", &outB).isOk());
-    EXPECT_EQ(serviceB, outB.get<Service::Tag::binder>());
+    EXPECT_EQ(serviceB, outB.get<Service::Tag::serviceWithMetadata>().service);
     sp<IBinder> outBinderB;
     EXPECT_TRUE(sm->getService("foo", &outBinderB).isOk());
     EXPECT_EQ(serviceB, outBinderB);
@@ -195,7 +200,7 @@
 
     Service out;
     EXPECT_TRUE(sm->getService2("foo", &out).isOk());
-    EXPECT_EQ(service, out.get<Service::Tag::binder>());
+    EXPECT_EQ(service, out.get<Service::Tag::serviceWithMetadata>().service);
     sp<IBinder> outBinder;
     EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
     EXPECT_EQ(service, outBinder);
@@ -206,7 +211,7 @@
 
     Service out;
     EXPECT_TRUE(sm->getService2("foo", &out).isOk());
-    EXPECT_EQ(nullptr, out.get<Service::Tag::binder>());
+    EXPECT_EQ(nullptr, out.get<Service::Tag::serviceWithMetadata>().service);
     sp<IBinder> outBinder;
     EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
     EXPECT_EQ(nullptr, outBinder);
@@ -227,7 +232,7 @@
     Service out;
     // returns nullptr but has OK status for legacy compatibility
     EXPECT_TRUE(sm->getService2("foo", &out).isOk());
-    EXPECT_EQ(nullptr, out.get<Service::Tag::binder>());
+    EXPECT_EQ(nullptr, out.get<Service::Tag::serviceWithMetadata>().service);
     sp<IBinder> outBinder;
     EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
     EXPECT_EQ(nullptr, outBinder);
@@ -257,7 +262,7 @@
 
     Service out;
     EXPECT_TRUE(sm->getService2("foo", &out).isOk());
-    EXPECT_EQ(service, out.get<Service::Tag::binder>());
+    EXPECT_EQ(service, out.get<Service::Tag::serviceWithMetadata>().service);
     sp<IBinder> outBinder;
     EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
     EXPECT_EQ(service, outBinder);
@@ -289,7 +294,7 @@
     Service out;
     // returns nullptr but has OK status for legacy compatibility
     EXPECT_TRUE(sm->getService2("foo", &out).isOk());
-    EXPECT_EQ(nullptr, out.get<Service::Tag::binder>());
+    EXPECT_EQ(nullptr, out.get<Service::Tag::serviceWithMetadata>().service);
     sp<IBinder> outBinder;
     EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
     EXPECT_EQ(nullptr, outBinder);
diff --git a/include/android/OWNERS b/include/android/OWNERS
index 5b014d0..9dfa279 100644
--- a/include/android/OWNERS
+++ b/include/android/OWNERS
@@ -9,4 +9,5 @@
 
 # ADPF
 per-file performance_hint.h = file:platform/frameworks/base:/ADPF_OWNERS
+per-file system_health.h = file:platform/frameworks/base:/ADPF_OWNERS
 per-file thermal.h = file:platform/frameworks/base:/ADPF_OWNERS
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index ca86c27..2b4a5f5 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -359,6 +359,31 @@
         bool cpu, bool gpu, const char* _Nonnull debugName) __INTRODUCED_IN(36);
 
 /**
+ * Informs the framework of an upcoming one-off expensive frame for a graphics pipeline
+ * bound to this session. This frame will be treated as not representative of the workload as a
+ * whole, and it will be discarded the purposes of load tracking. The user can specify
+ * whether the workload spike is expected to be on the CPU, GPU, or both.
+ *
+ * Sending hints for both CPU and GPU counts as two separate hints for the purposes of the
+ * rate limiter.
+ *
+ * @param cpu Indicates if the workload spike is expected to affect the CPU.
+ * @param gpu Indicates if the workload spike is expected to affect the GPU.
+ * @param debugName A required string used to identify this specific hint during
+ *        tracing. This debug string will only be held for the duration of the
+ *        method, and can be safely discarded after.
+ *
+ * @return 0 on success.
+ *         EINVAL if no hints were requested.
+ *         EBUSY if the hint was rate limited.
+ *         EPIPE if communication with the system service has failed.
+ *         ENOTSUP if the hint is not supported.
+ */
+int APerformanceHint_notifyWorkloadSpike(
+        APerformanceHintSession* _Nonnull session,
+        bool cpu, bool gpu, const char* _Nonnull debugName) __INTRODUCED_IN(36);
+
+/**
  * Associates a session with any {@link ASurfaceControl} or {@link ANativeWindow}
  * instances managed by this session.
  *
diff --git a/include/android/system_health.h b/include/android/system_health.h
new file mode 100644
index 0000000..69620df
--- /dev/null
+++ b/include/android/system_health.h
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/**
+* @defgroup SystemHealth
+* @{
+*/
+
+/**
+ * @file system_health.h
+ */
+
+#ifndef _ANDROID_SYSTEM_HEALTH_H
+#define _ANDROID_SYSTEM_HEALTH_H
+
+#include <sys/cdefs.h>
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ *   This file is part of Android's set of stable system headers
+ *   exposed by the Android NDK (Native Development Kit).
+ *
+ *   Third-party source AND binary code relies on the definitions
+ *   here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ *   - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ *   - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ *   - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Params used to customize the calculation of CPU headroom.
+ *
+ * Also see {@link ASystemHealth_getCpuHeadroom}.
+ */
+typedef struct ACpuHeadroomParams ACpuHeadroomParams;
+
+/**
+ * Params used to customize the calculation of GPU headroom.
+ *
+ * Also see {@link ASystemHealth_getGpuHeadroom}.
+ */
+typedef struct AGpuHeadroomParams AGpuHeadroomParams;
+
+/**
+ * Creates a new instance of ACpuHeadroomParams.
+ *
+ * When the client finishes using {@link ACpuHeadroomParams},
+ * {@link ACpuHeadroomParams_destroy()} must be called to destroy
+ * and free up the resources associated with {@link ACpuHeadroomParams}.
+ *
+ * Available since API level 36.
+ *
+ * @return A new instance of ACpuHeadroomParams.
+ */
+ACpuHeadroomParams *_Nonnull ACpuHeadroomParams_create()
+__INTRODUCED_IN(36);
+
+enum ACpuHeadroomCalculationType {
+    /**
+     * Use the minimum headroom value within the calculation window.
+     * Introduced in API level 36.
+     */
+    ACPU_HEADROOM_CALCULATION_TYPE_MIN = 0,
+    /**
+     * Use the average headroom value within the calculation window.
+     * Introduced in API level 36.
+     */
+    ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1,
+};
+typedef enum ACpuHeadroomCalculationType ACpuHeadroomCalculationType;
+
+enum AGpuHeadroomCalculationType {
+    /**
+     * Use the minimum headroom value within the calculation window.
+     * Introduced in API level 36.
+     */
+    AGPU_HEADROOM_CALCULATION_TYPE_MIN = 0,
+    /**
+     * Use the average headroom value within the calculation window.
+     * Introduced in API level 36.
+     */
+    AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1,
+};
+typedef enum AGpuHeadroomCalculationType AGpuHeadroomCalculationType;
+
+/**
+ * Sets the headroom calculation window size in ACpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @param windowMillis The window size in milliseconds ranged from [50, 10000]. The smaller the
+ *                     window size, the larger fluctuation in the headroom value should be expected.
+ *                     The default value can be retrieved from the
+ *                     {@link #ACpuHeadroomParams_getCalculationWindowMillis} if not set. The device
+ *                     will try to use the closest feasible window size to this param.
+ */
+void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams *_Nonnull params,
+                                                   int windowMillis)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets the headroom calculation window size in ACpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @return This will return the default value chosen by the device if the params is not set.
+ */
+int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams *_Nonnull params)
+__INTRODUCED_IN(36);
+
+/**
+ * Sets the headroom calculation window size in AGpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @param windowMillis The window size in milliseconds ranged from [50, 10000]. The smaller the
+ *                     window size, the larger fluctuation in the headroom value should be expected.
+ *                     The default value can be retrieved from the
+ *                     {@link #AGpuHeadroomParams_getCalculationWindowMillis} if not set. The device
+ *                     will try to use the closest feasible window size to this param.
+ */
+void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams *_Nonnull params,
+                                                   int windowMillis)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets the headroom calculation window size in AGpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @return This will return the default value chosen by the device if the params is not set.
+ */
+int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams *_Nonnull params)
+__INTRODUCED_IN(36);
+
+/**
+ * Sets the headroom calculation type in ACpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @param calculationType The headroom calculation type.
+ */
+void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams *_Nonnull params,
+                                           ACpuHeadroomCalculationType calculationType)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets the headroom calculation type in ACpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @return The headroom calculation type.
+ */
+ACpuHeadroomCalculationType
+ACpuHeadroomParams_getCalculationType(ACpuHeadroomParams *_Nonnull params)
+__INTRODUCED_IN(36);
+
+/**
+ * Sets the headroom calculation type in AGpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @param calculationType The headroom calculation type.
+ */
+void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams *_Nonnull params,
+                                           AGpuHeadroomCalculationType calculationType)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets the headroom calculation type in AGpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @return The headroom calculation type.
+ */
+AGpuHeadroomCalculationType
+AGpuHeadroomParams_getCalculationType(AGpuHeadroomParams *_Nonnull params)
+__INTRODUCED_IN(36);
+
+/**
+ * Sets the thread TIDs to track in ACpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @param tids Non-null array of TIDs, maximum 5.
+ * @param tidsSize The size of the tids array.
+ */
+void ACpuHeadroomParams_setTids(ACpuHeadroomParams *_Nonnull params, const int *_Nonnull tids,
+                                int tidsSize)
+__INTRODUCED_IN(36);
+
+/**
+ * Creates a new instance of AGpuHeadroomParams.
+ *
+ * When the client finishes using {@link AGpuHeadroomParams},
+ * {@link AGpuHeadroomParams_destroy()} must be called to destroy
+ * and free up the resources associated with {@link AGpuHeadroomParams}.
+ *
+ * Available since API level 36.
+ *
+ * @return A new instance of AGpuHeadroomParams.
+ */
+AGpuHeadroomParams *_Nonnull AGpuHeadroomParams_create()
+__INTRODUCED_IN(36);
+
+/**
+ * Deletes the ACpuHeadroomParams instance.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be deleted.
+ */
+void ACpuHeadroomParams_destroy(ACpuHeadroomParams *_Nonnull params)
+__INTRODUCED_IN(36);
+
+/**
+ * Deletes the AGpuHeadroomParams instance.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be deleted.
+ */
+void AGpuHeadroomParams_destroy(AGpuHeadroomParams *_Nonnull params)
+__INTRODUCED_IN(36);
+
+/**
+ * Provides an estimate of available CPU capacity headroom of the device.
+ *
+ * The value can be used by the calling application to determine if the workload was CPU bound and
+ * then take action accordingly to ensure that the workload can be completed smoothly. It can also
+ * be used with the thermal status and headroom to determine if reducing the CPU bound workload can
+ * help reduce the device temperature to avoid thermal throttling.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to customize the CPU headroom calculation, or nullptr to use the default
+ * @param outHeadroom Non-null output pointer to a single float, which will be set to the CPU
+ *                    headroom value. The value will be a single value or `Float.NaN` if it's
+ *                    temporarily unavailable.
+ *                    Each valid value ranges from [0, 100], where 0 indicates no more cpu resources
+ *                    can be granted.
+ * @return 0 on success
+ *         EPIPE if failed to get the CPU headroom.
+ *         EPERM if the TIDs do not belong to the same process.
+ *         ENOTSUP if API or requested params is unsupported.
+ */
+int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams *_Nullable params,
+                                 float *_Nonnull outHeadroom)
+__INTRODUCED_IN(36);
+
+/**
+ * Provides an estimate of available GPU capacity headroom of the device.
+ *
+ * The value can be used by the calling application to determine if the workload was GPU bound and
+ * then take action accordingly to ensure that the workload can be completed smoothly. It can also
+ * be used with the thermal status and headroom to determine if reducing the GPU bound workload can
+ * help reduce the device temperature to avoid thermal throttling.
+ *
+ * Available since API level 36
+ *
+ * @param params The params to customize the GPU headroom calculation, or nullptr to use the default
+ * @param outHeadroom Non-null output pointer to a single float, which will be set to the GPU
+ *                    headroom value. The value will be a single value or `Float.NaN` if it's
+ *                    temporarily unavailable.
+ *                    Each valid value ranges from [0, 100], where 0 indicates no more gpu resources
+ *                    can be granted.
+ * @return 0 on success
+ *         EPIPE if failed to get the GPU headroom.
+ *         ENOTSUP if API or requested params is unsupported.
+ */
+int ASystemHealth_getGpuHeadroom(const AGpuHeadroomParams *_Nullable params,
+                                 float *_Nonnull outHeadroom)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets minimum polling interval for calling {@link ASystemHealth_getCpuHeadroom} in milliseconds.
+ *
+ * The getCpuHeadroom API may return cached result if called more frequently than the interval.
+ *
+ * Available since API level 36.
+ *
+ * @param outMinIntervalMillis Non-null output pointer to a int64_t, which
+ *                will be set to the minimum polling interval in milliseconds.
+ * @return 0 on success
+ *         EPIPE if failed to get the minimum polling interval.
+ *         ENOTSUP if API is unsupported.
+ */
+int ASystemHealth_getCpuHeadroomMinIntervalMillis(int64_t *_Nonnull outMinIntervalMillis)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets minimum polling interval for calling {@link ASystemHealth_getGpuHeadroom} in milliseconds.
+ *
+ * The getGpuHeadroom API may return cached result if called more frequent than the interval.
+ *
+ * Available since API level 36.
+ *
+ * @param outMinIntervalMillis Non-null output pointer to a int64_t, which
+ *                will be set to the minimum polling interval in milliseconds.
+ * @return 0 on success
+ *         EPIPE if failed to get the minimum polling interval.
+ *         ENOTSUP if API is unsupported.
+ */
+int ASystemHealth_getGpuHeadroomMinIntervalMillis(int64_t *_Nonnull outMinIntervalMillis)
+__INTRODUCED_IN(36);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _ANDROID_SYSTEM_HEALTH_H
+
+/** @} */
diff --git a/include/input/DisplayTopologyGraph.h b/include/input/DisplayTopologyGraph.h
new file mode 100644
index 0000000..90427bd
--- /dev/null
+++ b/include/input/DisplayTopologyGraph.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2024 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 <ftl/enum.h>
+#include <ui/LogicalDisplayId.h>
+
+#include <cinttypes>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+
+/**
+ * The edge of the current display, where adjacent display is attached to.
+ */
+enum class DisplayTopologyPosition : int32_t {
+    LEFT = 0,
+    TOP = 1,
+    RIGHT = 2,
+    BOTTOM = 3,
+
+    ftl_last = BOTTOM
+};
+
+/**
+ * Directed edge in the graph of adjacent displays.
+ */
+struct DisplayTopologyAdjacentDisplay {
+    ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID;
+    DisplayTopologyPosition position;
+    float offsetPx;
+};
+
+/**
+ * Directed Graph representation of Display Topology.
+ */
+struct DisplayTopologyGraph {
+    ui::LogicalDisplayId primaryDisplayId = ui::LogicalDisplayId::INVALID;
+    std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>> graph;
+};
+
+} // namespace android
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index f150fb1..e3f98ba 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -74,6 +74,20 @@
      * baseline to prepare for an arbitrary load, and must wake up if inactive.
      */
     GPU_LOAD_RESET = 7,
+
+    /**
+     * This hint indicates an upcoming CPU workload that is abnormally large and
+     * not representative of the workload. This should be used for rare, one-time
+     * operations and should be ignored by any load tracking or session hysteresis.
+     */
+    CPU_LOAD_SPIKE = 8,
+
+    /**
+     * This hint indicates an upcoming GPU workload that is abnormally large and
+     * not representative of the workload. This should be used for rare, one-time
+     * operations and should be ignored by any load tracking or session hysteresis.
+     */
+    GPU_LOAD_SPIKE = 9,
 };
 
 // Allows access to PowerHAL's SessionTags without needing to import its AIDL
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index e10e4dd..aac369d 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -456,6 +456,28 @@
 }
 
 soong_config_module_type {
+    name: "libbinder_remove_cache_static_list_config",
+    module_type: "cc_defaults",
+    config_namespace: "libbinder",
+    bool_variables: ["release_libbinder_remove_cache_static_list"],
+    properties: [
+        "cflags",
+    ],
+}
+
+libbinder_remove_cache_static_list_config {
+    name: "libbinder_remove_cache_static_list_flag",
+    soong_config_variables: {
+        release_libbinder_remove_cache_static_list: {
+            cflags: ["-DLIBBINDER_REMOVE_CACHE_STATIC_LIST"],
+            conditions_default: {
+                cflags: ["-DNO_LIBBINDER_REMOVE_CACHE_STATIC_LIST"],
+            },
+        },
+    },
+}
+
+soong_config_module_type {
     name: "libbinder_client_cache_config",
     module_type: "cc_defaults",
     config_namespace: "libbinder",
@@ -504,6 +526,7 @@
     defaults: [
         "libbinder_client_cache_flag",
         "libbinder_addservice_cache_flag",
+        "libbinder_remove_cache_static_list_flag",
     ],
     srcs: [
         "BufferedTextOutput.cpp",
@@ -826,6 +849,7 @@
         "aidl/android/os/IServiceCallback.aidl",
         "aidl/android/os/IServiceManager.aidl",
         "aidl/android/os/Service.aidl",
+        "aidl/android/os/ServiceWithMetadata.aidl",
         "aidl/android/os/ServiceDebugInfo.aidl",
     ],
     path: "aidl",
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index 123dfd2..34d5a09 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -16,6 +16,7 @@
 #include "BackendUnifiedServiceManager.h"
 
 #include <android/os/IAccessor.h>
+#include <android/os/IServiceManager.h>
 #include <binder/RpcSession.h>
 
 #if defined(__BIONIC__) && !defined(__ANDROID_VNDK__)
@@ -36,6 +37,12 @@
 constexpr bool kUseCacheInAddService = false;
 #endif
 
+#ifdef LIBBINDER_REMOVE_CACHE_STATIC_LIST
+constexpr bool kRemoveStaticList = true;
+#else
+constexpr bool kRemoveStaticList = false;
+#endif
+
 using AidlServiceManager = android::os::IServiceManager;
 using android::os::IAccessor;
 using binder::Status;
@@ -110,6 +117,13 @@
         // go/keep-sorted end
 };
 
+os::ServiceWithMetadata createServiceWithMetadata(const sp<IBinder>& service, bool isLazyService) {
+    os::ServiceWithMetadata out = os::ServiceWithMetadata();
+    out.service = service;
+    out.isLazyService = isLazyService;
+    return out;
+}
+
 bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& serviceName) {
     sp<ProcessState> self = ProcessState::selfOrNull();
     if (!self || self->getThreadPoolMaxTotalThreadCount() <= 0) {
@@ -118,6 +132,7 @@
               serviceName.c_str());
         return false;
     }
+    if (kRemoveStaticList) return true;
     for (const char* name : kStaticCachableList) {
         if (name == serviceName) {
             return true;
@@ -132,15 +147,21 @@
         return Status::ok();
     }
 
-    if (service.getTag() == os::Service::Tag::binder) {
-        return updateCache(serviceName, service.get<os::Service::Tag::binder>());
+    if (service.getTag() == os::Service::Tag::serviceWithMetadata) {
+        auto serviceWithMetadata = service.get<os::Service::Tag::serviceWithMetadata>();
+        return updateCache(serviceName, serviceWithMetadata.service,
+                           serviceWithMetadata.isLazyService);
     }
     return Status::ok();
 }
 
 Status BackendUnifiedServiceManager::updateCache(const std::string& serviceName,
-                                                 const sp<IBinder>& binder) {
+                                                 const sp<IBinder>& binder, bool isServiceLazy) {
     std::string traceStr;
+    // Don't cache if service is lazy
+    if (kRemoveStaticList && isServiceLazy) {
+        return Status::ok();
+    }
     if (atrace_is_tag_enabled(ATRACE_TAG_AIDL)) {
         traceStr = "BinderCacheWithInvalidation::updateCache : " + serviceName;
     }
@@ -153,7 +174,9 @@
         binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
                                       "BinderCacheWithInvalidation::updateCache failed: "
                                       "isBinderAlive_false");
-    } else if (mCacheForGetService->isClientSideCachingEnabled(serviceName)) {
+    }
+    // If we reach here with kRemoveStaticList=true then we know service isn't lazy
+    else if (mCacheForGetService->isClientSideCachingEnabled(serviceName)) {
         binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
                                       "BinderCacheWithInvalidation::updateCache successful");
         return mCacheForGetService->setItem(serviceName, binder);
@@ -173,7 +196,7 @@
     sp<IBinder> item = mCacheForGetService->getItem(serviceName);
     // TODO(b/363177618): Enable caching for binders which are always null.
     if (item != nullptr && item->isBinderAlive()) {
-        *_out = os::Service::make<os::Service::Tag::binder>(item);
+        *_out = createServiceWithMetadata(item, false);
         return true;
     }
     return false;
@@ -188,7 +211,7 @@
                                                 sp<IBinder>* _aidl_return) {
     os::Service service;
     Status status = getService2(name, &service);
-    *_aidl_return = service.get<os::Service::Tag::binder>();
+    *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service;
     return status;
 }
 
@@ -227,14 +250,16 @@
 Status BackendUnifiedServiceManager::toBinderService(const ::std::string& name,
                                                      const os::Service& in, os::Service* _out) {
     switch (in.getTag()) {
-        case os::Service::Tag::binder: {
-            if (in.get<os::Service::Tag::binder>() == nullptr) {
+        case os::Service::Tag::serviceWithMetadata: {
+            auto serviceWithMetadata = in.get<os::Service::Tag::serviceWithMetadata>();
+            if (serviceWithMetadata.service == nullptr) {
                 // failed to find a service. Check to see if we have any local
                 // injected Accessors for this service.
                 os::Service accessor;
                 Status status = getInjectedAccessor(name, &accessor);
                 if (!status.isOk()) {
-                    *_out = os::Service::make<os::Service::Tag::binder>(nullptr);
+                    *_out = os::Service::make<os::Service::Tag::serviceWithMetadata>(
+                            createServiceWithMetadata(nullptr, false));
                     return status;
                 }
                 if (accessor.getTag() == os::Service::Tag::accessor &&
@@ -255,7 +280,8 @@
             sp<IAccessor> accessor = interface_cast<IAccessor>(accessorBinder);
             if (accessor == nullptr) {
                 ALOGE("Service#accessor doesn't have accessor. VM is maybe starting...");
-                *_out = os::Service::make<os::Service::Tag::binder>(nullptr);
+                *_out = os::Service::make<os::Service::Tag::serviceWithMetadata>(
+                        createServiceWithMetadata(nullptr, false));
                 return Status::ok();
             }
             auto request = [=] {
@@ -276,7 +302,8 @@
                 return Status::fromStatusT(status);
             }
             session->setSessionSpecificRoot(accessorBinder);
-            *_out = os::Service::make<os::Service::Tag::binder>(session->getRootObject());
+            *_out = os::Service::make<os::Service::Tag::serviceWithMetadata>(
+                    createServiceWithMetadata(session->getRootObject(), false));
             return Status::ok();
         }
         default: {
@@ -291,7 +318,8 @@
     Status status = mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority);
     // mEnableAddServiceCache is true by default.
     if (kUseCacheInAddService && mEnableAddServiceCache && status.isOk()) {
-        return updateCache(name, service);
+        return updateCache(name, service,
+                           dumpPriority & android::os::IServiceManager::FLAG_IS_LAZY_SERVICE);
     }
     return status;
 }
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index b46bf19..6a0d06a 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -159,7 +159,8 @@
     binder::Status toBinderService(const ::std::string& name, const os::Service& in,
                                    os::Service* _out);
     binder::Status updateCache(const std::string& serviceName, const os::Service& service);
-    binder::Status updateCache(const std::string& serviceName, const sp<IBinder>& binder);
+    binder::Status updateCache(const std::string& serviceName, const sp<IBinder>& binder,
+                               bool isLazyService);
     bool returnIfCached(const std::string& serviceName, os::Service* _out);
 };
 
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 61866d7..53435c3 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -152,7 +152,8 @@
     virtual Status realGetService(const std::string& name, sp<IBinder>* _aidl_return) {
         Service service;
         Status status = mUnifiedServiceManager->getService2(name, &service);
-        *_aidl_return = service.get<Service::Tag::binder>();
+        auto serviceWithMetadata = service.get<Service::Tag::serviceWithMetadata>();
+        *_aidl_return = serviceWithMetadata.service;
         return status;
     }
 };
@@ -607,7 +608,7 @@
     if (!mUnifiedServiceManager->checkService(String8(name).c_str(), &ret).isOk()) {
         return nullptr;
     }
-    return ret.get<Service::Tag::binder>();
+    return ret.get<Service::Tag::serviceWithMetadata>().service;
 }
 
 status_t CppBackendShim::addService(const String16& name, const sp<IBinder>& service,
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 0f0af0b..27cfe0c 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -19,7 +19,6 @@
 #include <android/os/BnClientCallback.h>
 #include <android/os/IServiceManager.h>
 #include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
 #include <binder/LazyServiceRegistrar.h>
 
 namespace android {
@@ -134,6 +133,12 @@
     std::string regStr = (reRegister) ? "Re-registering" : "Registering";
     ALOGI("%s service %s", regStr.c_str(), name.c_str());
 
+    if (dumpFlags & android::os::IServiceManager::FLAG_IS_LAZY_SERVICE) {
+        ALOGW("FLAG_IS_LAZY_SERVICE flag already set. This should only be set by "
+              "ClientCounterCallbackImpl in LazyServiceRegistrar");
+    }
+    dumpFlags |= android::os::IServiceManager::FLAG_IS_LAZY_SERVICE;
+
     if (Status status = manager->addService(name.c_str(), service, allowIsolated, dumpFlags);
         !status.isOk()) {
         ALOGE("Failed to register service %s (%s)", name.c_str(), status.toString8().c_str());
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index 1d1f84f..69edef8 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -49,6 +49,8 @@
              DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_HIGH
              | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PRIORITY_DEFAULT;
 
+    const int FLAG_IS_LAZY_SERVICE = 1 << 30;
+
     /* Allows services to dump sections in protobuf format. */
     const int DUMP_FLAG_PROTO = 1 << 4;
 
@@ -61,7 +63,8 @@
      *
      * Returns null if the service does not exist.
      *
-     * @deprecated TODO(b/355394904): Use getService2 instead.
+     * @deprecated TODO(b/355394904): Use getService2 instead. This does not return metadata
+     * that is included in ServiceWithMetadata
      */
     @UnsupportedAppUsage
     @nullable IBinder getService(@utf8InCpp String name);
diff --git a/libs/binder/aidl/android/os/Service.aidl b/libs/binder/aidl/android/os/Service.aidl
index 4c52109..3bc6588 100644
--- a/libs/binder/aidl/android/os/Service.aidl
+++ b/libs/binder/aidl/android/os/Service.aidl
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import android.os.ServiceWithMetadata;
+
 /**
  * Service is a union of different service types that can be returned
  * by the internal {@link ServiceManager#getService(name)} API.
@@ -23,6 +25,6 @@
  * @hide
  */
 union Service {
-    @nullable IBinder binder;
+    ServiceWithMetadata serviceWithMetadata;
     @nullable IBinder accessor;
 }
\ No newline at end of file
diff --git a/libs/binder/aidl/android/os/ServiceWithMetadata.aidl b/libs/binder/aidl/android/os/ServiceWithMetadata.aidl
new file mode 100644
index 0000000..96f76ff
--- /dev/null
+++ b/libs/binder/aidl/android/os/ServiceWithMetadata.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Service binder with metadata.
+ * @hide
+ */
+parcelable ServiceWithMetadata {
+    /**
+     * IBinder to service
+     */
+    @nullable IBinder service;
+    /**
+     * boolean if the IBinder can be cached by client.
+     */
+    boolean isLazyService;
+}
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 30e005c..5924a2d 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -220,8 +220,6 @@
         "android.app.IActivityManager",
         "android.app.IUidObserver",
         "android.drm.IDrm",
-        "android.dvr.IVsyncCallback",
-        "android.dvr.IVsyncService",
         "android.gfx.tests.ICallback",
         "android.gfx.tests.IIPCTest",
         "android.gfx.tests.ISafeInterfaceTest",
@@ -264,17 +262,12 @@
         "android.media.IResourceManagerService",
         "android.os.IComplexTypeInterface",
         "android.os.IPermissionController",
-        "android.os.IPingResponder",
         "android.os.IProcessInfoService",
         "android.os.ISchedulingPolicyService",
-        "android.os.IStringConstants",
         "android.os.storage.IObbActionListener",
         "android.os.storage.IStorageEventListener",
         "android.os.storage.IStorageManager",
         "android.os.storage.IStorageShutdownObserver",
-        "android.service.vr.IPersistentVrStateCallbacks",
-        "android.service.vr.IVrManager",
-        "android.service.vr.IVrStateCallbacks",
         "android.ui.ISurfaceComposer",
         "android.utils.IMemory",
         "android.utils.IMemoryHeap",
@@ -286,9 +279,6 @@
         "com.android.internal.os.IShellCallback",
         "drm.IDrmManagerService",
         "drm.IDrmServiceListener",
-        "IAAudioClient",
-        "IAAudioService",
-        "VtsFuzzer",
         nullptr,
 };
 
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index ca26a57..7d79baa 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -153,7 +153,7 @@
 
     /**
      * Directly enable or disable caching binder during addService calls.
-     * Only used for testing.
+     * Only used for testing. This is enabled by default.
      */
     virtual void enableAddServiceCache(bool value) = 0;
 };
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index cc4943b..f5df8d5 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -34,6 +34,7 @@
     ADD_SERVICE_DUMP_FLAG_PRIORITY_HIGH = 1 << 2,
     ADD_SERVICE_DUMP_FLAG_PRIORITY_NORMAL = 1 << 3,
     ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT = 1 << 4,
+    // All other bits are reserved for internal usage
 };
 
 /**
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 642fbf3..c21d7c6 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -73,6 +73,7 @@
     defaults: [
         "libbinder_client_cache_flag",
         "libbinder_addservice_cache_flag",
+        "libbinder_remove_cache_static_list_flag",
     ],
     test_suites: ["general-tests"],
     require_root: true,
diff --git a/libs/binder/tests/binderCacheUnitTest.cpp b/libs/binder/tests/binderCacheUnitTest.cpp
index 3195c55..19395c2 100644
--- a/libs/binder/tests/binderCacheUnitTest.cpp
+++ b/libs/binder/tests/binderCacheUnitTest.cpp
@@ -40,6 +40,12 @@
 constexpr bool kUseCacheInAddService = false;
 #endif
 
+#ifdef LIBBINDER_REMOVE_CACHE_STATIC_LIST
+constexpr bool kRemoveStaticList = true;
+#else
+constexpr bool kRemoveStaticList = false;
+#endif
+
 // A service name which is in the static list of cachable services
 const String16 kCachedServiceName = String16("isub");
 
@@ -69,8 +75,10 @@
     MockAidlServiceManager() : innerSm() {}
 
     binder::Status checkService(const ::std::string& name, os::Service* _out) override {
-        sp<IBinder> binder = innerSm.getService(String16(name.c_str()));
-        *_out = os::Service::make<os::Service::Tag::binder>(binder);
+        os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata();
+        serviceWithMetadata.service = innerSm.getService(String16(name.c_str()));
+        serviceWithMetadata.isLazyService = false;
+        *_out = os::Service::make<os::Service::Tag::serviceWithMetadata>(serviceWithMetadata);
         return binder::Status::ok();
     }
 
@@ -85,6 +93,75 @@
     FakeServiceManager innerSm;
 };
 
+// Returns services with isLazyService flag as true.
+class MockAidlServiceManager2 : public os::IServiceManagerDefault {
+public:
+    MockAidlServiceManager2() : innerSm() {}
+
+    binder::Status checkService(const ::std::string& name, os::Service* _out) override {
+        os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata();
+        serviceWithMetadata.service = innerSm.getService(String16(name.c_str()));
+        serviceWithMetadata.isLazyService = true;
+        *_out = os::Service::make<os::Service::Tag::serviceWithMetadata>(serviceWithMetadata);
+        return binder::Status::ok();
+    }
+
+    binder::Status addService(const std::string& name, const sp<IBinder>& service,
+                              bool allowIsolated, int32_t dumpPriority) override {
+        return binder::Status::fromStatusT(
+                innerSm.addService(String16(name.c_str()), service, allowIsolated, dumpPriority));
+    }
+
+    void clearServices() { innerSm.clear(); }
+
+    FakeServiceManager innerSm;
+};
+
+class LibbinderCacheRemoveStaticList : public ::testing::Test {
+protected:
+    void SetUp() override {
+        fakeServiceManager = sp<MockAidlServiceManager2>::make();
+        mServiceManager = getServiceManagerShimFromAidlServiceManagerForTests(fakeServiceManager);
+        mServiceManager->enableAddServiceCache(true);
+    }
+    void TearDown() override {}
+
+public:
+    void cacheAddServiceAndConfirmCacheMiss(const sp<IBinder>& binder1) {
+        // Add a service. This shouldn't cache it.
+        EXPECT_EQ(OK,
+                  mServiceManager->addService(kCachedServiceName, binder1,
+                                              /*allowIsolated = */ false,
+                                              android::os::IServiceManager::FLAG_IS_LAZY_SERVICE));
+        // Try to populate cache. Cache shouldn't be updated.
+        EXPECT_EQ(binder1, mServiceManager->checkService(kCachedServiceName));
+        fakeServiceManager->clearServices();
+        EXPECT_EQ(nullptr, mServiceManager->checkService(kCachedServiceName));
+    }
+
+    sp<MockAidlServiceManager2> fakeServiceManager;
+    sp<android::IServiceManager> mServiceManager;
+};
+
+TEST_F(LibbinderCacheRemoveStaticList, AddLocalServiceAndConfirmCacheMiss) {
+    if (!kRemoveStaticList) {
+        GTEST_SKIP() << "Skipping as feature is not enabled";
+        return;
+    }
+    sp<IBinder> binder1 = sp<BBinder>::make();
+    cacheAddServiceAndConfirmCacheMiss(binder1);
+}
+
+TEST_F(LibbinderCacheRemoveStaticList, AddRemoteServiceAndConfirmCacheMiss) {
+    if (!kRemoveStaticList) {
+        GTEST_SKIP() << "Skipping as feature is not enabled";
+        return;
+    }
+    sp<IBinder> binder1 = defaultServiceManager()->checkService(kServerName);
+    ASSERT_NE(binder1, nullptr);
+    cacheAddServiceAndConfirmCacheMiss(binder1);
+}
+
 class LibbinderCacheAddServiceTest : public ::testing::Test {
 protected:
     void SetUp() override {
@@ -231,7 +308,13 @@
     EXPECT_EQ(binder2, result);
 }
 
+// TODO(b/333854840): Remove this test removing the static list
 TEST_F(LibbinderCacheTest, DoNotCacheServiceNotInList) {
+    if (kRemoveStaticList) {
+        GTEST_SKIP() << "Skipping test as static list is disabled";
+        return;
+    }
+
     sp<IBinder> binder1 = sp<BBinder>::make();
     sp<IBinder> binder2 = sp<BBinder>::make();
     String16 serviceName = String16("NewLibbinderCacheTest");
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 9e92f95..c038c95 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -43,6 +43,7 @@
 #include <input/BlockingQueue.h>
 #include <processgroup/processgroup.h>
 #include <utils/Flattenable.h>
+#include <utils/SystemClock.h>
 
 #include <linux/sched.h>
 #include <sys/epoll.h>
@@ -343,7 +344,12 @@
         }
 
         bool checkFreezeSupport() {
-            std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze");
+            std::string path;
+            if (!CgroupGetAttributePathForTask("FreezerState", getpid(), &path)) {
+                return false;
+            }
+
+            std::ifstream freezer_file(path);
             // Pass test on devices where the cgroup v2 freezer is not supported
             if (freezer_file.fail()) {
                 return false;
@@ -1837,14 +1843,6 @@
     EXPECT_TRUE(reply.readBool());
 }
 
-size_t epochMillis() {
-    using std::chrono::duration_cast;
-    using std::chrono::milliseconds;
-    using std::chrono::seconds;
-    using std::chrono::system_clock;
-    return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
-}
-
 TEST_F(BinderLibTest, HangingServices) {
     Parcel data, reply;
     sp<IBinder> server = addServer();
@@ -1853,7 +1851,7 @@
     data.writeInt32(delay);
     // b/266537959 - must take before taking lock, since countdown is started in the remote
     // process there.
-    size_t epochMsBefore = epochMillis();
+    int64_t timeBefore = uptimeMillis();
     EXPECT_THAT(server->transact(BINDER_LIB_TEST_PROCESS_TEMPORARY_LOCK, data, &reply), NO_ERROR);
     std::vector<std::thread> ts;
     for (size_t i = 0; i < kKernelThreads + 1; i++) {
@@ -1867,10 +1865,10 @@
     for (auto &t : ts) {
         t.join();
     }
-    size_t epochMsAfter = epochMillis();
+    int64_t timeAfter = uptimeMillis();
 
     // deadlock occurred and threads only finished after 1s passed.
-    EXPECT_GE(epochMsAfter, epochMsBefore + delay);
+    EXPECT_GE(timeAfter, timeBefore + delay);
 }
 
 TEST_F(BinderLibTest, BinderProxyCount) {
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index dcda1ee..06f00a4 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -34,6 +34,8 @@
 #include <binder/Parcel.h>
 #include <binder/ProcessState.h>
 
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
@@ -116,7 +118,7 @@
         } else {
             android::os::InputChannelCore tempChannel;
             android::binder::Status result =
-                    mInputFlinger->createInputChannel("testchannels", &tempChannel);
+                    mInputFlinger->createInputChannel(sc->getName() + " channel", &tempChannel);
             if (!result.isOk()) {
                 ADD_FAILURE() << "binder call to createInputChannel failed";
             }
@@ -139,47 +141,51 @@
     }
 
     static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient>& scc,
-                                                               int width, int height) {
+                                                               int width, int height,
+                                                               const std::string& name) {
         sp<SurfaceControl> surfaceControl =
-                scc->createSurface(String8("Test Surface"), 0 /* bufHeight */, 0 /* bufWidth */,
+                scc->createSurface(String8(name.c_str()), 0 /* bufHeight */, 0 /* bufWidth */,
                                    PIXEL_FORMAT_RGBA_8888,
                                    ISurfaceComposerClient::eFXSurfaceEffect);
         return std::make_unique<InputSurface>(surfaceControl, width, height);
     }
 
     static std::unique_ptr<InputSurface> makeBufferInputSurface(
-            const sp<SurfaceComposerClient>& scc, int width, int height) {
+            const sp<SurfaceComposerClient>& scc, int width, int height,
+            const std::string& name = "Test Buffer Surface") {
         sp<SurfaceControl> surfaceControl =
-                scc->createSurface(String8("Test Buffer Surface"), width, height,
-                                   PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
+                scc->createSurface(String8(name.c_str()), width, height, PIXEL_FORMAT_RGBA_8888,
+                                   0 /* flags */);
         return std::make_unique<InputSurface>(surfaceControl, width, height);
     }
 
     static std::unique_ptr<InputSurface> makeContainerInputSurface(
-            const sp<SurfaceComposerClient>& scc, int width, int height) {
+            const sp<SurfaceComposerClient>& scc, int width, int height,
+            const std::string& name = "Test Container Surface") {
         sp<SurfaceControl> surfaceControl =
-                scc->createSurface(String8("Test Container Surface"), 0 /* bufHeight */,
-                                   0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
+                scc->createSurface(String8(name.c_str()), 0 /* bufHeight */, 0 /* bufWidth */,
+                                   PIXEL_FORMAT_RGBA_8888,
                                    ISurfaceComposerClient::eFXSurfaceContainer);
         return std::make_unique<InputSurface>(surfaceControl, width, height);
     }
 
     static std::unique_ptr<InputSurface> makeContainerInputSurfaceNoInputChannel(
-            const sp<SurfaceComposerClient>& scc, int width, int height) {
+            const sp<SurfaceComposerClient>& scc, int width, int height,
+            const std::string& name = "Test Container Surface") {
         sp<SurfaceControl> surfaceControl =
-                scc->createSurface(String8("Test Container Surface"), 100 /* height */,
-                                   100 /* width */, PIXEL_FORMAT_RGBA_8888,
+                scc->createSurface(String8(name.c_str()), 100 /* height */, 100 /* width */,
+                                   PIXEL_FORMAT_RGBA_8888,
                                    ISurfaceComposerClient::eFXSurfaceContainer);
         return std::make_unique<InputSurface>(surfaceControl, width, height,
                                               true /* noInputChannel */);
     }
 
     static std::unique_ptr<InputSurface> makeCursorInputSurface(
-            const sp<SurfaceComposerClient>& scc, int width, int height) {
+            const sp<SurfaceComposerClient>& scc, int width, int height,
+            const std::string& name = "Test Cursor Surface") {
         sp<SurfaceControl> surfaceControl =
-                scc->createSurface(String8("Test Cursor Surface"), 0 /* bufHeight */,
-                                   0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
-                                   ISurfaceComposerClient::eCursorWindow);
+                scc->createSurface(String8(name.c_str()), 0 /* bufHeight */, 0 /* bufWidth */,
+                                   PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eCursorWindow);
         return std::make_unique<InputSurface>(surfaceControl, width, height);
     }
 
@@ -410,8 +416,9 @@
 
     void TearDown() { mComposerClient->dispose(); }
 
-    std::unique_ptr<InputSurface> makeSurface(int width, int height) {
-        return InputSurface::makeColorInputSurface(mComposerClient, width, height);
+    std::unique_ptr<InputSurface> makeSurface(int width, int height,
+                                              const std::string& name = "Test Surface") const {
+        return InputSurface::makeColorInputSurface(mComposerClient, width, height, name);
     }
 
     void postBuffer(const sp<SurfaceControl>& layer, int32_t w, int32_t h) {
@@ -470,10 +477,10 @@
  * reverse order.
  */
 TEST_F(InputSurfacesTest, input_respects_positioning) {
-    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Left Surface");
     surface->showAt(100, 100);
 
-    std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100, "Right Surface");
     surface2->showAt(200, 200);
 
     injectTap(201, 201);
@@ -493,8 +500,8 @@
 }
 
 TEST_F(InputSurfacesTest, input_respects_layering) {
-    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
-    std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface 1");
+    std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100, "Test Surface 2");
 
     surface->showAt(10, 10);
     surface2->showAt(10, 10);
@@ -519,8 +526,8 @@
 // (such as shadows in dialogs). Inputs sent to the client are offset such that 0,0 is the start
 // of the client content.
 TEST_F(InputSurfacesTest, input_respects_surface_insets) {
-    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
-    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
     bgSurface->showAt(100, 100);
 
     fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
@@ -534,8 +541,8 @@
 }
 
 TEST_F(InputSurfacesTest, input_respects_surface_insets_with_replaceTouchableRegionWithCrop) {
-    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
-    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
     bgSurface->showAt(100, 100);
 
     fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
@@ -551,8 +558,8 @@
 
 // Ensure a surface whose insets are cropped, handles the touch offset correctly. ref:b/120413463
 TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) {
-    std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
-    std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100, "Parent Surface");
+    std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100, "Child Surface");
     parentSurface->showAt(100, 100);
 
     childSurface->mInputInfo->editInfo()->surfaceInset = 10;
@@ -572,8 +579,8 @@
 
 // Ensure a surface whose insets are scaled, handles the touch offset correctly.
 TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) {
-    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
-    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
     bgSurface->showAt(100, 100);
 
     fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
@@ -590,8 +597,8 @@
 }
 
 TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) {
-    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
-    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
     bgSurface->showAt(100, 100);
 
     // In case we pass the very big inset without any checking.
@@ -621,8 +628,8 @@
 }
 
 TEST_F(InputSurfacesTest, input_respects_touchable_region_offset_overflow) {
-    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
-    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
     bgSurface->showAt(100, 100);
 
     // Set the touchable region to the values at the limit of its corresponding type.
@@ -641,8 +648,8 @@
 }
 
 TEST_F(InputSurfacesTest, input_respects_scaled_touchable_region_overflow) {
-    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
-    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
     bgSurface->showAt(0, 0);
 
     fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf(
@@ -707,8 +714,8 @@
 }
 
 TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) {
-    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
-    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
 
     bgSurface->showAt(10, 10);
     fgSurface->showAt(10, 10);
@@ -839,12 +846,13 @@
 }
 
 TEST_F(InputSurfacesTest, touch_flag_obscured) {
-    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Touchable Surface");
     surface->showAt(100, 100);
 
     // Add non touchable window to fully cover touchable window. Window behind gets touch, but
     // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED
-    std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> nonTouchableSurface =
+            makeSurface(100, 100, "Non-Touchable Surface");
     nonTouchableSurface->mInputInfo->editInfo()
             ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
     nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
@@ -858,14 +866,15 @@
 }
 
 TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) {
-    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface");
     surface->showAt(100, 100);
 
     // Add non touchable window to cover touchable window, but parent is cropped to not cover area
     // that will be tapped. Window behind gets touch, but with flag
     // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED
-    std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
-    std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100, "Parent Surface");
+    std::unique_ptr<InputSurface> nonTouchableSurface =
+            makeSurface(100, 100, "Non-Touchable Surface");
     nonTouchableSurface->mInputInfo->editInfo()
             ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
     parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
@@ -885,13 +894,14 @@
 }
 
 TEST_F(InputSurfacesTest, touch_not_obscured_with_crop) {
-    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface");
     surface->showAt(100, 100);
 
     // Add non touchable window to cover touchable window, but parent is cropped to avoid covering
     // the touchable window. Window behind gets touch with no obscured flags.
-    std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
-    std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100, "Parent Surface");
+    std::unique_ptr<InputSurface> nonTouchableSurface =
+            makeSurface(100, 100, "Non-Touchable Surface");
     nonTouchableSurface->mInputInfo->editInfo()
             ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
     parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
@@ -975,12 +985,12 @@
 }
 
 TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) {
-    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface");
     surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111};
     surface->doTransaction(
             [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
     surface->showAt(100, 100);
-    std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100, "Obscuring Surface");
     obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
                                                              true);
     obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
@@ -995,12 +1005,12 @@
 }
 
 TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) {
-    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface");
     surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111};
     surface->doTransaction(
             [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
     surface->showAt(100, 100);
-    std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100, "Obscuring Surface");
     obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
                                                              true);
     obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
@@ -1017,10 +1027,10 @@
 }
 
 TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) {
-    std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300);
+    std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300, "Parent Surface");
     parentSurface->showAt(0, 0, Rect(0, 0, 300, 300));
 
-    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface");
     surface->showAt(100, 100);
     surface->doTransaction([&](auto& t, auto& sc) {
         t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
@@ -1039,10 +1049,10 @@
 }
 
 TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) {
-    std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300);
+    std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300, "Parent Surface");
     parentSurface->showAt(0, 0, Rect(0, 0, 300, 300));
 
-    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface");
     surface->doTransaction([&](auto& t, auto& sc) {
         t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
         t.reparent(sc, parentSurface->mSurfaceControl);
@@ -1105,9 +1115,11 @@
  */
 TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_crop) {
     std::unique_ptr<InputSurface> parentContainer =
-            InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
+            InputSurface::makeContainerInputSurface(mComposerClient, 0, 0,
+                                                    "Parent Container Surface");
     std::unique_ptr<InputSurface> containerSurface =
-            InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
+            InputSurface::makeContainerInputSurface(mComposerClient, 100, 100,
+                                                    "Test Container Surface");
     containerSurface->doTransaction(
             [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
     containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
@@ -1130,11 +1142,14 @@
  */
 TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_null_crop) {
     std::unique_ptr<InputSurface> bgContainer =
-            InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
+            InputSurface::makeContainerInputSurface(mComposerClient, 0, 0,
+                                                    "Background Container Surface");
     std::unique_ptr<InputSurface> parentContainer =
-            InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
+            InputSurface::makeContainerInputSurface(mComposerClient, 0, 0,
+                                                    "Parent Container Surface");
     std::unique_ptr<InputSurface> containerSurface =
-            InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
+            InputSurface::makeContainerInputSurface(mComposerClient, 100, 100,
+                                                    "Test Container Surface");
     containerSurface->doTransaction(
             [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
     containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
@@ -1160,11 +1175,11 @@
  */
 TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) {
     std::unique_ptr<InputSurface> cropLayer =
-            InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
+            InputSurface::makeContainerInputSurface(mComposerClient, 0, 0, "Crop Layer Surface");
     cropLayer->showAt(50, 50, Rect(0, 0, 20, 20));
 
     std::unique_ptr<InputSurface> containerSurface =
-            InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
+            InputSurface::makeContainerInputSurface(mComposerClient, 100, 100, "Container Surface");
     containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
     containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle =
             cropLayer->mSurfaceControl->getHandle();
@@ -1212,9 +1227,17 @@
         sp<IGraphicBufferConsumer> consumer;
         sp<IGraphicBufferProducer> producer;
         BufferQueue::createBufferQueue(&producer, &consumer);
-        consumer->setConsumerName(String8("Virtual disp consumer"));
+        consumer->setConsumerName(String8("Virtual disp consumer (MultiDisplayTests)"));
         consumer->setDefaultBufferSize(width, height);
-        mProducers.push_back(producer);
+
+        class StubConsumerListener : public BnConsumerListener {
+            virtual void onFrameAvailable(const BufferItem&) override {}
+            virtual void onBuffersReleased() override {}
+            virtual void onSidebandStreamChanged() override {}
+        };
+
+        consumer->consumerConnect(sp<StubConsumerListener>::make(), true);
+        mBufferQueues.push_back({consumer, producer});
 
         std::string name = "VirtualDisplay";
         name += std::to_string(mVirtualDisplays.size());
@@ -1231,7 +1254,7 @@
     }
 
     std::vector<sp<IBinder>> mVirtualDisplays;
-    std::vector<sp<IGraphicBufferProducer>> mProducers;
+    std::vector<std::tuple<sp<IGraphicBufferConsumer>, sp<IGraphicBufferProducer>>> mBufferQueues;
 };
 
 TEST_F(MultiDisplayTests, drop_touch_if_layer_on_invalid_display) {
diff --git a/libs/gui/tests/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp
index 5d3287d..9ffe91f 100644
--- a/libs/gui/tests/FrameRateUtilsTest.cpp
+++ b/libs/gui/tests/FrameRateUtilsTest.cpp
@@ -62,7 +62,7 @@
     // Invalid compatibility.
     EXPECT_FALSE(
             ValidateFrameRate(60.0f, -1, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
-    EXPECT_FALSE(ValidateFrameRate(60.0f, 2, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+    EXPECT_FALSE(ValidateFrameRate(60.0f, 3, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
 
     // Invalid change frame rate strategy.
     if (flags::bq_setframerate()) {
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index ca41346..3205c32 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -112,6 +112,10 @@
         static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RGBA_10101010) ==
                 AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM,
         "HAL and AHardwareBuffer pixel format don't match");
+static_assert(
+        static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::YCBCR_P210) ==
+                AHARDWAREBUFFER_FORMAT_YCbCr_P210,
+        "HAL and AHardwareBuffer pixel format don't match");
 
 static enum AHardwareBufferStatus filterStatus(status_t status) {
     switch (status) {
@@ -300,8 +304,10 @@
       if (result == 0) {
         outPlanes->planeCount = 3;
         outPlanes->planes[0].data = yuvData.y;
-        // P010 is word-aligned 10-bit semiplaner, and YCbCr_422_I is a single interleaved plane
+        // P010 & P210 are word-aligned 10-bit semiplaner, and YCbCr_422_I is a single interleaved
+        // plane
         if (format == AHARDWAREBUFFER_FORMAT_YCbCr_P010 ||
+            format == AHARDWAREBUFFER_FORMAT_YCbCr_P210 ||
             format == AHARDWAREBUFFER_FORMAT_YCbCr_422_I) {
             outPlanes->planes[0].pixelStride = 2;
         } else {
@@ -724,6 +730,7 @@
         case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
         case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
         case AHARDWAREBUFFER_FORMAT_YCbCr_P010:
+        case AHARDWAREBUFFER_FORMAT_YCbCr_P210:
             return true;
         default:
             return false;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index b3284e4..b986967 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -1009,7 +1009,7 @@
             const auto& item = layer.source.buffer;
             auto imageTextureRef = getOrCreateBackendTexture(item.buffer->getBuffer(), false);
 
-            // if the layer's buffer has a fence, then we must must respect the fence prior to using
+            // if the layer's buffer has a fence, then we must respect the fence prior to using
             // the buffer.
             if (layer.source.buffer.fence != nullptr) {
                 waitFence(context, layer.source.buffer.fence->get());
diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp
index 8d6f74b..c9f0761 100644
--- a/libs/ui/DisplayIdentification.cpp
+++ b/libs/ui/DisplayIdentification.cpp
@@ -417,6 +417,7 @@
     return DisplayIdentificationInfo{
             .id = displayId,
             .name = std::string(edid->displayName),
+            .port = port,
             .deviceProductInfo = buildDeviceProductInfo(*edid),
             .preferredDetailedTimingDescriptor = edid->preferredDetailedTimingDescriptor,
     };
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index b0c6e44..18c9a6b 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -596,6 +596,8 @@
             width = height = stride = format = usage_deprecated = 0;
             layerCount = 0;
             usage = 0;
+            native_handle_close(handle);
+            native_handle_delete(const_cast<native_handle_t*>(handle));
             handle = nullptr;
             ALOGE("unflatten: registerBuffer failed: %s (%d)", strerror(-err), err);
             return err;
diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h
index cf67d7b..cdac269 100644
--- a/libs/ui/include/ui/DisplayIdentification.h
+++ b/libs/ui/include/ui/DisplayIdentification.h
@@ -42,6 +42,7 @@
 struct DisplayIdentificationInfo {
     PhysicalDisplayId id;
     std::string name;
+    uint8_t port;
     std::optional<DeviceProductInfo> deviceProductInfo;
     std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor;
 };
diff --git a/libs/ui/include/ui/PublicFormat.h b/libs/ui/include/ui/PublicFormat.h
index e87931e..7c17763 100644
--- a/libs/ui/include/ui/PublicFormat.h
+++ b/libs/ui/include/ui/PublicFormat.h
@@ -60,6 +60,7 @@
     JPEG_R = 0x1005,
     HEIC = 0x48454946,
     HEIC_ULTRAHDR = 0x1006,
+    YCBCR_P210 = 0x3c,
 };
 
 /* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 7b2596a..8c80dd8 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -92,7 +92,7 @@
         "libaidlcommonsupport",
         "android.hardware.sensors@1.0-convert",
         "android.hardware.sensors-V1-convert",
-        "android.hardware.sensors-V2-ndk",
+        "android.hardware.sensors-V3-ndk",
         "sensorservice_flags_c_lib",
     ],
 
diff --git a/services/sensorservice/aidl/Android.bp b/services/sensorservice/aidl/Android.bp
index 542fcae..b9a3491 100644
--- a/services/sensorservice/aidl/Android.bp
+++ b/services/sensorservice/aidl/Android.bp
@@ -28,7 +28,7 @@
         "libbinder_ndk",
         "libsensor",
         "android.frameworks.sensorservice-V1-ndk",
-        "android.hardware.sensors-V2-ndk",
+        "android.hardware.sensors-V3-ndk",
     ],
     export_include_dirs: [
         "include/",
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
index b2dc89b..880df08 100644
--- a/services/sensorservice/aidl/fuzzer/Android.bp
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -20,7 +20,7 @@
         "android.companion.virtual.virtualdevice_aidl-cpp",
         "android.frameworks.sensorservice-V1-ndk",
         "android.hardware.sensors-V1-convert",
-        "android.hardware.sensors-V2-ndk",
+        "android.hardware.sensors-V3-ndk",
         "android.hardware.common-V2-ndk",
         "libsensor",
         "libfakeservicemanager",
diff --git a/services/surfaceflinger/Display/DisplaySnapshot.cpp b/services/surfaceflinger/Display/DisplaySnapshot.cpp
index 0c7a58e..3960740 100644
--- a/services/surfaceflinger/Display/DisplaySnapshot.cpp
+++ b/services/surfaceflinger/Display/DisplaySnapshot.cpp
@@ -26,11 +26,12 @@
 
 namespace android::display {
 
-DisplaySnapshot::DisplaySnapshot(PhysicalDisplayId displayId,
+DisplaySnapshot::DisplaySnapshot(PhysicalDisplayId displayId, uint8_t port,
                                  ui::DisplayConnectionType connectionType,
                                  DisplayModes&& displayModes, ui::ColorModes&& colorModes,
                                  std::optional<DeviceProductInfo>&& deviceProductInfo)
       : mDisplayId(displayId),
+        mPort(port),
         mConnectionType(connectionType),
         mDisplayModes(std::move(displayModes)),
         mColorModes(std::move(colorModes)),
@@ -62,6 +63,8 @@
 void DisplaySnapshot::dump(utils::Dumper& dumper) const {
     using namespace std::string_view_literals;
 
+    dumper.dump("port"sv, mPort);
+
     dumper.dump("connectionType"sv, ftl::enum_string(mConnectionType));
 
     dumper.dump("colorModes"sv);
diff --git a/services/surfaceflinger/Display/DisplaySnapshot.h b/services/surfaceflinger/Display/DisplaySnapshot.h
index 23471f5..0030aad 100644
--- a/services/surfaceflinger/Display/DisplaySnapshot.h
+++ b/services/surfaceflinger/Display/DisplaySnapshot.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <cstdint>
 #include <optional>
 
 #include <ui/ColorMode.h>
@@ -30,13 +31,14 @@
 // Immutable state of a physical display, captured on hotplug.
 class DisplaySnapshot {
 public:
-    DisplaySnapshot(PhysicalDisplayId, ui::DisplayConnectionType, DisplayModes&&, ui::ColorModes&&,
-                    std::optional<DeviceProductInfo>&&);
+    DisplaySnapshot(PhysicalDisplayId, uint8_t, ui::DisplayConnectionType, DisplayModes&&,
+                    ui::ColorModes&&, std::optional<DeviceProductInfo>&&);
 
     DisplaySnapshot(const DisplaySnapshot&) = delete;
     DisplaySnapshot(DisplaySnapshot&&) = default;
 
     PhysicalDisplayId displayId() const { return mDisplayId; }
+    uint8_t port() const { return mPort; }
     ui::DisplayConnectionType connectionType() const { return mConnectionType; }
 
     std::optional<DisplayModeId> translateModeId(hal::HWConfigId) const;
@@ -51,6 +53,7 @@
 
 private:
     const PhysicalDisplayId mDisplayId;
+    const uint8_t mPort;
     const ui::DisplayConnectionType mConnectionType;
 
     // Effectively const except in move constructor.
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 55ccdef..721cfd3 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -1180,6 +1180,7 @@
             return DisplayIdentificationInfo{.id = PhysicalDisplayId::fromPort(port),
                                              .name = isPrimary ? "Primary display"
                                                                : "Secondary display",
+                                             .port = port,
                                              .deviceProductInfo = std::nullopt};
         }();
 
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 171342d..630beb0 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -280,6 +280,9 @@
                     case Layer::FrameRateCompatibility::Exact:
                         return LayerVoteType::ExplicitExact;
                     case Layer::FrameRateCompatibility::Gte:
+                        if (frameRate.isNoVote()) {
+                            return LayerVoteType::NoVote;
+                        }
                         if (isVrrDevice) {
                             return LayerVoteType::ExplicitGte;
                         } else {
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 356c30e..6e2b943 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -562,7 +562,10 @@
 }
 
 bool LayerInfo::FrameRate::isNoVote() const {
-    return vote.type == FrameRateCompatibility::NoVote;
+    // A desired frame rate greater than or equal to 0 is treated as NoVote.
+    bool isNoVoteGte = FlagManager::getInstance().arr_setframerate_gte_enum() &&
+            vote.type == FrameRateCompatibility::Gte && !vote.rate.isValid();
+    return vote.type == FrameRateCompatibility::NoVote || isNoVoteGte;
 }
 
 bool LayerInfo::FrameRate::isValuelessType() const {
@@ -577,7 +580,12 @@
         case FrameRateCompatibility::Default:
         case FrameRateCompatibility::ExactOrMultiple:
         case FrameRateCompatibility::Exact:
+            return false;
         case FrameRateCompatibility::Gte:
+            if (isNoVote()) {
+                // Special case: GTE 0 is same as NoVote.
+                return true;
+            }
             return false;
     }
 }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 3fdddac..a2cdd46 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -209,6 +209,7 @@
         ftl::FakeGuard guard(kMainThreadContext);
         resyncToHardwareVsyncLocked(id, allowToEnable, modePtr);
     }
+    void resync() override EXCLUDES(mDisplayLock);
     void forceNextResync() { mLastResyncTime = 0; }
 
     // Passes a vsync sample to VsyncController. Returns true if
@@ -470,7 +471,6 @@
     bool throttleVsync(TimePoint, uid_t) override;
     // Get frame interval
     Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock);
-    void resync() override EXCLUDES(mDisplayLock);
     void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock);
 
     std::unique_ptr<EventThread> mRenderEventThread;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 59b1917..e6b6f4e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -877,6 +877,9 @@
     } else if (algorithm == "kawase2") {
         return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER;
     } else {
+        if (FlagManager::getInstance().window_blur_kawase2()) {
+            return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER;
+        }
         return renderengine::RenderEngine::BlurAlgorithm::KAWASE;
     }
 }
@@ -3631,10 +3634,13 @@
             deviceProductInfo = snapshot.deviceProductInfo();
         }
 
+        // Use the cached port via snapshot because we are updating an existing
+        // display on reconnect.
         const auto it =
                 mPhysicalDisplays.try_replace(displayId, display.token(), displayId,
-                                              snapshot.connectionType(), std::move(displayModes),
-                                              std::move(colorModes), std::move(deviceProductInfo));
+                                              snapshot.port(), snapshot.connectionType(),
+                                              std::move(displayModes), std::move(colorModes),
+                                              std::move(deviceProductInfo));
 
         auto& state = mCurrentState.displays.editValueFor(it->second.token());
         state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId.
@@ -3647,7 +3653,7 @@
     const ui::DisplayConnectionType connectionType =
             getHwComposer().getDisplayConnectionType(displayId);
 
-    mPhysicalDisplays.try_emplace(displayId, token, displayId, connectionType,
+    mPhysicalDisplays.try_emplace(displayId, token, displayId, info.port, connectionType,
                                   std::move(displayModes), std::move(colorModes),
                                   std::move(info.deviceProductInfo));
 
@@ -4563,6 +4569,7 @@
     SFTRACE_INT("mTransactionFlags", transactionFlags);
 
     if (const bool scheduled = transactionFlags & mask; !scheduled) {
+        mScheduler->resync();
         scheduleCommit(frameHint);
     } else if (frameHint == FrameHint::kActive) {
         // Even if the next frame is already scheduled, we should reset the idle timer
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index f9c99bf..8786f6e 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -38,6 +38,7 @@
     ],
     static_libs: [
         "libsurfaceflingerflags",
+        "aconfig_hardware_flags_c_lib",
         "android.os.flags-aconfig-cc",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
@@ -51,6 +52,7 @@
     ],
     static_libs: [
         "libsurfaceflingerflags_test",
+        "aconfig_hardware_flags_c_lib",
         "android.os.flags-aconfig-cc-test",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
@@ -67,6 +69,7 @@
     static_libs: [
         "libsurfaceflinger_common",
         "libsurfaceflingerflags",
+        "aconfig_hardware_flags_c_lib",
         "android.os.flags-aconfig-cc",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
@@ -83,6 +86,7 @@
     static_libs: [
         "libsurfaceflinger_common_test",
         "libsurfaceflingerflags_test",
+        "aconfig_hardware_flags_c_lib",
         "android.os.flags-aconfig-cc-test",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index e28a0c1..5e78426 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -27,6 +27,7 @@
 #include <cinttypes>
 
 #include <android_os.h>
+#include <android_hardware_flags.h>
 #include <com_android_graphics_libgui_flags.h>
 #include <com_android_graphics_surfaceflinger_flags.h>
 #include <com_android_server_display_feature_flags.h>
@@ -164,6 +165,7 @@
     DUMP_ACONFIG_FLAG(deprecate_frame_tracker);
     DUMP_ACONFIG_FLAG(skip_invisible_windows_in_input);
     DUMP_ACONFIG_FLAG(begone_bright_hlg);
+    DUMP_ACONFIG_FLAG(window_blur_kawase2);
 
 #undef DUMP_ACONFIG_FLAG
 #undef DUMP_LEGACY_SERVER_FLAG
@@ -263,6 +265,7 @@
 FLAG_MANAGER_ACONFIG_FLAG(deprecate_frame_tracker, "");
 FLAG_MANAGER_ACONFIG_FLAG(skip_invisible_windows_in_input, "");
 FLAG_MANAGER_ACONFIG_FLAG(begone_bright_hlg, "debug.sf.begone_bright_hlg");
+FLAG_MANAGER_ACONFIG_FLAG(window_blur_kawase2, "");
 
 /// Trunk stable server (R/W) flags ///
 FLAG_MANAGER_ACONFIG_FLAG(refresh_rate_overlay_on_external_display, "")
@@ -279,5 +282,6 @@
 FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel_fixed, "", android::os)
 FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(trace_frame_rate_override, "",
                                    com::android::graphics::libgui::flags);
-
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(luts_api, "",
+                                   android::hardware::flags);
 } // namespace android
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 1a857c8..d8887f5 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -102,6 +102,8 @@
     bool deprecate_frame_tracker() const;
     bool skip_invisible_windows_in_input() const;
     bool begone_bright_hlg() const;
+    bool luts_api() const;
+    bool window_blur_kawase2() const;
 
 protected:
     // overridden for unit tests
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index d6a1ad4..bdd826d 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -31,6 +31,7 @@
   description: "New SDK Surface#setFrameRate API and Surface.FrameRateParams for Android 16"
   bug: "356987016"
   is_fixed_read_only: true
+  is_exported: true
 } # arr_setframerate_api
 
 flag {
@@ -47,6 +48,7 @@
   description: "New SDK SurfaceControl.Transaction#setFrameRate API for Android 16"
   bug: "356987016"
   is_fixed_read_only: true
+  is_exported: true
 } # arr_surfacecontrol_setframerate_api
 
 flag {
@@ -293,4 +295,11 @@
   }
 } # vrr_bugfix_dropped_frame
 
+flag {
+  name: "window_blur_kawase2"
+  namespace: "core_graphics"
+  description: "Flag for using Kawase2 algorithm for window blur"
+  bug: "353826438"
+} # window_blur_kawase2
+
 # IMPORTANT - please keep alphabetize to reduce merge conflicts
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 56cf13d..65add63 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -19,6 +19,7 @@
 #pragma clang diagnostic ignored "-Wconversion"
 
 #include <common/FlagManager.h>
+#include <gui/IConsumerListener.h>
 #include <ui/DisplayState.h>
 
 #include "LayerTransactionTest.h"
@@ -45,11 +46,17 @@
         SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState);
         SurfaceComposerClient::getActiveDisplayMode(mMainDisplay, &mMainDisplayMode);
 
-        sp<IGraphicBufferConsumer> consumer;
-        BufferQueue::createBufferQueue(&mProducer, &consumer);
-        consumer->setConsumerName(String8("Virtual disp consumer"));
-        consumer->setDefaultBufferSize(mMainDisplayMode.resolution.getWidth(),
-                                       mMainDisplayMode.resolution.getHeight());
+        BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+        mConsumer->setConsumerName(String8("Virtual disp consumer (MultiDisplayLayerBounds)"));
+        mConsumer->setDefaultBufferSize(mMainDisplayMode.resolution.getWidth(),
+                                        mMainDisplayMode.resolution.getHeight());
+
+        class StubConsumerListener : public BnConsumerListener {
+            virtual void onFrameAvailable(const BufferItem&) override {}
+            virtual void onBuffersReleased() override {}
+            virtual void onSidebandStreamChanged() override {}
+        };
+        mConsumer->consumerConnect(sp<StubConsumerListener>::make(), true);
     }
 
     virtual void TearDown() {
@@ -92,6 +99,7 @@
     sp<IBinder> mMainDisplay;
     PhysicalDisplayId mMainDisplayId;
     sp<IBinder> mVirtualDisplay;
+    sp<IGraphicBufferConsumer> mConsumer;
     sp<IGraphicBufferProducer> mProducer;
     sp<SurfaceControl> mColorLayer;
     Color mExpectedColor = {63, 63, 195, 255};
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index bf5957a..c95c875 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -58,7 +58,7 @@
                         GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_SW_READ_OFTEN);
                 sp<BufferListener> listener = sp<BufferListener>::make(this);
                 itemConsumer->setFrameAvailableListener(listener);
-                itemConsumer->setName(String8("Virtual disp consumer"));
+                itemConsumer->setName(String8("Virtual disp consumer (TransactionTest)"));
                 itemConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
 #else
                 sp<IGraphicBufferProducer> producer;
@@ -66,7 +66,7 @@
                 sp<BufferItemConsumer> itemConsumer;
                 BufferQueue::createBufferQueue(&producer, &consumer);
 
-                consumer->setConsumerName(String8("Virtual disp consumer"));
+                consumer->setConsumerName(String8("Virtual disp consumer (TransactionTest)"));
                 consumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
 
                 itemConsumer = sp<BufferItemConsumer>::make(consumer,
diff --git a/services/surfaceflinger/tests/VirtualDisplay_test.cpp b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
index d69378c..1108c7f 100644
--- a/services/surfaceflinger/tests/VirtualDisplay_test.cpp
+++ b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
@@ -29,14 +29,14 @@
     void SetUp() override {
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
         mGLConsumer = sp<GLConsumer>::make(GLConsumer::TEXTURE_EXTERNAL, true, false, false);
-        mGLConsumer->setName(String8("Virtual disp consumer"));
+        mGLConsumer->setName(String8("Virtual disp consumer (VirtualDisplayTest)"));
         mGLConsumer->setDefaultBufferSize(100, 100);
         mProducer = mGLConsumer->getSurface()->getIGraphicBufferProducer();
 #else
         sp<IGraphicBufferConsumer> consumer;
 
         BufferQueue::createBufferQueue(&mProducer, &consumer);
-        consumer->setConsumerName(String8("Virtual disp consumer"));
+        consumer->setConsumerName(String8("Virtual disp consumer (VirtualDisplayTest)"));
         consumer->setDefaultBufferSize(100, 100);
 
         mGLConsumer = sp<GLConsumer>::make(consumer, GLConsumer::TEXTURE_EXTERNAL, true, false);
diff --git a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
index b517ff0..c858d9a 100644
--- a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
+++ b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
@@ -54,8 +54,8 @@
                 compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(),
                                                        std::move(compostionEngineDisplayArgs));
         mDisplay = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
-                                             ui::DisplayConnectionType::Internal, HWC_DISPLAY,
-                                             kIsPrimary)
+                                             ui::DisplayConnectionType::Internal,
+                                             DEFAULT_DISPLAY_PORT, HWC_DISPLAY, kIsPrimary)
                            .setDisplaySurface(mDisplaySurface)
                            .setNativeWindow(mNativeWindow)
                            .setPowerMode(hal::PowerMode::ON)
@@ -68,7 +68,9 @@
     using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
 
     static constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID;
-    static constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+    static constexpr uint8_t DEFAULT_DISPLAY_PORT = 42u;
+    static constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID =
+            PhysicalDisplayId::fromPort(DEFAULT_DISPLAY_PORT);
     static constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
     static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
 
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 860ad2e..71cafbf 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -76,7 +76,8 @@
 constexpr hal::HWLayerId HWC_LAYER = 5000;
 constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0);
 
-constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+constexpr uint8_t DEFAULT_DISPLAY_PORT = 42u;
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(DEFAULT_DISPLAY_PORT);
 constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
 constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
 
@@ -276,7 +277,8 @@
 
         test->mDisplay =
                 FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
-                                          kDisplayConnectionType, HWC_DISPLAY, kIsPrimary)
+                                          kDisplayConnectionType, DEFAULT_DISPLAY_PORT, HWC_DISPLAY,
+                                          kIsPrimary)
                         .setDisplaySurface(test->mDisplaySurface)
                         .setNativeWindow(test->mNativeWindow)
                         .setSecure(Derived::IS_SECURE)
diff --git a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
index 29a1fab..c6cbe52 100644
--- a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
@@ -71,7 +71,7 @@
         ASSERT_TRUE(infoOpt);
 
         mDisplayId = infoOpt->id;
-        mDisplaySnapshotOpt.emplace(mDisplayId, ui::DisplayConnectionType::Internal,
+        mDisplaySnapshotOpt.emplace(mDisplayId, infoOpt->port, ui::DisplayConnectionType::Internal,
                                     makeModes(kMode60, kMode90, kMode120), ui::ColorModes{},
                                     std::nullopt);
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index fa976c8..6e231aa 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -231,6 +231,16 @@
     static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID;
 };
 
+template <typename>
+struct PortGetter {
+    static constexpr std::optional<uint8_t> value;
+};
+
+template <typename PhysicalDisplay>
+struct PortGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+    static constexpr std::optional<uint8_t> value = PhysicalDisplay::PORT;
+};
+
 // DisplayIdType can be:
 //     1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC.
 //     2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
@@ -241,6 +251,7 @@
     using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
     using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
     using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
+    using PORT = PortGetter<DisplayIdType>;
 
     static constexpr int WIDTH = width;
     static constexpr int HEIGHT = height;
@@ -277,6 +288,7 @@
                 TestableSurfaceFlinger::FakeDisplayDeviceInjector(test->mFlinger,
                                                                   compositionDisplay,
                                                                   CONNECTION_TYPE::value,
+                                                                  PORT::value,
                                                                   HWC_DISPLAY_ID_OPT::value,
                                                                   static_cast<bool>(PRIMARY));
 
@@ -558,6 +570,10 @@
                                                 /*hasIdentificationData=*/true, kNonSecure>,
                                1080, 2092>;
 
+using ExternalDisplayWithIdentificationVariant =
+        PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External,
+                                                /*hasIdentificationData=*/true, kNonSecure>,
+                               1920, 1280>;
 using ExternalDisplayVariant =
         PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External,
                                                 /*hasIdentificationData=*/false, kSecure>,
diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
index 744c536..5f7a9f2 100644
--- a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
+++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
@@ -24,12 +24,15 @@
 
 namespace android {
 
+static constexpr uint8_t kDefaultPort = 255u;
+
 using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
 using android::adpf::mock::PowerAdvisor;
 using android::hardware::graphics::composer::hal::HWDisplayId;
 
 struct FakeDisplayInjectorArgs {
-    PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(255u);
+    PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(kDefaultPort);
+    uint8_t port = kDefaultPort;
     HWDisplayId hwcDisplayId = 0;
     bool isPrimary = true;
 };
@@ -73,7 +76,7 @@
                                       .build());
 
         auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
-                                                  ui::DisplayConnectionType::Internal,
+                                                  ui::DisplayConnectionType::Internal, args.port,
                                                   args.hwcDisplayId, args.isPrimary);
 
         injector.setNativeWindow(mNativeWindow);
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index f9bfe97..53a9062 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -654,6 +654,72 @@
     EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
 }
 
+TEST_F(LayerHistoryIntegrationTest, oneLayerGteNoVote_arr) {
+    SET_FLAG_FOR_TEST(flags::arr_setframerate_gte_enum, true);
+    // Set the test to be on a vrr mode.
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // Layer is active but GTE with 0 should be considered NoVote, thus nothing from summarize.
+    ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive.
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerGteNoVote_mrr) {
+    SET_FLAG_FOR_TEST(flags::arr_setframerate_gte_enum, true);
+    // True by default on MRR devices as well, but the device is not set to VRR mode.
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, 0);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // Layer is active but GTE with 0 should be considered NoVote, thus nothing from summarize.
+    ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive.
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
 TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) {
     SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index b0dd5c2..a506873 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -126,8 +126,9 @@
 
     static constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
     static constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
-
-    static constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u);
+    static constexpr uint8_t kOuterDisplayPort = 254u;
+    static constexpr PhysicalDisplayId kOuterDisplayId =
+            PhysicalDisplayId::fromPort(kOuterDisplayPort);
 
     auto injectOuterDisplay() {
         // For the inner display, this is handled by setupHwcHotplugCallExpectations.
@@ -149,6 +150,7 @@
                                              kModeId120);
                 },
                 {.displayId = kOuterDisplayId,
+                 .port = kOuterDisplayPort,
                  .hwcDisplayId = kOuterDisplayHwcId,
                  .isPrimary = kIsPrimary});
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index aef467a..4e7a174 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -59,6 +59,137 @@
     EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
 }
 
+TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithIdentificationData) {
+    // Configure a primary display with identification data.
+    using PrimaryDisplay = InnerDisplayVariant;
+    PrimaryDisplay::setupHwcHotplugCallExpectations(this);
+    PrimaryDisplay::setupHwcGetActiveConfigCallExpectations(this);
+    PrimaryDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+    // TODO(b/241286146): Remove this unnecessary call.
+    EXPECT_CALL(*mComposer,
+                setVsyncEnabled(PrimaryDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+            .WillOnce(Return(Error::NONE));
+
+    // A single commit should be scheduled.
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
+
+    mFlinger.configure();
+
+    // Configure an external display with identification info.
+    using ExternalDisplay = ExternalDisplayWithIdentificationVariant;
+    ExternalDisplay::setupHwcHotplugCallExpectations(this);
+    ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+    // TODO(b/241286146): Remove this unnecessary call.
+    EXPECT_CALL(*mComposer,
+                setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+            .WillOnce(Return(Error::NONE));
+
+    mFlinger.configure();
+
+    EXPECT_TRUE(hasPhysicalHwcDisplay(PrimaryDisplay::HWC_DISPLAY_ID));
+    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(PrimaryDisplay::DISPLAY_ID::get()));
+    const auto primaryDisplayIdOpt =
+            mFlinger.getHwComposer().toPhysicalDisplayId(PrimaryDisplay::HWC_DISPLAY_ID);
+    ASSERT_TRUE(primaryDisplayIdOpt.has_value());
+    const auto primaryPhysicalDisplayOpt =
+            mFlinger.physicalDisplays().get(primaryDisplayIdOpt.value());
+    ASSERT_TRUE(primaryPhysicalDisplayOpt.has_value());
+    const auto primaryDisplaySnapshotRef = primaryPhysicalDisplayOpt->get().snapshotRef();
+    EXPECT_EQ(PrimaryDisplay::DISPLAY_ID::get(), primaryDisplaySnapshotRef.get().displayId());
+    EXPECT_EQ(PrimaryDisplay::PORT::value, primaryDisplaySnapshotRef.get().port());
+    EXPECT_EQ(PrimaryDisplay::CONNECTION_TYPE::value,
+              primaryDisplaySnapshotRef.get().connectionType());
+
+    EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
+    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(ExternalDisplay::DISPLAY_ID::get()));
+    const auto externalDisplayIdOpt =
+            mFlinger.getHwComposer().toPhysicalDisplayId(ExternalDisplay::HWC_DISPLAY_ID);
+    ASSERT_TRUE(externalDisplayIdOpt.has_value());
+    const auto externalPhysicalDisplayOpt =
+            mFlinger.physicalDisplays().get(externalDisplayIdOpt.value());
+    ASSERT_TRUE(externalPhysicalDisplayOpt.has_value());
+    const auto externalDisplaySnapshotRef = externalPhysicalDisplayOpt->get().snapshotRef();
+    EXPECT_EQ(ExternalDisplay::DISPLAY_ID::get(), externalDisplaySnapshotRef.get().displayId());
+    EXPECT_EQ(ExternalDisplay::PORT::value, externalDisplaySnapshotRef.get().port());
+    EXPECT_EQ(ExternalDisplay::CONNECTION_TYPE::value,
+              externalDisplaySnapshotRef.get().connectionType());
+}
+
+TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithoutIdentificationData) {
+    // Configure a primary display without identification data.
+    using PrimaryDisplay = PrimaryDisplayVariant;
+    PrimaryDisplay::setupHwcHotplugCallExpectations(this);
+    PrimaryDisplay::setupHwcGetActiveConfigCallExpectations(this);
+    PrimaryDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+    // TODO(b/241286146): Remove this unnecessary call.
+    EXPECT_CALL(*mComposer,
+                setVsyncEnabled(PrimaryDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+            .WillOnce(Return(Error::NONE));
+
+    // A single commit should be scheduled.
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
+
+    mFlinger.configure();
+
+    // Configure an external display with identification info.
+    using ExternalDisplay = ExternalDisplayWithIdentificationVariant;
+    ExternalDisplay::setupHwcHotplugCallExpectations(this);
+    ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+    // TODO(b/241286146): Remove this unnecessary call.
+    EXPECT_CALL(*mComposer,
+                setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+            .WillOnce(Return(Error::NONE));
+
+    mFlinger.configure();
+
+    // Both ID and port are expected to be equal to 0 for primary internal display due to no
+    // identification data.
+    constexpr uint8_t primaryInternalDisplayPort = 0u;
+    constexpr PhysicalDisplayId primaryInternalDisplayId =
+            PhysicalDisplayId::fromPort(primaryInternalDisplayPort);
+    EXPECT_TRUE(hasPhysicalHwcDisplay(PrimaryDisplay::HWC_DISPLAY_ID));
+    ASSERT_EQ(primaryInternalDisplayId, PrimaryDisplay::DISPLAY_ID::get());
+    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(PrimaryDisplay::DISPLAY_ID::get()));
+    const auto primaryDisplayIdOpt =
+            mFlinger.getHwComposer().toPhysicalDisplayId(PrimaryDisplay::HWC_DISPLAY_ID);
+    ASSERT_TRUE(primaryDisplayIdOpt.has_value());
+    const auto primaryPhysicalDisplayOpt =
+            mFlinger.physicalDisplays().get(primaryDisplayIdOpt.value());
+    ASSERT_TRUE(primaryPhysicalDisplayOpt.has_value());
+    const auto primaryDisplaySnapshotRef = primaryPhysicalDisplayOpt->get().snapshotRef();
+    EXPECT_EQ(primaryInternalDisplayId, primaryDisplaySnapshotRef.get().displayId());
+    EXPECT_EQ(primaryInternalDisplayPort, primaryDisplaySnapshotRef.get().port());
+    EXPECT_EQ(PrimaryDisplay::CONNECTION_TYPE::value,
+              primaryDisplaySnapshotRef.get().connectionType());
+
+    // Even though the external display has identification data available, the lack of data for the
+    // internal display has set of the legacy multi-display mode in SF and therefore the external
+    // display's identification data will be ignored.
+    // Both ID and port are expected to be equal to 1 for external internal display.
+    constexpr uint8_t externalDisplayPort = 1u;
+    constexpr PhysicalDisplayId externalDisplayId =
+            PhysicalDisplayId::fromPort(externalDisplayPort);
+    EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
+    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(externalDisplayId));
+    const auto externalDisplayIdOpt =
+            mFlinger.getHwComposer().toPhysicalDisplayId(ExternalDisplay::HWC_DISPLAY_ID);
+    ASSERT_TRUE(externalDisplayIdOpt.has_value());
+    const auto externalPhysicalDisplayOpt =
+            mFlinger.physicalDisplays().get(externalDisplayIdOpt.value());
+    ASSERT_TRUE(externalPhysicalDisplayOpt.has_value());
+    const auto externalDisplaySnapshotRef = externalPhysicalDisplayOpt->get().snapshotRef();
+    EXPECT_EQ(externalDisplayId, externalDisplaySnapshotRef.get().displayId());
+    EXPECT_EQ(externalDisplayPort, externalDisplaySnapshotRef.get().port());
+    EXPECT_EQ(ExternalDisplay::CONNECTION_TYPE::value,
+              externalDisplaySnapshotRef.get().connectionType());
+}
+
 TEST_F(HotplugTest, ignoresDuplicateDisconnection) {
     // Inject a primary display.
     PrimaryDisplayVariant::injectHwcDisplay(this);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 352000e..6951eaf 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -239,6 +239,8 @@
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
+        const auto port = Case::Display::PORT::value;
+        ASSERT_TRUE(port);
         mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId, std::nullopt);
         DisplayModePtr activeMode = DisplayMode::Builder(Case::Display::HWC_ACTIVE_CONFIG_ID)
                                             .setResolution(Case::Display::RESOLUTION)
@@ -258,7 +260,7 @@
         }
 
         const auto it = mFlinger.mutablePhysicalDisplays()
-                                .emplace_or_replace(*displayId, displayToken, *displayId,
+                                .emplace_or_replace(*displayId, displayToken, *displayId, *port,
                                                     *kConnectionTypeOpt, makeModes(activeMode),
                                                     std::move(colorModes), std::nullopt)
                                 .first;
@@ -299,6 +301,13 @@
                   mFlinger.mutableDisplayModeController()
                           .getActiveMode(device->getPhysicalId())
                           .modePtr->getHwcId());
+
+        EXPECT_EQ(Case::Display::PORT::value,
+                  mFlinger.physicalDisplays()
+                          .get(device->getPhysicalId())
+                          .transform([](const display::PhysicalDisplay& display) {
+                              return display.snapshot().port();
+                          }));
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 7f0b7a6..bd1382e 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -947,11 +947,13 @@
         FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger,
                                   std::shared_ptr<compositionengine::Display> display,
                                   std::optional<ui::DisplayConnectionType> connectionType,
+                                  std::optional<uint8_t> port,
                                   std::optional<hal::HWDisplayId> hwcDisplayId, bool isPrimary)
               : mFlinger(flinger),
                 mCreationArgs(flinger.mFlinger, flinger.mFlinger->getHwComposer(), mDisplayToken,
                               display),
                 mConnectionType(connectionType),
+                mPort(port),
                 mHwcDisplayId(hwcDisplayId) {
             mCreationArgs.isPrimary = isPrimary;
             mCreationArgs.initialPowerMode = hal::PowerMode::ON;
@@ -1100,11 +1102,12 @@
                                   .hwcDisplayId = *mHwcDisplayId,
                                   .activeMode = activeModeOpt->get()};
 
-                const auto it = mFlinger.mutablePhysicalDisplays()
-                                        .emplace_or_replace(*physicalId, mDisplayToken, *physicalId,
-                                                            *mConnectionType, std::move(modes),
-                                                            ui::ColorModes(), std::nullopt)
-                                        .first;
+                const auto it =
+                        mFlinger.mutablePhysicalDisplays()
+                                .emplace_or_replace(*physicalId, mDisplayToken, *physicalId, *mPort,
+                                                    *mConnectionType, std::move(modes),
+                                                    ui::ColorModes(), std::nullopt)
+                                .first;
 
                 mFlinger.mutableDisplayModeController()
                         .registerDisplay(*physicalId, it->second.snapshot(),
@@ -1138,6 +1141,7 @@
         DisplayModeId mActiveModeId;
         bool mSchedulerRegistration = true;
         const std::optional<ui::DisplayConnectionType> mConnectionType;
+        const std::optional<uint8_t> mPort;
         const std::optional<hal::HWDisplayId> mHwcDisplayId;
     };
 
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index 9b508aa..3cb9405 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -400,6 +400,78 @@
   }
 };
 
+template <>
+struct EnumTraits<VkPipelineRobustnessBufferBehavior> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DEVICE_DEFAULT:
+      case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED:
+      case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS:
+      case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_2:
+        return true;
+    }
+    return false;
+  }
+};
+
+template <>
+struct EnumTraits<VkPipelineRobustnessImageBehavior> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_DEVICE_DEFAULT:
+      case VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_DISABLED:
+      case VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_ROBUST_IMAGE_ACCESS:
+      case VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_ROBUST_IMAGE_ACCESS_2:
+        return true;
+    }
+    return false;
+  }
+};
+
+template <>
+struct EnumTraits<VkImageLayout> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_IMAGE_LAYOUT_UNDEFINED:
+      case VK_IMAGE_LAYOUT_GENERAL:
+      case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
+      case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
+      case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
+      case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
+      case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
+      case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
+      case VK_IMAGE_LAYOUT_PREINITIALIZED:
+      case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL:
+      case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL:
+      case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL:
+      case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL:
+      case VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL:
+      case VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL:
+      case VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL:
+      case VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL:
+      case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
+      case VK_IMAGE_LAYOUT_VIDEO_DECODE_DST_KHR:
+      case VK_IMAGE_LAYOUT_VIDEO_DECODE_SRC_KHR:
+      case VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR:
+      case VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR:
+      case VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT:
+      case VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR:
+#ifdef VK_ENABLE_BETA_EXTENSIONS
+      case VK_IMAGE_LAYOUT_VIDEO_ENCODE_DST_KHR:
+#endif
+#ifdef VK_ENABLE_BETA_EXTENSIONS
+      case VK_IMAGE_LAYOUT_VIDEO_ENCODE_SRC_KHR:
+#endif
+#ifdef VK_ENABLE_BETA_EXTENSIONS
+      case VK_IMAGE_LAYOUT_VIDEO_ENCODE_DPB_KHR:
+#endif
+      case VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT:
+        return true;
+    }
+    return false;
+  }
+};
+
 // VkSparseImageFormatProperties
 
 template <typename Visitor>
@@ -810,6 +882,68 @@
     visitor->Visit("shaderIntegerDotProduct", &features->shaderIntegerDotProduct) &&
     visitor->Visit("maintenance4", &features->maintenance4);
 }
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonCore14* core) {
+  return
+    visitor->Visit("features", &core->features) &&
+    visitor->Visit("properties", &core->properties);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan14Properties* properties) {
+  return
+    visitor->Visit("lineSubPixelPrecisionBits", &properties->lineSubPixelPrecisionBits) &&
+    visitor->Visit("maxVertexAttribDivisor", &properties->maxVertexAttribDivisor) &&
+    visitor->Visit("supportsNonZeroFirstInstance", &properties->supportsNonZeroFirstInstance) &&
+    visitor->Visit("maxPushDescriptors", &properties->maxPushDescriptors) &&
+    visitor->Visit("dynamicRenderingLocalReadDepthStencilAttachments", &properties->dynamicRenderingLocalReadDepthStencilAttachments) &&
+    visitor->Visit("dynamicRenderingLocalReadMultisampledAttachments", &properties->dynamicRenderingLocalReadMultisampledAttachments) &&
+    visitor->Visit("earlyFragmentMultisampleCoverageAfterSampleCounting", &properties->earlyFragmentMultisampleCoverageAfterSampleCounting) &&
+    visitor->Visit("earlyFragmentSampleMaskTestBeforeSampleCounting", &properties->earlyFragmentSampleMaskTestBeforeSampleCounting) &&
+    visitor->Visit("depthStencilSwizzleOneSupport", &properties->depthStencilSwizzleOneSupport) &&
+    visitor->Visit("polygonModePointSize", &properties->polygonModePointSize) &&
+    visitor->Visit("nonStrictSinglePixelWideLinesUseParallelogram", &properties->nonStrictSinglePixelWideLinesUseParallelogram) &&
+    visitor->Visit("nonStrictWideLinesUseParallelogram", &properties->nonStrictWideLinesUseParallelogram) &&
+    visitor->Visit("blockTexelViewCompatibleMultipleLayers", &properties->blockTexelViewCompatibleMultipleLayers) &&
+    visitor->Visit("maxCombinedImageSamplerDescriptorCount", &properties->maxCombinedImageSamplerDescriptorCount) &&
+    visitor->Visit("fragmentShadingRateClampCombinerInputs", &properties->fragmentShadingRateClampCombinerInputs) &&
+    visitor->Visit("defaultRobustnessStorageBuffers", &properties->defaultRobustnessStorageBuffers) &&
+    visitor->Visit("defaultRobustnessUniformBuffers", &properties->defaultRobustnessUniformBuffers) &&
+    visitor->Visit("defaultRobustnessVertexInputs", &properties->defaultRobustnessVertexInputs) &&
+    visitor->Visit("defaultRobustnessImages", &properties->defaultRobustnessImages) &&
+    visitor->Visit("copySrcLayoutCount", &properties->copySrcLayoutCount) &&
+    visitor->VisitArray("pCopySrcLayouts", properties->copySrcLayoutCount, &properties->pCopySrcLayouts) &&
+    visitor->Visit("copyDstLayoutCount", &properties->copyDstLayoutCount) &&
+    visitor->VisitArray("pCopyDstLayouts", properties->copyDstLayoutCount, &properties->pCopyDstLayouts) &&
+    visitor->Visit("optimalTilingLayoutUUID", &properties->optimalTilingLayoutUUID) &&
+    visitor->Visit("identicalMemoryTypeRequirements", &properties->identicalMemoryTypeRequirements);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan14Features* features) {
+  return
+    visitor->Visit("globalPriorityQuery", &features->globalPriorityQuery) &&
+    visitor->Visit("shaderSubgroupRotate", &features->shaderSubgroupRotate) &&
+    visitor->Visit("shaderSubgroupRotateClustered", &features->shaderSubgroupRotateClustered) &&
+    visitor->Visit("shaderFloatControls2", &features->shaderFloatControls2) &&
+    visitor->Visit("shaderExpectAssume", &features->shaderExpectAssume) &&
+    visitor->Visit("rectangularLines", &features->rectangularLines) &&
+    visitor->Visit("bresenhamLines", &features->bresenhamLines) &&
+    visitor->Visit("smoothLines", &features->smoothLines) &&
+    visitor->Visit("stippledRectangularLines", &features->stippledRectangularLines) &&
+    visitor->Visit("stippledBresenhamLines", &features->stippledBresenhamLines) &&
+    visitor->Visit("stippledSmoothLines", &features->stippledSmoothLines) &&
+    visitor->Visit("vertexAttributeInstanceRateDivisor", &features->vertexAttributeInstanceRateDivisor) &&
+    visitor->Visit("vertexAttributeInstanceRateZeroDivisor", &features->vertexAttributeInstanceRateZeroDivisor) &&
+    visitor->Visit("indexTypeUint8", &features->indexTypeUint8) &&
+    visitor->Visit("dynamicRenderingLocalRead", &features->dynamicRenderingLocalRead) &&
+    visitor->Visit("maintenance5", &features->maintenance5) &&
+    visitor->Visit("maintenance6", &features->maintenance6) &&
+    visitor->Visit("pipelineProtectedAccess", &features->pipelineProtectedAccess) &&
+    visitor->Visit("pipelineRobustness", &features->pipelineRobustness) &&
+    visitor->Visit("hostImageCopy", &features->hostImageCopy);
+}
 // clang-format on
 
 template <typename Visitor>
@@ -1051,7 +1185,7 @@
   switch (device->properties.apiVersion ^
           VK_API_VERSION_PATCH(device->properties.apiVersion)) {
     case VK_API_VERSION_1_4:
-      // TODO: real 1.4 support here
+      ret &= visitor->Visit("core14", &device->core14);
       FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_3:
       ret &= visitor->Visit("core13", &device->core13);
@@ -1224,6 +1358,12 @@
     return true;
   }
 
+  template <typename T>
+  bool VisitArray(const char* key, uint32_t count, const T *value) {
+    object_[key] = ArrayToJsonValue(count, *value);
+    return true;
+  }
+
   Json::Value get_object() const { return object_; }
 
  private:
@@ -1288,6 +1428,15 @@
   return true;
 }
 
+inline bool AsValue(Json::Value* json_value, VkImageLayout* t) {
+  uint32_t value = 0;
+  if (!AsValue(json_value, &value))
+    return false;
+  if (!EnumTraits<VkImageLayout>::exist(value)) return false;
+  *t = static_cast<VkImageLayout>(value);
+  return true;
+}
+
 template <typename T>
 inline bool AsArray(Json::Value* json_value, uint32_t count, T* values) {
   if (json_value->type() != Json::arrayValue || json_value->size() != count)
@@ -1398,6 +1547,20 @@
     return false;
   }
 
+  template <typename T>
+  bool VisitArray(const char* key, uint32_t count, T *value) {
+    Json::Value json_value = (*object_)[key];
+    if (!json_value) {
+      if (errors_)
+        *errors_ = std::string(key) + " missing.";
+      return false;
+    }
+    if (AsArray(&json_value, count, *value)) return true;
+    if (errors_)
+      *errors_ = std::string("Wrong type for ") + std::string(key) + ".";
+    return false;
+  }
+
 
  private:
   Json::Value* object_;
diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h
index 88f6e7d..28de680 100644
--- a/vulkan/vkjson/vkjson.h
+++ b/vulkan/vkjson/vkjson.h
@@ -84,6 +84,11 @@
   VkPhysicalDeviceVulkan13Features features;
 };
 
+struct VkJsonCore14 {
+  VkPhysicalDeviceVulkan14Properties properties;
+  VkPhysicalDeviceVulkan14Features features;
+};
+
 struct VkJsonDevice {
   VkJsonDevice() {
     memset(&properties, 0, sizeof(VkPhysicalDeviceProperties));
@@ -110,6 +115,7 @@
            sizeof(VkPhysicalDeviceShaderDrawParameterFeatures));
     memset(&core12, 0, sizeof(VkJsonCore12));
     memset(&core13, 0, sizeof(VkJsonCore13));
+    memset(&core14, 0, sizeof(VkJsonCore14));
   }
   VkPhysicalDeviceProperties properties;
   VkPhysicalDeviceFeatures features;
@@ -139,6 +145,7 @@
       external_semaphore_properties;
   VkJsonCore12 core12;
   VkJsonCore13 core13;
+  VkJsonCore14 core14;
 };
 
 struct VkJsonDeviceGroup {
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index 0ffe7e0..32bc50b 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -292,6 +292,22 @@
     vkGetPhysicalDeviceFeatures2(physical_device, &features);
   }
 
+  if (device.properties.apiVersion >= VK_API_VERSION_1_4) {
+    device.core14.properties.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_PROPERTIES;
+    device.core14.properties.pNext = properties.pNext;
+    properties.pNext = &device.core14.properties;
+
+    vkGetPhysicalDeviceProperties2(physical_device, &properties);
+
+    device.core14.features.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES;
+    device.core14.features.pNext = features.pNext;
+    features.pNext = &device.core14.features;
+
+    vkGetPhysicalDeviceFeatures2(physical_device, &features);
+  }
+
   return device;
 }