patchpanel: Add guest device callbacks.

ARC and Crostini services create their own virtual network devices and
these events are not connected with the Manager's interaction with shill
(meaning to say that the ARC service has its own shill client and
Crostini service works differently anyway).
In order to support DBus signals that communicate when virtual guest
devices areeadded and removed, a callback is added will be fired for
each device at the appropriate time.
The alternative - but more direct approach - is to plumb some
kind of DBus awareness into these services. Since this breaks the
current convention of isolation, this option was discarded.

BUG=b:174432555
TEST=units

Change-Id: I8c9bf6b7c16edc0b95c0e6cccfb4562603c53899
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2566854
Tested-by: Garrick Evans <garrick@chromium.org>
Commit-Queue: Garrick Evans <garrick@chromium.org>
Reviewed-by: Hugo Benichi <hugobenichi@google.com>
diff --git a/patchpanel/arc_service_test.cc b/patchpanel/arc_service_test.cc
index e8a267d..1de745d 100644
--- a/patchpanel/arc_service_test.cc
+++ b/patchpanel/arc_service_test.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include <base/bind.h>
+#include <base/bind_helpers.h>
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -24,6 +25,7 @@
 using testing::_;
 using testing::AnyNumber;
 using testing::Eq;
+using testing::Pair;
 using testing::Pointee;
 using testing::Return;
 using testing::ReturnRef;
@@ -75,11 +77,21 @@
     datapath_ = std::make_unique<MockDatapath>(runner_.get(), &firewall_);
     shill_client_ = shill_helper_.Client();
     addr_mgr_ = std::make_unique<AddressManager>();
+    guest_devices_.clear();
   }
 
   std::unique_ptr<ArcService> NewService(GuestMessage::GuestType guest) {
-    return std::make_unique<ArcService>(shill_client_.get(), datapath_.get(),
-                                        addr_mgr_.get(), &forwarder_, guest);
+    return std::make_unique<ArcService>(
+        shill_client_.get(), datapath_.get(), addr_mgr_.get(), &forwarder_,
+        guest,
+        base::BindRepeating(&ArcServiceTest::DeviceHandler,
+                            base::Unretained(this)));
+  }
+
+  void DeviceHandler(const Device& device,
+                     Device::ChangeEvent event,
+                     GuestMessage::GuestType guest_type) {
+    guest_devices_[device.host_ifname()] = event;
   }
 
   FakeShillClientHelper shill_helper_;
@@ -89,6 +101,7 @@
   std::unique_ptr<MockDatapath> datapath_;
   std::unique_ptr<FakeProcessRunner> runner_;
   MockFirewall firewall_;
+  std::map<std::string, Device::ChangeEvent> guest_devices_;
 };
 
 TEST_F(ArcServiceTest, NotStarted_AddDevice) {
@@ -292,6 +305,36 @@
               UnorderedElementsAre(StrEq("arc_eth0"), StrEq("arc_wlan0")));
 }
 
+TEST_F(ArcServiceTest, ContainerImpl_DeviceHandler) {
+  EXPECT_CALL(*datapath_, NetnsAttachName(_, _)).WillRepeatedly(Return(true));
+  EXPECT_CALL(*datapath_, ConnectVethPair(_, _, _, _, _, _, _, _))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(*datapath_, AddBridge(_, _, _)).WillRepeatedly(Return(true));
+  EXPECT_CALL(*datapath_, AddToBridge(_, _)).WillRepeatedly(Return(true));
+
+  auto svc = NewService(GuestMessage::ARC);
+  svc->Start(kTestPID);
+
+  svc->OnDevicesChanged({"eth0", "wlan0"}, {});
+  EXPECT_EQ(guest_devices_.size(), 2);
+  EXPECT_THAT(guest_devices_,
+              UnorderedElementsAre(
+                  Pair(StrEq("arc_eth0"), Device::ChangeEvent::ADDED),
+                  Pair(StrEq("arc_wlan0"), Device::ChangeEvent::ADDED)));
+
+  svc->OnDevicesChanged({}, {"wlan0"});
+  EXPECT_THAT(guest_devices_,
+              UnorderedElementsAre(
+                  Pair(StrEq("arc_eth0"), Device::ChangeEvent::ADDED),
+                  Pair(StrEq("arc_wlan0"), Device::ChangeEvent::REMOVED)));
+
+  guest_devices_.clear();
+  svc->OnDevicesChanged({"wlan0"}, {});
+  EXPECT_THAT(guest_devices_,
+              UnorderedElementsAre(
+                  Pair(StrEq("arc_wlan0"), Device::ChangeEvent::ADDED)));
+}
+
 TEST_F(ArcServiceTest, ContainerImpl_StartAfterDevice) {
   EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
       .WillOnce(Return(true));
@@ -585,4 +628,39 @@
                                          StrEq("arc_eth1")));
 }
 
+TEST_F(ArcServiceTest, VmImpl_DeviceHandler) {
+  // Expectations for tap devices pre-creation.
+  EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
+      .WillOnce(Return("vmtap0"))
+      .WillOnce(Return("vmtap1"))
+      .WillOnce(Return("vmtap2"))
+      .WillOnce(Return("vmtap3"))
+      .WillOnce(Return("vmtap4"))
+      .WillOnce(Return("vmtap5"));
+  EXPECT_CALL(*datapath_, AddBridge(_, _, _)).WillRepeatedly(Return(true));
+  EXPECT_CALL(*datapath_, AddToBridge(_, _)).WillRepeatedly(Return(true));
+
+  auto svc = NewService(GuestMessage::ARC_VM);
+  svc->Start(kTestPID);
+
+  svc->OnDevicesChanged({"eth0", "wlan0"}, {});
+  EXPECT_EQ(guest_devices_.size(), 2);
+  EXPECT_THAT(guest_devices_,
+              UnorderedElementsAre(
+                  Pair(StrEq("arc_eth0"), Device::ChangeEvent::ADDED),
+                  Pair(StrEq("arc_wlan0"), Device::ChangeEvent::ADDED)));
+
+  svc->OnDevicesChanged({}, {"wlan0"});
+  EXPECT_THAT(guest_devices_,
+              UnorderedElementsAre(
+                  Pair(StrEq("arc_eth0"), Device::ChangeEvent::ADDED),
+                  Pair(StrEq("arc_wlan0"), Device::ChangeEvent::REMOVED)));
+
+  guest_devices_.clear();
+  svc->OnDevicesChanged({"wlan0"}, {});
+  EXPECT_THAT(guest_devices_,
+              UnorderedElementsAre(
+                  Pair(StrEq("arc_wlan0"), Device::ChangeEvent::ADDED)));
+}
+
 }  // namespace patchpanel