Merge "Extract RenderArea elements into screenshot args" into main
diff --git a/include/input/DisplayTopologyGraph.h b/include/input/DisplayTopologyGraph.h
index f3f5148..3ae865a 100644
--- a/include/input/DisplayTopologyGraph.h
+++ b/include/input/DisplayTopologyGraph.h
@@ -42,7 +42,9 @@
  */
 struct DisplayTopologyAdjacentDisplay {
     ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID;
+    // Position of the adjacent display, relative to the source display.
     DisplayTopologyPosition position;
+    // The offset in DP of the adjacent display, relative to the source display.
     float offsetDp;
 };
 
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index f5df8d5..2c2e2c8 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -30,10 +30,14 @@
      * not be added with this flag for privacy concerns.
      */
     ADD_SERVICE_ALLOW_ISOLATED = 1 << 0,
+    /**
+     * Allows services to dump sections according to priorities and format
+     */
     ADD_SERVICE_DUMP_FLAG_PRIORITY_CRITICAL = 1 << 1,
     ADD_SERVICE_DUMP_FLAG_PRIORITY_HIGH = 1 << 2,
     ADD_SERVICE_DUMP_FLAG_PRIORITY_NORMAL = 1 << 3,
     ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT = 1 << 4,
+    ADD_SERVICE_DUMP_FLAG_PROTO = 1 << 5,
     // All other bits are reserved for internal usage
 };
 
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index d6ac4ac..14bc5d2 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -63,6 +63,9 @@
     if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT) {
         dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT;
     }
+    if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PROTO) {
+        dumpFlags |= IServiceManager::DUMP_FLAG_PROTO;
+    }
     if (dumpFlags == 0) {
         dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT;
     }
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
index c0c0aae..339ce4b 100644
--- a/libs/binder/tests/binderAllocationLimits.cpp
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -22,17 +22,33 @@
 #include <binder/RpcServer.h>
 #include <binder/RpcSession.h>
 #include <cutils/trace.h>
+#include <gtest/gtest-spi.h>
 #include <gtest/gtest.h>
 #include <utils/CallStack.h>
 
 #include <malloc.h>
+#include <atomic>
 #include <functional>
+#include <numeric>
 #include <vector>
 
 using namespace android::binder::impl;
 
 static android::String8 gEmpty(""); // make sure first allocation from optimization runs
 
+struct State {
+    State(std::vector<size_t>&& expectedMallocs) : expectedMallocs(std::move(expectedMallocs)) {}
+    ~State() {
+        size_t num = numMallocs.load();
+        if (expectedMallocs.size() != num) {
+            ADD_FAILURE() << "Expected " << expectedMallocs.size() << " allocations, but got "
+                          << num;
+        }
+    }
+    const std::vector<size_t> expectedMallocs;
+    std::atomic<size_t> numMallocs;
+};
+
 struct DestructionAction {
     DestructionAction(std::function<void()> f) : mF(std::move(f)) {}
     ~DestructionAction() { mF(); };
@@ -95,8 +111,7 @@
 
 // Action to execute when malloc is hit. Supports nesting. Malloc is not
 // restricted when the allocation hook is being processed.
-__attribute__((warn_unused_result))
-DestructionAction OnMalloc(LambdaHooks::AllocationHook f) {
+__attribute__((warn_unused_result)) DestructionAction OnMalloc(LambdaHooks::AllocationHook f) {
     MallocHooks before = MallocHooks::save();
     LambdaHooks::lambdas.emplace_back(std::move(f));
     LambdaHooks::lambda_malloc_hooks.overwrite();
@@ -106,6 +121,22 @@
     });
 }
 
+DestructionAction setExpectedMallocs(std::vector<size_t>&& expected) {
+    auto state = std::make_shared<State>(std::move(expected));
+    return OnMalloc([state = state](size_t bytes) {
+        size_t num = state->numMallocs.fetch_add(1);
+        if (num >= state->expectedMallocs.size() || state->expectedMallocs[num] != bytes) {
+            ADD_FAILURE() << "Unexpected allocation number " << num << " of size " << bytes
+                          << " bytes" << std::endl
+                          << android::CallStack::stackToString("UNEXPECTED ALLOCATION",
+                                                               android::CallStack::getCurrent(
+                                                                       4 /*ignoreDepth*/)
+                                                                       .get())
+                          << std::endl;
+        }
+    });
+}
+
 // exported symbol, to force compiler not to optimize away pointers we set here
 const void* imaginary_use;
 
@@ -119,16 +150,53 @@
 
         imaginary_use = new int[10];
     }
+    delete[] reinterpret_cast<const int*>(imaginary_use);
     EXPECT_EQ(mallocs, 1u);
 }
 
+TEST(TestTheTest, OnMallocWithExpectedMallocs) {
+    std::vector<size_t> expectedMallocs = {
+            4,
+            16,
+            8,
+    };
+    {
+        const auto on_malloc = setExpectedMallocs(std::move(expectedMallocs));
+        imaginary_use = new int32_t[1];
+        delete[] reinterpret_cast<const int*>(imaginary_use);
+        imaginary_use = new int32_t[4];
+        delete[] reinterpret_cast<const int*>(imaginary_use);
+        imaginary_use = new int32_t[2];
+        delete[] reinterpret_cast<const int*>(imaginary_use);
+    }
+}
+
+TEST(TestTheTest, OnMallocWithExpectedMallocsWrongSize) {
+    std::vector<size_t> expectedMallocs = {
+            4,
+            16,
+            100000,
+    };
+    EXPECT_NONFATAL_FAILURE(
+            {
+                const auto on_malloc = setExpectedMallocs(std::move(expectedMallocs));
+                imaginary_use = new int32_t[1];
+                delete[] reinterpret_cast<const int*>(imaginary_use);
+                imaginary_use = new int32_t[4];
+                delete[] reinterpret_cast<const int*>(imaginary_use);
+                imaginary_use = new int32_t[2];
+                delete[] reinterpret_cast<const int*>(imaginary_use);
+            },
+            "Unexpected allocation number 2 of size 8 bytes");
+}
 
 __attribute__((warn_unused_result))
 DestructionAction ScopeDisallowMalloc() {
     return OnMalloc([&](size_t bytes) {
-        ADD_FAILURE() << "Unexpected allocation: " << bytes;
+        FAIL() << "Unexpected allocation: " << bytes;
         using android::CallStack;
-        std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION", CallStack::getCurrent(4 /*ignoreDepth*/).get())
+        std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION",
+                                              CallStack::getCurrent(4 /*ignoreDepth*/).get())
                   << std::endl;
     });
 }
@@ -224,6 +292,51 @@
     EXPECT_EQ(mallocs, 1u);
 }
 
+TEST(BinderAccessorAllocation, AddAccessorCheckService) {
+    // Need to call defaultServiceManager() before checking malloc because it
+    // will allocate an instance in the call_once
+    const auto sm = defaultServiceManager();
+    const std::string kInstanceName1 = "foo.bar.IFoo/default";
+    const std::string kInstanceName2 = "foo.bar.IFoo2/default";
+    const String16 kInstanceName16(kInstanceName1.c_str());
+    std::vector<size_t> expectedMallocs = {
+            // addAccessorProvider
+            112, // new AccessorProvider
+            16,  // new AccessorProviderEntry
+            // checkService
+            45,  // String8 from String16 in CppShim::checkService
+            128, // writeInterfaceToken
+            16,  // getInjectedAccessor, new AccessorProviderEntry
+            66,  // getInjectedAccessor, String16
+            45,  // String8 from String16 in AccessorProvider::provide
+    };
+    std::set<std::string> supportedInstances = {kInstanceName1, kInstanceName2};
+    auto onMalloc = setExpectedMallocs(std::move(expectedMallocs));
+
+    auto receipt =
+            android::addAccessorProvider(std::move(supportedInstances),
+                                         [&](const String16&) -> sp<IBinder> { return nullptr; });
+    EXPECT_FALSE(receipt.expired());
+
+    sp<IBinder> binder = sm->checkService(kInstanceName16);
+
+    status_t status = android::removeAccessorProvider(receipt);
+}
+
+TEST(BinderAccessorAllocation, AddAccessorEmpty) {
+    std::vector<size_t> expectedMallocs = {
+            48, // From ALOGE with empty set of instances
+    };
+    std::set<std::string> supportedInstances = {};
+    auto onMalloc = setExpectedMallocs(std::move(expectedMallocs));
+
+    auto receipt =
+            android::addAccessorProvider(std::move(supportedInstances),
+                                         [&](const String16&) -> sp<IBinder> { return nullptr; });
+
+    EXPECT_TRUE(receipt.expired());
+}
+
 TEST(RpcBinderAllocation, SetupRpcServer) {
     std::string tmp = getenv("TMPDIR") ?: "/tmp";
     std::string addr = tmp + "/binderRpcBenchmark";
@@ -255,6 +368,7 @@
 }
 
 int main(int argc, char** argv) {
+    LOG(INFO) << "Priming static log variables for binderAllocationLimits.";
     if (getenv("LIBC_HOOKS_ENABLE") == nullptr) {
         CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/));
         execv(argv[0], argv);
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 389fb7f..cd6fe90 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -27,9 +27,9 @@
     name: "inputconstants_aidl",
     srcs: [
         "android/os/IInputConstants.aidl",
+        "android/os/InputConfig.aidl",
         "android/os/InputEventInjectionResult.aidl",
         "android/os/InputEventInjectionSync.aidl",
-        "android/os/InputConfig.aidl",
         "android/os/MotionEventFlag.aidl",
         "android/os/PointerIconType.aidl",
     ],
@@ -85,56 +85,56 @@
     source_stem: "bindings",
 
     bindgen_flags: [
-        "--verbose",
-        "--allowlist-var=AMOTION_EVENT_ACTION_CANCEL",
-        "--allowlist-var=AMOTION_EVENT_ACTION_UP",
-        "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_DOWN",
-        "--allowlist-var=AMOTION_EVENT_ACTION_DOWN",
-        "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT",
-        "--allowlist-var=MAX_POINTER_ID",
-        "--allowlist-var=AINPUT_SOURCE_CLASS_NONE",
-        "--allowlist-var=AINPUT_SOURCE_CLASS_BUTTON",
-        "--allowlist-var=AINPUT_SOURCE_CLASS_POINTER",
-        "--allowlist-var=AINPUT_SOURCE_CLASS_NAVIGATION",
-        "--allowlist-var=AINPUT_SOURCE_CLASS_POSITION",
-        "--allowlist-var=AINPUT_SOURCE_CLASS_JOYSTICK",
-        "--allowlist-var=AINPUT_SOURCE_UNKNOWN",
-        "--allowlist-var=AINPUT_SOURCE_KEYBOARD",
-        "--allowlist-var=AINPUT_SOURCE_DPAD",
-        "--allowlist-var=AINPUT_SOURCE_GAMEPAD",
-        "--allowlist-var=AINPUT_SOURCE_TOUCHSCREEN",
-        "--allowlist-var=AINPUT_SOURCE_MOUSE",
-        "--allowlist-var=AINPUT_SOURCE_STYLUS",
-        "--allowlist-var=AINPUT_SOURCE_BLUETOOTH_STYLUS",
-        "--allowlist-var=AINPUT_SOURCE_TRACKBALL",
-        "--allowlist-var=AINPUT_SOURCE_MOUSE_RELATIVE",
-        "--allowlist-var=AINPUT_SOURCE_TOUCHPAD",
-        "--allowlist-var=AINPUT_SOURCE_TOUCH_NAVIGATION",
-        "--allowlist-var=AINPUT_SOURCE_JOYSTICK",
-        "--allowlist-var=AINPUT_SOURCE_HDMI",
-        "--allowlist-var=AINPUT_SOURCE_SENSOR",
-        "--allowlist-var=AINPUT_SOURCE_ROTARY_ENCODER",
+        "--allowlist-var=AINPUT_KEYBOARD_TYPE_ALPHABETIC",
         "--allowlist-var=AINPUT_KEYBOARD_TYPE_NONE",
         "--allowlist-var=AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC",
-        "--allowlist-var=AINPUT_KEYBOARD_TYPE_ALPHABETIC",
-        "--allowlist-var=AMETA_NONE",
-        "--allowlist-var=AMETA_ALT_ON",
+        "--allowlist-var=AINPUT_SOURCE_BLUETOOTH_STYLUS",
+        "--allowlist-var=AINPUT_SOURCE_CLASS_BUTTON",
+        "--allowlist-var=AINPUT_SOURCE_CLASS_JOYSTICK",
+        "--allowlist-var=AINPUT_SOURCE_CLASS_NAVIGATION",
+        "--allowlist-var=AINPUT_SOURCE_CLASS_NONE",
+        "--allowlist-var=AINPUT_SOURCE_CLASS_POINTER",
+        "--allowlist-var=AINPUT_SOURCE_CLASS_POSITION",
+        "--allowlist-var=AINPUT_SOURCE_DPAD",
+        "--allowlist-var=AINPUT_SOURCE_GAMEPAD",
+        "--allowlist-var=AINPUT_SOURCE_HDMI",
+        "--allowlist-var=AINPUT_SOURCE_JOYSTICK",
+        "--allowlist-var=AINPUT_SOURCE_KEYBOARD",
+        "--allowlist-var=AINPUT_SOURCE_MOUSE",
+        "--allowlist-var=AINPUT_SOURCE_MOUSE_RELATIVE",
+        "--allowlist-var=AINPUT_SOURCE_ROTARY_ENCODER",
+        "--allowlist-var=AINPUT_SOURCE_SENSOR",
+        "--allowlist-var=AINPUT_SOURCE_STYLUS",
+        "--allowlist-var=AINPUT_SOURCE_TOUCHPAD",
+        "--allowlist-var=AINPUT_SOURCE_TOUCHSCREEN",
+        "--allowlist-var=AINPUT_SOURCE_TOUCH_NAVIGATION",
+        "--allowlist-var=AINPUT_SOURCE_TRACKBALL",
+        "--allowlist-var=AINPUT_SOURCE_UNKNOWN",
         "--allowlist-var=AMETA_ALT_LEFT_ON",
+        "--allowlist-var=AMETA_ALT_ON",
         "--allowlist-var=AMETA_ALT_RIGHT_ON",
-        "--allowlist-var=AMETA_SHIFT_ON",
-        "--allowlist-var=AMETA_SHIFT_LEFT_ON",
-        "--allowlist-var=AMETA_SHIFT_RIGHT_ON",
-        "--allowlist-var=AMETA_SYM_ON",
-        "--allowlist-var=AMETA_FUNCTION_ON",
-        "--allowlist-var=AMETA_CTRL_ON",
-        "--allowlist-var=AMETA_CTRL_LEFT_ON",
-        "--allowlist-var=AMETA_CTRL_RIGHT_ON",
-        "--allowlist-var=AMETA_META_ON",
-        "--allowlist-var=AMETA_META_LEFT_ON",
-        "--allowlist-var=AMETA_META_RIGHT_ON",
         "--allowlist-var=AMETA_CAPS_LOCK_ON",
+        "--allowlist-var=AMETA_CTRL_LEFT_ON",
+        "--allowlist-var=AMETA_CTRL_ON",
+        "--allowlist-var=AMETA_CTRL_RIGHT_ON",
+        "--allowlist-var=AMETA_FUNCTION_ON",
+        "--allowlist-var=AMETA_META_LEFT_ON",
+        "--allowlist-var=AMETA_META_ON",
+        "--allowlist-var=AMETA_META_RIGHT_ON",
+        "--allowlist-var=AMETA_NONE",
         "--allowlist-var=AMETA_NUM_LOCK_ON",
         "--allowlist-var=AMETA_SCROLL_LOCK_ON",
+        "--allowlist-var=AMETA_SHIFT_LEFT_ON",
+        "--allowlist-var=AMETA_SHIFT_ON",
+        "--allowlist-var=AMETA_SHIFT_RIGHT_ON",
+        "--allowlist-var=AMETA_SYM_ON",
+        "--allowlist-var=AMOTION_EVENT_ACTION_CANCEL",
+        "--allowlist-var=AMOTION_EVENT_ACTION_DOWN",
+        "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_DOWN",
+        "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT",
+        "--allowlist-var=AMOTION_EVENT_ACTION_UP",
+        "--allowlist-var=MAX_POINTER_ID",
+        "--verbose",
     ],
 
     static_libs: [
@@ -143,9 +143,9 @@
     ],
     shared_libs: ["libc++"],
     header_libs: [
-        "native_headers",
-        "jni_headers",
         "flatbuffer_headers",
+        "jni_headers",
+        "native_headers",
     ],
 }
 
@@ -179,8 +179,8 @@
     host_supported: true,
     cflags: [
         "-Wall",
-        "-Wextra",
         "-Werror",
+        "-Wextra",
     ],
     srcs: [
         "FromRustToCpp.cpp",
@@ -205,15 +205,15 @@
     cpp_std: "c++20",
     host_supported: true,
     cflags: [
+        "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
         "-Wall",
-        "-Wextra",
         "-Werror",
+        "-Wextra",
         "-Wno-unused-parameter",
-        "-Wthread-safety",
         "-Wshadow",
         "-Wshadow-field-in-constructor-modified",
         "-Wshadow-uncaptured-local",
-        "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+        "-Wthread-safety",
     ],
     srcs: [
         "AccelerationCurve.cpp",
@@ -225,10 +225,10 @@
         "InputEventLabels.cpp",
         "InputTransport.cpp",
         "InputVerifier.cpp",
-        "Keyboard.cpp",
         "KeyCharacterMap.cpp",
-        "KeyboardClassifier.cpp",
         "KeyLayoutMap.cpp",
+        "Keyboard.cpp",
+        "KeyboardClassifier.cpp",
         "MotionPredictor.cpp",
         "MotionPredictorMetricsManager.cpp",
         "OneEuroFilter.cpp",
@@ -262,13 +262,13 @@
 
     shared_libs: [
         "android.companion.virtualdevice.flags-aconfig-cc",
+        "libPlatformProperties",
         "libaconfig_storage_read_api_cc",
         "libbase",
         "libbinder",
         "libbinder_ndk",
         "libcutils",
         "liblog",
-        "libPlatformProperties",
         "libtinyxml2",
         "libutils",
         "libz", // needed by libkernelconfigs
@@ -287,15 +287,15 @@
 
     static_libs: [
         "inputconstants-cpp",
-        "libui-types",
-        "libtflite_static",
         "libkernelconfigs",
+        "libtflite_static",
+        "libui-types",
     ],
 
     whole_static_libs: [
         "com.android.input.flags-aconfig-cc",
-        "libinput_rust_ffi",
         "iinputflinger_aidl_lib_static",
+        "libinput_rust_ffi",
     ],
 
     export_static_lib_headers: [
@@ -310,8 +310,8 @@
     target: {
         android: {
             required: [
-                "motion_predictor_model_prebuilt",
                 "motion_predictor_model_config",
+                "motion_predictor_model_prebuilt",
             ],
             static_libs: [
                 "libstatslog_libinput",
@@ -372,9 +372,9 @@
     cpp_std: "c++20",
     host_supported: true,
     shared_libs: [
-        "libutils",
         "libbase",
         "liblog",
+        "libutils",
     ],
 }
 
diff --git a/libs/input/rust/Android.bp b/libs/input/rust/Android.bp
index 63853f7..fae9074 100644
--- a/libs/input/rust/Android.bp
+++ b/libs/input/rust/Android.bp
@@ -18,12 +18,12 @@
     srcs: ["lib.rs"],
     host_supported: true,
     rustlibs: [
+        "inputconstants-rust",
         "libbitflags",
         "libcxx",
         "libinput_bindgen",
-        "liblogger",
         "liblog_rust",
-        "inputconstants-rust",
+        "liblogger",
         "libserde",
         "libserde_json",
     ],
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 85a37fe..968fa5f 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -16,16 +16,16 @@
         "BlockingQueue_test.cpp",
         "IdGenerator_test.cpp",
         "InputChannel_test.cpp",
-        "InputConsumer_test.cpp",
         "InputConsumerFilteredResampling_test.cpp",
         "InputConsumerResampling_test.cpp",
+        "InputConsumer_test.cpp",
         "InputDevice_test.cpp",
         "InputEvent_test.cpp",
-        "InputPublisherAndConsumer_test.cpp",
         "InputPublisherAndConsumerNoResampling_test.cpp",
+        "InputPublisherAndConsumer_test.cpp",
         "InputVerifier_test.cpp",
-        "MotionPredictor_test.cpp",
         "MotionPredictorMetricsManager_test.cpp",
+        "MotionPredictor_test.cpp",
         "OneEuroFilter_test.cpp",
         "Resampler_test.cpp",
         "RingBuffer_test.cpp",
@@ -53,8 +53,8 @@
     ],
     cflags: [
         "-Wall",
-        "-Wextra",
         "-Werror",
+        "-Wextra",
         "-Wno-unused-parameter",
     ],
     sanitize: {
@@ -68,26 +68,26 @@
             memtag_heap: true,
             undefined: true,
             misc_undefined: [
-                "bounds",
                 "all",
+                "bounds",
             ],
         },
     },
     shared_libs: [
+        "libPlatformProperties",
         "libaconfig_storage_read_api_cc",
         "libbase",
         "libbinder",
         "libcutils",
         "liblog",
-        "libPlatformProperties",
         "libstatslog",
         "libtinyxml2",
         "libutils",
         "server_configurable_flags",
     ],
     data: [
-        "data/*",
         ":motion_predictor_model",
+        "data/*",
     ],
     test_options: {
         unit_test: true,
@@ -117,10 +117,10 @@
         "-Wextra",
     ],
     shared_libs: [
-        "libinput",
-        "libcutils",
-        "libutils",
-        "libbinder",
         "libbase",
+        "libbinder",
+        "libcutils",
+        "libinput",
+        "libutils",
     ],
 }
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
index f92f6df..4478732 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
@@ -14,15 +14,16 @@
  * limitations under the License.
  */
 
+// Should match the definitions in: frameworks/native/cmds/atrace/atrace.cpp
 #define FRAMEWORK_CATEGORIES(C)                                  \
   C(always, "always", "Always category")                         \
-  C(graphics, "graphics", "Graphics category")                   \
+  C(graphics, "gfx", "Graphics category")                        \
   C(input, "input", "Input category")                            \
   C(view, "view", "View category")                               \
   C(webview, "webview", "WebView category")                      \
   C(windowmanager, "wm", "WindowManager category")               \
   C(activitymanager, "am", "ActivityManager category")           \
-  C(syncmanager, "syncmanager", "SyncManager category")          \
+  C(syncmanager, "sm", "SyncManager category")                   \
   C(audio, "audio", "Audio category")                            \
   C(video, "video", "Video category")                            \
   C(camera, "camera", "Camera category")                         \
@@ -33,7 +34,7 @@
   C(rs, "rs", "RS category")                                     \
   C(bionic, "bionic", "Bionic category")                         \
   C(power, "power", "Power category")                            \
-  C(packagemanager, "packagemanager", "PackageManager category") \
+  C(packagemanager, "pm", "PackageManager category")             \
   C(systemserver, "ss", "System Server category")                \
   C(database, "database", "Database category")                   \
   C(network, "network", "Network category")                      \
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index ca92ab5..107fd20 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -32,15 +32,15 @@
     host_supported: true,
     cpp_std: "c++20",
     cflags: [
+        "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
         "-Wall",
-        "-Wextra",
         "-Werror",
+        "-Wextra",
         "-Wno-unused-parameter",
-        "-Wthread-safety",
         "-Wshadow",
         "-Wshadow-field-in-constructor-modified",
         "-Wshadow-uncaptured-local",
-        "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+        "-Wthread-safety",
     ],
     sanitize: {
         misc_undefined: [
@@ -62,8 +62,8 @@
                     memtag_heap: true,
                     undefined: true,
                     misc_undefined: [
-                        "bounds",
                         "all",
+                        "bounds",
                     ],
                 },
             },
@@ -114,16 +114,16 @@
         "liblog",
         "libprotobuf-cpp-lite",
         "libstatslog",
-        "libutils",
         "libstatspull",
         "libstatssocket",
+        "libutils",
         "packagemanager_aidl-cpp",
         "server_configurable_flags",
     ],
     static_libs: [
         "libattestation",
-        "libperfetto_client_experimental",
         "libpalmrejection",
+        "libperfetto_client_experimental",
         "libui-types",
     ],
     generated_headers: [
@@ -161,10 +161,10 @@
     shared_libs: [
         // This should consist only of dependencies from inputflinger. Other dependencies should be
         // in cc_defaults so that they are included in the tests.
+        "libPlatformProperties",
         "libinputflinger_base",
         "libinputreader",
         "libinputreporter",
-        "libPlatformProperties",
     ],
     static_libs: [
         "libinputdispatcher",
@@ -185,8 +185,8 @@
     name: "libinputflinger_headers",
     host_supported: true,
     export_include_dirs: [
-        "include",
         ".",
+        "include",
     ],
     header_libs: [
         "libchrome-gestures_headers",
@@ -247,49 +247,40 @@
 phony {
     name: "checkinput",
     required: [
-        // native targets
-        "libgui_test",
-        "libinput",
-        "libinputreader_static",
-        "libinputflinger",
-        "inputflinger_tests",
-        "inputflinger_benchmarks",
-        "libinput_tests",
-        "libpalmrejection_test",
-        "libandroid_runtime",
-        "libinputservice_test",
         "Bug-115739809",
-        "StructLayout_test",
-
-        // jni
-        "libservices.core",
-
-        // rust targets
-        "libinput_rust_test",
-
-        // native fuzzers
-        "inputflinger_latencytracker_fuzzer",
-        "inputflinger_cursor_input_fuzzer",
-        "inputflinger_keyboard_input_fuzzer",
-        "inputflinger_multitouch_input_fuzzer",
-        "inputflinger_switch_input_fuzzer",
-        "inputflinger_touchpad_input_fuzzer",
-        "inputflinger_input_reader_fuzzer",
-        "inputflinger_blocking_queue_fuzzer",
-        "inputflinger_input_classifier_fuzzer",
-        "inputflinger_input_dispatcher_fuzzer",
-
-        // Java/Kotlin targets
-        "CtsWindowManagerDeviceWindow",
-        "InputTests",
         "CtsHardwareTestCases",
         "CtsInputTestCases",
+        "CtsSecurityBulletinHostTestCases",
+        "CtsSecurityTestCases",
         "CtsViewTestCases",
         "CtsWidgetTestCases",
+        "CtsWindowManagerDeviceWindow",
         "FrameworksCoreTests",
         "FrameworksServicesTests",
-        "CtsSecurityTestCases",
-        "CtsSecurityBulletinHostTestCases",
+        "InputTests",
+        "StructLayout_test",
+        "inputflinger_benchmarks",
+        "inputflinger_blocking_queue_fuzzer",
+        "inputflinger_cursor_input_fuzzer",
+        "inputflinger_input_classifier_fuzzer",
+        "inputflinger_input_dispatcher_fuzzer",
+        "inputflinger_input_reader_fuzzer",
+        "inputflinger_keyboard_input_fuzzer",
+        "inputflinger_latencytracker_fuzzer",
+        "inputflinger_multitouch_input_fuzzer",
+        "inputflinger_switch_input_fuzzer",
+        "inputflinger_tests",
+        "inputflinger_touchpad_input_fuzzer",
+        "libandroid_runtime",
+        "libgui_test",
+        "libinput",
+        "libinput_rust_test",
+        "libinput_tests",
+        "libinputflinger",
+        "libinputreader_static",
+        "libinputservice_test",
+        "libpalmrejection_test",
+        "libservices.core",
         "monkey_test",
     ],
 }
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index 2ef94fb..bb4e617 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -56,7 +56,7 @@
 void InputFilter::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
     mDeviceInfos.clear();
     mDeviceInfos.reserve(args.inputDeviceInfos.size());
-    for (auto info : args.inputDeviceInfos) {
+    for (const auto& info : args.inputDeviceInfos) {
         AidlDeviceInfo& aidlInfo = mDeviceInfos.emplace_back();
         aidlInfo.deviceId = info.getId();
         aidlInfo.external = info.isExternal();
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 9f91285..3140dc8 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "PointerChoreographer"
 
 #include <android-base/logging.h>
+#include <android/configuration.h>
 #include <com_android_input_flags.h>
 #if defined(__ANDROID__)
 #include <gui/SurfaceComposerClient.h>
@@ -114,6 +115,17 @@
     }
 }
 
+// The standardised medium display density for which 1 px  = 1 dp
+constexpr int32_t DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM;
+
+inline float pxToDp(int px, int dpi) {
+    return static_cast<float>(px * DENSITY_MEDIUM) / static_cast<float>(dpi);
+}
+
+inline int dpToPx(float dp, int dpi) {
+    return static_cast<int>((dp * dpi) / DENSITY_MEDIUM);
+}
+
 } // namespace
 
 // --- PointerChoreographer ---
@@ -385,8 +397,7 @@
     pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
     pc.setDisplayViewport(destinationViewport);
     vec2 destinationPosition =
-            calculatePositionOnDestinationViewport(destinationViewport,
-                                                   cursorOffset - destinationOffset,
+            calculatePositionOnDestinationViewport(destinationViewport, destinationOffset,
                                                    sourceBoundary);
 
     // Transform position back to un-rotated coordinate space before sending it to controller
@@ -990,10 +1001,10 @@
     return ConstructorDelegate(std::move(ctor));
 }
 
-std::optional<std::pair<const DisplayViewport*, float /*offset*/>>
+std::optional<std::pair<const DisplayViewport*, float /*offsetPx*/>>
 PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,
                                                    const DisplayTopologyPosition sourceBoundary,
-                                                   float cursorOffset) const {
+                                                   int32_t sourceCursorOffsetPx) const {
     const auto& sourceNode = mTopology.graph.find(sourceDisplayId);
     if (sourceNode == mTopology.graph.end()) {
         // Topology is likely out of sync with viewport info, wait for it to be updated
@@ -1004,22 +1015,32 @@
         if (adjacentDisplay.position != sourceBoundary) {
             continue;
         }
-        const DisplayViewport* destinationViewport =
-                findViewportByIdLocked(adjacentDisplay.displayId);
-        if (destinationViewport == nullptr) {
+        const DisplayViewport* adjacentViewport = findViewportByIdLocked(adjacentDisplay.displayId);
+        if (adjacentViewport == nullptr) {
             // Topology is likely out of sync with viewport info, wait for them to be updated
             LOG(WARNING) << "Cannot find viewport for adjacent display "
                          << adjacentDisplay.displayId << "of source display " << sourceDisplayId;
             continue;
         }
-        // target position must be within target display boundary
-        const int32_t edgeSize = sourceBoundary == DisplayTopologyPosition::TOP ||
+        // As displays can have different densities we need to do all calculations in
+        // density-independent-pixels a.k.a. dp values.
+        const int sourceDensity = mTopology.displaysDensity.at(sourceDisplayId);
+        const int adjacentDisplayDensity = mTopology.displaysDensity.at(adjacentDisplay.displayId);
+        const float sourceCursorOffsetDp = pxToDp(sourceCursorOffsetPx, sourceDensity);
+        const int32_t edgeSizePx = sourceBoundary == DisplayTopologyPosition::TOP ||
                         sourceBoundary == DisplayTopologyPosition::BOTTOM
-                ? (destinationViewport->logicalRight - destinationViewport->logicalLeft)
-                : (destinationViewport->logicalBottom - destinationViewport->logicalTop);
-        if (cursorOffset >= adjacentDisplay.offsetDp &&
-            cursorOffset <= adjacentDisplay.offsetDp + edgeSize) {
-            return std::make_pair(destinationViewport, adjacentDisplay.offsetDp);
+                ? (adjacentViewport->logicalRight - adjacentViewport->logicalLeft)
+                : (adjacentViewport->logicalBottom - adjacentViewport->logicalTop);
+        const float adjacentEdgeSizeDp = pxToDp(edgeSizePx, adjacentDisplayDensity);
+        // Target position must be within target display boundary.
+        // Cursor should also be able to cross displays when only display corners are touching and
+        // there may be zero overlapping pixels. To accommodate this we have margin of one pixel
+        // around the end of the overlapping edge.
+        if (sourceCursorOffsetDp >= adjacentDisplay.offsetDp &&
+            sourceCursorOffsetDp <= adjacentDisplay.offsetDp + adjacentEdgeSizeDp) {
+            const int destinationOffsetPx =
+                    dpToPx(sourceCursorOffsetDp - adjacentDisplay.offsetDp, adjacentDisplayDensity);
+            return std::make_pair(adjacentViewport, destinationOffsetPx);
         }
     }
     return std::nullopt;
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index c2f5ec0..a9d971a 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -163,10 +163,10 @@
     void handleUnconsumedDeltaLocked(PointerControllerInterface& pc, const vec2& unconsumedDelta)
             REQUIRES(getLock());
 
-    std::optional<std::pair<const DisplayViewport*, float /*offset*/>> findDestinationDisplayLocked(
-            const ui::LogicalDisplayId sourceDisplayId,
-            const DisplayTopologyPosition sourceBoundary, float cursorOffset) const
-            REQUIRES(getLock());
+    std::optional<std::pair<const DisplayViewport*, float /*offsetPx*/>>
+    findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,
+                                 const DisplayTopologyPosition sourceBoundary,
+                                 int32_t sourceCursorOffsetPx) const REQUIRES(getLock());
 
     /* Topology is initialized with default-constructed value, which is an empty topology. Till we
      * receive setDisplayTopology call.
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 8b2b843..1aa8b2b 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -49,8 +49,8 @@
         "LatencyAggregatorWithHistograms.cpp",
         "LatencyTracker.cpp",
         "Monitor.cpp",
-        "TouchedWindow.cpp",
         "TouchState.cpp",
+        "TouchedWindow.cpp",
         "trace/*.cpp",
     ],
 }
@@ -71,9 +71,9 @@
         "liblog",
         "libprotobuf-cpp-lite",
         "libstatslog",
-        "libutils",
         "libstatspull",
         "libstatssocket",
+        "libutils",
         "packagemanager_aidl-cpp",
         "server_configurable_flags",
     ],
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 098019f..11ba592 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1267,13 +1267,9 @@
             if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {
                 // The event is stale. However, only drop stale events if there isn't an ongoing
                 // gesture. That would allow us to complete the processing of the current stroke.
-                const auto touchStateIt = mTouchStatesByDisplay.find(motionEntry->displayId);
-                if (touchStateIt != mTouchStatesByDisplay.end()) {
-                    const TouchState& touchState = touchStateIt->second;
-                    if (!touchState.hasTouchingPointers(motionEntry->deviceId) &&
-                        !touchState.hasHoveringPointers(motionEntry->deviceId)) {
-                        dropReason = DropReason::STALE;
-                    }
+                if (!mTouchStates.hasTouchingOrHoveringPointers(motionEntry->displayId,
+                                                                motionEntry->deviceId)) {
+                    dropReason = DropReason::STALE;
                 }
             }
             if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
@@ -1355,7 +1351,8 @@
         // Alternatively, maybe there's a spy window that could handle this event.
         const std::vector<sp<WindowInfoHandle>> touchedSpies =
                 mWindowInfos.findTouchedSpyWindowsAt(displayId, x, y, isStylus,
-                                                     motionEntry.deviceId, mTouchStatesByDisplay);
+                                                     motionEntry.deviceId,
+                                                     mTouchStates.mTouchStatesByDisplay);
         for (const auto& windowHandle : touchedSpies) {
             const std::shared_ptr<Connection> connection =
                     mConnectionManager.getConnection(windowHandle->getToken());
@@ -1480,15 +1477,16 @@
     return nullptr;
 }
 
-std::vector<InputTarget> InputDispatcher::findOutsideTargetsLocked(
-        ui::LogicalDisplayId displayId, const sp<WindowInfoHandle>& touchedWindow,
-        int32_t pointerId) const {
+std::vector<InputTarget> InputDispatcher::DispatcherTouchState::findOutsideTargets(
+        ui::LogicalDisplayId displayId, const sp<gui::WindowInfoHandle>& touchedWindow,
+        int32_t pointerId, const ConnectionManager& connections,
+        const DispatcherWindowInfo& windowInfos, std::function<void()> dump) {
     if (touchedWindow == nullptr) {
         return {};
     }
     // Traverse windows from front to back until we encounter the touched window.
     std::vector<InputTarget> outsideTargets;
-    const auto& windowHandles = mWindowInfos.getWindowHandlesForDisplay(displayId);
+    const auto& windowHandles = windowInfos.getWindowHandlesForDisplay(displayId);
     for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
         if (windowHandle == touchedWindow) {
             // Stop iterating once we found a touched window. Any WATCH_OUTSIDE_TOUCH window
@@ -1500,9 +1498,13 @@
         if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
             std::bitset<MAX_POINTER_ID + 1> pointerIds;
             pointerIds.set(pointerId);
-            addPointerWindowTargetLocked(windowHandle, InputTarget::DispatchMode::OUTSIDE,
-                                         ftl::Flags<InputTarget::Flags>(), pointerIds,
-                                         /*firstDownTimeInTarget=*/std::nullopt, outsideTargets);
+            DispatcherTouchState::addPointerWindowTarget(windowHandle,
+                                                         InputTarget::DispatchMode::OUTSIDE,
+                                                         ftl::Flags<InputTarget::Flags>(),
+                                                         pointerIds,
+                                                         /*firstDownTimeInTarget=*/std::nullopt,
+                                                         connections, windowInfos, dump,
+                                                         outsideTargets);
         }
     }
     return outsideTargets;
@@ -1709,9 +1711,7 @@
     synthesizeCancelationEventsForAllConnectionsLocked(options);
 
     // Remove all active pointers from this device
-    for (auto& [_, touchState] : mTouchStatesByDisplay) {
-        touchState.removeAllPointersForDevice(entry.deviceId);
-    }
+    mTouchStates.removeAllPointersForDevice(entry.deviceId);
     return true;
 }
 
@@ -2073,7 +2073,16 @@
         }
 
         Result<std::vector<InputTarget>, InputEventInjectionResult> result =
-                findTouchedWindowTargetsLocked(currentTime, *entry);
+                mTouchStates
+                        .findTouchedWindowTargets(currentTime, *entry, mConnectionManager,
+                                                  mWindowInfos,
+                                                  mDragState ? mDragState->dragWindow : nullptr,
+                                                  std::bind_front(&InputDispatcher::
+                                                                          addDragEventLocked,
+                                                                  this),
+                                                  std::bind_front(&InputDispatcher::
+                                                                          logDispatchStateLocked,
+                                                                  this));
 
         if (result.ok()) {
             inputTargets = std::move(*result);
@@ -2318,7 +2327,8 @@
     }
 
     // Drop key events if requested by input feature
-    if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {
+    if (focusedWindowHandle != nullptr &&
+        shouldDropInput(entry, focusedWindowHandle, mWindowInfos)) {
         return injectionError(InputEventInjectionResult::FAILED);
     }
 
@@ -2387,8 +2397,12 @@
     return focusedWindowHandle;
 }
 
-base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult>
-InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry) {
+base::Result<std::vector<InputTarget>, os::InputEventInjectionResult>
+InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
+        nsecs_t currentTime, const MotionEntry& entry, const ConnectionManager& connections,
+        const DispatcherWindowInfo& windowInfos,
+        const sp<android::gui::WindowInfoHandle> dragWindow,
+        std::function<void(const MotionEntry&)> addDragEvent, std::function<void()> dump) {
     ATRACE_CALL();
 
     std::vector<InputTarget> targets;
@@ -2448,10 +2462,12 @@
         // be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
         const bool isStylus = isPointerFromStylus(entry, pointerIndex);
         sp<WindowInfoHandle> newTouchedWindowHandle =
-                mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
+                windowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
 
         if (isDown) {
-            targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointer.id);
+            targets += DispatcherTouchState::findOutsideTargets(displayId, newTouchedWindowHandle,
+                                                                pointer.id, connections,
+                                                                windowInfos, dump);
         }
         LOG_IF(INFO, newTouchedWindowHandle == nullptr)
                 << "No new touched window at (" << std::format("{:.1f}, {:.1f}", x, y)
@@ -2464,8 +2480,8 @@
         }
 
         std::vector<sp<WindowInfoHandle>> newTouchedWindows =
-                mWindowInfos.findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId,
-                                                     mTouchStatesByDisplay);
+                windowInfos.findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId,
+                                                    mTouchStatesByDisplay);
         if (newTouchedWindowHandle != nullptr) {
             // Process the foreground window first so that it is the first to receive the event.
             newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
@@ -2478,7 +2494,8 @@
         }
 
         for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
-            if (!canWindowReceiveMotionLocked(windowHandle, entry)) {
+            if (!canWindowReceiveMotion(windowHandle, entry, connections, windowInfos,
+                                        mTouchStatesByDisplay)) {
                 continue;
             }
 
@@ -2489,21 +2506,9 @@
             }
 
             // Set target flags.
-            ftl::Flags<InputTarget::Flags> targetFlags;
-
-            if (canReceiveForegroundTouches(*windowHandle->getInfo())) {
-                // There should only be one touched window that can be "foreground" for the pointer.
-                targetFlags |= InputTarget::Flags::FOREGROUND;
-            }
-
-            if (isSplit) {
-                targetFlags |= InputTarget::Flags::SPLIT;
-            }
-            if (mWindowInfos.isWindowObscuredAtPoint(windowHandle, x, y)) {
-                targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
-            } else if (mWindowInfos.isWindowObscured(windowHandle)) {
-                targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
-            }
+            ftl::Flags<InputTarget::Flags> targetFlags =
+                    DispatcherTouchState::getTargetFlags(windowHandle, {x, y}, isSplit,
+                                                         windowInfos);
 
             // Update the temporary touch state.
 
@@ -2521,7 +2526,7 @@
                 if (!addResult.ok()) {
                     LOG(ERROR) << "Error while processing " << entry << " for "
                                << windowHandle->getName();
-                    logDispatchStateLocked();
+                    dump();
                 }
                 // If this is the pointer going down and the touched window has a wallpaper
                 // then also add the touched wallpaper windows so they are locked in for the
@@ -2533,7 +2538,7 @@
                     windowHandle->getInfo()->inputConfig.test(
                             gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
                     sp<WindowInfoHandle> wallpaper =
-                            mWindowInfos.findWallpaperWindowBelow(windowHandle);
+                            windowInfos.findWallpaperWindowBelow(windowHandle);
                     if (wallpaper != nullptr) {
                         ftl::Flags<InputTarget::Flags> wallpaperFlags =
                                 InputTarget::Flags::WINDOW_IS_OBSCURED |
@@ -2592,7 +2597,7 @@
             tempTouchState.removeHoveringPointer(entry.deviceId, pointerId);
         }
 
-        addDragEventLocked(entry);
+        addDragEvent(entry);
 
         // Check whether touches should slip outside of the current foreground window.
         if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.getPointerCount() == 1 &&
@@ -2603,7 +2608,7 @@
                     tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
             LOG_ALWAYS_FATAL_IF(oldTouchedWindowHandle == nullptr);
             sp<WindowInfoHandle> newTouchedWindowHandle =
-                    mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
+                    windowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
 
             // Verify targeted injection.
             if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
@@ -2613,7 +2618,8 @@
 
             // Do not slide events to the window which can not receive motion event
             if (newTouchedWindowHandle != nullptr &&
-                !canWindowReceiveMotionLocked(newTouchedWindowHandle, entry)) {
+                !canWindowReceiveMotion(newTouchedWindowHandle, entry, connections, windowInfos,
+                                        mTouchStatesByDisplay)) {
                 newTouchedWindowHandle = nullptr;
             }
 
@@ -2630,26 +2636,18 @@
 
                 const TouchedWindow& touchedWindow =
                         tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
-                addPointerWindowTargetLocked(oldTouchedWindowHandle,
-                                             InputTarget::DispatchMode::SLIPPERY_EXIT,
-                                             ftl::Flags<InputTarget::Flags>(), pointerIds,
-                                             touchedWindow.getDownTimeInTarget(entry.deviceId),
-                                             targets);
+                DispatcherTouchState::
+                        addPointerWindowTarget(oldTouchedWindowHandle,
+                                               InputTarget::DispatchMode::SLIPPERY_EXIT,
+                                               ftl::Flags<InputTarget::Flags>(), pointerIds,
+                                               touchedWindow.getDownTimeInTarget(entry.deviceId),
+                                               connections, windowInfos, dump, targets);
 
                 // Make a slippery entrance into the new window.
 
-                ftl::Flags<InputTarget::Flags> targetFlags;
-                if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) {
-                    targetFlags |= InputTarget::Flags::FOREGROUND;
-                }
-                if (isSplit) {
-                    targetFlags |= InputTarget::Flags::SPLIT;
-                }
-                if (mWindowInfos.isWindowObscuredAtPoint(newTouchedWindowHandle, x, y)) {
-                    targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
-                } else if (mWindowInfos.isWindowObscured(newTouchedWindowHandle)) {
-                    targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
-                }
+                ftl::Flags<InputTarget::Flags> targetFlags =
+                        DispatcherTouchState::getTargetFlags(newTouchedWindowHandle, {x, y},
+                                                             isSplit, windowInfos);
 
                 tempTouchState.addOrUpdateWindow(newTouchedWindowHandle,
                                                  InputTarget::DispatchMode::SLIPPERY_ENTER,
@@ -2657,8 +2655,10 @@
                                                  entry.eventTime);
 
                 // Check if the wallpaper window should deliver the corresponding event.
-                slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
-                                   tempTouchState, entry, targets);
+                DispatcherTouchState::slipWallpaperTouch(targetFlags, oldTouchedWindowHandle,
+                                                         newTouchedWindowHandle, tempTouchState,
+                                                         entry, targets, connections, windowInfos,
+                                                         dump);
                 tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id,
                                                                oldTouchedWindowHandle);
             }
@@ -2671,7 +2671,7 @@
             std::vector<PointerProperties> touchingPointers{entry.pointerProperties[pointerIndex]};
             for (TouchedWindow& touchedWindow : tempTouchState.windows) {
                 // Ignore drag window for it should just track one pointer.
-                if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
+                if (dragWindow == touchedWindow.windowHandle) {
                     continue;
                 }
                 if (!touchedWindow.hasTouchingPointers(entry.deviceId)) {
@@ -2685,17 +2685,17 @@
     // Update dispatching for hover enter and exit.
     {
         std::vector<TouchedWindow> hoveringWindows =
-                getHoveringWindowsLocked(oldState, tempTouchState, entry,
-                                         std::bind_front(&InputDispatcher::logDispatchStateLocked,
-                                                         this));
+                getHoveringWindowsLocked(oldState, tempTouchState, entry, dump);
         // Hardcode to single hovering pointer for now.
         std::bitset<MAX_POINTER_ID + 1> pointerIds;
         pointerIds.set(entry.pointerProperties[0].id);
         for (const TouchedWindow& touchedWindow : hoveringWindows) {
-            addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode,
-                                         touchedWindow.targetFlags, pointerIds,
-                                         touchedWindow.getDownTimeInTarget(entry.deviceId),
-                                         targets);
+            DispatcherTouchState::addPointerWindowTarget(touchedWindow.windowHandle,
+                                                         touchedWindow.dispatchMode,
+                                                         touchedWindow.targetFlags, pointerIds,
+                                                         touchedWindow.getDownTimeInTarget(
+                                                                 entry.deviceId),
+                                                         connections, windowInfos, dump, targets);
         }
     }
 
@@ -2724,7 +2724,7 @@
             for (InputTarget& target : targets) {
                 if (target.dispatchMode == InputTarget::DispatchMode::OUTSIDE) {
                     sp<WindowInfoHandle> targetWindow =
-                            mWindowInfos.findWindowHandle(target.connection->getToken());
+                            windowInfos.findWindowHandle(target.connection->getToken());
                     if (targetWindow->getInfo()->ownerUid != foregroundWindowUid) {
                         target.flags |= InputTarget::Flags::ZERO_COORDS;
                     }
@@ -2748,9 +2748,13 @@
         if (touchingPointers.empty()) {
             continue;
         }
-        addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode,
-                                     touchedWindow.targetFlags, getPointerIds(touchingPointers),
-                                     touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
+        DispatcherTouchState::addPointerWindowTarget(touchedWindow.windowHandle,
+                                                     touchedWindow.dispatchMode,
+                                                     touchedWindow.targetFlags,
+                                                     getPointerIds(touchingPointers),
+                                                     touchedWindow.getDownTimeInTarget(
+                                                             entry.deviceId),
+                                                     connections, windowInfos, dump, targets);
     }
 
     // During targeted injection, only allow owned targets to receive events
@@ -2953,11 +2957,12 @@
     }
 }
 
-void InputDispatcher::addPointerWindowTargetLocked(
+void InputDispatcher::DispatcherTouchState::addPointerWindowTarget(
         const sp<android::gui::WindowInfoHandle>& windowHandle,
         InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
         std::bitset<MAX_POINTER_ID + 1> pointerIds, std::optional<nsecs_t> firstDownTimeInTarget,
-        std::vector<InputTarget>& inputTargets) const REQUIRES(mLock) {
+        const ConnectionManager& connections, const DispatcherWindowInfo& windowInfos,
+        std::function<void()> dump, std::vector<InputTarget>& inputTargets) {
     if (pointerIds.none()) {
         for (const auto& target : inputTargets) {
             LOG(INFO) << "Target: " << target;
@@ -2982,19 +2987,17 @@
         it = inputTargets.end();
     }
 
-    const WindowInfo* windowInfo = windowHandle->getInfo();
+    const WindowInfo& windowInfo = *windowHandle->getInfo();
 
     if (it == inputTargets.end()) {
-        std::shared_ptr<Connection> connection =
-                mConnectionManager.getConnection(windowHandle->getToken());
+        std::shared_ptr<Connection> connection = connections.getConnection(windowInfo.token);
         if (connection == nullptr) {
-            ALOGW("Not creating InputTarget for %s, no input channel",
-                  windowHandle->getName().c_str());
+            ALOGW("Not creating InputTarget for %s, no input channel", windowInfo.name.c_str());
             return;
         }
         inputTargets.push_back(
                 createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
-                                  mWindowInfos.getRawTransform(*windowHandle->getInfo()),
+                                  windowInfos.getRawTransform(*windowHandle->getInfo()),
                                   firstDownTimeInTarget));
         it = inputTargets.end() - 1;
     }
@@ -3007,14 +3010,14 @@
         LOG(ERROR) << __func__ << ": Flags don't match! new targetFlags=" << targetFlags.string()
                    << ", it=" << *it;
     }
-    if (it->globalScaleFactor != windowInfo->globalScaleFactor) {
+    if (it->globalScaleFactor != windowInfo.globalScaleFactor) {
         LOG(ERROR) << __func__ << ": Mismatch! it->globalScaleFactor=" << it->globalScaleFactor
-                   << ", windowInfo->globalScaleFactor=" << windowInfo->globalScaleFactor;
+                   << ", windowInfo->globalScaleFactor=" << windowInfo.globalScaleFactor;
     }
 
-    Result<void> result = it->addPointers(pointerIds, windowInfo->transform);
+    Result<void> result = it->addPointers(pointerIds, windowInfo.transform);
     if (!result.ok()) {
-        logDispatchStateLocked();
+        dump();
         LOG(FATAL) << result.error().message();
     }
 }
@@ -4069,7 +4072,7 @@
     // Generate cancellations for touched windows first. This is to avoid generating cancellations
     // through a non-touched window if there are more than one window for an input channel.
     if (cancelPointers) {
-        for (const auto& [displayId, touchState] : mTouchStatesByDisplay) {
+        for (const auto& [displayId, touchState] : mTouchStates.mTouchStatesByDisplay) {
             if (options.displayId.has_value() && options.displayId != displayId) {
                 continue;
             }
@@ -4204,9 +4207,15 @@
                         sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0);
                         mDragState.reset();
                     }
-                    addPointerWindowTargetLocked(window, InputTarget::DispatchMode::AS_IS,
-                                                 ftl::Flags<InputTarget::Flags>(), pointerIds,
-                                                 motionEntry.downTime, targets);
+                    DispatcherTouchState::
+                            addPointerWindowTarget(window, InputTarget::DispatchMode::AS_IS,
+                                                   ftl::Flags<InputTarget::Flags>(), pointerIds,
+                                                   motionEntry.downTime, mConnectionManager,
+                                                   mWindowInfos,
+                                                   std::bind_front(&InputDispatcher::
+                                                                           logDispatchStateLocked,
+                                                                   this),
+                                                   targets);
                 } else {
                     targets.emplace_back(fallbackTarget);
                     // Since we don't have a window, use the display transform as the raw transform.
@@ -4268,7 +4277,8 @@
     }
 
     const auto [_, touchedWindowState, displayId] =
-            findTouchStateWindowAndDisplay(connection->getToken(), mTouchStatesByDisplay);
+            findTouchStateWindowAndDisplay(connection->getToken(),
+                                           mTouchStates.mTouchStatesByDisplay);
     if (touchedWindowState == nullptr) {
         LOG(FATAL) << __func__ << ": Touch state is out of sync: No touched window for token";
     }
@@ -4290,9 +4300,14 @@
                          pointerIndex++) {
                         pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
                     }
-                    addPointerWindowTargetLocked(windowHandle, InputTarget::DispatchMode::AS_IS,
-                                                 targetFlags, pointerIds, motionEntry.downTime,
-                                                 targets);
+                    DispatcherTouchState::
+                            addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::AS_IS,
+                                                   targetFlags, pointerIds, motionEntry.downTime,
+                                                   mConnectionManager, mWindowInfos,
+                                                   std::bind_front(&InputDispatcher::
+                                                                           logDispatchStateLocked,
+                                                                   this),
+                                                   targets);
                 } else {
                     targets.emplace_back(connection, targetFlags);
                     // Since we don't have a window, use the display transform as the raw transform.
@@ -4559,13 +4574,8 @@
         if (!(policyFlags & POLICY_FLAG_PASS_TO_USER)) {
             // Set the flag anyway if we already have an ongoing gesture. That would allow us to
             // complete the processing of the current stroke.
-            const auto touchStateIt = mTouchStatesByDisplay.find(args.displayId);
-            if (touchStateIt != mTouchStatesByDisplay.end()) {
-                const TouchState& touchState = touchStateIt->second;
-                if (touchState.hasTouchingPointers(args.deviceId) ||
-                    touchState.hasHoveringPointers(args.deviceId)) {
-                    policyFlags |= POLICY_FLAG_PASS_TO_USER;
-                }
+            if (mTouchStates.hasTouchingOrHoveringPointers(args.displayId, args.deviceId)) {
+                policyFlags |= POLICY_FLAG_PASS_TO_USER;
             }
         }
 
@@ -4871,13 +4881,8 @@
             if (!(policyFlags & POLICY_FLAG_PASS_TO_USER)) {
                 // Set the flag anyway if we already have an ongoing motion gesture. That
                 // would allow us to complete the processing of the current stroke.
-                const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
-                if (touchStateIt != mTouchStatesByDisplay.end()) {
-                    const TouchState& touchState = touchStateIt->second;
-                    if (touchState.hasTouchingPointers(resolvedDeviceId) ||
-                        touchState.hasHoveringPointers(resolvedDeviceId)) {
-                        policyFlags |= POLICY_FLAG_PASS_TO_USER;
-                    }
+                if (mTouchStates.hasTouchingOrHoveringPointers(displayId, resolvedDeviceId)) {
+                    policyFlags |= POLICY_FLAG_PASS_TO_USER;
                 }
             }
 
@@ -5266,9 +5271,11 @@
     return dump;
 }
 
-bool InputDispatcher::canWindowReceiveMotionLocked(
+bool InputDispatcher::canWindowReceiveMotion(
         const sp<android::gui::WindowInfoHandle>& window,
-        const android::inputdispatcher::MotionEntry& motionEntry) const {
+        const android::inputdispatcher::MotionEntry& motionEntry,
+        const ConnectionManager& connections, const DispatcherWindowInfo& windowInfos,
+        const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStates) {
     const WindowInfo& info = *window->getInfo();
 
     // Skip spy window targets that are not valid for targeted injection.
@@ -5287,7 +5294,7 @@
         return false;
     }
 
-    std::shared_ptr<Connection> connection = mConnectionManager.getConnection(window->getToken());
+    std::shared_ptr<Connection> connection = connections.getConnection(window->getToken());
     if (connection == nullptr) {
         ALOGW("Not sending touch to %s because there's no corresponding connection",
               window->getName().c_str());
@@ -5302,8 +5309,8 @@
     // Drop events that can't be trusted due to occlusion
     const auto [x, y] = resolveTouchedPosition(motionEntry);
     DispatcherWindowInfo::TouchOcclusionInfo occlusionInfo =
-            mWindowInfos.computeTouchOcclusionInfo(window, x, y);
-    if (!mWindowInfos.isTouchTrusted(occlusionInfo)) {
+            windowInfos.computeTouchOcclusionInfo(window, x, y);
+    if (!windowInfos.isTouchTrusted(occlusionInfo)) {
         if (DEBUG_TOUCH_OCCLUSION) {
             ALOGD("Stack of obscuring windows during untrusted touch (%.1f, %.1f):", x, y);
             for (const auto& log : occlusionInfo.debugInfo) {
@@ -5316,13 +5323,13 @@
     }
 
     // Drop touch events if requested by input feature
-    if (shouldDropInput(motionEntry, window)) {
+    if (shouldDropInput(motionEntry, window, windowInfos)) {
         return false;
     }
 
     // Ignore touches if stylus is down anywhere on screen
     if (info.inputConfig.test(WindowInfo::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH) &&
-        isStylusActiveInDisplay(info.displayId, mTouchStatesByDisplay)) {
+        isStylusActiveInDisplay(info.displayId, touchStates)) {
         LOG(INFO) << "Dropping touch from " << window->getName() << " because stylus is active";
         return false;
     }
@@ -5446,70 +5453,38 @@
         onFocusChangedLocked(*changes, traceContext.getTracker(), removedFocusedWindowHandle);
     }
 
-    if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
-        TouchState& state = it->second;
-        for (size_t i = 0; i < state.windows.size();) {
-            TouchedWindow& touchedWindow = state.windows[i];
-            if (mWindowInfos.isWindowPresent(touchedWindow.windowHandle)) {
-                i++;
-                continue;
-            }
-            LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName()
-                      << " in display %" << displayId;
-            CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
-                                       "touched window was removed", traceContext.getTracker());
-            synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options);
-            // Since we are about to drop the touch, cancel the events for the wallpaper as
-            // well.
-            if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) &&
-                touchedWindow.windowHandle->getInfo()->inputConfig.test(
-                        gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
-                for (const DeviceId deviceId : touchedWindow.getTouchingDeviceIds()) {
-                    if (const auto& ww = state.getWallpaperWindow(deviceId); ww != nullptr) {
-                        options.deviceId = deviceId;
-                        synthesizeCancelationEventsForWindowLocked(ww, options);
-                    }
-                }
-            }
-            state.windows.erase(state.windows.begin() + i);
-        }
-
-        // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. We
-        // could just clear the state here.
-        if (mDragState && mDragState->dragWindow->getInfo()->displayId == displayId &&
-            std::find(windowHandles.begin(), windowHandles.end(), mDragState->dragWindow) ==
-                    windowHandles.end()) {
-            ALOGI("Drag window went away: %s", mDragState->dragWindow->getName().c_str());
-            sendDropWindowCommandLocked(nullptr, 0, 0);
-            mDragState.reset();
+    CancelationOptions pointerCancellationOptions(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+                                                  "touched window was removed",
+                                                  traceContext.getTracker());
+    CancelationOptions hoverCancellationOptions(CancelationOptions::Mode::CANCEL_HOVER_EVENTS,
+                                                "WindowInfo changed", traceContext.getTracker());
+    const std::list<DispatcherTouchState::CancellationArgs> cancellations =
+            mTouchStates.updateFromWindowInfo(displayId, mWindowInfos);
+    for (const auto& cancellationArgs : cancellations) {
+        switch (cancellationArgs.mode) {
+            case CancelationOptions::Mode::CANCEL_POINTER_EVENTS:
+                pointerCancellationOptions.deviceId = cancellationArgs.deviceId;
+                synthesizeCancelationEventsForWindowLocked(cancellationArgs.windowHandle,
+                                                           pointerCancellationOptions);
+                break;
+            case CancelationOptions::Mode::CANCEL_HOVER_EVENTS:
+                hoverCancellationOptions.deviceId = cancellationArgs.deviceId;
+                synthesizeCancelationEventsForWindowLocked(cancellationArgs.windowHandle,
+                                                           hoverCancellationOptions);
+                break;
+            default:
+                LOG_ALWAYS_FATAL("Unexpected cancellation Mode");
         }
     }
 
-    // Check if the hovering should stop because the window is no longer eligible to receive it
-    // (for example, if the touchable region changed)
-    if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
-        TouchState& state = it->second;
-        for (TouchedWindow& touchedWindow : state.windows) {
-            std::vector<DeviceId> erasedDevices = touchedWindow.eraseHoveringPointersIf(
-                    [this, displayId, &touchedWindow](const PointerProperties& properties, float x,
-                                                      float y) REQUIRES(mLock) {
-                        const bool isStylus = properties.toolType == ToolType::STYLUS;
-                        const ui::Transform displayTransform =
-                                mWindowInfos.getDisplayTransform(displayId);
-                        const bool stillAcceptsTouch =
-                                windowAcceptsTouchAt(*touchedWindow.windowHandle->getInfo(),
-                                                     displayId, x, y, isStylus, displayTransform);
-                        return !stillAcceptsTouch;
-                    });
-
-            for (DeviceId deviceId : erasedDevices) {
-                CancelationOptions options(CancelationOptions::Mode::CANCEL_HOVER_EVENTS,
-                                           "WindowInfo changed",
-                                           traceContext.getTracker());
-                options.deviceId = deviceId;
-                synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options);
-            }
-        }
+    // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. We
+    // could just clear the state here.
+    if (mDragState && mDragState->dragWindow->getInfo()->displayId == displayId &&
+        std::find(windowHandles.begin(), windowHandles.end(), mDragState->dragWindow) ==
+                windowHandles.end()) {
+        ALOGI("Drag window went away: %s", mDragState->dragWindow->getName().c_str());
+        sendDropWindowCommandLocked(nullptr, 0, 0);
+        mDragState.reset();
     }
 
     // Release information for windows that are no longer present.
@@ -5526,6 +5501,76 @@
     }
 }
 
+std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
+InputDispatcher::DispatcherTouchState::updateFromWindowInfo(
+        ui::LogicalDisplayId displayId, const DispatcherWindowInfo& windowInfos) {
+    std::list<CancellationArgs> cancellations;
+    if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
+        TouchState& state = it->second;
+        cancellations = eraseRemovedWindowsFromWindowInfo(state, displayId, windowInfos);
+        cancellations.splice(cancellations.end(),
+                             updateHoveringStateFromWindowInfo(state, displayId, windowInfos));
+    }
+    return cancellations;
+}
+
+std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
+InputDispatcher::DispatcherTouchState::eraseRemovedWindowsFromWindowInfo(
+        TouchState& state, ui::LogicalDisplayId displayId,
+        const DispatcherWindowInfo& windowInfos) {
+    std::list<CancellationArgs> cancellations;
+    for (auto it = state.windows.begin(); it != state.windows.end();) {
+        TouchedWindow& touchedWindow = *it;
+        if (windowInfos.isWindowPresent(touchedWindow.windowHandle)) {
+            it++;
+            continue;
+        }
+        LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName()
+                  << " in display %" << displayId;
+        cancellations.emplace_back(touchedWindow.windowHandle,
+                                   CancelationOptions::Mode::CANCEL_POINTER_EVENTS, std::nullopt);
+        // Since we are about to drop the touch, cancel the events for the wallpaper as well.
+        if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) &&
+            touchedWindow.windowHandle->getInfo()->inputConfig.test(
+                    gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
+            for (const DeviceId deviceId : touchedWindow.getTouchingDeviceIds()) {
+                if (const auto& ww = state.getWallpaperWindow(deviceId); ww != nullptr) {
+                    cancellations.emplace_back(ww, CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+                                               deviceId);
+                }
+            }
+        }
+        it = state.windows.erase(it);
+    }
+    return cancellations;
+}
+
+std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
+InputDispatcher::DispatcherTouchState::updateHoveringStateFromWindowInfo(
+        TouchState& state, ui::LogicalDisplayId displayId,
+        const DispatcherWindowInfo& windowInfos) {
+    std::list<CancellationArgs> cancellations;
+    // Check if the hovering should stop because the window is no longer eligible to receive it
+    // (for example, if the touchable region changed)
+    ui::Transform displayTransform = windowInfos.getDisplayTransform(displayId);
+    for (TouchedWindow& touchedWindow : state.windows) {
+        std::vector<DeviceId> erasedDevices = touchedWindow.eraseHoveringPointersIf(
+                [&](const PointerProperties& properties, float x, float y) {
+                    const bool isStylus = properties.toolType == ToolType::STYLUS;
+                    const bool stillAcceptsTouch =
+                            windowAcceptsTouchAt(*touchedWindow.windowHandle->getInfo(), displayId,
+                                                 x, y, isStylus, displayTransform);
+                    return !stillAcceptsTouch;
+                });
+
+        for (DeviceId deviceId : erasedDevices) {
+            cancellations.emplace_back(touchedWindow.windowHandle,
+                                       CancelationOptions::Mode::CANCEL_HOVER_EVENTS, deviceId);
+        }
+    }
+    return cancellations;
+}
+
 void InputDispatcher::setFocusedApplication(
         ui::LogicalDisplayId displayId,
         const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
@@ -5793,7 +5838,7 @@
 
         // Find the target touch state and touched window by fromToken.
         auto [state, touchedWindow, displayId] =
-                findTouchStateWindowAndDisplay(fromToken, mTouchStatesByDisplay);
+                findTouchStateWindowAndDisplay(fromToken, mTouchStates.mTouchStatesByDisplay);
 
         if (state == nullptr || touchedWindow == nullptr) {
             ALOGD("Touch transfer failed because from window is not being touched.");
@@ -5885,11 +5930,10 @@
  * Return null if there are no windows touched on that display, or if more than one foreground
  * window is being touched.
  */
-sp<WindowInfoHandle> InputDispatcher::findTouchedForegroundWindow(
-        const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay,
-        ui::LogicalDisplayId displayId) {
-    const auto stateIt = touchStatesByDisplay.find(displayId);
-    if (stateIt == touchStatesByDisplay.end()) {
+sp<WindowInfoHandle> InputDispatcher::DispatcherTouchState::findTouchedForegroundWindow(
+        ui::LogicalDisplayId displayId) const {
+    const auto stateIt = mTouchStatesByDisplay.find(displayId);
+    if (stateIt == mTouchStatesByDisplay.end()) {
         ALOGI("No touch state on display %s", displayId.toString().c_str());
         return nullptr;
     }
@@ -5925,7 +5969,7 @@
             return false;
         }
 
-        sp<WindowInfoHandle> from = findTouchedForegroundWindow(mTouchStatesByDisplay, displayId);
+        sp<WindowInfoHandle> from = mTouchStates.findTouchedForegroundWindow(displayId);
         if (from == nullptr) {
             ALOGE("Could not find a source window in %s for %p", __func__, destChannelToken.get());
             return false;
@@ -5953,7 +5997,7 @@
     resetNoFocusedWindowTimeoutLocked();
 
     mAnrTracker.clear();
-    mTouchStatesByDisplay.clear();
+    mTouchStates.clear();
 }
 
 void InputDispatcher::logDispatchStateLocked() const {
@@ -6011,15 +6055,7 @@
     dump += mFocusResolver.dump();
     dump += dumpPointerCaptureStateLocked();
 
-    if (!mTouchStatesByDisplay.empty()) {
-        dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
-        for (const auto& [displayId, state] : mTouchStatesByDisplay) {
-            std::string touchStateDump = addLinePrefix(state.dump(), INDENT2);
-            dump += INDENT2 + displayId.toString() + " : " + touchStateDump;
-        }
-    } else {
-        dump += INDENT "TouchStates: <no displays touched>\n";
-    }
+    dump += addLinePrefix(mTouchStates.dump(), INDENT);
 
     if (mDragState) {
         dump += StringPrintf(INDENT "DragState:\n");
@@ -6222,7 +6258,7 @@
     }
 
     auto [statePtr, windowPtr, displayId] =
-            findTouchStateWindowAndDisplay(token, mTouchStatesByDisplay);
+            findTouchStateWindowAndDisplay(token, mTouchStates.mTouchStatesByDisplay);
     if (statePtr == nullptr || windowPtr == nullptr) {
         LOG(WARNING)
                 << "Attempted to pilfer points from a channel without any on-going pointer streams."
@@ -7037,12 +7073,13 @@
     mLooper->wake();
 }
 
-bool InputDispatcher::shouldDropInput(
-        const EventEntry& entry, const sp<android::gui::WindowInfoHandle>& windowHandle) const {
+bool InputDispatcher::shouldDropInput(const EventEntry& entry,
+                                      const sp<WindowInfoHandle>& windowHandle,
+                                      const DispatcherWindowInfo& windowInfos) {
     if (windowHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::DROP_INPUT) ||
         (windowHandle->getInfo()->inputConfig.test(
                  WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED) &&
-         mWindowInfos.isWindowObscured(windowHandle))) {
+         windowInfos.isWindowObscured(windowHandle))) {
         ALOGW("Dropping %s event targeting %s as requested by the input configuration {%s} on "
               "display %s.",
               ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(),
@@ -7067,7 +7104,7 @@
                                    "cancel current touch", traceContext.getTracker());
         synthesizeCancelationEventsForAllConnectionsLocked(options);
 
-        mTouchStatesByDisplay.clear();
+        mTouchStates.clear();
     }
     // Wake up poll loop since there might be work to do.
     mLooper->wake();
@@ -7078,11 +7115,11 @@
     mMonitorDispatchingTimeout = timeout;
 }
 
-void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
-                                         const sp<WindowInfoHandle>& oldWindowHandle,
-                                         const sp<WindowInfoHandle>& newWindowHandle,
-                                         TouchState& state, const MotionEntry& entry,
-                                         std::vector<InputTarget>& targets) const {
+void InputDispatcher::DispatcherTouchState::slipWallpaperTouch(
+        ftl::Flags<InputTarget::Flags> targetFlags, const sp<WindowInfoHandle>& oldWindowHandle,
+        const sp<WindowInfoHandle>& newWindowHandle, TouchState& state, const MotionEntry& entry,
+        std::vector<InputTarget>& targets, const ConnectionManager& connections,
+        const DispatcherWindowInfo& windowInfos, std::function<void()> dump) {
     LOG_IF(FATAL, entry.getPointerCount() != 1) << "Entry not eligible for slip: " << entry;
     const DeviceId deviceId = entry.deviceId;
     const PointerProperties& pointerProperties = entry.pointerProperties[0];
@@ -7095,16 +7132,19 @@
     const sp<WindowInfoHandle> oldWallpaper =
             oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr;
     const sp<WindowInfoHandle> newWallpaper =
-            newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(newWindowHandle) : nullptr;
+            newHasWallpaper ? windowInfos.findWallpaperWindowBelow(newWindowHandle) : nullptr;
     if (oldWallpaper == newWallpaper) {
         return;
     }
 
     if (oldWallpaper != nullptr) {
         const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper);
-        addPointerWindowTargetLocked(oldWallpaper, InputTarget::DispatchMode::SLIPPERY_EXIT,
-                                     oldTouchedWindow.targetFlags, getPointerIds(pointers),
-                                     oldTouchedWindow.getDownTimeInTarget(deviceId), targets);
+        DispatcherTouchState::addPointerWindowTarget(oldWallpaper,
+                                                     InputTarget::DispatchMode::SLIPPERY_EXIT,
+                                                     oldTouchedWindow.targetFlags,
+                                                     getPointerIds(pointers),
+                                                     oldTouchedWindow.getDownTimeInTarget(deviceId),
+                                                     connections, windowInfos, dump, targets);
         state.removeTouchingPointerFromWindow(deviceId, pointerProperties.id, oldWallpaper);
     }
 
@@ -7199,18 +7239,7 @@
                                         ui::LogicalDisplayId displayId, DeviceId deviceId,
                                         int32_t pointerId) {
     std::scoped_lock _l(mLock);
-    auto touchStateIt = mTouchStatesByDisplay.find(displayId);
-    if (touchStateIt == mTouchStatesByDisplay.end()) {
-        return false;
-    }
-    for (const TouchedWindow& window : touchStateIt->second.windows) {
-        if (window.windowHandle->getToken() == token &&
-            (window.hasTouchingPointer(deviceId, pointerId) ||
-             window.hasHoveringPointer(deviceId, pointerId))) {
-            return true;
-        }
-    }
-    return false;
+    return mTouchStates.isPointerInWindow(token, displayId, deviceId, pointerId);
 }
 
 void InputDispatcher::setInputMethodConnectionIsActive(bool isActive) {
@@ -7355,4 +7384,75 @@
     mMaximumObscuringOpacityForTouch = opacity;
 }
 
+ftl::Flags<InputTarget::Flags> InputDispatcher::DispatcherTouchState::getTargetFlags(
+        const sp<WindowInfoHandle>& targetWindow, vec2 targetPosition, bool isSplit,
+        const DispatcherWindowInfo& windowInfos) {
+    ftl::Flags<InputTarget::Flags> targetFlags;
+    if (canReceiveForegroundTouches(*targetWindow->getInfo())) {
+        // There should only be one touched window that can be "foreground" for the pointer.
+        targetFlags |= InputTarget::Flags::FOREGROUND;
+    }
+    if (isSplit) {
+        targetFlags |= InputTarget::Flags::SPLIT;
+    }
+    if (windowInfos.isWindowObscuredAtPoint(targetWindow, targetPosition.x, targetPosition.y)) {
+        targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
+    } else if (windowInfos.isWindowObscured(targetWindow)) {
+        targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
+    }
+    return targetFlags;
+}
+
+bool InputDispatcher::DispatcherTouchState::hasTouchingOrHoveringPointers(
+        ui::LogicalDisplayId displayId, int32_t deviceId) const {
+    const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
+    if (touchStateIt == mTouchStatesByDisplay.end()) {
+        return false;
+    }
+    return touchStateIt->second.hasTouchingPointers(deviceId) ||
+            touchStateIt->second.hasHoveringPointers(deviceId);
+}
+
+bool InputDispatcher::DispatcherTouchState::isPointerInWindow(const sp<android::IBinder>& token,
+                                                              ui::LogicalDisplayId displayId,
+                                                              android::DeviceId deviceId,
+                                                              int32_t pointerId) const {
+    const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
+    if (touchStateIt == mTouchStatesByDisplay.end()) {
+        return false;
+    }
+    for (const TouchedWindow& window : touchStateIt->second.windows) {
+        if (window.windowHandle->getToken() == token &&
+            (window.hasTouchingPointer(deviceId, pointerId) ||
+             window.hasHoveringPointer(deviceId, pointerId))) {
+            return true;
+        }
+    }
+    return false;
+}
+
+std::string InputDispatcher::DispatcherTouchState::dump() const {
+    std::string dump;
+    if (!mTouchStatesByDisplay.empty()) {
+        dump += StringPrintf("TouchStatesByDisplay:\n");
+        for (const auto& [displayId, state] : mTouchStatesByDisplay) {
+            std::string touchStateDump = addLinePrefix(state.dump(), INDENT);
+            dump += INDENT + displayId.toString() + " : " + touchStateDump;
+        }
+    } else {
+        dump += "TouchStates: <no displays touched>\n";
+    }
+    return dump;
+}
+
+void InputDispatcher::DispatcherTouchState::removeAllPointersForDevice(android::DeviceId deviceId) {
+    for (auto& [_, touchState] : mTouchStatesByDisplay) {
+        touchState.removeAllPointersForDevice(deviceId);
+    }
+}
+
+void InputDispatcher::DispatcherTouchState::clear() {
+    mTouchStatesByDisplay.clear();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 415f4c8..f590806 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -350,6 +350,96 @@
 
     DispatcherWindowInfo mWindowInfos GUARDED_BY(mLock);
 
+    class DispatcherTouchState {
+    public:
+        struct CancellationArgs {
+            const sp<gui::WindowInfoHandle> windowHandle;
+            CancelationOptions::Mode mode;
+            std::optional<DeviceId> deviceId;
+        };
+
+        static void addPointerWindowTarget(const sp<android::gui::WindowInfoHandle>& windowHandle,
+                                           InputTarget::DispatchMode dispatchMode,
+                                           ftl::Flags<InputTarget::Flags> targetFlags,
+                                           std::bitset<MAX_POINTER_ID + 1> pointerIds,
+                                           std::optional<nsecs_t> firstDownTimeInTarget,
+                                           const ConnectionManager& connections,
+                                           const DispatcherWindowInfo& windowInfos,
+                                           std::function<void()> dump,
+                                           std::vector<InputTarget>& inputTargets);
+
+        base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult>
+        findTouchedWindowTargets(nsecs_t currentTime, const MotionEntry& entry,
+                                 const ConnectionManager& connections,
+                                 const DispatcherWindowInfo& windowInfos,
+                                 const sp<android::gui::WindowInfoHandle> dragWindow,
+                                 std::function<void(const MotionEntry&)> addDragEvent,
+                                 std::function<void()> dump);
+
+        sp<android::gui::WindowInfoHandle> findTouchedForegroundWindow(
+                ui::LogicalDisplayId displayId) const;
+
+        bool hasTouchingOrHoveringPointers(ui::LogicalDisplayId displayId, int32_t deviceId) const;
+
+        bool isPointerInWindow(const sp<android::IBinder>& token, ui::LogicalDisplayId displayId,
+                               DeviceId deviceId, int32_t pointerId) const;
+
+        std::string dump() const;
+
+        // Updates the touchState for display from WindowInfo,
+        // return vector of CancellationArgs for every cancelled touch
+        std::list<CancellationArgs> updateFromWindowInfo(ui::LogicalDisplayId displayId,
+                                                         const DispatcherWindowInfo& windowInfos);
+
+        void removeAllPointersForDevice(DeviceId deviceId);
+
+        void clear();
+
+        std::unordered_map<ui::LogicalDisplayId, TouchState> mTouchStatesByDisplay;
+
+    private:
+        static std::list<CancellationArgs> eraseRemovedWindowsFromWindowInfo(
+                TouchState& state, ui::LogicalDisplayId displayId,
+                const DispatcherWindowInfo& windowInfos);
+
+        static std::list<CancellationArgs> updateHoveringStateFromWindowInfo(
+                TouchState& state, ui::LogicalDisplayId displayId,
+                const DispatcherWindowInfo& windowInfos);
+
+        static std::vector<InputTarget> findOutsideTargets(
+                ui::LogicalDisplayId displayId, const sp<gui::WindowInfoHandle>& touchedWindow,
+                int32_t pointerId, const ConnectionManager& connections,
+                const DispatcherWindowInfo& windowInfos, std::function<void()> dump);
+
+        /**
+         * Slip the wallpaper touch if necessary.
+         *
+         * @param targetFlags the target flags
+         * @param oldWindowHandle the old window that the touch slipped out of
+         * @param newWindowHandle the new window that the touch is slipping into
+         * @param state the current touch state. This will be updated if necessary to reflect the
+         * new windows that are receiving touch.
+         * @param deviceId the device id of the current motion being processed
+         * @param pointerProperties the pointer properties of the current motion being processed
+         * @param targets the current targets to add the walpaper ones to
+         * @param eventTime the new downTime for the wallpaper target
+         */
+        static void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
+                                       const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
+                                       const sp<android::gui::WindowInfoHandle>& newWindowHandle,
+                                       TouchState& state, const MotionEntry& entry,
+                                       std::vector<InputTarget>& targets,
+                                       const ConnectionManager& connections,
+                                       const DispatcherWindowInfo& windowInfos,
+                                       std::function<void()> dump);
+
+        static ftl::Flags<InputTarget::Flags> getTargetFlags(
+                const sp<android::gui::WindowInfoHandle>& targetWindow, vec2 targetPosition,
+                bool isSplit, const DispatcherWindowInfo& windowInfos);
+    };
+
+    DispatcherTouchState mTouchStates GUARDED_BY(mLock);
+
     // With each iteration, InputDispatcher nominally processes one queued event,
     // a timeout, or a response from an input consumer.
     // This method should only be called on the input dispatcher's own thread.
@@ -378,14 +468,6 @@
     // to transfer focus to a new application.
     std::shared_ptr<const EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
 
-    std::vector<InputTarget> findOutsideTargetsLocked(
-            ui::LogicalDisplayId displayId, const sp<android::gui::WindowInfoHandle>& touchedWindow,
-            int32_t pointerId) const REQUIRES(mLock);
-
-    static sp<android::gui::WindowInfoHandle> findTouchedForegroundWindow(
-            const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay,
-            ui::LogicalDisplayId displayId);
-
     status_t pilferPointersLocked(const sp<IBinder>& token) REQUIRES(mLock);
 
     const HmacKeyManager mHmacKeyManager;
@@ -470,8 +552,11 @@
 
     sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked(
             ui::LogicalDisplayId displayId) const REQUIRES(mLock);
-    bool canWindowReceiveMotionLocked(const sp<android::gui::WindowInfoHandle>& window,
-                                      const MotionEntry& motionEntry) const REQUIRES(mLock);
+
+    static bool canWindowReceiveMotion(
+            const sp<android::gui::WindowInfoHandle>& window, const MotionEntry& motionEntry,
+            const ConnectionManager& connections, const DispatcherWindowInfo& windowInfos,
+            const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStates);
 
     // Returns all the input targets (with their respective input channels) from the window handles
     // passed as argument.
@@ -486,8 +571,6 @@
             const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles,
             ui::LogicalDisplayId displayId) REQUIRES(mLock);
 
-    std::unordered_map<ui::LogicalDisplayId /*displayId*/, TouchState> mTouchStatesByDisplay
-            GUARDED_BY(mLock);
     std::unique_ptr<DragState> mDragState GUARDED_BY(mLock);
 
     void setFocusedApplicationLocked(
@@ -627,20 +710,12 @@
     base::Result<sp<android::gui::WindowInfoHandle>, android::os::InputEventInjectionResult>
     findFocusedWindowTargetLocked(nsecs_t currentTime, const EventEntry& entry,
                                   nsecs_t& nextWakeupTime) REQUIRES(mLock);
-    base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult>
-    findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry) REQUIRES(mLock);
 
     void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
                                InputTarget::DispatchMode dispatchMode,
                                ftl::Flags<InputTarget::Flags> targetFlags,
                                std::optional<nsecs_t> firstDownTimeInTarget,
                                std::vector<InputTarget>& inputTargets) const REQUIRES(mLock);
-    void addPointerWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
-                                      InputTarget::DispatchMode dispatchMode,
-                                      ftl::Flags<InputTarget::Flags> targetFlags,
-                                      std::bitset<MAX_POINTER_ID + 1> pointerIds,
-                                      std::optional<nsecs_t> firstDownTimeInTarget,
-                                      std::vector<InputTarget>& inputTargets) const REQUIRES(mLock);
     void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
                                           ui::LogicalDisplayId displayId) REQUIRES(mLock);
     void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
@@ -652,9 +727,9 @@
     std::string getApplicationWindowLabel(const InputApplicationHandle* applicationHandle,
                                           const sp<android::gui::WindowInfoHandle>& windowHandle);
 
-    bool shouldDropInput(const EventEntry& entry,
-                         const sp<android::gui::WindowInfoHandle>& windowHandle) const
-            REQUIRES(mLock);
+    static bool shouldDropInput(const EventEntry& entry,
+                                const sp<android::gui::WindowInfoHandle>& windowHandle,
+                                const DispatcherWindowInfo& windowInfo);
 
     // Manage the dispatch cycle for a single connection.
     // These methods are deliberately not Interruptible because doing all of the work
@@ -774,24 +849,6 @@
 
     sp<InputReporterInterface> mReporter;
 
-    /**
-     * Slip the wallpaper touch if necessary.
-     *
-     * @param targetFlags the target flags
-     * @param oldWindowHandle the old window that the touch slipped out of
-     * @param newWindowHandle the new window that the touch is slipping into
-     * @param state the current touch state. This will be updated if necessary to reflect the new
-     *        windows that are receiving touch.
-     * @param deviceId the device id of the current motion being processed
-     * @param pointerProperties the pointer properties of the current motion being processed
-     * @param targets the current targets to add the walpaper ones to
-     * @param eventTime the new downTime for the wallpaper target
-     */
-    void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
-                            const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
-                            const sp<android::gui::WindowInfoHandle>& newWindowHandle,
-                            TouchState& state, const MotionEntry& entry,
-                            std::vector<InputTarget>& targets) const REQUIRES(mLock);
     void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
                                 ftl::Flags<InputTarget::Flags> newTargetFlags,
                                 const sp<android::gui::WindowInfoHandle> fromWindowHandle,
diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp
index 5b7cc2d..78674e5 100644
--- a/services/inputflinger/rust/Android.bp
+++ b/services/inputflinger/rust/Android.bp
@@ -40,14 +40,14 @@
     crate_name: "inputflinger",
     srcs: ["lib.rs"],
     rustlibs: [
-        "libcxx",
-        "com.android.server.inputflinger-rust",
         "android.hardware.input.common-V1-rust",
+        "com.android.server.inputflinger-rust",
         "libbinder_rs",
+        "libcxx",
+        "libinput_rust",
         "liblog_rust",
         "liblogger",
         "libnix",
-        "libinput_rust",
     ],
     host_supported: true,
 }
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 368db1b..c0e2060 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -9959,57 +9959,63 @@
         InputDispatcherUserActivityPokeTests, MinPokeTimeObserved,
         REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
                                             rate_limit_user_activity_poke_in_dispatcher))) {
+    // Use current time otherwise events may be dropped due to being stale.
+    const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
     mDispatcher->setMinTimeBetweenUserActivityPokes(50ms);
 
     // First event of type TOUCH. Should poke.
     notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           milliseconds_to_nanoseconds(50));
+                           currentTime + milliseconds_to_nanoseconds(50));
     mFakePolicy->assertUserActivityPoked(
-            {{milliseconds_to_nanoseconds(50), USER_ACTIVITY_EVENT_TOUCH,
+            {{currentTime + milliseconds_to_nanoseconds(50), USER_ACTIVITY_EVENT_TOUCH,
               ui::LogicalDisplayId::DEFAULT}});
 
     // 80ns > 50ns has passed since previous TOUCH event. Should poke.
     notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           milliseconds_to_nanoseconds(130));
+                           currentTime + milliseconds_to_nanoseconds(130));
     mFakePolicy->assertUserActivityPoked(
-            {{milliseconds_to_nanoseconds(130), USER_ACTIVITY_EVENT_TOUCH,
+            {{currentTime + milliseconds_to_nanoseconds(130), USER_ACTIVITY_EVENT_TOUCH,
               ui::LogicalDisplayId::DEFAULT}});
 
     // First event of type OTHER. Should poke (despite being within 50ns of previous TOUCH event).
     notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
-                           ui::LogicalDisplayId::DEFAULT, milliseconds_to_nanoseconds(135));
+                           ui::LogicalDisplayId::DEFAULT,
+                           currentTime + milliseconds_to_nanoseconds(135));
     mFakePolicy->assertUserActivityPoked(
-            {{milliseconds_to_nanoseconds(135), USER_ACTIVITY_EVENT_OTHER,
+            {{currentTime + milliseconds_to_nanoseconds(135), USER_ACTIVITY_EVENT_OTHER,
               ui::LogicalDisplayId::DEFAULT}});
 
     // Within 50ns of previous TOUCH event. Should NOT poke.
     notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           milliseconds_to_nanoseconds(140));
+                           currentTime + milliseconds_to_nanoseconds(140));
     mFakePolicy->assertUserActivityNotPoked();
 
     // Within 50ns of previous OTHER event. Should NOT poke.
     notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
-                           ui::LogicalDisplayId::DEFAULT, milliseconds_to_nanoseconds(150));
+                           ui::LogicalDisplayId::DEFAULT,
+                           currentTime + milliseconds_to_nanoseconds(150));
     mFakePolicy->assertUserActivityNotPoked();
 
     // Within 50ns of previous TOUCH event (which was at time 130). Should NOT poke.
     // Note that STYLUS is mapped to TOUCH user activity, since it's a pointer-type source.
     notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
-                           milliseconds_to_nanoseconds(160));
+                           currentTime + milliseconds_to_nanoseconds(160));
     mFakePolicy->assertUserActivityNotPoked();
 
     // 65ns > 50ns has passed since previous OTHER event. Should poke.
     notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
-                           ui::LogicalDisplayId::DEFAULT, milliseconds_to_nanoseconds(200));
+                           ui::LogicalDisplayId::DEFAULT,
+                           currentTime + milliseconds_to_nanoseconds(200));
     mFakePolicy->assertUserActivityPoked(
-            {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_OTHER,
+            {{currentTime + milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_OTHER,
               ui::LogicalDisplayId::DEFAULT}});
 
     // 170ns > 50ns has passed since previous TOUCH event. Should poke.
     notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
-                           milliseconds_to_nanoseconds(300));
+                           currentTime + milliseconds_to_nanoseconds(300));
     mFakePolicy->assertUserActivityPoked(
-            {{milliseconds_to_nanoseconds(300), USER_ACTIVITY_EVENT_TOUCH,
+            {{currentTime + milliseconds_to_nanoseconds(300), USER_ACTIVITY_EVENT_TOUCH,
               ui::LogicalDisplayId::DEFAULT}});
 
     // Assert that there's no more user activity poke event.
@@ -10020,20 +10026,22 @@
         InputDispatcherUserActivityPokeTests, DefaultMinPokeTimeOf100MsUsed,
         REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
                                             rate_limit_user_activity_poke_in_dispatcher))) {
+    // Use current time otherwise events may be dropped due to being stale.
+    const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           milliseconds_to_nanoseconds(200));
+                           currentTime + milliseconds_to_nanoseconds(200));
     mFakePolicy->assertUserActivityPoked(
-            {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_TOUCH,
+            {{currentTime + milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_TOUCH,
               ui::LogicalDisplayId::DEFAULT}});
 
     notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           milliseconds_to_nanoseconds(280));
+                           currentTime + milliseconds_to_nanoseconds(280));
     mFakePolicy->assertUserActivityNotPoked();
 
     notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           milliseconds_to_nanoseconds(340));
+                           currentTime + milliseconds_to_nanoseconds(340));
     mFakePolicy->assertUserActivityPoked(
-            {{milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH,
+            {{currentTime + milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH,
               ui::LogicalDisplayId::DEFAULT}});
 }
 
@@ -10041,14 +10049,16 @@
         InputDispatcherUserActivityPokeTests, ZeroMinPokeTimeDisablesRateLimiting,
         REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
                                             rate_limit_user_activity_poke_in_dispatcher))) {
+    // Use current time otherwise events may be dropped due to being stale.
+    const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     mDispatcher->setMinTimeBetweenUserActivityPokes(0ms);
 
     notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           20);
+                           currentTime + 20);
     mFakePolicy->assertUserActivityPoked();
 
     notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
-                           30);
+                           currentTime + 30);
     mFakePolicy->assertUserActivityPoked();
 }
 
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 1ca2998..1286a36 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -2617,12 +2617,17 @@
     static constexpr ui::LogicalDisplayId DISPLAY_BOTTOM_ID = ui::LogicalDisplayId{40};
     static constexpr ui::LogicalDisplayId DISPLAY_LEFT_ID = ui::LogicalDisplayId{50};
     static constexpr ui::LogicalDisplayId DISPLAY_TOP_RIGHT_CORNER_ID = ui::LogicalDisplayId{60};
+    static constexpr ui::LogicalDisplayId DISPLAY_HIGH_DENSITY_ID = ui::LogicalDisplayId{70};
+
+    static constexpr int DENSITY_MEDIUM = 160;
+    static constexpr int DENSITY_HIGH = 320;
 
     PointerChoreographerDisplayTopologyTestFixture() {
         com::android::input::flags::connected_displays_cursor(true);
     }
 
 protected:
+    // Note: viewport size is in pixels and offsets in topology are in dp
     std::vector<DisplayViewport> mViewports{
             createViewport(DISPLAY_CENTER_ID, /*width*/ 100, /*height*/ 100, ui::ROTATION_0),
             createViewport(DISPLAY_TOP_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_0),
@@ -2631,16 +2636,28 @@
             createViewport(DISPLAY_LEFT_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_270),
             createViewport(DISPLAY_TOP_RIGHT_CORNER_ID, /*width*/ 90, /*height*/ 90,
                            ui::ROTATION_0),
+            // Create a high density display size 100x100 dp i.e. 200x200 px
+            createViewport(DISPLAY_HIGH_DENSITY_ID, /*width*/ 200, /*height*/ 200, ui::ROTATION_0),
     };
 
-    DisplayTopologyGraph mTopology{DISPLAY_CENTER_ID,
-                                   {{DISPLAY_CENTER_ID,
-                                     {{DISPLAY_TOP_ID, DisplayTopologyPosition::TOP, 10.0f},
-                                      {DISPLAY_RIGHT_ID, DisplayTopologyPosition::RIGHT, 10.0f},
-                                      {DISPLAY_BOTTOM_ID, DisplayTopologyPosition::BOTTOM, 10.0f},
-                                      {DISPLAY_LEFT_ID, DisplayTopologyPosition::LEFT, 10.0f},
-                                      {DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT,
-                                       -90.0f}}}}};
+    DisplayTopologyGraph
+            mTopology{DISPLAY_CENTER_ID,
+                      {{DISPLAY_CENTER_ID,
+                        {{DISPLAY_TOP_ID, DisplayTopologyPosition::TOP, 50.0f},
+                         // Place a high density display on the left of DISPLAY_TOP_ID with 25 dp
+                         // gap
+                         {DISPLAY_HIGH_DENSITY_ID, DisplayTopologyPosition::TOP, -75.0f},
+                         {DISPLAY_RIGHT_ID, DisplayTopologyPosition::RIGHT, 10.0f},
+                         {DISPLAY_BOTTOM_ID, DisplayTopologyPosition::BOTTOM, 10.0f},
+                         {DISPLAY_LEFT_ID, DisplayTopologyPosition::LEFT, 10.0f},
+                         {DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT, -90.0f}}}},
+                      {{DISPLAY_CENTER_ID, DENSITY_MEDIUM},
+                       {DISPLAY_TOP_ID, DENSITY_MEDIUM},
+                       {DISPLAY_RIGHT_ID, DENSITY_MEDIUM},
+                       {DISPLAY_BOTTOM_ID, DENSITY_MEDIUM},
+                       {DISPLAY_LEFT_ID, DENSITY_MEDIUM},
+                       {DISPLAY_TOP_RIGHT_CORNER_ID, DENSITY_MEDIUM},
+                       {DISPLAY_HIGH_DENSITY_ID, DENSITY_HIGH}}};
 
 private:
     DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
@@ -2731,7 +2748,7 @@
                                 ToolType::FINGER, vec2(50, 50) /* initial x/y */,
                                 vec2(25, -100) /* delta x/y */,
                                 PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_ID,
-                                vec2(50 + 25 - 10,
+                                vec2(50 + 25 - 50,
                                      90) /* Bottom edge: (source + delta - offset, height) */),
                 std::make_tuple("TransitionToBottomDisplay",
                                 AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
@@ -2739,11 +2756,12 @@
                                 vec2(25, 100) /* delta x/y */,
                                 PointerChoreographerDisplayTopologyTestFixture::DISPLAY_BOTTOM_ID,
                                 vec2(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */),
+                // move towards 25 dp gap between DISPLAY_HIGH_DENSITY_ID and DISPLAY_TOP_ID
                 std::make_tuple("NoTransitionAtTopOffset", AINPUT_SOURCE_MOUSE,
                                 ControllerType::MOUSE, ToolType::MOUSE,
-                                vec2(5, 50) /* initial x/y */, vec2(0, -100) /* Move Up */,
+                                vec2(35, 50) /* initial x/y */, vec2(0, -100) /* Move Up */,
                                 PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
-                                vec2(5, 0) /* Top edge */),
+                                vec2(35, 0) /* Top edge */),
                 std::make_tuple("NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE,
                                 ControllerType::MOUSE, ToolType::MOUSE,
                                 vec2(95, 5) /* initial x/y */, vec2(100, 0) /* Move Right */,
@@ -2764,9 +2782,16 @@
                 std::make_tuple(
                         "TransitionAtTopRightCorner", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
                         ControllerType::MOUSE, ToolType::FINGER, vec2(95, 5) /* initial x/y */,
-                        vec2(10, -10) /* Move dignally to top right corner */,
+                        vec2(10, -10) /* Move diagonally to top right corner */,
                         PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_RIGHT_CORNER_ID,
-                        vec2(0, 90) /* bottom left corner */)),
+                        vec2(0, 90) /* bottom left corner */),
+                std::make_tuple(
+                        "TransitionToHighDpDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+                        ControllerType::MOUSE, ToolType::MOUSE, vec2(20, 20) /* initial x/y */,
+                        vec2(0, -50) /* delta x/y */,
+                        PointerChoreographerDisplayTopologyTestFixture::DISPLAY_HIGH_DENSITY_ID,
+                        /* Bottom edge: ((source + delta - offset) * density, height) */
+                        vec2((20 + 0 + 75) * 2, 200))),
         [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyTestFixtureParam>& p) {
             return std::string{std::get<0>(p.param)};
         });
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index 48e1954..5000db7 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -33,8 +33,8 @@
         "frameworks/native/services/inputflinger",
     ],
     shared_libs: [
-        "libinputreader",
         "libinputflinger_base",
+        "libinputreader",
     ],
     sanitize: {
         hwaddress: true,
diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
index 61ab47a..3a1b426 100644
--- a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
@@ -40,10 +40,10 @@
 using namespace std::chrono_literals;
 
 // Values from Boost.aidl and Mode.aidl.
-static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(Boost::INTERACTION);
-static constexpr int64_t LAST_BOOST = static_cast<int64_t>(Boost::CAMERA_SHOT);
-static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE);
-static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH);
+static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(*ndk::enum_range<Boost>().begin());
+static constexpr int64_t LAST_BOOST = static_cast<int64_t>(*(ndk::enum_range<Boost>().end()-1));
+static constexpr int64_t FIRST_MODE = static_cast<int64_t>(*ndk::enum_range<Mode>().begin());
+static constexpr int64_t LAST_MODE = static_cast<int64_t>(*(ndk::enum_range<Mode>().end()-1));
 
 class DurationWrapper : public WorkDuration {
 public:
@@ -81,14 +81,17 @@
         return;
     }
 
-    while (state.KeepRunning()) {
+    for (auto _ : state) {
         ret = (*hal.*fn)(std::forward<Args1>(args1)...);
-        state.PauseTiming();
-        if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
-        if (delay > 0us) {
-            testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+        if (!ret.isOk()) {
+            state.SkipWithError(ret.getDescription().c_str());
+            break;
         }
-        state.ResumeTiming();
+        if (delay > 0us) {
+            state.PauseTiming();
+            testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+            state.ResumeTiming();
+        }
     }
 }
 
@@ -123,14 +126,15 @@
         return;
     }
 
-    while (state.KeepRunning()) {
+    for (auto _ : state) {
         ret = (*session.*fn)(std::forward<Args1>(args1)...);
-        state.PauseTiming();
-        if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
-        if (ONEWAY_API_DELAY > 0us) {
-            testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY)
-                                  .count());
+        if (!ret.isOk()) {
+            state.SkipWithError(ret.getDescription().c_str());
+            break;
         }
+        state.PauseTiming();
+        testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY)
+                                .count());
         state.ResumeTiming();
     }
     session->close();
@@ -150,11 +154,41 @@
 
 static void BM_PowerHalAidlBenchmarks_setBoost(benchmark::State& state) {
     Boost boost = static_cast<Boost>(state.range(0));
+    bool isSupported;
+    std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl();
+
+    if (hal == nullptr) {
+        ALOGV("Power HAL not available, skipping test...");
+        state.SkipWithMessage("Power HAL unavailable");
+        return;
+    }
+
+    ndk::ScopedAStatus ret = hal->isBoostSupported(boost, &isSupported);
+    if (!ret.isOk() || !isSupported) {
+        state.SkipWithMessage("operation unsupported");
+        return;
+    }
+
     runBenchmark(state, ONEWAY_API_DELAY, &IPower::setBoost, boost, 1);
 }
 
 static void BM_PowerHalAidlBenchmarks_setMode(benchmark::State& state) {
     Mode mode = static_cast<Mode>(state.range(0));
+    bool isSupported;
+    std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl();
+
+    if (hal == nullptr) {
+        ALOGV("Power HAL not available, skipping test...");
+        state.SkipWithMessage("Power HAL unavailable");
+        return;
+    }
+
+    ndk::ScopedAStatus ret = hal->isModeSupported(mode, &isSupported);
+    if (!ret.isOk() || !isSupported) {
+        state.SkipWithMessage("operation unsupported");
+        return;
+    }
+
     runBenchmark(state, ONEWAY_API_DELAY, &IPower::setMode, mode, false);
 }
 
@@ -178,12 +212,20 @@
         ALOGV("Power HAL does not support this operation, skipping test...");
         state.SkipWithMessage("operation unsupported");
         return;
+    } else if (!ret.isOk()) {
+        state.SkipWithError(ret.getDescription().c_str());
+        return;
+    } else {
+        appSession->close();
     }
 
-    while (state.KeepRunning()) {
+    for (auto _ : state) {
         ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
+        if (!ret.isOk()) {
+            state.SkipWithError(ret.getDescription().c_str());
+            break;
+        }
         state.PauseTiming();
-        if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
         appSession->close();
         state.ResumeTiming();
     }
diff --git a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
index effddda..0fda686 100644
--- a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
@@ -19,9 +19,9 @@
 #include <aidl/android/hardware/power/Boost.h>
 #include <aidl/android/hardware/power/Mode.h>
 #include <benchmark/benchmark.h>
+#include <chrono>
 #include <powermanager/PowerHalController.h>
 #include <testUtil.h>
-#include <chrono>
 
 using aidl::android::hardware::power::Boost;
 using aidl::android::hardware::power::Mode;
@@ -32,10 +32,10 @@
 using namespace std::chrono_literals;
 
 // Values from Boost.aidl and Mode.aidl.
-static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(Boost::INTERACTION);
-static constexpr int64_t LAST_BOOST = static_cast<int64_t>(Boost::CAMERA_SHOT);
-static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE);
-static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH);
+static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(*ndk::enum_range<Boost>().begin());
+static constexpr int64_t LAST_BOOST = static_cast<int64_t>(*(ndk::enum_range<Boost>().end()-1));
+static constexpr int64_t FIRST_MODE = static_cast<int64_t>(*ndk::enum_range<Mode>().begin());
+static constexpr int64_t LAST_MODE = static_cast<int64_t>(*(ndk::enum_range<Mode>().end()-1));
 
 // Delay between oneway method calls to avoid overflowing the binder buffers.
 static constexpr std::chrono::microseconds ONEWAY_API_DELAY = 100us;
@@ -43,11 +43,27 @@
 template <typename T, class... Args0, class... Args1>
 static void runBenchmark(benchmark::State& state, HalResult<T> (PowerHalController::*fn)(Args0...),
                          Args1&&... args1) {
-    while (state.KeepRunning()) {
-        PowerHalController controller;
+    PowerHalController initController;
+    HalResult<T> result = (initController.*fn)(std::forward<Args1>(args1)...);
+    if (result.isFailed()) {
+        state.SkipWithError(result.errorMessage());
+        return;
+    } else if (result.isUnsupported()) {
+        ALOGV("Power HAL does not support this operation, skipping test...");
+        state.SkipWithMessage("operation unsupported");
+        return;
+    }
+
+    for (auto _ : state) {
+        PowerHalController controller; // new controller to avoid caching
         HalResult<T> ret = (controller.*fn)(std::forward<Args1>(args1)...);
+        if (ret.isFailed()) {
+            state.SkipWithError(ret.errorMessage());
+            break;
+        }
         state.PauseTiming();
-        if (ret.isFailed()) state.SkipWithError("Power HAL request failed");
+        testDelaySpin(
+                std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY).count());
         state.ResumeTiming();
     }
 }
@@ -57,22 +73,27 @@
                                HalResult<T> (PowerHalController::*fn)(Args0...), Args1&&... args1) {
     PowerHalController controller;
     // First call out of test, to cache HAL service and isSupported result.
-    (controller.*fn)(std::forward<Args1>(args1)...);
+    HalResult<T> result = (controller.*fn)(std::forward<Args1>(args1)...);
+    if (result.isFailed()) {
+        state.SkipWithError(result.errorMessage());
+        return;
+    } else if (result.isUnsupported()) {
+        ALOGV("Power HAL does not support this operation, skipping test...");
+        state.SkipWithMessage("operation unsupported");
+        return;
+    }
 
-    while (state.KeepRunning()) {
+    for (auto _ : state) {
         HalResult<T> ret = (controller.*fn)(std::forward<Args1>(args1)...);
-        state.PauseTiming();
         if (ret.isFailed()) {
-            state.SkipWithError("Power HAL request failed");
+            state.SkipWithError(ret.errorMessage());
+            break;
         }
-        testDelaySpin(
-                std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY).count());
-        state.ResumeTiming();
     }
 }
 
 static void BM_PowerHalControllerBenchmarks_init(benchmark::State& state) {
-    while (state.KeepRunning()) {
+    for (auto _ : state) {
         PowerHalController controller;
         controller.init();
     }
@@ -90,12 +111,12 @@
 
 static void BM_PowerHalControllerBenchmarks_setBoost(benchmark::State& state) {
     Boost boost = static_cast<Boost>(state.range(0));
-    runBenchmark(state, &PowerHalController::setBoost, boost, 0);
+    runBenchmark(state, &PowerHalController::setBoost, boost, 1);
 }
 
 static void BM_PowerHalControllerBenchmarks_setBoostCached(benchmark::State& state) {
     Boost boost = static_cast<Boost>(state.range(0));
-    runCachedBenchmark(state, &PowerHalController::setBoost, boost, 0);
+    runCachedBenchmark(state, &PowerHalController::setBoost, boost, 1);
 }
 
 static void BM_PowerHalControllerBenchmarks_setMode(benchmark::State& state) {
diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
index bcb376b..95fd0c2 100644
--- a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
@@ -54,14 +54,17 @@
         return;
     }
 
-    while (state.KeepRunning()) {
+    for (auto _ : state) {
         Return<R> ret = (*hal.*fn)(std::forward<Args1>(args1)...);
-        state.PauseTiming();
-        if (!ret.isOk()) state.SkipWithError(ret.description().c_str());
-        if (delay > 0us) {
-            testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+        if (!ret.isOk()) {
+            state.SkipWithError(ret.description().c_str());
+            break;
         }
-        state.ResumeTiming();
+        if (delay > 0us) {
+            state.PauseTiming();
+            testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+            state.ResumeTiming();
+        }
     }
 }
 
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 8c80dd8..9ecd101 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -11,7 +11,7 @@
     name: "sensorservice_flags",
     package: "com.android.frameworks.sensorservice.flags",
     container: "system",
-    srcs: ["senserservice_flags.aconfig"],
+    srcs: ["sensorservice_flags.aconfig"],
 }
 
 cc_aconfig_library {
@@ -61,38 +61,38 @@
     ],
 
     shared_libs: [
-        "libcutils",
-        "libhardware",
-        "libhardware_legacy",
-        "libutils",
-        "liblog",
-        "libactivitymanager_aidl",
-        "libbatterystats_aidl",
-        "libbinder",
-        "libsensor",
-        "libsensorprivacy",
-        "libpermission",
-        "libprotoutil",
-        "libcrypto",
-        "libbase",
-        "libhidlbase",
-        "libfmq",
-        "libbinder_ndk",
-        "packagemanager_aidl-cpp",
+        "android.hardware.common-V2-ndk",
+        "android.hardware.common.fmq-V1-ndk",
         "android.hardware.sensors@1.0",
         "android.hardware.sensors@2.0",
         "android.hardware.sensors@2.1",
-        "android.hardware.common-V2-ndk",
-        "android.hardware.common.fmq-V1-ndk",
-        "server_configurable_flags",
         "libaconfig_storage_read_api_cc",
+        "libactivitymanager_aidl",
+        "libbase",
+        "libbatterystats_aidl",
+        "libbinder",
+        "libbinder_ndk",
+        "libcrypto",
+        "libcutils",
+        "libfmq",
+        "libhardware",
+        "libhardware_legacy",
+        "libhidlbase",
+        "liblog",
+        "libpermission",
+        "libprotoutil",
+        "libsensor",
+        "libsensorprivacy",
+        "libutils",
+        "packagemanager_aidl-cpp",
+        "server_configurable_flags",
     ],
 
     static_libs: [
-        "libaidlcommonsupport",
-        "android.hardware.sensors@1.0-convert",
         "android.hardware.sensors-V1-convert",
         "android.hardware.sensors-V3-ndk",
+        "android.hardware.sensors@1.0-convert",
+        "libaidlcommonsupport",
         "sensorservice_flags_c_lib",
     ],
 
@@ -100,9 +100,9 @@
 
     export_shared_lib_headers: [
         "libactivitymanager_aidl",
+        "libpermission",
         "libsensor",
         "libsensorprivacy",
-        "libpermission",
     ],
 
     afdo: true,
@@ -120,9 +120,9 @@
     srcs: ["main_sensorservice.cpp"],
 
     shared_libs: [
-        "libsensorservice",
-        "libsensorprivacy",
         "libbinder",
+        "libsensorprivacy",
+        "libsensorservice",
         "libutils",
     ],
 
diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/sensorservice_flags.aconfig
similarity index 79%
rename from services/sensorservice/senserservice_flags.aconfig
rename to services/sensorservice/sensorservice_flags.aconfig
index 7abfbaa..452b428 100644
--- a/services/sensorservice/senserservice_flags.aconfig
+++ b/services/sensorservice/sensorservice_flags.aconfig
@@ -28,3 +28,13 @@
   description: "When this flag is enabled, sensor service will only erase dynamic sensor data at the end of the threadLoop to prevent race condition."
   bug: "329020894"
 }
+
+flag {
+  name: "enforce_permissions_for_all_target_sdk"
+  namespace: "sensors"
+  description: "When this flag is enabled, sensor service will enforce permissions for all target sdks."
+  bug: "389176817"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index fece312..008b057 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -654,7 +654,7 @@
             // We try to do this by moving the deadline. Since the queue could be stuffed by more
             // than one buffer, we take the last latch time as reference and give one vsync
             // worth of time for the frame to be ready.
-            nsecs_t adjustedDeadline = mLastLatchTime + refreshRate.getPeriodNsecs();
+            nsecs_t adjustedDeadline = mLastLatchTime + displayFrameRenderRate.getPeriodNsecs();
             if (adjustedDeadline > mActuals.endTime) {
                 mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
             } else {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7e3d109..1dc7b2e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2975,22 +2975,21 @@
     }
 
     int index = 0;
-    std::array<char, WorkloadTracer::COMPOSITION_SUMMARY_SIZE> compositionSummary = {0};
+    ftl::StaticVector<char, WorkloadTracer::COMPOSITION_SUMMARY_SIZE> compositionSummary;
     auto lastLayerStack = ui::INVALID_LAYER_STACK;
     for (auto& [layer, layerFE] : layers) {
         CompositionResult compositionResult{layerFE->stealCompositionResult()};
-        if (index < compositionSummary.size()) {
-            if (lastLayerStack != ui::INVALID_LAYER_STACK &&
-                lastLayerStack != layerFE->mSnapshot->outputFilter.layerStack) {
+        if (lastLayerStack != layerFE->mSnapshot->outputFilter.layerStack) {
+            if (lastLayerStack != ui::INVALID_LAYER_STACK) {
                 // add a space to separate displays
-                compositionSummary[index++] = ' ';
+                compositionSummary.push_back(' ');
             }
             lastLayerStack = layerFE->mSnapshot->outputFilter.layerStack;
-            compositionSummary[index++] = layerFE->mSnapshot->classifyCompositionForDebug(
-                    layerFE->getHwcCompositionType());
-            if (layerFE->mSnapshot->hasEffect()) {
-                compositedWorkload |= adpf::Workload::EFFECTS;
-            }
+        }
+        compositionSummary.push_back(
+                layerFE->mSnapshot->classifyCompositionForDebug(layerFE->getHwcCompositionType()));
+        if (layerFE->mSnapshot->hasEffect()) {
+            compositedWorkload |= adpf::Workload::EFFECTS;
         }
 
         if (compositionResult.lastClientCompositionFence) {
@@ -3007,7 +3006,8 @@
     SFTRACE_INSTANT_FOR_TRACK(WorkloadTracer::TRACK_NAME,
                               ftl::Concat("Layers: ", layers.size(), " ",
                                           ftl::truncated<WorkloadTracer::COMPOSITION_SUMMARY_SIZE>(
-                                                  compositionSummary.data()))
+                                                  std::string_view(compositionSummary.begin(),
+                                                                   compositionSummary.size())))
                                       .c_str());
 
     mPowerAdvisor->setCompositedWorkload(compositedWorkload);
@@ -4602,27 +4602,6 @@
 status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle,
                                         const sp<Layer>& layer, const wp<Layer>& parent,
                                         uint32_t* outTransformHint) {
-    if (mNumLayers >= MAX_LAYERS) {
-        static std::atomic<nsecs_t> lasttime{0};
-        nsecs_t now = systemTime();
-        if (lasttime != 0 && ns2s(now - lasttime.load()) < 10) {
-            ALOGE("AddClientLayer already dumped 10s before");
-            return NO_MEMORY;
-        } else {
-            lasttime = now;
-        }
-
-        ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
-              MAX_LAYERS);
-        static_cast<void>(mScheduler->schedule([&]() FTL_FAKE_GUARD(kMainThreadContext) {
-            ALOGE("Dumping on-screen layers.");
-            mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getHierarchy());
-            ALOGE("Dumping off-screen layers.");
-            mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getOffscreenHierarchy());
-        }));
-        return NO_MEMORY;
-    }
-
     if (outTransformHint) {
         *outTransformHint = mActiveDisplayTransformHint;
     }
@@ -5405,14 +5384,13 @@
         mirrorArgs.addToRoot = true;
         mirrorArgs.layerStackToMirror = layerStack;
         result = createEffectLayer(mirrorArgs, &outResult.handle, &rootMirrorLayer);
+        if (result != NO_ERROR) {
+            return result;
+        }
         outResult.layerId = rootMirrorLayer->sequence;
         outResult.layerName = String16(rootMirrorLayer->getDebugName());
-        result |= addClientLayer(mirrorArgs, outResult.handle, rootMirrorLayer /* layer */,
-                                 nullptr /* parent */, nullptr /* outTransformHint */);
-    }
-
-    if (result != NO_ERROR) {
-        return result;
+        addClientLayer(mirrorArgs, outResult.handle, rootMirrorLayer /* layer */,
+                       nullptr /* parent */, nullptr /* outTransformHint */);
     }
 
     setTransactionFlags(eTransactionFlushNeeded);
@@ -5432,6 +5410,9 @@
             [[fallthrough]];
         case ISurfaceComposerClient::eFXSurfaceEffect: {
             result = createBufferStateLayer(args, &outResult.handle, &layer);
+            if (result != NO_ERROR) {
+                return result;
+            }
             std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
             if (pendingBufferCounter) {
                 std::string counterName = layer->getPendingBufferCounterName();
@@ -5472,6 +5453,9 @@
 
 status_t SurfaceFlinger::createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* handle,
                                                 sp<Layer>* outLayer) {
+    if (checkLayerLeaks() != NO_ERROR) {
+        return NO_MEMORY;
+    }
     *outLayer = getFactory().createBufferStateLayer(args);
     *handle = (*outLayer)->getHandle();
     return NO_ERROR;
@@ -5479,11 +5463,38 @@
 
 status_t SurfaceFlinger::createEffectLayer(const LayerCreationArgs& args, sp<IBinder>* handle,
                                            sp<Layer>* outLayer) {
+    if (checkLayerLeaks() != NO_ERROR) {
+        return NO_MEMORY;
+    }
     *outLayer = getFactory().createEffectLayer(args);
     *handle = (*outLayer)->getHandle();
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::checkLayerLeaks() {
+    if (mNumLayers >= MAX_LAYERS) {
+        static std::atomic<nsecs_t> lasttime{0};
+        nsecs_t now = systemTime();
+        if (lasttime != 0 && ns2s(now - lasttime.load()) < 10) {
+            ALOGE("CreateLayer already dumped 10s before");
+            return NO_MEMORY;
+        } else {
+            lasttime = now;
+        }
+
+        ALOGE("CreateLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
+              MAX_LAYERS);
+        static_cast<void>(mScheduler->schedule([&]() FTL_FAKE_GUARD(kMainThreadContext) {
+            ALOGE("Dumping on-screen layers.");
+            mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getHierarchy());
+            ALOGE("Dumping off-screen layers.");
+            mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getOffscreenHierarchy());
+        }));
+        return NO_MEMORY;
+    }
+    return NO_ERROR;
+}
+
 void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer, uint32_t layerId) {
     {
         // Used to remove stalled transactions which uses an internal lock.
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index fa255ea..935a2da 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -838,6 +838,9 @@
     status_t createEffectLayer(const LayerCreationArgs& args, sp<IBinder>* outHandle,
                                sp<Layer>* outLayer);
 
+    // Checks if there are layer leaks before creating layer
+    status_t checkLayerLeaks();
+
     status_t mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle,
                          gui::CreateSurfaceResult& outResult);