Untag and stop watch on network sockets before closing them.

This patch untags the network sockets tagged with qtaguid_tagSocket()
before closing them, releasing the socket from the qtaguid kernel
module. Without this patch, the socket won't be actually closed.

This patch also removes the socket watch right before closing it, which
forces the epoll to stop watching. Closing the file descriptor would
also stop the watch if no other copy of the socket is kept open.
Explicitly stoping the watch is safer.

Bug: 36264049
Test: Ran update_engine under strace; the /proc/net/xt_qtaguid/ctrl
is called and the socket object destroyed and removed from the epoll.

Change-Id: I75c8befe8929e8e60c9534e0e4072f63b2fab1f4
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index 9dcd654..9d9f58b 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -68,6 +68,32 @@
 
 }  // namespace
 
+// static
+int LibcurlHttpFetcher::LibcurlCloseSocketCallback(void* clientp,
+                                                   curl_socket_t item) {
+#ifdef __ANDROID__
+  qtaguid_untagSocket(item);
+#endif  // __ANDROID__
+  LibcurlHttpFetcher* fetcher = static_cast<LibcurlHttpFetcher*>(clientp);
+  // Stop watching the socket before closing it.
+  for (size_t t = 0; t < arraysize(fetcher->fd_task_maps_); ++t) {
+    const auto fd_task_pair = fetcher->fd_task_maps_[t].find(item);
+    if (fd_task_pair != fetcher->fd_task_maps_[t].end()) {
+      if (!MessageLoop::current()->CancelTask(fd_task_pair->second)) {
+        LOG(WARNING) << "Error canceling the watch task "
+                     << fd_task_pair->second << " for "
+                     << (t ? "writing" : "reading") << " the fd " << item;
+      }
+      fetcher->fd_task_maps_[t].erase(item);
+    }
+  }
+
+  // Documentation for this callback says to return 0 on success or 1 on error.
+  if (!IGNORE_EINTR(close(item)))
+    return 0;
+  return 1;
+}
+
 LibcurlHttpFetcher::LibcurlHttpFetcher(ProxyResolver* proxy_resolver,
                                        HardwareInterface* hardware)
     : HttpFetcher(proxy_resolver), hardware_(hardware) {
@@ -126,9 +152,12 @@
   CHECK(curl_handle_);
   ignore_failure_ = false;
 
-  // Tag the socket for network usage stats.
+  // Tag and untag the socket for network usage stats.
   curl_easy_setopt(
       curl_handle_, CURLOPT_SOCKOPTFUNCTION, LibcurlSockoptCallback);
+  curl_easy_setopt(
+      curl_handle_, CURLOPT_CLOSESOCKETFUNCTION, LibcurlCloseSocketCallback);
+  curl_easy_setopt(curl_handle_, CURLOPT_CLOSESOCKETDATA, this);
 
   CHECK(HasProxy());
   bool is_direct = (GetCurrentProxy() == kNoProxy);