UdpSocket: Update constructor

This change adds the UdpSocket::Client interface (as discussed in previous CL
https://chromium-review.googlesource.com/c/openscreen/+/1754731) and updates
the UDP Socket constructor to make the following changes:
- Adds the Client to the constructor
- Adds the Task Runner to the constructor
The remaining changes are to support updating the constructor

Future changes will update the UdpSocket methods to use this Client and Task
Runner, but I want to do those as follow-up CLs to keep this one small

Change-Id: Icac05cf5825e4f84e4090d27849db41b6221a69a
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/1755645
Commit-Queue: Ryan Keane <rwkeane@google.com>
Reviewed-by: mark a. foltz <mfoltz@chromium.org>
Reviewed-by: Max Yakimakha <yakimakha@chromium.org>
diff --git a/platform/api/udp_socket.cc b/platform/api/udp_socket.cc
index 1bd96fd..46b3d9e 100644
--- a/platform/api/udp_socket.cc
+++ b/platform/api/udp_socket.cc
@@ -4,10 +4,14 @@
 
 #include "platform/api/udp_socket.h"
 
+#include "platform/api/task_runner.h"
+
 namespace openscreen {
 namespace platform {
 
-UdpSocket::UdpSocket() {
+UdpSocket::UdpSocket(TaskRunner* task_runner, Client* client)
+    : client_(client), task_runner_(task_runner) {
+  OSP_CHECK(task_runner_);
   deletion_callback_ = [](UdpSocket* socket) {};
 }
 
diff --git a/platform/api/udp_socket.h b/platform/api/udp_socket.h
index aeee300..703f6b0 100644
--- a/platform/api/udp_socket.h
+++ b/platform/api/udp_socket.h
@@ -18,6 +18,7 @@
 namespace openscreen {
 namespace platform {
 
+class TaskRunner;
 class UdpSocket;
 
 using UdpSocketUniquePtr = std::unique_ptr<UdpSocket>;
@@ -39,6 +40,26 @@
  public:
   virtual ~UdpSocket();
 
+  // Client for the UdpSocket class.
+  class Client {
+   public:
+    virtual ~Client() = default;
+
+    // Method called on socket configuration operations when an error occurs.
+    // These specific APIs are:
+    //   UdpSocket::Bind()
+    //   UdpSocket::SetMulticastOutboundInterface(...)
+    //   UdpSocket::JoinMulticastGroup(...)
+    //   UdpSocket::SetDscp(...)
+    virtual void OnError(UdpSocket* socket, Error error) = 0;
+
+    // Method called when an error occurs during a SendMessage call.
+    virtual void OnSendError(UdpSocket* socket, Error error) = 0;
+
+    // Method called when a packet is read.
+    virtual void OnRead(UdpSocket* socket, ErrorOr<UdpPacket> packet) = 0;
+  };
+
   // Constants used to specify how we want packets sent from this socket.
   enum class DscpMode : uint8_t {
     // Default value set by the system on creation of a new socket.
@@ -58,8 +79,13 @@
 
   // Creates a new, scoped UdpSocket within the IPv4 or IPv6 family.
   // |local_endpoint| may be zero (see comments for Bind()). This method must be
-  // defined in the platform-level implementation.
-  static ErrorOr<UdpSocketUniquePtr> Create(const IPEndpoint& local_endpoint);
+  // defined in the platform-level implementation. All client_ methods called
+  // will be queued on the provided task_runner. For this reason, the provided
+  // task_runner and client must exist for the duration of the created socket's
+  // lifetime.
+  static ErrorOr<UdpSocketUniquePtr> Create(TaskRunner* task_runner,
+                                            Client* client,
+                                            const IPEndpoint& local_endpoint);
 
   // Returns true if |socket| belongs to the IPv4/IPv6 address family.
   virtual bool IsIPv4() const = 0;
@@ -109,7 +135,17 @@
   void SetDeletionCallback(std::function<void(UdpSocket*)> callback);
 
  protected:
-  UdpSocket();
+  // Creates a new UdpSocket. The provided client and task_runner must exist for
+  // the duration of this socket's lifetime.
+  UdpSocket(TaskRunner* task_runner, Client* client);
+
+  // Client to use for callbacks.
+  // NOTE: client_ can be nullptr if the user does not want any callbacks (for
+  // example, in the send-only case).
+  Client* const client_;
+
+  // Task runner to use for queuing client_ callbacks.
+  TaskRunner* const task_runner_;
 
  private:
   // This callback allows other objects to observe the socket's destructor and
diff --git a/platform/api/udp_socket_unittest.cc b/platform/api/udp_socket_unittest.cc
index f50ab72..5c73d14 100644
--- a/platform/api/udp_socket_unittest.cc
+++ b/platform/api/udp_socket_unittest.cc
@@ -5,6 +5,9 @@
 #include "platform/api/udp_socket.h"
 
 #include "gtest/gtest.h"
+#include "platform/api/time.h"
+#include "platform/test/fake_clock.h"
+#include "platform/test/fake_task_runner.h"
 #include "platform/test/mock_udp_socket.h"
 
 namespace openscreen {
@@ -15,12 +18,20 @@
 // which will then crash the running code. This test ensures that deleting a
 // new, unmodified UDP Socket object doesn't hit this edge case.
 TEST(UdpSocketTest, TestDeletionWithoutCallbackSet) {
-  UdpSocket* socket = new MockUdpSocket(UdpSocket::Version::kV4);
+  FakeClock clock(Clock::now());
+  FakeTaskRunner task_runner(&clock);
+  MockUdpSocket::MockClient client;
+  UdpSocket* socket =
+      new MockUdpSocket(&task_runner, &client, UdpSocket::Version::kV4);
   delete socket;
 }
 
 TEST(UdpSocketTest, TestCallbackCalledOnDeletion) {
-  UdpSocket* socket = new MockUdpSocket(UdpSocket::Version::kV4);
+  FakeClock clock(Clock::now());
+  FakeTaskRunner task_runner(&clock);
+  MockUdpSocket::MockClient client;
+  UdpSocket* socket =
+      new MockUdpSocket(&task_runner, &client, UdpSocket::Version::kV4);
   int call_count = 0;
   std::function<void(UdpSocket*)> callback = [&call_count](UdpSocket* socket) {
     call_count++;
@@ -38,8 +49,11 @@
 // auto-assigned socket name (i.e., the local endpoint's port will not be zero).
 TEST(UdpSocketTest, ResolvesLocalEndpoint_IPv4) {
   const uint8_t kIpV4AddrAny[4] = {};
-  ErrorOr<UdpSocketUniquePtr> create_result =
-      UdpSocket::Create(IPEndpoint{IPAddress(kIpV4AddrAny), 0});
+  FakeClock clock(Clock::now());
+  FakeTaskRunner task_runner(&clock);
+  MockUdpSocket::MockClient client;
+  ErrorOr<UdpSocketUniquePtr> create_result = UdpSocket::Create(
+      &task_runner, &client, IPEndpoint{IPAddress(kIpV4AddrAny), 0});
   ASSERT_TRUE(create_result) << create_result.error();
   const auto socket = create_result.MoveValue();
   const Error bind_result = socket->Bind();
@@ -53,8 +67,11 @@
 // auto-assigned socket name (i.e., the local endpoint's port will not be zero).
 TEST(UdpSocketTest, ResolvesLocalEndpoint_IPv6) {
   const uint8_t kIpV6AddrAny[16] = {};
-  ErrorOr<UdpSocketUniquePtr> create_result =
-      UdpSocket::Create(IPEndpoint{IPAddress(kIpV6AddrAny), 0});
+  FakeClock clock(Clock::now());
+  FakeTaskRunner task_runner(&clock);
+  MockUdpSocket::MockClient client;
+  ErrorOr<UdpSocketUniquePtr> create_result = UdpSocket::Create(
+      &task_runner, &client, IPEndpoint{IPAddress(kIpV6AddrAny), 0});
   ASSERT_TRUE(create_result) << create_result.error();
   const auto socket = create_result.MoveValue();
   const Error bind_result = socket->Bind();