Call DeviceReachableChanged when subscription fails (#36175)

* Call DeviceReachableChanged when subscription fails

* Restyled by clang-format

---------

Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/examples/fabric-admin/device_manager/DeviceSubscription.cpp b/examples/fabric-admin/device_manager/DeviceSubscription.cpp
index 0da2bdd..434d349 100644
--- a/examples/fabric-admin/device_manager/DeviceSubscription.cpp
+++ b/examples/fabric-admin/device_manager/DeviceSubscription.cpp
@@ -125,6 +125,16 @@
 
 void DeviceSubscription::OnError(CHIP_ERROR error)
 {
+#if defined(PW_RPC_ENABLED)
+    if (error == CHIP_ERROR_TIMEOUT && mState == State::SubscriptionStarted)
+    {
+        chip_rpc_ReachabilityChanged reachabilityChanged;
+        reachabilityChanged.has_id       = true;
+        reachabilityChanged.id           = mCurrentAdministratorCommissioningAttributes.id;
+        reachabilityChanged.reachability = false;
+        DeviceReachableChanged(reachabilityChanged);
+    }
+#endif
     ChipLogProgress(NotSpecified, "Error subscribing: %" CHIP_ERROR_FORMAT, error.Format());
 }
 
@@ -198,7 +208,16 @@
 {
     VerifyOrDie(mState == State::Connecting || mState == State::Stopping);
     ChipLogError(NotSpecified, "DeviceSubscription failed to connect to " ChipLogFormatX64, ChipLogValueX64(peerId.GetNodeId()));
-    // TODO(#35333) Figure out how we should recover if we fail to connect and mState == State::Connecting.
+#if defined(PW_RPC_ENABLED)
+    if (mState == State::Connecting)
+    {
+        chip_rpc_ReachabilityChanged reachabilityChanged;
+        reachabilityChanged.has_id       = true;
+        reachabilityChanged.id           = mCurrentAdministratorCommissioningAttributes.id;
+        reachabilityChanged.reachability = false;
+        DeviceReachableChanged(reachabilityChanged);
+    }
+#endif
 
     // After calling mOnDoneCallback we are indicating that `this` is deleted and we shouldn't do anything else with
     // DeviceSubscription.
diff --git a/examples/fabric-admin/device_manager/DeviceSynchronization.cpp b/examples/fabric-admin/device_manager/DeviceSynchronization.cpp
index 6c22840..1e8728e 100644
--- a/examples/fabric-admin/device_manager/DeviceSynchronization.cpp
+++ b/examples/fabric-admin/device_manager/DeviceSynchronization.cpp
@@ -277,12 +277,16 @@
     if (!mCurrentDeviceData.is_icd)
     {
         VerifyOrDie(mController);
-        // TODO(#35333) Figure out how we should recover in this circumstance.
         ScopedNodeId scopedNodeId(mNodeId, mController->GetFabricIndex());
         CHIP_ERROR err = DeviceSubscriptionManager::Instance().StartSubscription(*mController, scopedNodeId);
         if (err != CHIP_NO_ERROR)
         {
             ChipLogError(NotSpecified, "Failed start subscription to NodeId:" ChipLogFormatX64, ChipLogValueX64(mNodeId));
+            chip_rpc_ReachabilityChanged reachabilityChanged;
+            reachabilityChanged.has_id       = true;
+            reachabilityChanged.id           = mCurrentDeviceData.id;
+            reachabilityChanged.reachability = false;
+            DeviceReachableChanged(reachabilityChanged);
         }
     }
 #endif
diff --git a/examples/fabric-admin/rpc/RpcClient.cpp b/examples/fabric-admin/rpc/RpcClient.cpp
index e33dd04..9964dc6 100644
--- a/examples/fabric-admin/rpc/RpcClient.cpp
+++ b/examples/fabric-admin/rpc/RpcClient.cpp
@@ -205,3 +205,25 @@
 
     return WaitForResponse(call);
 }
+
+CHIP_ERROR DeviceReachableChanged(const chip_rpc_ReachabilityChanged & data)
+{
+    ChipLogProgress(NotSpecified, "DeviceReachableChanged");
+    // TODO(#35333): When there is some sort of device manager in fabric-admin that handles all the devices we
+    // are currently connected to (and not just device on the remote bridge), we should notify that manager
+    // so that it can properly handle any sort of reconnection logic. This can either be done here when
+    // `data.reachability == false`, or more control can be given wherever DeviceReachableChanged is currently
+    // called
+
+    // The RPC call is kept alive until it completes. When a response is received, it will be logged by the handler
+    // function and the call will complete.
+    auto call = fabricBridgeClient.DeviceReachableChanged(data, RpcCompletedWithEmptyResponse);
+
+    if (!call.active())
+    {
+        // The RPC call was not sent. This could occur due to, for example, an invalid channel ID. Handle if necessary.
+        return CHIP_ERROR_INTERNAL;
+    }
+
+    return WaitForResponse(call);
+}
diff --git a/examples/fabric-admin/rpc/RpcClient.h b/examples/fabric-admin/rpc/RpcClient.h
index da6a821..a4cefe8 100644
--- a/examples/fabric-admin/rpc/RpcClient.h
+++ b/examples/fabric-admin/rpc/RpcClient.h
@@ -88,3 +88,14 @@
  * - CHIP_ERROR_INTERNAL: An internal error occurred while activating the RPC call.
  */
 CHIP_ERROR AdminCommissioningAttributeChanged(const chip_rpc_AdministratorCommissioningChanged & data);
+
+/**
+ * @brief Notify that reachachability of the bridged device has changed
+ *
+ * @param data information regarding change in reachability of the bridged device.
+ * @return CHIP_ERROR An error code indicating the success or failure of the operation.
+ * - CHIP_NO_ERROR: The RPC command was successfully processed.
+ * - CHIP_ERROR_BUSY: Another operation is currently in progress.
+ * - CHIP_ERROR_INTERNAL: An internal error occurred while activating the RPC call.
+ */
+CHIP_ERROR DeviceReachableChanged(const chip_rpc_ReachabilityChanged & data);