[vm] Support ListInterfaces for android

Support ListInterfaces() for android. If android api level during build time
is greater than or equal to 24, it will use getifaddrs() from android ndk.
Since it didn't support until 24. Otherwise, use system call to query the
network interfaces.

Bug: https://github.com/dart-lang/sdk/issues/36449
Change-Id: I615b31b804b751382b775ad9c6af4411b123d5ed
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/98949
Reviewed-by: Siva Annamalai <asiva@google.com>
Reviewed-by: Zach Anderson <zra@google.com>
Commit-Queue: Zichang Guo <zichangguo@google.com>
diff --git a/runtime/bin/ifaddrs-android.cc b/runtime/bin/ifaddrs-android.cc
new file mode 100644
index 0000000..bd4c5f5
--- /dev/null
+++ b/runtime/bin/ifaddrs-android.cc
@@ -0,0 +1,232 @@
+// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#if defined(ANDROID) && __ANDROID_API__ < 24
+
+#include "bin/ifaddrs-android.h"
+
+#include <errno.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include "bin/fdutils.h"
+#include "platform/signal_blocker.h"
+
+namespace dart {
+namespace bin {
+
+const int kMaxReadSize = 2048;
+
+static bool SetIfName(struct ifaddrs* ifaddr, int interface) {
+  char buf[IFNAMSIZ] = {0};
+  char* name = if_indextoname(interface, buf);
+  if (name == NULL) {
+    return false;
+  }
+  ifaddr->ifa_name = new char[strlen(name) + 1];
+  strncpy(ifaddr->ifa_name, name, strlen(name) + 1);
+  return true;
+}
+
+static void SetFlags(struct ifaddrs* ifaddr, int flag) {
+  ifaddr->ifa_flags = flag;
+}
+
+static void SetAddresses(struct ifaddrs* ifaddr,
+                         int family,
+                         int index,
+                         void* data,
+                         size_t len) {
+  if (family == AF_INET6) {
+    sockaddr_in6* socketaddr = new sockaddr_in6;
+    socketaddr->sin6_family = AF_INET6;
+    socketaddr->sin6_scope_id = index;
+    memmove(&socketaddr->sin6_addr, data, len);
+    ifaddr->ifa_addr = reinterpret_cast<sockaddr*>(socketaddr);
+    return;
+  }
+  ASSERT(family == AF_INET);
+  sockaddr_in* socketaddr = new sockaddr_in;
+  socketaddr->sin_family = AF_INET;
+  memmove(&socketaddr->sin_addr, data, len);
+  ifaddr->ifa_addr = reinterpret_cast<sockaddr*>(socketaddr);
+}
+
+static void SetNetmask(struct ifaddrs* ifaddr, int family) {
+  if (family == AF_INET6) {
+    sockaddr_in6* mask = new sockaddr_in6;
+    mask->sin6_family = AF_INET6;
+    memset(&mask->sin6_addr, 0, sizeof(mask->sin6_addr));
+    ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask);
+    return;
+  }
+  ASSERT(family == AF_INET);
+  sockaddr_in* mask = new sockaddr_in;
+  mask->sin_family = AF_INET;
+  memset(&mask->sin_addr, 0, sizeof(mask->sin_addr));
+  ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask);
+}
+
+static bool SetIfAddrsFromAddrMsg(struct ifaddrs* ifaddr,
+                                  ifaddrmsg* msg,
+                                  void* bytes,
+                                  size_t len,
+                                  nlmsghdr* header) {
+  SetAddresses(ifaddr, msg->ifa_family, msg->ifa_index, bytes, len);
+  SetNetmask(ifaddr, msg->ifa_family);
+  SetFlags(ifaddr, msg->ifa_flags);
+  return SetIfName(ifaddr, msg->ifa_index);
+}
+
+static bool SetIfAddrsFromInfoMsg(struct ifaddrs* ifaddr,
+                                  ifinfomsg* ifi,
+                                  void* bytes,
+                                  size_t len,
+                                  nlmsghdr* header) {
+  SetAddresses(ifaddr, ifi->ifi_family, ifi->ifi_index, bytes, len);
+  SetNetmask(ifaddr, ifi->ifi_family);
+  SetFlags(ifaddr, ifi->ifi_flags);
+  return SetIfName(ifaddr, ifi->ifi_index);
+}
+
+static int SendRequest() {
+  int file_descriptor =
+      NO_RETRY_EXPECTED(socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE));
+  if (file_descriptor < 0) {
+    return -1;
+  }
+  nlmsghdr header;
+  memset(&header, 0, sizeof(header));
+  header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
+  header.nlmsg_type = RTM_GETADDR;
+  header.nlmsg_len = NLMSG_LENGTH(sizeof(ifaddrmsg));
+  ssize_t num =
+      TEMP_FAILURE_RETRY(send(file_descriptor, &header, header.nlmsg_len, 0));
+  if (static_cast<size_t>(num) != header.nlmsg_len) {
+    FDUtils::SaveErrorAndClose(file_descriptor);
+    return -1;
+  }
+  return file_descriptor;
+}
+
+static int FailAndExit(int fd, ifaddrs* head) {
+  FDUtils::SaveErrorAndClose(fd);
+  freeifaddrs(head);
+  return -1;
+}
+
+int getifaddrs(struct ifaddrs** result) {
+  int file_descriptor = SendRequest();
+  if (file_descriptor < 0) {
+    return -1;
+  }
+  struct ifaddrs* head = NULL;
+  struct ifaddrs* cur = NULL;
+  char buf[kMaxReadSize];
+  ssize_t amount_read;
+  while (true) {
+    amount_read =
+        TEMP_FAILURE_RETRY(recv(file_descriptor, &buf, kMaxReadSize, 0));
+    if (amount_read <= 0) {
+      break;
+    }
+    nlmsghdr* header = reinterpret_cast<nlmsghdr*>(&buf[0]);
+    size_t header_size = static_cast<size_t>(amount_read);
+    for (; NLMSG_OK(header, header_size);
+         header = NLMSG_NEXT(header, header_size)) {
+      switch (header->nlmsg_type) {
+        case RTM_NEWADDR: {
+          ifaddrmsg* address_msg =
+              reinterpret_cast<ifaddrmsg*>(NLMSG_DATA(header));
+          ssize_t payload_len = IFA_PAYLOAD(header);
+          for (rtattr* rta = IFA_RTA(address_msg); RTA_OK(rta, payload_len);
+               rta = RTA_NEXT(rta, payload_len)) {
+            if (rta->rta_type != IFA_ADDRESS) {
+              continue;
+            }
+            int family = address_msg->ifa_family;
+            if (family != AF_INET && family != AF_INET6) {
+              continue;
+            }
+            ifaddrs* next = new ifaddrs;
+            memset(next, 0, sizeof(*next));
+            if (cur != NULL) {
+              cur->ifa_next = next;
+            } else {
+              head = next;
+            }
+            if (!SetIfAddrsFromAddrMsg(next, address_msg, RTA_DATA(rta),
+                                       RTA_PAYLOAD(rta), header)) {
+              return FailAndExit(file_descriptor, head);
+            }
+            cur = next;
+          }
+          break;
+        }
+        case RTM_NEWLINK: {
+          ifinfomsg* ifi = reinterpret_cast<ifinfomsg*>(NLMSG_DATA(header));
+          ssize_t payload_len = IFLA_PAYLOAD(header);
+          for (rtattr* rta = IFLA_RTA(ifi); RTA_OK(rta, payload_len);
+               rta = RTA_NEXT(rta, payload_len)) {
+            if (rta->rta_type != IFA_ADDRESS) {
+              continue;
+            }
+            int family = ifi->ifi_family;
+            if (family != AF_INET && family != AF_INET6) {
+              continue;
+            }
+            ifaddrs* next = new ifaddrs;
+            memset(next, 0, sizeof(*next));
+            if (cur != NULL) {
+              cur->ifa_next = next;
+            } else {
+              head = next;
+            }
+            if (!SetIfAddrsFromInfoMsg(next, ifi, RTA_DATA(rta),
+                                       RTA_PAYLOAD(rta), header)) {
+              return FailAndExit(file_descriptor, head);
+            }
+            cur = next;
+          }
+          break;
+        }
+        case NLMSG_DONE:
+          *result = head;
+          FDUtils::SaveErrorAndClose(file_descriptor);
+          return 0;
+        case NLMSG_ERROR:
+          return FailAndExit(file_descriptor, head);
+      }
+    }
+  }
+  return FailAndExit(file_descriptor, head);
+}
+
+void freeifaddrs(struct ifaddrs* addrs) {
+  int err = errno;
+  struct ifaddrs* previous = NULL;
+  while (addrs != NULL) {
+    delete[] addrs->ifa_name;
+    delete addrs->ifa_addr;
+    delete addrs->ifa_netmask;
+    previous = addrs;
+    addrs = addrs->ifa_next;
+    delete previous;
+  }
+  errno = err;
+}
+
+}  // namespace bin
+}  // namespace dart
+
+#endif  // defined(ANDROID) && __ANDROID_API__ < 24
diff --git a/runtime/bin/ifaddrs-android.h b/runtime/bin/ifaddrs-android.h
new file mode 100644
index 0000000..ec0ecd2
--- /dev/null
+++ b/runtime/bin/ifaddrs-android.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#ifndef RUNTIME_BIN_IFADDRS_ANDROID_H_
+#define RUNTIME_BIN_IFADDRS_ANDROID_H_
+
+#if defined(ANDROID) && __ANDROID_API__ < 24
+#include <sys/socket.h>
+
+namespace dart {
+namespace bin {
+
+struct ifaddrs {
+  struct ifaddrs* ifa_next;
+  char* ifa_name;
+  unsigned int ifa_flags;
+  struct sockaddr* ifa_addr;
+  struct sockaddr* ifa_netmask;
+  union {
+    struct sockaddr* ifu_broadaddr;
+    struct sockaddr* ifu_dstaddr;
+  } ifa_ifu;
+  void* ifa_data;
+};
+
+void freeifaddrs(struct ifaddrs* __ptr);
+int getifaddrs(struct ifaddrs** __list_ptr);
+
+}  // namespace bin
+}  // namespace dart
+
+#endif  // defined(ANDROID) && __ANDROID_API__ < 24
+#endif  // RUNTIME_BIN_IFADDRS_ANDROID_H_
diff --git a/runtime/bin/io_impl_sources.gni b/runtime/bin/io_impl_sources.gni
index a476664..e40832d 100644
--- a/runtime/bin/io_impl_sources.gni
+++ b/runtime/bin/io_impl_sources.gni
@@ -29,6 +29,8 @@
   "file_system_watcher_win.cc",
   "filter.cc",
   "filter.h",
+  "ifaddrs-android.cc",
+  "ifaddrs-android.h",
   "io_service.cc",
   "io_service.h",
   "io_service_no_ssl.cc",
diff --git a/runtime/bin/socket_base_android.cc b/runtime/bin/socket_base_android.cc
index 084ab2f..c72c276 100644
--- a/runtime/bin/socket_base_android.cc
+++ b/runtime/bin/socket_base_android.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
@@ -7,16 +7,19 @@
 
 #include "bin/socket_base.h"
 
-#include <errno.h>        // NOLINT
-#include <netinet/tcp.h>  // NOLINT
-#include <stdio.h>        // NOLINT
-#include <stdlib.h>       // NOLINT
-#include <string.h>       // NOLINT
-#include <sys/stat.h>     // NOLINT
-#include <unistd.h>       // NOLINT
+#include <errno.h>
+#include <ifaddrs.h>
+#include <net/if.h>
+#include <netinet/tcp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 #include "bin/fdutils.h"
 #include "bin/file.h"
+#include "bin/ifaddrs-android.h"
 #include "bin/socket_base_android.h"
 #include "platform/signal_blocker.h"
 
@@ -241,21 +244,57 @@
   return (result == 1);
 }
 
+static bool ShouldIncludeIfaAddrs(struct ifaddrs* ifa, int lookup_family) {
+  if (ifa->ifa_addr == NULL) {
+    // OpenVPN's virtual device tun0.
+    return false;
+  }
+  int family = ifa->ifa_addr->sa_family;
+  return ((lookup_family == family) ||
+          (((lookup_family == AF_UNSPEC) &&
+            ((family == AF_INET) || (family == AF_INET6)))));
+}
+
 bool SocketBase::ListInterfacesSupported() {
-  return false;
+  return true;
 }
 
 AddressList<InterfaceSocketAddress>* SocketBase::ListInterfaces(
     int type,
     OSError** os_error) {
-  // The ifaddrs.h header is not provided on Android.  An Android
-  // implementation would have to use IOCTL or netlink.
-  ASSERT(*os_error == NULL);
-  *os_error = new OSError(-1,
-                          "Listing interfaces is not supported "
-                          "on this platform",
-                          OSError::kSystem);
-  return NULL;
+  struct ifaddrs* ifaddr;
+
+  int status = NO_RETRY_EXPECTED(getifaddrs(&ifaddr));
+  if (status != 0) {
+    ASSERT(*os_error == NULL);
+    *os_error =
+        new OSError(status, gai_strerror(status), OSError::kGetAddressInfo);
+    return NULL;
+  }
+
+  int lookup_family = SocketAddress::FromType(type);
+
+  intptr_t count = 0;
+  for (struct ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+    if (ShouldIncludeIfaAddrs(ifa, lookup_family)) {
+      count++;
+    }
+  }
+
+  AddressList<InterfaceSocketAddress>* addresses =
+      new AddressList<InterfaceSocketAddress>(count);
+  int i = 0;
+  for (struct ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+    if (ShouldIncludeIfaAddrs(ifa, lookup_family)) {
+      char* ifa_name = DartUtils::ScopedCopyCString(ifa->ifa_name);
+      addresses->SetAt(
+          i, new InterfaceSocketAddress(ifa->ifa_addr, ifa_name,
+                                        if_nametoindex(ifa->ifa_name)));
+      i++;
+    }
+  }
+  freeifaddrs(ifaddr);
+  return addresses;
 }
 
 void SocketBase::Close(intptr_t fd) {