Fabric removal should only sync-deliver events for that fabric. (#21760)

* Fabric removal should only sync-deliver events for that fabric.

We were running into an issue where fabric removal would do sync event
delivery on other fabrics, and then removal of _those_ fabrics could
not deliver events (because there was a Report Data outstanding that
had not gotten a Status Response yet).

The fix is to only sync-deliver events for the fabric about to be
removed when a fabric is removed.

Fixes https://github.com/project-chip/connectedhomeip/issues/21744

* Address review comment.
diff --git a/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp b/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp
index 2c53a69..ab0b3f8 100644
--- a/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp
+++ b/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp
@@ -364,13 +364,13 @@
             }
         }
 
-        // Try to send the queued events as soon as possible. If the just emitted leave event won't
+        // Try to send the queued events as soon as possible for this fabric. If the just emitted leave event won't
         // be sent this time, it will likely not be delivered at all for the following reasons:
         // - removing the fabric expires all associated ReadHandlers, so all subscriptions to
         //   the leave event will be cancelled.
         // - removing the fabric removes all associated access control entries, so generating
         //   subsequent reports containing the leave event will fail the access control check.
-        InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleUrgentEventDeliverySync();
+        InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleUrgentEventDeliverySync(MakeOptional(fabricIndex));
     }
 
     // Gets called when a fabric is deleted
diff --git a/src/app/reporting/Engine.cpp b/src/app/reporting/Engine.cpp
index 55527a5..792631f 100644
--- a/src/app/reporting/Engine.cpp
+++ b/src/app/reporting/Engine.cpp
@@ -981,14 +981,19 @@
     return ScheduleBufferPressureEventDelivery(aBytesWritten);
 }
 
-void Engine::ScheduleUrgentEventDeliverySync()
+void Engine::ScheduleUrgentEventDeliverySync(Optional<FabricIndex> fabricIndex)
 {
-    InteractionModelEngine::GetInstance()->mReadHandlers.ForEachActiveObject([](ReadHandler * handler) {
+    InteractionModelEngine::GetInstance()->mReadHandlers.ForEachActiveObject([fabricIndex](ReadHandler * handler) {
         if (handler->IsType(ReadHandler::InteractionType::Read))
         {
             return Loop::Continue;
         }
 
+        if (fabricIndex.HasValue() && fabricIndex.Value() != handler->GetAccessingFabricIndex())
+        {
+            return Loop::Continue;
+        }
+
         handler->UnblockUrgentEventDelivery();
 
         return Loop::Continue;
diff --git a/src/app/reporting/Engine.h b/src/app/reporting/Engine.h
index 620cecf..dc6b6f5 100644
--- a/src/app/reporting/Engine.h
+++ b/src/app/reporting/Engine.h
@@ -119,7 +119,13 @@
 
     uint64_t GetDirtySetGeneration() const { return mDirtyGeneration; }
 
-    void ScheduleUrgentEventDeliverySync();
+    /**
+     * Schedule event delivery to happen immediately and run reporting to get
+     * those reports into messages and on the wire.  This can be done either for
+     * a specific fabric, identified by the provided FabricIndex, or across all
+     * fabrics if no FabricIndex is provided.
+     */
+    void ScheduleUrgentEventDeliverySync(Optional<FabricIndex> fabricIndex = NullOptional);
 
 #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
     size_t GetGlobalDirtySetSize() { return mGlobalDirtySet.Allocated(); }