Put remote boot UUID in ServerStatistics and RemoteMessage

This makes it so we can see when a remote reboots easily, and gives us a
stepping stone to putting it in the log file header.

RemoteMessage then goes to the logger, so we are ready to teach the
logger how to detect and handle reboots.

Change-Id: I943de46094b60535c92a677b430d6828933b820d
diff --git a/aos/events/BUILD b/aos/events/BUILD
index 239b324..646f597 100644
--- a/aos/events/BUILD
+++ b/aos/events/BUILD
@@ -83,6 +83,7 @@
         "//aos:configuration_fbs",
         "//aos:flatbuffers",
         "//aos:ftrace",
+        "//aos/events/logging:uuid",
         "//aos/ipc_lib:data_alignment",
         "//aos/logging:implementations",
         "//aos/time",
diff --git a/aos/events/event_loop.cc b/aos/events/event_loop.cc
index 08fa7fd..8d01549 100644
--- a/aos/events/event_loop.cc
+++ b/aos/events/event_loop.cc
@@ -79,8 +79,9 @@
 
 PhasedLoopHandler::~PhasedLoopHandler() {}
 
-EventLoop::EventLoop(const Configuration *configuration)
-    : timing_report_(flatbuffers::DetachedBuffer()),
+EventLoop::EventLoop(const Configuration *configuration, UUID boot_uuid)
+    : boot_uuid_(boot_uuid),
+      timing_report_(flatbuffers::DetachedBuffer()),
       configuration_(configuration) {}
 
 EventLoop::~EventLoop() {
diff --git a/aos/events/event_loop.h b/aos/events/event_loop.h
index bf7381c..3916ce4 100644
--- a/aos/events/event_loop.h
+++ b/aos/events/event_loop.h
@@ -12,6 +12,7 @@
 #include "aos/events/channel_preallocated_allocator.h"
 #include "aos/events/event_loop_event.h"
 #include "aos/events/event_loop_generated.h"
+#include "aos/events/logging/uuid.h"
 #include "aos/events/timing_statistics.h"
 #include "aos/flatbuffers.h"
 #include "aos/ftrace.h"
@@ -473,7 +474,7 @@
 
 class EventLoop {
  public:
-  EventLoop(const Configuration *configuration);
+  EventLoop(const Configuration *configuration, UUID boot_uuid);
 
   virtual ~EventLoop();
 
@@ -653,6 +654,9 @@
   // range of Context::buffer_index values for this channel.
   virtual int NumberBuffers(const Channel *channel) = 0;
 
+  // Returns the boot UUID.
+  const UUID &boot_uuid() { return boot_uuid_; }
+
  protected:
   // Sets the name of the event loop.  This is the application name.
   virtual void set_name(const std::string_view name) = 0;
@@ -732,6 +736,8 @@
   // If true, don't send AOS_LOG to /aos
   bool skip_logger_ = false;
 
+  UUID boot_uuid_;
+
  private:
   virtual pid_t GetTid() = 0;
 
diff --git a/aos/events/logging/BUILD b/aos/events/logging/BUILD
index e05f2b3..45ec394 100644
--- a/aos/events/logging/BUILD
+++ b/aos/events/logging/BUILD
@@ -26,8 +26,8 @@
     visibility = ["//visibility:public"],
     deps = [
         ":buffer_encoder",
-        ":uuid",
         ":logger_fbs",
+        ":uuid",
         "//aos:configuration",
         "//aos:flatbuffer_merge",
         "//aos:flatbuffers",
@@ -316,6 +316,10 @@
     srcs = ["uuid.cc"],
     hdrs = ["uuid.h"],
     target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "@com_github_google_glog//:glog",
+    ],
 )
 
 cc_test(
diff --git a/aos/events/logging/logger_test.cc b/aos/events/logging/logger_test.cc
index c536711..8115400 100644
--- a/aos/events/logging/logger_test.cc
+++ b/aos/events/logging/logger_test.cc
@@ -686,25 +686,31 @@
         UnorderedElementsAre(
             std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 200),
             std::make_tuple("/pi1/aos", "aos.timing.Report", 40),
-            std::make_tuple("/test", "aos.examples.Ping", 2001)));
+            std::make_tuple("/test", "aos.examples.Ping", 2001)))
+        << " : " << logfiles_[0];
     // Timestamps for pong
     EXPECT_THAT(
         CountChannelsTimestamp(logfiles_[0]),
         UnorderedElementsAre(
             std::make_tuple("/test", "aos.examples.Pong", 2001),
-            std::make_tuple("/pi2/aos", "aos.message_bridge.Timestamp", 200)));
+            std::make_tuple("/pi2/aos", "aos.message_bridge.Timestamp", 200)))
+        << " : " << logfiles_[0];
 
     // Pong data.
-    EXPECT_THAT(CountChannelsData(logfiles_[1]),
-                UnorderedElementsAre(
-                    std::make_tuple("/test", "aos.examples.Pong", 101)));
+    EXPECT_THAT(
+        CountChannelsData(logfiles_[1]),
+        UnorderedElementsAre(std::make_tuple("/test", "aos.examples.Pong", 91)))
+        << " : " << logfiles_[1];
     EXPECT_THAT(CountChannelsData(logfiles_[2]),
                 UnorderedElementsAre(
-                    std::make_tuple("/test", "aos.examples.Pong", 1900)));
+                    std::make_tuple("/test", "aos.examples.Pong", 1910)))
+        << " : " << logfiles_[1];
 
     // No timestamps
-    EXPECT_THAT(CountChannelsTimestamp(logfiles_[1]), UnorderedElementsAre());
-    EXPECT_THAT(CountChannelsTimestamp(logfiles_[2]), UnorderedElementsAre());
+    EXPECT_THAT(CountChannelsTimestamp(logfiles_[1]), UnorderedElementsAre())
+        << " : " << logfiles_[1];
+    EXPECT_THAT(CountChannelsTimestamp(logfiles_[2]), UnorderedElementsAre())
+        << " : " << logfiles_[2];
 
     // Timing reports and pongs.
     EXPECT_THAT(
@@ -712,56 +718,74 @@
         UnorderedElementsAre(
             std::make_tuple("/pi2/aos", "aos.message_bridge.Timestamp", 200),
             std::make_tuple("/pi2/aos", "aos.timing.Report", 40),
-            std::make_tuple("/test", "aos.examples.Pong", 2001)));
+            std::make_tuple("/test", "aos.examples.Pong", 2001)))
+        << " : " << logfiles_[3];
     // And ping timestamps.
     EXPECT_THAT(
         CountChannelsTimestamp(logfiles_[3]),
         UnorderedElementsAre(
             std::make_tuple("/test", "aos.examples.Ping", 2001),
-            std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 200)));
+            std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 200)))
+        << " : " << logfiles_[3];
 
     // Timestamps from pi2 on pi1, and the other way.
-    EXPECT_THAT(CountChannelsData(logfiles_[4]), UnorderedElementsAre());
-    EXPECT_THAT(CountChannelsData(logfiles_[5]), UnorderedElementsAre());
-    EXPECT_THAT(CountChannelsData(logfiles_[6]), UnorderedElementsAre());
-    EXPECT_THAT(CountChannelsData(logfiles_[7]), UnorderedElementsAre());
+    EXPECT_THAT(CountChannelsData(logfiles_[4]), UnorderedElementsAre())
+        << " : " << logfiles_[4];
+    EXPECT_THAT(CountChannelsData(logfiles_[5]), UnorderedElementsAre())
+        << " : " << logfiles_[5];
+    EXPECT_THAT(CountChannelsData(logfiles_[6]), UnorderedElementsAre())
+        << " : " << logfiles_[6];
+    EXPECT_THAT(CountChannelsData(logfiles_[7]), UnorderedElementsAre())
+        << " : " << logfiles_[7];
     EXPECT_THAT(
         CountChannelsTimestamp(logfiles_[4]),
         UnorderedElementsAre(
-            std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 10),
-            std::make_tuple("/test", "aos.examples.Ping", 101)));
+            std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 9),
+            std::make_tuple("/test", "aos.examples.Ping", 91)))
+        << " : " << logfiles_[4];
     EXPECT_THAT(
         CountChannelsTimestamp(logfiles_[5]),
         UnorderedElementsAre(
-            std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 190),
-            std::make_tuple("/test", "aos.examples.Ping", 1900)));
+            std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 191),
+            std::make_tuple("/test", "aos.examples.Ping", 1910)))
+        << " : " << logfiles_[5];
     EXPECT_THAT(CountChannelsTimestamp(logfiles_[6]),
                 UnorderedElementsAre(std::make_tuple(
-                    "/pi2/aos", "aos.message_bridge.Timestamp", 10)));
+                    "/pi2/aos", "aos.message_bridge.Timestamp", 9)))
+        << " : " << logfiles_[6];
     EXPECT_THAT(CountChannelsTimestamp(logfiles_[7]),
                 UnorderedElementsAre(std::make_tuple(
-                    "/pi2/aos", "aos.message_bridge.Timestamp", 190)));
+                    "/pi2/aos", "aos.message_bridge.Timestamp", 191)))
+        << " : " << logfiles_[7];
 
     // And then test that the remotely logged timestamp data files only have
     // timestamps in them.
-    EXPECT_THAT(CountChannelsTimestamp(logfiles_[8]), UnorderedElementsAre());
-    EXPECT_THAT(CountChannelsTimestamp(logfiles_[9]), UnorderedElementsAre());
-    EXPECT_THAT(CountChannelsTimestamp(logfiles_[10]), UnorderedElementsAre());
-    EXPECT_THAT(CountChannelsTimestamp(logfiles_[11]), UnorderedElementsAre());
+    EXPECT_THAT(CountChannelsTimestamp(logfiles_[8]), UnorderedElementsAre())
+        << " : " << logfiles_[8];
+    EXPECT_THAT(CountChannelsTimestamp(logfiles_[9]), UnorderedElementsAre())
+        << " : " << logfiles_[9];
+    EXPECT_THAT(CountChannelsTimestamp(logfiles_[10]), UnorderedElementsAre())
+        << " : " << logfiles_[10];
+    EXPECT_THAT(CountChannelsTimestamp(logfiles_[11]), UnorderedElementsAre())
+        << " : " << logfiles_[11];
 
     EXPECT_THAT(CountChannelsData(logfiles_[8]),
                 UnorderedElementsAre(std::make_tuple(
-                    "/pi1/aos", "aos.message_bridge.Timestamp", 10)));
+                    "/pi1/aos", "aos.message_bridge.Timestamp", 9)))
+        << " : " << logfiles_[8];
     EXPECT_THAT(CountChannelsData(logfiles_[9]),
                 UnorderedElementsAre(std::make_tuple(
-                    "/pi1/aos", "aos.message_bridge.Timestamp", 190)));
+                    "/pi1/aos", "aos.message_bridge.Timestamp", 191)))
+        << " : " << logfiles_[9];
 
     EXPECT_THAT(CountChannelsData(logfiles_[10]),
                 UnorderedElementsAre(std::make_tuple(
-                    "/pi2/aos", "aos.message_bridge.Timestamp", 10)));
+                    "/pi2/aos", "aos.message_bridge.Timestamp", 9)))
+        << " : " << logfiles_[10];
     EXPECT_THAT(CountChannelsData(logfiles_[11]),
                 UnorderedElementsAre(std::make_tuple(
-                    "/pi2/aos", "aos.message_bridge.Timestamp", 190)));
+                    "/pi2/aos", "aos.message_bridge.Timestamp", 191)))
+        << " : " << logfiles_[11];
   }
 
   LogReader reader(SortParts(logfiles_));
diff --git a/aos/events/logging/uuid.cc b/aos/events/logging/uuid.cc
index 376fa95..3914740 100644
--- a/aos/events/logging/uuid.cc
+++ b/aos/events/logging/uuid.cc
@@ -1,9 +1,14 @@
 #include "aos/events/logging/uuid.h"
 
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <array>
 #include <random>
 #include <string_view>
 
+#include "glog/logging.h"
+
 namespace aos {
 namespace {
 char ToHex(int val) {
@@ -57,9 +62,24 @@
   return result;
 }
 
-UUID UUID::Zero() {
+UUID UUID::Zero() { return FromString("00000000-0000-0000-0000-000000000000"); }
+
+UUID UUID::FromString(std::string_view str) {
   UUID result;
-  result.data_.fill(0);
+  CHECK_EQ(str.size(), kSize);
+
+  std::copy(str.begin(), str.end(), result.data_.begin());
+  return result;
+}
+
+UUID UUID::BootUUID() {
+  int fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY);
+  PCHECK(fd != -1);
+
+  UUID result;
+  CHECK_EQ(static_cast<ssize_t>(kSize), read(fd, result.data_.begin(), kSize));
+  close(fd);
+
   return result;
 }
 
diff --git a/aos/events/logging/uuid.h b/aos/events/logging/uuid.h
index 0387a4b..d7ec33a 100644
--- a/aos/events/logging/uuid.h
+++ b/aos/events/logging/uuid.h
@@ -13,8 +13,16 @@
   // Returns a randomly generated UUID.  This is known as a UUID4.
   static UUID Random();
 
+  // Returns a uuid with all '0' characters.
   static UUID Zero();
 
+  static UUID FromString(std::string_view);
+
+  static UUID BootUUID();
+
+  // Size of a UUID.
+  static constexpr size_t kSize = 36;
+
   std::string_view string_view() const {
     return std::string_view(data_.data(), data_.size());
   }
@@ -30,7 +38,7 @@
   UUID() {}
 
   // Fixed size storage for the data.  Non-null terminated.
-  std::array<char, 36> data_;
+  std::array<char, kSize> data_;
 };
 
 }  // namespace aos
diff --git a/aos/events/shm_event_loop.cc b/aos/events/shm_event_loop.cc
index fed5f4c..2a0d332 100644
--- a/aos/events/shm_event_loop.cc
+++ b/aos/events/shm_event_loop.cc
@@ -222,7 +222,7 @@
 }  // namespace
 
 ShmEventLoop::ShmEventLoop(const Configuration *configuration)
-    : EventLoop(configuration),
+    : EventLoop(configuration, UUID::BootUUID()),
       shm_base_(FLAGS_shm_base),
       name_(FLAGS_application_name),
       node_(MaybeMyNode(configuration)) {
diff --git a/aos/events/simulated_event_loop.cc b/aos/events/simulated_event_loop.cc
index 9c8bb09..80ba88c 100644
--- a/aos/events/simulated_event_loop.cc
+++ b/aos/events/simulated_event_loop.cc
@@ -455,7 +455,8 @@
       std::vector<std::pair<EventLoop *, std::function<void(bool)>>>
           *raw_event_loops,
       const Node *node, pid_t tid)
-      : EventLoop(CHECK_NOTNULL(configuration)),
+      : EventLoop(CHECK_NOTNULL(configuration),
+                  node_event_loop_factory->boot_uuid()),
         scheduler_(scheduler),
         node_event_loop_factory_(node_event_loop_factory),
         channels_(channels),
diff --git a/aos/events/simulated_event_loop.h b/aos/events/simulated_event_loop.h
index 5ba7a8a..40deb43 100644
--- a/aos/events/simulated_event_loop.h
+++ b/aos/events/simulated_event_loop.h
@@ -13,6 +13,7 @@
 #include "absl/container/btree_map.h"
 #include "aos/events/event_loop.h"
 #include "aos/events/event_scheduler.h"
+#include "aos/events/logging/uuid.h"
 #include "aos/events/simple_channel.h"
 #include "aos/flatbuffer_merge.h"
 #include "aos/flatbuffers.h"
@@ -175,6 +176,15 @@
     scheduler_.SetDistributedOffset(monotonic_offset, monotonic_slope);
   }
 
+  // Returns the boot UUID for this node.
+  const UUID &boot_uuid() const { return boot_uuid_; }
+
+  // Reboots the node.  This just resets the boot_uuid_, nothing else.
+  // TODO(austin): This is here for a test case or two, not for general
+  // consumption.  The interactions with the rest of the system need to be
+  // worked out better.  Don't use this for anything real yet.
+  void Reboot() { boot_uuid_ = UUID::Random(); }
+
  private:
   friend class SimulatedEventLoopFactory;
   NodeEventLoopFactory(
@@ -186,6 +196,8 @@
   EventScheduler scheduler_;
   SimulatedEventLoopFactory *const factory_;
 
+  UUID boot_uuid_ = UUID::Random();
+
   const Node *const node_;
 
   std::vector<std::pair<EventLoop *, std::function<void(bool)>>>
diff --git a/aos/events/simulated_event_loop_test.cc b/aos/events/simulated_event_loop_test.cc
index fb144c3..3768348 100644
--- a/aos/events/simulated_event_loop_test.cc
+++ b/aos/events/simulated_event_loop_test.cc
@@ -448,6 +448,7 @@
         for (const message_bridge::ServerConnection *connection :
              *stats.connections()) {
           EXPECT_EQ(connection->state(), message_bridge::State::CONNECTED);
+          EXPECT_TRUE(connection->has_boot_uuid());
           if (connection->node()->name()->string_view() == "pi2") {
             EXPECT_GT(connection->sent_packets(), 50);
           } else if (connection->node()->name()->string_view() == "pi3") {
@@ -471,6 +472,7 @@
 
         const message_bridge::ServerConnection *connection =
             stats.connections()->Get(0);
+        EXPECT_TRUE(connection->has_boot_uuid());
         EXPECT_EQ(connection->state(), message_bridge::State::CONNECTED);
         EXPECT_GT(connection->sent_packets(), 50);
         EXPECT_TRUE(connection->has_monotonic_offset());
@@ -487,6 +489,7 @@
 
         const message_bridge::ServerConnection *connection =
             stats.connections()->Get(0);
+        EXPECT_TRUE(connection->has_boot_uuid());
         EXPECT_EQ(connection->state(), message_bridge::State::CONNECTED);
         EXPECT_GE(connection->sent_packets(), 5);
         EXPECT_TRUE(connection->has_monotonic_offset());
@@ -577,8 +580,14 @@
       "/pi1/aos/remote_timestamps/pi2",
       [pi1_timestamp_channel, ping_timestamp_channel, &ping_on_pi2_fetcher,
        &ping_on_pi1_fetcher, &pi1_on_pi2_timestamp_fetcher,
-       &pi1_on_pi1_timestamp_fetcher](const RemoteMessage &header) {
+       &pi1_on_pi1_timestamp_fetcher, &simulated_event_loop_factory,
+       pi2](const RemoteMessage &header) {
         VLOG(1) << aos::FlatbufferToJson(&header);
+        EXPECT_TRUE(header.has_boot_uuid());
+        EXPECT_EQ(header.boot_uuid()->string_view(),
+                  simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
+                      ->boot_uuid()
+                      .string_view());
 
         const aos::monotonic_clock::time_point header_monotonic_sent_time(
             chrono::nanoseconds(header.monotonic_sent_time()));
@@ -661,9 +670,9 @@
   EXPECT_EQ(pi3_on_pi1_timestamp_counter.count(), 100);
   EXPECT_EQ(pi3_on_pi3_timestamp_counter.count(), 100);
 
-  EXPECT_EQ(pi1_server_statistics_count, 9);
-  EXPECT_EQ(pi2_server_statistics_count, 9);
-  EXPECT_EQ(pi3_server_statistics_count, 9);
+  EXPECT_EQ(pi1_server_statistics_count, 10);
+  EXPECT_EQ(pi2_server_statistics_count, 10);
+  EXPECT_EQ(pi3_server_statistics_count, 10);
 
   EXPECT_EQ(pi1_client_statistics_count, 95);
   EXPECT_EQ(pi2_client_statistics_count, 95);
@@ -721,6 +730,7 @@
         for (const message_bridge::ServerConnection *connection :
              *stats.connections()) {
           EXPECT_EQ(connection->state(), message_bridge::State::CONNECTED);
+          EXPECT_TRUE(connection->has_boot_uuid());
           if (connection->node()->name()->string_view() == "pi2") {
             EXPECT_EQ(connection->monotonic_offset(),
                       chrono::nanoseconds(kOffset).count());
@@ -744,6 +754,7 @@
 
         const message_bridge::ServerConnection *connection =
             stats.connections()->Get(0);
+        EXPECT_TRUE(connection->has_boot_uuid());
         EXPECT_EQ(connection->state(), message_bridge::State::CONNECTED);
         EXPECT_TRUE(connection->has_monotonic_offset());
         EXPECT_EQ(connection->monotonic_offset(),
@@ -760,6 +771,7 @@
 
         const message_bridge::ServerConnection *connection =
             stats.connections()->Get(0);
+        EXPECT_TRUE(connection->has_boot_uuid());
         EXPECT_EQ(connection->state(), message_bridge::State::CONNECTED);
         EXPECT_TRUE(connection->has_monotonic_offset());
         EXPECT_EQ(connection->monotonic_offset(), 0);
@@ -770,9 +782,9 @@
                                       chrono::milliseconds(500) +
                                       chrono::milliseconds(5));
 
-  EXPECT_EQ(pi1_server_statistics_count, 9);
+  EXPECT_EQ(pi1_server_statistics_count, 10);
   EXPECT_EQ(pi2_server_statistics_count, 9);
-  EXPECT_EQ(pi3_server_statistics_count, 9);
+  EXPECT_EQ(pi3_server_statistics_count, 10);
 }
 
 // Test that disabling statistics actually disables them.
@@ -1024,8 +1036,13 @@
   int reliable_timestamp_count = 0;
   pi1_remote_timestamp->MakeWatcher(
       "/pi1/aos/remote_timestamps/pi2",
-      [reliable_channel_index,
-       &reliable_timestamp_count](const RemoteMessage &header) {
+      [reliable_channel_index, &reliable_timestamp_count,
+       &simulated_event_loop_factory, pi2](const RemoteMessage &header) {
+        EXPECT_TRUE(header.has_boot_uuid());
+        EXPECT_EQ(header.boot_uuid()->string_view(),
+                  simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
+                      ->boot_uuid()
+                      .string_view());
         VLOG(1) << aos::FlatbufferToJson(&header);
         if (header.channel_index() == reliable_channel_index) {
           ++reliable_timestamp_count;
@@ -1051,5 +1068,84 @@
   EXPECT_EQ(reliable_timestamp_count, 2u);
 }
 
+// Tests that rebooting a node changes the ServerStatistics message and the
+// RemoteTimestamp message.
+TEST(SimulatedEventLoopTest, BootUUIDTest) {
+  aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+      aos::configuration::ReadConfig(ConfigPrefix() +
+                                     "events/multinode_pingpong_config.json");
+  const Node *pi1 = configuration::GetNode(&config.message(), "pi1");
+  const Node *pi2 = configuration::GetNode(&config.message(), "pi2");
+
+  SimulatedEventLoopFactory simulated_event_loop_factory(&config.message());
+
+  std::unique_ptr<EventLoop> ping_event_loop =
+      simulated_event_loop_factory.MakeEventLoop("ping", pi1);
+  Ping ping(ping_event_loop.get());
+
+  std::unique_ptr<EventLoop> pong_event_loop =
+      simulated_event_loop_factory.MakeEventLoop("pong", pi2);
+  Pong pong(pong_event_loop.get());
+
+  std::unique_ptr<EventLoop> pi1_remote_timestamp =
+      simulated_event_loop_factory.MakeEventLoop("pi1_remote_timestamp", pi1);
+  std::string expected_boot_uuid(
+      simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
+          ->boot_uuid()
+          .string_view());
+
+  int timestamp_count = 0;
+  pi1_remote_timestamp->MakeWatcher(
+      "/pi1/aos/remote_timestamps/pi2",
+      [&timestamp_count, &expected_boot_uuid](const RemoteMessage &header) {
+        EXPECT_TRUE(header.has_boot_uuid());
+        EXPECT_EQ(header.boot_uuid()->string_view(), expected_boot_uuid);
+        VLOG(1) << aos::FlatbufferToJson(&header);
+        ++timestamp_count;
+      });
+
+  int pi1_server_statistics_count = 0;
+  pi1_remote_timestamp->MakeWatcher(
+      "/pi1/aos", [&pi1_server_statistics_count, &expected_boot_uuid](
+                      const message_bridge::ServerStatistics &stats) {
+        VLOG(1) << "pi1 ServerStatistics " << FlatbufferToJson(&stats);
+        for (const message_bridge::ServerConnection *connection :
+             *stats.connections()) {
+          EXPECT_TRUE(connection->has_boot_uuid());
+          if (connection->node()->name()->string_view() == "pi2") {
+            EXPECT_EQ(expected_boot_uuid,
+                      connection->boot_uuid()->string_view())
+                << " : Got " << aos::FlatbufferToJson(&stats);
+            ++pi1_server_statistics_count;
+          }
+        }
+      });
+
+  // Let a couple of ServerStatistics messages show up before rebooting.
+  simulated_event_loop_factory.RunFor(chrono::milliseconds(2001));
+
+  EXPECT_GT(timestamp_count, 100);
+  EXPECT_GE(pi1_server_statistics_count, 1u);
+
+  // Confirm that reboot changes the UUID.
+  simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)->Reboot();
+
+  EXPECT_NE(expected_boot_uuid,
+            simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
+                ->boot_uuid()
+                .string_view());
+
+  expected_boot_uuid =
+      simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
+          ->boot_uuid()
+          .string_view();
+  timestamp_count = 0;
+  pi1_server_statistics_count = 0;
+
+  simulated_event_loop_factory.RunFor(chrono::milliseconds(2000));
+  EXPECT_GT(timestamp_count, 100);
+  EXPECT_GE(pi1_server_statistics_count, 1u);
+}
+
 }  // namespace testing
 }  // namespace aos
diff --git a/aos/events/simulated_network_bridge.cc b/aos/events/simulated_network_bridge.cc
index 1ba1408..45bc6e7 100644
--- a/aos/events/simulated_network_bridge.cc
+++ b/aos/events/simulated_network_bridge.cc
@@ -21,6 +21,8 @@
                     aos::EventLoop *send_event_loop,
                     std::unique_ptr<aos::RawFetcher> fetcher,
                     std::unique_ptr<aos::RawSender> sender,
+                    MessageBridgeServerStatus *server_status,
+                    size_t destination_node_index,
                     ServerConnection *server_connection, int client_index,
                     MessageBridgeClientStatus *client_status,
                     size_t channel_index,
@@ -30,6 +32,8 @@
         send_event_loop_(send_event_loop),
         fetcher_(std::move(fetcher)),
         sender_(std::move(sender)),
+        server_status_(server_status),
+        destination_node_index_(destination_node_index),
         server_connection_(server_connection),
         client_status_(client_status),
         client_index_(client_index),
@@ -123,6 +127,20 @@
       aos::Sender<RemoteMessage>::Builder builder =
           timestamp_logger_->MakeBuilder();
 
+      // Reset the filter every time the UUID changes.  There's probably a more
+      // clever way to do this, but that means a better concept of rebooting.
+      if (server_status_->BootUUID(destination_node_index_) !=
+          send_node_factory_->boot_uuid().string_view()) {
+        server_status_->ResetFilter(destination_node_index_);
+        server_status_->SetBootUUID(
+            destination_node_index_,
+            send_node_factory_->boot_uuid().string_view());
+      }
+
+      flatbuffers::Offset<flatbuffers::String> boot_uuid_offset =
+          builder.fbb()->CreateString(
+              send_node_factory_->boot_uuid().string_view());
+
       RemoteMessage::Builder message_header_builder =
           builder.MakeBuilder<RemoteMessage>();
 
@@ -142,6 +160,7 @@
       message_header_builder.add_realtime_sent_time(
           sender_->realtime_sent_time().time_since_epoch().count());
       message_header_builder.add_queue_index(sender_->sent_queue_index());
+      message_header_builder.add_boot_uuid(boot_uuid_offset);
 
       builder.Send(message_header_builder.Finish());
     }
@@ -172,6 +191,9 @@
   std::unique_ptr<aos::RawFetcher> fetcher_;
   // Sender to send them back out.
   std::unique_ptr<aos::RawSender> sender_;
+
+  MessageBridgeServerStatus *server_status_;
+  const size_t destination_node_index_;
   // True if we have sent the message in the fetcher.
   bool sent_ = false;
 
@@ -222,6 +244,28 @@
     }
   }
 
+  for (const Node *node : simulated_event_loop_factory->nodes()) {
+    auto it = event_loop_map_.find(node);
+
+    CHECK(it != event_loop_map_.end());
+
+    size_t node_index = 0;
+    for (ServerConnection *connection :
+         it->second.server_status.server_connection()) {
+      if (connection != nullptr) {
+        const Node *client_node =
+            simulated_event_loop_factory->configuration()->nodes()->Get(
+                node_index);
+        auto client_event_loop = event_loop_map_.find(client_node);
+        it->second.server_status.ResetFilter(node_index);
+        it->second.server_status.SetBootUUID(
+            node_index,
+            client_event_loop->second.event_loop->boot_uuid().string_view());
+      }
+      ++node_index;
+    }
+  }
+
   for (const Channel *channel :
        *simulated_event_loop_factory->configuration()->channels()) {
     if (!channel->has_destination_nodes()) {
@@ -268,6 +312,7 @@
           destination_event_loop->second.event_loop.get(),
           source_event_loop->second.event_loop->MakeRawFetcher(channel),
           destination_event_loop->second.event_loop->MakeRawSender(channel),
+          &source_event_loop->second.server_status, destination_node_index,
           server_connection, client_index,
           &destination_event_loop->second.client_status,
           configuration::ChannelIndex(