Revert "Remove notifyConfigurationChanged listener and policy calls"

Revert submission 28338641-remove_notify_configuration_changed

Reason for revert: Droidmonitor created revert due to http://b/353980868 - verifying through ABTD before submission.  

Reverted changes: /q/submissionid:28338641-remove_notify_configuration_changed

Change-Id: I06b37a72b5cb67e377a2deb79c14d266afdcf81f
diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index 4621144..b5cb3cb 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -133,6 +133,15 @@
     mNextListener.notify(args);
 }
 
+void InputDeviceMetricsCollector::notifyConfigurationChanged(
+        const NotifyConfigurationChangedArgs& args) {
+    {
+        std::scoped_lock lock(mLock);
+        reportCompletedSessions();
+    }
+    mNextListener.notify(args);
+}
+
 void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
     {
         std::scoped_lock lock(mLock);
diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h
index 0a520e6..1bcd527 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.h
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -107,6 +107,7 @@
                                 std::chrono::nanoseconds usageSessionTimeout);
 
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
     void notifyKey(const NotifyKeyArgs& args) override;
     void notifyMotion(const NotifyMotionArgs& args) override;
     void notifySwitch(const NotifySwitchArgs& args) override;
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index e4d73fc..8e73ce5 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -67,6 +67,10 @@
     mNextListener.notify(args);
 }
 
+void InputFilter::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
+    mNextListener.notify(args);
+}
+
 void InputFilter::notifyKey(const NotifyKeyArgs& args) {
     if (isFilterEnabled()) {
         LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk());
diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h
index f626703..4ddc9f4 100644
--- a/services/inputflinger/InputFilter.h
+++ b/services/inputflinger/InputFilter.h
@@ -53,6 +53,7 @@
                          InputFilterPolicyInterface& policy);
     ~InputFilter() override = default;
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
     void notifyKey(const NotifyKeyArgs& args) override;
     void notifyMotion(const NotifyMotionArgs& args) override;
     void notifySwitch(const NotifySwitchArgs& args) override;
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 8b6accf..016ae04 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -47,6 +47,7 @@
 void InputListenerInterface::notify(const NotifyArgs& generalArgs) {
     Visitor v{
             [&](const NotifyInputDevicesChangedArgs& args) { notifyInputDevicesChanged(args); },
+            [&](const NotifyConfigurationChangedArgs& args) { notifyConfigurationChanged(args); },
             [&](const NotifyKeyArgs& args) { notifyKey(args); },
             [&](const NotifyMotionArgs& args) { notifyMotion(args); },
             [&](const NotifySwitchArgs& args) { notifySwitch(args); },
@@ -67,6 +68,10 @@
     mArgsQueue.emplace_back(args);
 }
 
+void QueuedInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
+    mArgsQueue.emplace_back(args);
+}
+
 void QueuedInputListener::notifyKey(const NotifyKeyArgs& args) {
     mArgsQueue.emplace_back(args);
 }
@@ -114,6 +119,13 @@
     mInnerListener.notify(args);
 }
 
+void TracedInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
+    constexpr static auto& fnName = __func__;
+    ATRACE_NAME_IF(ATRACE_ENABLED(),
+                   StringPrintf("%s::%s(id=0x%" PRIx32 ")", mName, fnName, args.id));
+    mInnerListener.notify(args);
+}
+
 void TracedInputListener::notifyKey(const NotifyKeyArgs& args) {
     constexpr static auto& fnName = __func__;
     ATRACE_NAME_IF(ATRACE_ENABLED(),
diff --git a/services/inputflinger/InputProcessor.cpp b/services/inputflinger/InputProcessor.cpp
index 8b8b1ad..6dd267c 100644
--- a/services/inputflinger/InputProcessor.cpp
+++ b/services/inputflinger/InputProcessor.cpp
@@ -419,6 +419,12 @@
     mQueuedListener.flush();
 }
 
+void InputProcessor::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
+    // pass through
+    mQueuedListener.notifyConfigurationChanged(args);
+    mQueuedListener.flush();
+}
+
 void InputProcessor::notifyKey(const NotifyKeyArgs& args) {
     // pass through
     mQueuedListener.notifyKey(args);
diff --git a/services/inputflinger/InputProcessor.h b/services/inputflinger/InputProcessor.h
index 2945dd2..7a00a2d 100644
--- a/services/inputflinger/InputProcessor.h
+++ b/services/inputflinger/InputProcessor.h
@@ -246,6 +246,7 @@
     explicit InputProcessor(InputListenerInterface& listener);
 
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
     void notifyKey(const NotifyKeyArgs& args) override;
     void notifyMotion(const NotifyMotionArgs& args) override;
     void notifySwitch(const NotifySwitchArgs& args) override;
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
index b2680a2..19a4f26 100644
--- a/services/inputflinger/NotifyArgs.cpp
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -35,6 +35,11 @@
                                                              std::vector<InputDeviceInfo> infos)
       : id(id), inputDeviceInfos(std::move(infos)) {}
 
+// --- NotifyConfigurationChangedArgs ---
+
+NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime)
+      : id(id), eventTime(eventTime) {}
+
 // --- NotifyKeyArgs ---
 
 NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
@@ -193,6 +198,7 @@
 const char* toString(const NotifyArgs& args) {
     Visitor toStringVisitor{
             [&](const NotifyInputDevicesChangedArgs&) { return "NotifyInputDevicesChangedArgs"; },
+            [&](const NotifyConfigurationChangedArgs&) { return "NotifyConfigurationChangedArgs"; },
             [&](const NotifyKeyArgs&) { return "NotifyKeyArgs"; },
             [&](const NotifyMotionArgs&) { return "NotifyMotionArgs"; },
             [&](const NotifySensorArgs&) { return "NotifySensorArgs"; },
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 2ea09d2..31fbb51 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -165,6 +165,10 @@
     mNextListener.notify(args);
 }
 
+void PointerChoreographer::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
+    mNextListener.notify(args);
+}
+
 void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) {
     fadeMouseCursorOnKeyPress(args);
     mNextListener.notify(args);
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 635487b..aaf1e3e 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -105,6 +105,7 @@
     void setFocusedDisplay(ui::LogicalDisplayId displayId) override;
 
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
     void notifyKey(const NotifyKeyArgs& args) override;
     void notifyMotion(const NotifyMotionArgs& args) override;
     void notifySwitch(const NotifySwitchArgs& args) override;
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index 0e9ec91..1e2b9b3a 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -342,6 +342,12 @@
                                                        bool enablePalmRejection)
       : mQueuedListener(listener), mEnablePalmRejection(enablePalmRejection) {}
 
+void UnwantedInteractionBlocker::notifyConfigurationChanged(
+        const NotifyConfigurationChangedArgs& args) {
+    mQueuedListener.notifyConfigurationChanged(args);
+    mQueuedListener.flush();
+}
+
 void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs& args) {
     mQueuedListener.notifyKey(args);
     mQueuedListener.flush();
diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h
index 8a66e25..419da83 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.h
+++ b/services/inputflinger/UnwantedInteractionBlocker.h
@@ -91,6 +91,7 @@
     explicit UnwantedInteractionBlocker(InputListenerInterface& listener, bool enablePalmRejection);
 
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
     void notifyKey(const NotifyKeyArgs& args) override;
     void notifyMotion(const NotifyMotionArgs& args) override;
     void notifySwitch(const NotifySwitchArgs& args) override;
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index ff407af..ad9cec1 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -68,6 +68,15 @@
         injectionState(nullptr),
         dispatchInProgress(false) {}
 
+// --- ConfigurationChangedEntry ---
+
+ConfigurationChangedEntry::ConfigurationChangedEntry(int32_t id, nsecs_t eventTime)
+      : EventEntry(id, Type::CONFIGURATION_CHANGED, eventTime, 0) {}
+
+std::string ConfigurationChangedEntry::getDescription() const {
+    return StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
+}
+
 // --- DeviceResetEntry ---
 
 DeviceResetEntry::DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId)
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index becfb05..f2f31d8 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -32,6 +32,7 @@
 
 struct EventEntry {
     enum class Type {
+        CONFIGURATION_CHANGED,
         DEVICE_RESET,
         FOCUS,
         KEY,
@@ -77,6 +78,11 @@
     virtual ~EventEntry() = default;
 };
 
+struct ConfigurationChangedEntry : EventEntry {
+    explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime);
+    std::string getDescription() const override;
+};
+
 struct DeviceResetEntry : EventEntry {
     int32_t deviceId;
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index d1dc1ee..6c4870f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -558,6 +558,7 @@
 // Returns true if the event type passed as argument represents a user activity.
 bool isUserActivityEvent(const EventEntry& eventEntry) {
     switch (eventEntry.type) {
+        case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET:
         case EventEntry::Type::DRAG:
         case EventEntry::Type::FOCUS:
@@ -1152,6 +1153,14 @@
     }
 
     switch (mPendingEvent->type) {
+        case EventEntry::Type::CONFIGURATION_CHANGED: {
+            const ConfigurationChangedEntry& typedEntry =
+                    static_cast<const ConfigurationChangedEntry&>(*mPendingEvent);
+            done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
+            dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped
+            break;
+        }
+
         case EventEntry::Type::DEVICE_RESET: {
             const DeviceResetEntry& typedEntry =
                     static_cast<const DeviceResetEntry&>(*mPendingEvent);
@@ -1383,6 +1392,7 @@
             break;
         }
         case EventEntry::Type::TOUCH_MODE_CHANGED:
+        case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET:
         case EventEntry::Type::SENSOR:
         case EventEntry::Type::POINTER_CAPTURE_CHANGED:
@@ -1556,6 +1566,7 @@
         }
         case EventEntry::Type::FOCUS:
         case EventEntry::Type::TOUCH_MODE_CHANGED:
+        case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET: {
             LOG_ALWAYS_FATAL("Should not drop %s events", ftl::enum_string(entry.type).c_str());
             break;
@@ -1644,6 +1655,18 @@
     return newEntry;
 }
 
+bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime,
+                                                         const ConfigurationChangedEntry& entry) {
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
+    }
+
+    // Reset key repeating in case a keyboard device was added or removed or something.
+    resetKeyRepeatLocked();
+
+    return true;
+}
+
 bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime,
                                                 const DeviceResetEntry& entry) {
     if (DEBUG_OUTBOUND_EVENT_DETAILS) {
@@ -2201,6 +2224,7 @@
         case EventEntry::Type::TOUCH_MODE_CHANGED:
         case EventEntry::Type::POINTER_CAPTURE_CHANGED:
         case EventEntry::Type::FOCUS:
+        case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET:
         case EventEntry::Type::SENSOR:
         case EventEntry::Type::DRAG: {
@@ -3596,6 +3620,7 @@
             LOG_ALWAYS_FATAL("SENSOR events should not go to apps via input channel");
             break;
         }
+        case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET: {
             LOG_ALWAYS_FATAL("%s events should not go to apps",
                              ftl::enum_string(eventEntry->type).c_str());
@@ -3856,6 +3881,7 @@
                 break;
             }
 
+            case EventEntry::Type::CONFIGURATION_CHANGED:
             case EventEntry::Type::DEVICE_RESET:
             case EventEntry::Type::SENSOR: {
                 LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
@@ -4251,6 +4277,7 @@
                                  ftl::enum_string(cancelationEventEntry->type).c_str());
                 break;
             }
+            case EventEntry::Type::CONFIGURATION_CHANGED:
             case EventEntry::Type::DEVICE_RESET:
             case EventEntry::Type::SENSOR: {
                 LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
@@ -4333,6 +4360,7 @@
             case EventEntry::Type::KEY:
             case EventEntry::Type::FOCUS:
             case EventEntry::Type::TOUCH_MODE_CHANGED:
+            case EventEntry::Type::CONFIGURATION_CHANGED:
             case EventEntry::Type::DEVICE_RESET:
             case EventEntry::Type::POINTER_CAPTURE_CHANGED:
             case EventEntry::Type::SENSOR:
@@ -4418,11 +4446,28 @@
 
 void InputDispatcher::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
     std::scoped_lock _l(mLock);
-    // Reset key repeating in case a keyboard device was added or removed or something.
-    resetKeyRepeatLocked();
     mLatencyTracker.setInputDevices(args.inputDeviceInfos);
 }
 
+void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
+    if (debugInboundEventDetails()) {
+        ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args.eventTime);
+    }
+
+    bool needWake = false;
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+
+        std::unique_ptr<ConfigurationChangedEntry> newEntry =
+                std::make_unique<ConfigurationChangedEntry>(args.id, args.eventTime);
+        needWake = enqueueInboundEventLocked(std::move(newEntry));
+    } // release lock
+
+    if (needWake) {
+        mLooper->wake();
+    }
+}
+
 void InputDispatcher::notifyKey(const NotifyKeyArgs& args) {
     ALOGD_IF(debugInboundEventDetails(),
              "notifyKey - id=%" PRIx32 ", eventTime=%" PRId64
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index b2d5274..2125226 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -97,6 +97,7 @@
     status_t stop() override;
 
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
     void notifyKey(const NotifyKeyArgs& args) override;
     void notifyMotion(const NotifyMotionArgs& args) override;
     void notifySwitch(const NotifySwitchArgs& args) override;
@@ -446,6 +447,8 @@
             REQUIRES(mLock);
 
     // Dispatch inbound events.
+    bool dispatchConfigurationChangedLocked(nsecs_t currentTime,
+                                            const ConfigurationChangedEntry& entry) REQUIRES(mLock);
     bool dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry)
             REQUIRES(mLock);
     bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry,
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index d8a9afa..0b7f7c2 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -38,6 +38,7 @@
     virtual ~InputListenerInterface() { }
 
     virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) = 0;
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) = 0;
     virtual void notifyKey(const NotifyKeyArgs& args) = 0;
     virtual void notifyMotion(const NotifyMotionArgs& args) = 0;
     virtual void notifySwitch(const NotifySwitchArgs& args) = 0;
@@ -59,6 +60,7 @@
     explicit QueuedInputListener(InputListenerInterface& innerListener);
 
     virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
     virtual void notifyKey(const NotifyKeyArgs& args) override;
     virtual void notifyMotion(const NotifyMotionArgs& args) override;
     virtual void notifySwitch(const NotifySwitchArgs& args) override;
@@ -82,6 +84,7 @@
     explicit TracedInputListener(const char* name, InputListenerInterface& innerListener);
 
     virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
     virtual void notifyKey(const NotifyKeyArgs& args) override;
     virtual void notifyMotion(const NotifyMotionArgs& args) override;
     virtual void notifySwitch(const NotifySwitchArgs& args) override;
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 42a03c1..35519d1 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -454,6 +454,9 @@
      */
     virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) = 0;
 
+    /* Notifies the system that a configuration change has occurred. */
+    virtual void notifyConfigurationChanged(nsecs_t when) = 0;
+
     /* Gets the keyboard layout for a particular input device. */
     virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
             const InputDeviceIdentifier& identifier,
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
index 14487fe..db417cf 100644
--- a/services/inputflinger/include/NotifyArgs.h
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -39,6 +39,21 @@
     NotifyInputDevicesChangedArgs& operator=(const NotifyInputDevicesChangedArgs&) = default;
 };
 
+/* Describes a configuration change event. */
+struct NotifyConfigurationChangedArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    inline NotifyConfigurationChangedArgs() {}
+
+    NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime);
+
+    bool operator==(const NotifyConfigurationChangedArgs& rhs) const = default;
+
+    NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other) = default;
+    NotifyConfigurationChangedArgs& operator=(const NotifyConfigurationChangedArgs&) = default;
+};
+
 /* Describes a key event. */
 struct NotifyKeyArgs {
     int32_t id;
@@ -219,8 +234,8 @@
 };
 
 using NotifyArgs =
-        std::variant<NotifyInputDevicesChangedArgs, NotifyKeyArgs, NotifyMotionArgs,
-                     NotifySensorArgs, NotifySwitchArgs, NotifyDeviceResetArgs,
+        std::variant<NotifyInputDevicesChangedArgs, NotifyConfigurationChangedArgs, NotifyKeyArgs,
+                     NotifyMotionArgs, NotifySensorArgs, NotifySwitchArgs, NotifyDeviceResetArgs,
                      NotifyPointerCaptureChangedArgs, NotifyVibratorStateArgs>;
 
 const char* toString(const NotifyArgs& args);
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index e11adb8..9381580 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -888,6 +888,7 @@
       : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
         mNextDeviceId(1),
         mControllerNumbers(),
+        mNeedToSendFinishedDeviceScan(false),
         mNeedToReopenDevices(false),
         mNeedToScanDevices(true),
         mPendingEventCount(0),
@@ -1876,6 +1877,7 @@
                     .type = DEVICE_REMOVED,
             });
             it = mClosingDevices.erase(it);
+            mNeedToSendFinishedDeviceScan = true;
             if (events.size() == EVENT_BUFFER_SIZE) {
                 break;
             }
@@ -1884,6 +1886,7 @@
         if (mNeedToScanDevices) {
             mNeedToScanDevices = false;
             scanDevicesLocked();
+            mNeedToSendFinishedDeviceScan = true;
         }
 
         while (!mOpeningDevices.empty()) {
@@ -1912,6 +1915,18 @@
             if (!inserted) {
                 ALOGW("Device id %d exists, replaced.", device->id);
             }
+            mNeedToSendFinishedDeviceScan = true;
+            if (events.size() == EVENT_BUFFER_SIZE) {
+                break;
+            }
+        }
+
+        if (mNeedToSendFinishedDeviceScan) {
+            mNeedToSendFinishedDeviceScan = false;
+            events.push_back({
+                    .when = now,
+                    .type = FINISHED_DEVICE_SCAN,
+            });
             if (events.size() == EVENT_BUFFER_SIZE) {
                 break;
             }
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index f0e53b5..9e9e816 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -180,9 +180,6 @@
         }
 
         if (oldGeneration != mGeneration) {
-            // Reset global meta state because it depends on connected input devices.
-            updateGlobalMetaStateLocked();
-
             inputDevicesChanged = true;
             inputDevices = getInputDevicesLocked();
             mPendingArgs.emplace_back(
@@ -215,6 +212,15 @@
         mPolicy->notifyInputDevicesChanged(inputDevices);
     }
 
+    // Notify the policy of configuration change. This must be after policy is notified about input
+    // device changes so that policy can fetch newly added input devices on configuration change.
+    for (const auto& args : notifyArgs) {
+        const auto* configArgs = std::get_if<NotifyConfigurationChangedArgs>(&args);
+        if (configArgs != nullptr) {
+            mPolicy->notifyConfigurationChanged(configArgs->eventTime);
+        }
+    }
+
     // Notify the policy of the start of every new stylus gesture.
     for (const auto& args : notifyArgs) {
         const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args);
@@ -250,6 +256,9 @@
                 case EventHubInterface::DEVICE_REMOVED:
                     removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                     break;
+                case EventHubInterface::FINISHED_DEVICE_SCAN:
+                    handleConfigurationChangedLocked(rawEvent->when);
+                    break;
                 default:
                     ALOG_ASSERT(false); // can't happen
                     break;
@@ -414,6 +423,14 @@
     return ++mNextInputDeviceId;
 }
 
+void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
+    // Reset global meta state because it depends on the list of all configured devices.
+    updateGlobalMetaStateLocked();
+
+    // Enqueue configuration changed.
+    mPendingArgs.emplace_back(NotifyConfigurationChangedArgs{mContext.getNextId(), when});
+}
+
 void InputReader::refreshConfigurationLocked(ConfigurationChanges changes) {
     mPolicy->getReaderConfiguration(&mConfig);
     mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 657126a..c647558 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -254,6 +254,9 @@
         DEVICE_ADDED = 0x10000000,
         // Sent when a device is removed.
         DEVICE_REMOVED = 0x20000000,
+        // Sent when all added/removed devices from the most recent scan have been reported.
+        // This event is always sent at least once.
+        FINISHED_DEVICE_SCAN = 0x30000000,
 
         FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
     };
@@ -786,6 +789,7 @@
     std::vector<std::unique_ptr<Device>> mOpeningDevices;
     std::vector<std::unique_ptr<Device>> mClosingDevices;
 
+    bool mNeedToSendFinishedDeviceScan;
     bool mNeedToReopenDevices;
     bool mNeedToScanDevices;
     std::vector<std::string> mExcludedDevices;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 4f60a8a..03ca840 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -221,6 +221,8 @@
                                                                      size_t count) REQUIRES(mLock);
     [[nodiscard]] std::list<NotifyArgs> timeoutExpiredLocked(nsecs_t when) REQUIRES(mLock);
 
+    void handleConfigurationChangedLocked(nsecs_t when) REQUIRES(mLock);
+
     int32_t mGlobalMetaState GUARDED_BY(mLock);
     void updateGlobalMetaStateLocked() REQUIRES(mLock);
     int32_t getGlobalMetaStateLocked() REQUIRES(mLock);
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
index 0e3d15a..2e296da 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -48,6 +48,9 @@
                 case EventHubInterface::DEVICE_REMOVED:
                     ALOGI("Device removed: %i", event.deviceId);
                     break;
+                case EventHubInterface::FINISHED_DEVICE_SCAN:
+                    ALOGI("Finished device scan.");
+                    break;
             }
         } else {
             ALOGI("Device %" PRId32 " : time = %" PRId64 ", type %i, code %i, value %i",
@@ -142,13 +145,15 @@
     // None of the existing system devices should be changing while this test is run.
     // Check that the returned device ids are unique for all of the existing devices.
     EXPECT_EQ(existingDevices.size(), events.size() - 1);
+    // The last event should be "finished device scan"
+    EXPECT_EQ(EventHubInterface::FINISHED_DEVICE_SCAN, events[events.size() - 1].type);
 }
 
 int32_t EventHubTest::waitForDeviceCreation() {
     // Wait a little longer than usual, to ensure input device has time to be created
     std::vector<RawEvent> events = getEvents(2);
-    if (events.size() != 1) {
-        ADD_FAILURE() << "Instead of 1 event, received " << events.size();
+    if (events.size() != 2) {
+        ADD_FAILURE() << "Instead of 2 events, received " << events.size();
         return 0; // this value is unused
     }
     const RawEvent& deviceAddedEvent = events[0];
@@ -156,15 +161,21 @@
     InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId);
     const int32_t deviceId = deviceAddedEvent.deviceId;
     EXPECT_EQ(identifier.name, mKeyboard->getName());
+    const RawEvent& finishedDeviceScanEvent = events[1];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
+              finishedDeviceScanEvent.type);
     return deviceId;
 }
 
 void EventHubTest::waitForDeviceClose(int32_t deviceId) {
     std::vector<RawEvent> events = getEvents(2);
-    ASSERT_EQ(1U, events.size());
+    ASSERT_EQ(2U, events.size());
     const RawEvent& deviceRemovedEvent = events[0];
     EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_REMOVED), deviceRemovedEvent.type);
     EXPECT_EQ(deviceId, deviceRemovedEvent.deviceId);
+    const RawEvent& finishedDeviceScanEvent = events[1];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
+              finishedDeviceScanEvent.type);
 }
 
 void EventHubTest::assertNoMoreEvents() {
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index 31fbf20..7079278 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -88,6 +88,10 @@
     return device->disable();
 }
 
+void FakeEventHub::finishDeviceScan() {
+    enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
+}
+
 void FakeEventHub::addConfigurationProperty(int32_t deviceId, const char* key, const char* value) {
     getDevice(deviceId)->configuration.addProperty(key, value);
 }
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index 3d8dddd..c2c875f 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -112,6 +112,8 @@
     status_t enableDevice(int32_t deviceId) override;
     status_t disableDevice(int32_t deviceId) override;
 
+    void finishDeviceScan();
+
     void addConfigurationProperty(int32_t deviceId, const char* key, const char* value);
     void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration);
 
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 6099c91..56a8918 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -65,6 +65,23 @@
     ASSERT_FALSE(mDeviceIdOfNotifiedStylusGesture);
 }
 
+void FakeInputReaderPolicy::assertConfigurationChanged() {
+    std::unique_lock lock(mLock);
+    base::ScopedLockAssertion assumeLocked(mLock);
+
+    const bool configurationChanged =
+            mConfigurationChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+                return mConfigurationChanged;
+            });
+    ASSERT_TRUE(configurationChanged) << "Timed out waiting for configuration change";
+    mConfigurationChanged = false;
+}
+
+void FakeInputReaderPolicy::assertConfigurationNotChanged() {
+    std::scoped_lock lock(mLock);
+    ASSERT_FALSE(mConfigurationChanged);
+}
+
 void FakeInputReaderPolicy::clearViewports() {
     mViewports.clear();
     mConfig.setDisplayViewports(mViewports);
@@ -234,6 +251,12 @@
     mDevicesChangedCondition.notify_all();
 }
 
+void FakeInputReaderPolicy::notifyConfigurationChanged(nsecs_t when) {
+    std::scoped_lock lock(mLock);
+    mConfigurationChanged = true;
+    mConfigurationChangedCondition.notify_all();
+}
+
 std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
         const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) {
     return nullptr;
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 94f1311..ea6e7f2 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -42,6 +42,8 @@
     void assertInputDevicesNotChanged();
     void assertStylusGestureNotified(int32_t deviceId);
     void assertStylusGestureNotNotified();
+    void assertConfigurationChanged();
+    void assertConfigurationNotChanged();
 
     virtual void clearViewports();
     std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const;
@@ -82,6 +84,7 @@
 private:
     void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
     void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
+    void notifyConfigurationChanged(nsecs_t when) override;
     std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
             const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override;
     std::string getDeviceAlias(const InputDeviceIdentifier&) override;
@@ -90,10 +93,12 @@
 
     mutable std::mutex mLock;
     std::condition_variable mDevicesChangedCondition;
+    std::condition_variable mConfigurationChangedCondition;
 
     InputReaderConfiguration mConfig;
     std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
     bool mInputDevicesChanged GUARDED_BY(mLock){false};
+    bool mConfigurationChanged GUARDED_BY(mLock){false};
     std::vector<DisplayViewport> mViewports;
     TouchAffineTransformation transform;
     bool mIsInputMethodConnectionActive{false};
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 93caad3..4e662d4 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -411,6 +411,8 @@
             << "Should reject motion events with duplicate pointer ids.";
 }
 
+/* Test InputDispatcher for notifyConfigurationChanged and notifySwitch events */
+
 TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) {
     NotifySwitchArgs args(InputEvent::nextId(), /*eventTime=*/20, /*policyFlags=*/0,
                           /*switchValues=*/1,
diff --git a/services/inputflinger/tests/InputProcessor_test.cpp b/services/inputflinger/tests/InputProcessor_test.cpp
index d4c5a00..f7e5e67 100644
--- a/services/inputflinger/tests/InputProcessor_test.cpp
+++ b/services/inputflinger/tests/InputProcessor_test.cpp
@@ -63,6 +63,20 @@
     void SetUp() override { mProcessor = std::make_unique<InputProcessor>(mTestListener); }
 };
 
+/**
+ * Create a basic configuration change and send it to input processor.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputProcessorTest, SendToNextStage_NotifyConfigurationChangedArgs) {
+    // Create a basic configuration change and send to processor
+    NotifyConfigurationChangedArgs args(/*sequenceNum=*/1, /*eventTime=*/2);
+
+    mProcessor->notifyConfigurationChanged(args);
+    NotifyConfigurationChangedArgs outArgs;
+    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs));
+    ASSERT_EQ(args, outArgs);
+}
+
 TEST_F(InputProcessorTest, SendToNextStage_NotifyKeyArgs) {
     // Create a basic key event and send to processor
     NotifyKeyArgs args(/*sequenceNum=*/1, /*eventTime=*/2, /*readTime=*/21, /*deviceId=*/3,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 9bbc44a..1ba79a9 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -624,6 +624,7 @@
         if (configuration) {
             mFakeEventHub->addConfigurationMap(eventHubId, configuration);
         }
+        mFakeEventHub->finishDeviceScan();
         mReader->loopOnce();
         mReader->loopOnce();
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -757,6 +758,8 @@
     mReader->pushNextDevice(device);
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
 
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
+
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_EQ(deviceId, resetArgs.deviceId);
@@ -772,6 +775,7 @@
     disableDevice(deviceId);
     mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled());
     ASSERT_EQ(device->isEnabled(), false);
 
     enableDevice(deviceId);
@@ -956,6 +960,17 @@
     ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]);
 }
 
+TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
+    constexpr int32_t eventHubId = 1;
+    addDevice(eventHubId, "ignored", InputDeviceClass::KEYBOARD, nullptr);
+
+    NotifyConfigurationChangedArgs args;
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationChanged());
+    ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+}
+
 TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     constexpr ftl::Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
@@ -1060,6 +1075,7 @@
     // The device is added after the input port associations are processed since
     // we do not yet support dynamic device-to-display associations.
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled());
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
     ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
 
@@ -1089,6 +1105,8 @@
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr));
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "fake2", deviceClass, nullptr));
 
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
+
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_EQ(deviceId, resetArgs.deviceId);
@@ -1461,7 +1479,9 @@
         // to the test device will show up in mReader. We wait for those input devices to
         // show up before beginning the tests.
         ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyInputDevicesChangedWasCalled());
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationChanged());
     }
 };
 
@@ -1481,10 +1501,12 @@
     // consider it as a valid device.
     std::unique_ptr<UinputDevice> invalidDevice = createUinputDevice<InvalidUinputDevice>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesNotChanged());
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationNotChanged());
     ASSERT_EQ(numDevices, mFakePolicy->getInputDevices().size());
 
     invalidDevice.reset();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesNotChanged());
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationNotChanged());
     ASSERT_EQ(numDevices, mFakePolicy->getInputDevices().size());
 }
 
@@ -1493,6 +1515,7 @@
 
     std::unique_ptr<UinputHomeKey> keyboard = createUinputDevice<UinputHomeKey>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationChanged());
     ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());
 
     const auto device = waitForDevice(keyboard->getName());
@@ -1503,6 +1526,7 @@
 
     keyboard.reset();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationChanged());
     ASSERT_EQ(initialNumDevices, mFakePolicy->getInputDevices().size());
 }
 
@@ -1510,14 +1534,21 @@
     std::unique_ptr<UinputHomeKey> keyboard = createUinputDevice<UinputHomeKey>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
 
+    NotifyConfigurationChangedArgs configChangedArgs;
+    ASSERT_NO_FATAL_FAILURE(
+            mTestListener->assertNotifyConfigurationChangedWasCalled(&configChangedArgs));
+    int32_t prevId = configChangedArgs.id;
+    nsecs_t prevTimestamp = configChangedArgs.eventTime;
+
     NotifyKeyArgs keyArgs;
     keyboard->pressAndReleaseHomeKey();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_NE(prevId, keyArgs.id);
+    prevId = keyArgs.id;
+    ASSERT_LE(prevTimestamp, keyArgs.eventTime);
     ASSERT_LE(keyArgs.eventTime, keyArgs.readTime);
-
-    int32_t prevId = keyArgs.id;
-    nsecs_t prevTimestamp = keyArgs.eventTime;
+    prevTimestamp = keyArgs.eventTime;
 
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
@@ -1640,6 +1671,8 @@
 
         mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationChanged());
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
         const auto info = waitForDevice(mDevice->getName());
         ASSERT_TRUE(info);
         mDeviceInfo = *info;
@@ -1708,6 +1741,8 @@
                                      UNIQUE_ID, isInputPortAssociation ? DISPLAY_PORT : NO_PORT,
                                      ViewportType::INTERNAL);
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationChanged());
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
         const auto info = waitForDevice(mDevice->getName());
         ASSERT_TRUE(info);
         mDeviceInfo = *info;
@@ -2040,6 +2075,7 @@
     // Connecting an external stylus mid-gesture should not interrupt the ongoing gesture stream.
     auto externalStylus = createUinputDevice<UinputExternalStylus>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationChanged());
     const auto stylusInfo = waitForDevice(externalStylus->getName());
     ASSERT_TRUE(stylusInfo);
 
@@ -2052,6 +2088,7 @@
     // Disconnecting an external stylus mid-gesture should not interrupt the ongoing gesture stream.
     externalStylus.reset();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationChanged());
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
 
     // Up
@@ -2109,6 +2146,8 @@
         mStylusDeviceLifecycleTracker = createUinputDevice<T>();
         mStylus = mStylusDeviceLifecycleTracker.get();
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationChanged());
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
         const auto info = waitForDevice(mStylus->getName());
         ASSERT_TRUE(info);
         mStylusInfo = *info;
@@ -2378,6 +2417,7 @@
     std::unique_ptr<UinputExternalStylusWithPressure> stylus =
             createUinputDevice<UinputExternalStylusWithPressure>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationChanged());
     const auto stylusInfo = waitForDevice(stylus->getName());
     ASSERT_TRUE(stylusInfo);
 
@@ -2395,6 +2435,7 @@
     std::unique_ptr<UinputExternalStylusWithPressure> stylus =
             createUinputDevice<UinputExternalStylusWithPressure>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationChanged());
     const auto stylusInfo = waitForDevice(stylus->getName());
     ASSERT_TRUE(stylusInfo);
 
@@ -2440,6 +2481,7 @@
     std::unique_ptr<UinputExternalStylusWithPressure> stylus =
             createUinputDevice<UinputExternalStylusWithPressure>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationChanged());
     const auto stylusInfo = waitForDevice(stylus->getName());
     ASSERT_TRUE(stylusInfo);
 
@@ -2519,6 +2561,7 @@
     // touch pointers.
     std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertConfigurationChanged());
     const auto stylusInfo = waitForDevice(stylus->getName());
     ASSERT_TRUE(stylusInfo);
 
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index bd9a905..144e723 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -196,6 +196,7 @@
 TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) {
     const std::vector<NotifyArgs>
             allArgs{NotifyInputDevicesChangedArgs{},
+                    NotifyConfigurationChangedArgs{},
                     KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).build(),
                     MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                             .pointer(FIRST_TOUCH_POINTER)
@@ -213,6 +214,9 @@
                                    [&](const NotifyInputDevicesChangedArgs& args) {
                                        mTestListener.assertNotifyInputDevicesChangedWasCalled();
                                    },
+                                   [&](const NotifyConfigurationChangedArgs& args) {
+                                       mTestListener.assertNotifyConfigurationChangedWasCalled();
+                                   },
                                    [&](const NotifyKeyArgs& args) {
                                        mTestListener.assertNotifyKeyWasCalled();
                                    },
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 369f9cc..41e250f 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -37,6 +37,19 @@
                                                         "to have been called."));
 }
 
+void TestInputListener::assertNotifyConfigurationChangedWasCalled(
+        NotifyConfigurationChangedArgs* outEventArgs) {
+    ASSERT_NO_FATAL_FAILURE(
+            assertCalled<NotifyConfigurationChangedArgs>(outEventArgs,
+                                                         "Expected notifyConfigurationChanged() "
+                                                         "to have been called."));
+}
+
+void TestInputListener::assertNotifyConfigurationChangedWasNotCalled() {
+    ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyConfigurationChangedArgs>(
+            "notifyConfigurationChanged() should not be called."));
+}
+
 void TestInputListener::assertNotifyDeviceResetWasCalled(NotifyDeviceResetArgs* outEventArgs) {
     ASSERT_NO_FATAL_FAILURE(
             assertCalled<
@@ -179,6 +192,10 @@
     addToQueue<NotifyInputDevicesChangedArgs>(args);
 }
 
+void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
+    addToQueue<NotifyConfigurationChangedArgs>(args);
+}
+
 void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
     addToQueue<NotifyDeviceResetArgs>(args);
 }
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 47eae4d..3c5e014 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -38,6 +38,11 @@
     void assertNotifyInputDevicesChangedWasCalled(
             NotifyInputDevicesChangedArgs* outEventArgs = nullptr);
 
+    void assertNotifyConfigurationChangedWasCalled(
+            NotifyConfigurationChangedArgs* outEventArgs = nullptr);
+
+    void assertNotifyConfigurationChangedWasNotCalled();
+
     void clearNotifyDeviceResetCalls();
 
     void assertNotifyDeviceResetWasCalled(const ::testing::Matcher<NotifyDeviceResetArgs>& matcher);
@@ -80,6 +85,8 @@
 
     virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
 
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
+
     virtual void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
 
     virtual void notifyKey(const NotifyKeyArgs& args) override;
@@ -100,6 +107,7 @@
     const std::chrono::milliseconds mEventDidNotHappenTimeout;
 
     std::tuple<std::vector<NotifyInputDevicesChangedArgs>,   //
+               std::vector<NotifyConfigurationChangedArgs>,  //
                std::vector<NotifyDeviceResetArgs>,           //
                std::vector<NotifyKeyArgs>,                   //
                std::vector<NotifyMotionArgs>,                //
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index bbb2fc8..853f628 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -414,6 +414,20 @@
 };
 
 /**
+ * Create a basic configuration change and send it to input processor.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(UnwantedInteractionBlockerTest, ConfigurationChangedIsPassedToNextListener) {
+    // Create a basic configuration change and send to blocker
+    NotifyConfigurationChangedArgs args(/*sequenceNum=*/1, /*eventTime=*/2);
+
+    mBlocker->notifyConfigurationChanged(args);
+    NotifyConfigurationChangedArgs outArgs;
+    ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs));
+    ASSERT_EQ(args, outArgs);
+}
+
+/**
  * Keys are not handled in 'UnwantedInteractionBlocker' and should be passed
  * to next stage unmodified.
  */
diff --git a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
index 46a6189..0b4ac1f 100644
--- a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
@@ -39,6 +39,12 @@
     while (fdp.remaining_bytes() > 0) {
         fdp.PickValueInArray<std::function<void()>>({
                 [&]() -> void {
+                    // SendToNextStage_NotifyConfigurationChangedArgs
+                    mClassifier->notifyConfigurationChanged(
+                            {/*sequenceNum=*/fdp.ConsumeIntegral<int32_t>(),
+                             /*eventTime=*/fdp.ConsumeIntegral<nsecs_t>()});
+                },
+                [&]() -> void {
                     // SendToNextStage_NotifyKeyArgs
                     const nsecs_t eventTime =
                             fdp.ConsumeIntegralInRange<nsecs_t>(0,
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index fea0d9a..d986b31 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -32,7 +32,8 @@
                                   EV_MSC,
                                   EV_REL,
                                   android::EventHubInterface::DEVICE_ADDED,
-                                  android::EventHubInterface::DEVICE_REMOVED};
+                                  android::EventHubInterface::DEVICE_REMOVED,
+                                  android::EventHubInterface::FINISHED_DEVICE_SCAN};
 
 constexpr size_t kValidCodes[] = {
         SYN_REPORT,
@@ -295,6 +296,7 @@
     }
     void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
     void notifyStylusGestureStarted(int32_t, nsecs_t) {}
+    void notifyConfigurationChanged(nsecs_t) {}
     bool isInputMethodConnectionActive() override { return mFdp->ConsumeBool(); }
     std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay(
             ui::LogicalDisplayId associatedDisplayId) override {
@@ -305,6 +307,7 @@
 class FuzzInputListener : public virtual InputListenerInterface {
 public:
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override {}
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override {}
     void notifyKey(const NotifyKeyArgs& args) override {}
     void notifyMotion(const NotifyMotionArgs& args) override {}
     void notifySwitch(const NotifySwitchArgs& args) override {}
@@ -343,6 +346,7 @@
     void updateLedMetaState(int32_t metaState) override{};
     int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); };
     void notifyStylusGestureStarted(int32_t, nsecs_t) {}
+    void notifyConfigurationChanged(nsecs_t) {}
 
     void setPreventingTouchpadTaps(bool prevent) override {}
     bool isPreventingTouchpadTaps() override { return mFdp->ConsumeBool(); };