face: Moving idle UdpFace closing logic to UdpFace class

Change-Id: Ia7310fa18681f17d93c8214ce744da909fea7022
Refs: #1686
diff --git a/daemon/face/udp-face.cpp b/daemon/face/udp-face.cpp
index d2d7823..d4fe8cf 100644
--- a/daemon/face/udp-face.cpp
+++ b/daemon/face/udp-face.cpp
@@ -33,10 +33,14 @@
 
 NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(DatagramFace, UdpFace::protocol, "UdpFace");
 
-UdpFace::UdpFace(const shared_ptr<UdpFace::protocol::socket>& socket, bool isOnDemand)
+UdpFace::UdpFace(const shared_ptr<UdpFace::protocol::socket>& socket,
+                 bool isOnDemand,
+                 const time::seconds& idleTimeout)
   : DatagramFace<protocol>(FaceUri(socket->remote_endpoint()),
                            FaceUri(socket->local_endpoint()),
                            socket, isOnDemand)
+  , m_idleTimeout(idleTimeout)
+  , m_lastIdleCheck(time::steady_clock::now())
 {
 #ifdef __linux__
   //
@@ -60,6 +64,16 @@
                    << "] Failed to disable path MTU discovery");
     }
 #endif
+
+  if (isOnDemand && m_idleTimeout > time::seconds::zero()) {
+    m_closeIfIdleEvent = scheduler::schedule(m_idleTimeout,
+                                             bind(&UdpFace::closeIfIdle, this));
+  }
+}
+
+UdpFace::~UdpFace()
+{
+  scheduler::cancel(m_closeIfIdleEvent);
 }
 
 void
@@ -84,4 +98,49 @@
   receiveDatagram(buffer, nBytesReceived, error);
 }
 
+ndn::nfd::FaceStatus
+UdpFace::getFaceStatus() const
+{
+  ndn::nfd::FaceStatus status = Face::getFaceStatus();
+  if (isOnDemand()) {
+    time::milliseconds left = time::duration_cast<time::milliseconds>(
+      time::steady_clock::now() - m_lastIdleCheck);
+
+    if (hasBeenUsedRecently()) {
+      status.setExpirationPeriod(left + m_idleTimeout);
+    }
+    else {
+      status.setExpirationPeriod(left);
+    }
+  }
+  return status;
+}
+
+void
+UdpFace::closeIfIdle()
+{
+  // Face can be switched from on-demand to non-on-demand mode
+  // (non-on-demand -> on-demand transition is not allowed)
+  if (isOnDemand()) {
+    if (!hasBeenUsedRecently()) {
+      // face has been idle since the last time closeIfIdle
+      // has been called. Going to close it
+      NFD_LOG_DEBUG("Found idle face id: " << getId());
+
+      NFD_LOG_INFO("[id:" << this->getId()
+                   << ",endpoint:" << m_socket->local_endpoint()
+                   << "] Idle for more than " << m_idleTimeout << ", closing");
+      close();
+    }
+    else {
+      resetRecentUsage();
+
+      m_lastIdleCheck = time::steady_clock::now();
+      m_closeIfIdleEvent = scheduler::schedule(m_idleTimeout,
+                                               bind(&UdpFace::closeIfIdle, this));
+    }
+  }
+  // else do nothing and do not reschedule the event
+}
+
 } // namespace nfd