Rewrite InetAddress' DNS cache.

Replace harmony's two 5-element linked lists with a single 512-element
LinkedHashMap. Greatly reduce the time we spend under locks (and no network
I/O is done under a lock any more!). Take advantage of various properties
of how the cache is used to avoid having to do much explicit work to handle
expiry.

I've also optimized the usual no-SecurityManager/no custom system properties
configuring cache TTLs case (without making the slow path much slower than it
already was).

I've also updated the native method names to correspond to the C functions
they're really wrapping (rather than completely different IPv4-only ones
they probably used to wrap long ago).

I've also improved the InetAddress documentation.

Bug: 2320435
diff --git a/libcore/luni/src/main/java/java/net/AddressCache.java b/libcore/luni/src/main/java/java/net/AddressCache.java
new file mode 100644
index 0000000..8388b48
--- /dev/null
+++ b/libcore/luni/src/main/java/java/net/AddressCache.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.net;
+
+import java.security.AccessController;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.harmony.luni.util.PriviAction;
+
+/**
+ * Implements caching for {@code InetAddress}. We use a unified cache for both positive and negative
+ * cache entries.
+ */
+class AddressCache {
+    /**
+     * When the cache contains more entries than this, we start dropping the oldest ones.
+     * This should be a power of two to avoid wasted space in our custom map.
+     */
+    private static final int MAX_ENTRIES = 512;
+    
+    // This isn't used by our HashMap implementation, but the API demands it.
+    private static final float DEFAULT_LOAD_FACTOR = .75F;
+    
+    // Default time-to-live for positive cache entries. 600 seconds (10 minutes).
+    private static final long DEFAULT_POSITIVE_TTL_NANOS = 600 * 1000000000L;
+    // Default time-to-live for negative cache entries. 10 seconds.
+    private static final long DEFAULT_NEGATIVE_TTL_NANOS = 10 * 1000000000L;
+    
+    // Failed lookups are represented in the cache my mappings to this empty array.
+    private static final InetAddress[] NO_ADDRESSES = new InetAddress[0];
+    
+    // The actual cache.
+    private final Map<String, AddressCacheEntry> map;
+    
+    class AddressCacheEntry {
+        // The addresses. May be the empty array for a negative cache entry.
+        InetAddress[] addresses;
+        
+        /**
+         * The absolute expiry time in nanoseconds. Nanoseconds from System.nanoTime is ideal
+         * because -- unlike System.currentTimeMillis -- it can never go backwards.
+         * 
+         * Unless we need to cope with DNS TTLs of 292 years, we don't need to worry about overflow.
+         */
+        long expiryNanos;
+        
+        AddressCacheEntry(InetAddress[] addresses, long expiryNanos) {
+            this.addresses = addresses;
+            this.expiryNanos = expiryNanos;
+        }
+    }
+    
+    public AddressCache() {
+        // We pass 'true' so removeEldestEntry removes the least-recently accessed entry, rather
+        // than the least-recently inserted.
+        map = new LinkedHashMap<String, AddressCacheEntry>(0, DEFAULT_LOAD_FACTOR, true) {
+            @Override protected boolean removeEldestEntry(Entry<String, AddressCacheEntry> eldest) {
+                // By the time this method is called, the new entry has already been inserted and
+                // the map will have grown to accommodate it. Using == lets us prevent resizing.
+                return size() == MAX_ENTRIES;
+            }
+        };
+    }
+    
+    /**
+     * Returns the cached addresses associated with 'hostname'. Returns null if nothing is known
+     * about 'hostname'. Returns an empty array if 'hostname' is known not to exist.
+     */
+    public InetAddress[] get(String hostname) {
+        AddressCacheEntry entry;
+        synchronized (map) {
+            entry = map.get(hostname);
+        }
+        // Do we have a valid cache entry?
+        if (entry != null && entry.expiryNanos >= System.nanoTime()) {
+            return entry.addresses;
+        }
+        // Either we didn't find anything, or it had expired.
+        // No need to remove expired entries: the caller will provide a replacement shortly.
+        return null;
+    }
+    
+    /**
+     * Associates the given 'addresses' with 'hostname'. The association will expire after a
+     * certain length of time.
+     */
+    public void put(String hostname, InetAddress[] addresses) {
+        // Calculate the expiry time.
+        boolean isPositive = (addresses.length > 0);
+        String propertyName = isPositive ? "networkaddress.cache.ttl" : "networkaddress.cache.negative.ttl";
+        long defaultTtlNanos = isPositive ? DEFAULT_POSITIVE_TTL_NANOS : DEFAULT_NEGATIVE_TTL_NANOS;
+        // Fast-path the default case...
+        long expiryNanos = System.nanoTime() + defaultTtlNanos;
+        if (System.getSecurityManager() != null || System.getProperty(propertyName, null) != null) {
+            // ...and let those using a SecurityManager or custom properties pay full price.
+            expiryNanos = customTtl(propertyName, defaultTtlNanos);
+            if (expiryNanos == Long.MIN_VALUE) {
+                return;
+            }
+        }
+        // Update the cache.
+        synchronized (map) {
+            map.put(hostname, new AddressCacheEntry(addresses, expiryNanos));
+        }
+    }
+    
+    /**
+     * Records that 'hostname' is known not to have any associated addresses. (I.e. insert a
+     * negative cache entry.)
+     */
+    public void putUnknownHost(String hostname) {
+        put(hostname, NO_ADDRESSES);
+    }
+    
+    private long customTtl(String propertyName, long defaultTtlNanos) {
+        String ttlString = AccessController.doPrivileged(new PriviAction<String>(propertyName, null));
+        if (ttlString == null) {
+            return System.nanoTime() + defaultTtlNanos;
+        }
+        try {
+            long ttlS = Long.parseLong(ttlString);
+            // For the system properties, -1 means "cache forever" and 0 means "don't cache".
+            if (ttlS == -1) {
+                return Long.MAX_VALUE;
+            } else if (ttlS == 0) {
+                return Long.MIN_VALUE;
+            } else {
+                return System.nanoTime() + ttlS * 1000000000L;
+            }
+        } catch (NumberFormatException ex) {
+            return System.nanoTime() + defaultTtlNanos;
+        }
+    }
+}
diff --git a/libcore/luni/src/main/java/java/net/InetAddress.java b/libcore/luni/src/main/java/java/net/InetAddress.java
index 849cfba..75d27ca 100644
--- a/libcore/luni/src/main/java/java/net/InetAddress.java
+++ b/libcore/luni/src/main/java/java/net/InetAddress.java
@@ -39,12 +39,32 @@
 import org.apache.harmony.luni.util.PriviAction;
 
 /**
- * The Internet Protocol (IP) address representation class. This class
- * encapsulates an IP address and provides name and reverse name resolution
- * functions. The address is stored in network order, but as a signed (rather
- * than unsigned) integer.
+ * An Internet Protocol (IP) address. This can be either an IPv4 address or an IPv6 address, and
+ * in practice you'll have an instance of either {@code Inet4Address} or {@code Inet6Address} (this
+ * class cannot be instantiated directly). Most code does not need to distinguish between the two
+ * families, and should use {@code InetAddress}.
+ * <p>
+ * An {@code InetAddress} may have a hostname (accessible via {@code getHostName}), but may not,
+ * depending on how the {@code InetAddress} was created.
+ * <p>
+ * On Android, addresses are cached for 600 seconds (10 minutes) by default. Failed lookups are
+ * cached for 10 seconds. The underlying C library or OS may cache for longer, but you can control
+ * the Java-level caching with the usual {@code "networkaddress.cache.ttl"} and
+ * {@code "networkaddress.cache.negative.ttl"} system properties. These are parsed as integer
+ * numbers of seconds, where the special value 0 means "don't cache" and -1 means "cache forever".
+ * <p>
+ * Note also that on Android &ndash; unlike the RI &ndash; the cache is not unbounded. The current
+ * implementation caches around 512 entries, removed on a least-recently-used basis.
+ * (Obviously, you should not rely on these details.)
+ * 
+ * @see Inet4Address
+ * @see Inet6Address
  */
-public class InetAddress extends Object implements Serializable {
+public class InetAddress implements Serializable {
+    // BEGIN android-added: better DNS caching.
+    // Our Java-side DNS cache.
+    private static final AddressCache addressCache = new AddressCache();
+    // END android-added
 
     private final static INetworkSystem NETIMPL = Platform.getNetworkSystem();
 
@@ -52,13 +72,6 @@
 
     private static final long serialVersionUID = 3286316764910316507L;
 
-    // BEGIN android-added
-    /**
-     * default time-to-live for DNS cache entries; 600 seconds == 10 minutes
-     */
-    private static final String DEFAULT_NETADDR_CACHE_TTL_SECS = "600";
-    // END android-added
-
     String hostName;
 
     private static class WaitReachable {
@@ -410,7 +423,7 @@
      *             if the address lookup fails.
      */
     public static InetAddress getLocalHost() throws UnknownHostException {
-        String host = getHostNameImpl();
+        String host = gethostname();
         SecurityManager security = System.getSecurityManager();
         try {
             if (security != null) {
@@ -421,6 +434,7 @@
         }
         return lookupHostByName(host)[0];
     }
+    private static native String gethostname();
 
     /**
      * Gets the hashcode of the represented IP address.
@@ -447,96 +461,42 @@
     }
     // END android-changed
 
-
     /**
-     * Resolves a hostname to its IP addresses using a cache for faster lookups.
+     * Resolves a hostname to its IP addresses using a cache.
      *
      * @param host the hostname to resolve.
      * @return the IP addresses of the host.
      */
-    static synchronized InetAddress[] lookupHostByName(String host)
-            throws UnknownHostException {
-        int ttl = -1;
-
-        // BEGIN android-changed
-        String ttlValue = AccessController
-                .doPrivileged(new PriviAction<String>(
-                        "networkaddress.cache.ttl", DEFAULT_NETADDR_CACHE_TTL_SECS)); //$NON-NLS-1$
-        // END android-changed
+    // BEGIN android-changed
+    private static InetAddress[] lookupHostByName(String host) throws UnknownHostException {
+        // Do we have a result cached?
+        InetAddress[] cachedResult = addressCache.get(host);
+        if (cachedResult != null) {
+            if (cachedResult.length > 0) {
+                // A cached positive result.
+                return cachedResult;
+            } else {
+                // A cached negative result.
+                throw new UnknownHostException(host);
+            }
+        }
         try {
-            if (ttlValue != null) {
-                ttl = Integer.decode(ttlValue).intValue();
-            }
-        } catch (NumberFormatException e) {
-            // Ignored
-        }
-        CacheElement element = null;
-        // BEGIN android-changed
-        if (ttl == 0) {
-            Cache.clear();
-        } else {
-            element = Cache.get(host);
-            if (element != null && ttl > 0) {
-                long delta = System.nanoTime() - element.nanoTimeAdded;
-                if (delta > secondsToNanos(ttl)) {
-                    element = null;
-                }
-            }
-        }
-        if (element != null) {
-            return element.addresses();
-        }
-        // END android-changed
-
-        // TODO Clean up NegativeCache; there's no need to maintain the failure message
-
-        // now try the negative cache
-        String failedMessage = NegativeCache.getFailedMessage(host);
-        if (failedMessage != null) {
+            InetAddress[] addresses = bytesToInetAddresses(getaddrinfo(host), host);
+            addressCache.put(host, addresses);
+            return addresses;
+        } catch (UnknownHostException e) {
+            addressCache.putUnknownHost(host);
             throw new UnknownHostException(host);
         }
-
-        // BEGIN android-changed
-        // TODO: Avoid doing I/O from a static synchronized lock.
-        byte[][] rawAddresses;
-        try {
-            rawAddresses = getallbyname(host, Socket.preferIPv4Stack());
-        } catch (UnknownHostException e) {
-            // put the entry in the negative cache
-            NegativeCache.put(host, e.getMessage());
-            // use host for message to match RI, save the cause for giggles
-            throw (UnknownHostException)new UnknownHostException(host).initCause(e);
-        }
-
-        InetAddress[] addresses = bytesToInetAddresses(rawAddresses, host);
-
-        Cache.add(host, addresses);
-        return addresses;
-        // END android-changed
     }
-
-    // BEGIN android-added
-    /**
-     * Multiplies value by 1 billion.
-     */
-    private static long secondsToNanos(int ttl) {
-        return (long) ttl * 1000000000;
-    }
-    // END android-added
+    private static native byte[][] getaddrinfo(String name) throws UnknownHostException;
+    // END android-changed
 
     // BEGIN android-deleted
     // static native InetAddress[] getAliasesByNameImpl(String name)
     //     throws UnknownHostException;
     // END android-deleted
 
-    // BEGIN android-added
-    /**
-     * Resolves a host name to its IP addresses. Thread safe.
-     */
-    private static native byte[][] getallbyname(String name,
-            boolean preferIPv4Stack) throws UnknownHostException;
-    // END android-added
-
     /**
      * Query the IP stack for the host address. The host is in address form.
      *
@@ -551,9 +511,9 @@
     static InetAddress getHostByAddrImpl(byte[] addr)
             throws UnknownHostException {
         if (addr.length == 4) {
-            return new Inet4Address(addr, gethostbyaddr(addr));
+            return new Inet4Address(addr, getnameinfo(addr));
         } else if (addr.length == 16) {
-            return new Inet6Address(addr, gethostbyaddr(addr));
+            return new Inet6Address(addr, getnameinfo(addr));
         } else {
             throw new UnknownHostException(Msg.getString(
                     "K0339")); //$NON-NLS-1$
@@ -563,7 +523,7 @@
     /**
      * Resolves an IP address to a hostname. Thread safe.
      */
-    private static native String gethostbyaddr(byte[] addr);
+    private static native String getnameinfo(byte[] addr);
     // END android-changed
 
     // BEGIN android-removed
@@ -604,20 +564,6 @@
     //         boolean preferIPv6Address) throws UnknownHostException;
     // END android-removed
 
-    /**
-     * Gets the host name of the system.
-     *
-     * @return String the system hostname
-     */
-    // BEGIN android-changed
-    static String getHostNameImpl() {
-        // TODO Mapped Harmony to Android native. Get rid of indirection later.
-
-        return gethostname();
-    }
-    static native String gethostname();
-    // END android-changed
-
     static String getHostNameInternal(String host, boolean isCheck) throws UnknownHostException {
         if (host == null || 0 == host.length()) {
             return Inet4Address.LOOPBACK.getHostAddress();
@@ -645,98 +591,6 @@
         return (hostName == null ? "" : hostName) + "/" + getHostAddress(); //$NON-NLS-1$ //$NON-NLS-2$
     }
 
-    // BEGIN android-changed
-    // Partly copied from a newer version of harmony
-    static class CacheElement {
-        final long nanoTimeAdded = System.nanoTime();
-
-        CacheElement next;
-        final String hostName;
-        final InetAddress[] addresses;
-
-        CacheElement(String hostName, InetAddress[] addresses) {
-            this.addresses = addresses;
-            this.hostName = hostName;
-        }
-
-        String hostName() {
-            return hostName;
-        }
-
-        InetAddress[] addresses() {
-            return addresses;
-        }
-        // END android-changed
-    }
-
-    static class Cache {
-        private static int maxSize = 5;
-
-        private static int size = 0;
-
-        private static CacheElement head;
-
-        static synchronized void clear() {
-            size = 0;
-            head = null;
-        }
-
-        static synchronized void add(String hostName, InetAddress[] addresses) {
-            CacheElement newElement = new CacheElement(hostName, addresses);
-            if (size < maxSize) {
-                size++;
-            } else {
-                deleteTail();
-            }
-            newElement.next = head; // If the head is null, this does no harm.
-            head = newElement;
-        }
-
-        static synchronized CacheElement get(String name) {
-            CacheElement previous = null;
-            CacheElement current = head;
-            boolean notFound = true;
-            while ((null != current)
-                    && (notFound = !(name.equals(current.hostName())))) {
-                previous = current;
-                current = current.next;
-            }
-            if (notFound) {
-                return null;
-            }
-            moveToHead(current, previous);
-            return current;
-        }
-
-        private synchronized static void deleteTail() {
-            if (0 == size) {
-                return;
-            }
-            if (1 == size) {
-                head = null;
-            }
-
-            CacheElement previous = null;
-            CacheElement current = head;
-            while (null != current.next) {
-                previous = current;
-                current = current.next;
-            }
-            previous.next = null;
-        }
-
-        private synchronized static void moveToHead(CacheElement element,
-                CacheElement elementPredecessor) {
-            if (null == elementPredecessor) {
-                head = element;
-            } else {
-                elementPredecessor.next = element.next;
-                element.next = head;
-                head = element;
-            }
-        }
-    }
-
     /**
      * Returns true if the string is a host name, false if it is an IP Address.
      */
diff --git a/libcore/luni/src/main/java/java/net/NegCacheElement.java b/libcore/luni/src/main/java/java/net/NegCacheElement.java
deleted file mode 100644
index e195f31..0000000
--- a/libcore/luni/src/main/java/java/net/NegCacheElement.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package java.net;
-
-/**
- * This class is used to hold information about failed host name lookups.
- *
- * @see NegativeCache
- */
-class NegCacheElement {
-
-    // we need the time to figure out when the entry is stale
-    // BEGIN android-changed
-    final long nanoTimeAdded = System.nanoTime();
-    // END android-changed
-
-    // holds the name of the lookup failure message for this host
-    final String failedMessage;
-
-    /**
-     * Constructor used to set the hostname for the entry for which the lookup
-     * failed.
-     *
-     * @param failedMessage
-     *            name of the host for which the lookup failed.
-     */
-    NegCacheElement(String failedMessage) {
-        this.failedMessage = failedMessage;
-    }
-}
diff --git a/libcore/luni/src/main/java/java/net/NegativeCache.java b/libcore/luni/src/main/java/java/net/NegativeCache.java
deleted file mode 100644
index d6b1515..0000000
--- a/libcore/luni/src/main/java/java/net/NegativeCache.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package java.net;
-
-import java.security.AccessController;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.apache.harmony.luni.util.PriviAction;
-
-/**
- * This class is used to maintain the negative name lookup cache, which caches
- * host names which could not be resolved, as a security feature.
- *
- * @see NegCacheElement
- */
-class NegativeCache {
-   // maximum number of entries in the cache
-    private static final int MAX_NEGATIVE_ENTRIES = 5;
-
-    // the loading for the cache
-    private static final float LOAD_FACTOR = 0.75F;
-
-    private static final Map<String, NegCacheElement> negCache
-            = new LinkedHashMap<String, NegCacheElement>(
-                2 * MAX_NEGATIVE_ENTRIES, LOAD_FACTOR, true) {
-
-        /**
-         * Returns whether the eldest entry should be removed. It is removed if
-         * the size has grown beyond the maximum size allowed for the cache.
-         *
-         * @param eldest
-         *            the LRU entry, which will be deleted if we return true.
-         */
-        @Override protected boolean removeEldestEntry(
-                Entry<String, NegCacheElement> eldest) {
-            return size() > MAX_NEGATIVE_ENTRIES;
-        }
-    };
-
-    /** Ensures non-instantiability */
-    private NegativeCache() {
-    }
-
-    /**
-     * Adds the host name and the corresponding name lookup fail message to the
-     * cache.
-     *
-     * @param hostName
-     *            the name of the host for which the lookup failed.
-     * @param failedMessage
-     *            the message returned when the lookup fails.
-     */
-    static synchronized void put(String hostName, String failedMessage) {
-        negCache.put(hostName, new NegCacheElement(failedMessage));
-    }
-
-    /**
-     * Returns the message of the negative cache if the entry has not yet
-     * expired.
-     *
-     * @param hostName
-     *            the name of the host for which we look up the entry.
-     * @return the message which was returned when the host lookup failed if the
-     *         entry has not yet expired.
-     */
-    static synchronized String getFailedMessage(String hostName) {
-        NegCacheElement element = negCache.get(hostName);
-        if (element != null) {
-            // check if element is still valid
-            String ttlValue = AccessController
-                    .doPrivileged(new PriviAction<String>(
-                            "networkaddress.cache.negative.ttl")); //$NON-NLS-1$
-            int ttl = 10;
-            try {
-                if (ttlValue != null) {
-                    ttl = Integer.decode(ttlValue);
-                }
-            } catch (NumberFormatException e) {
-                // If exception, go with ttl == 10
-            }
-            if (ttl == 0) {
-                negCache.clear();
-                element = null;
-            } else if (ttl != -1) {
-                // BEGIN android-changed
-                long delta = System.nanoTime() - element.nanoTimeAdded;
-                if (delta > secondsToNanos(ttl)) {
-                    // remove the element from the cache and return null
-                    negCache.remove(hostName);
-                    element = null;
-                }
-                // END android-changed
-            }
-        }
-        if (element != null) {
-            return element.failedMessage;
-        }
-        return null;
-    }
-
-    // BEGIN android-added
-    /**
-     * Multiplies value by 1 billion.
-     */
-    private static int secondsToNanos(int ttl) {
-        return ttl * 1000000000;
-    }
-    // END android-added
-}
diff --git a/libcore/luni/src/main/native/java_net_InetAddress.cpp b/libcore/luni/src/main/native/java_net_InetAddress.cpp
index 2af493c..63053a8 100644
--- a/libcore/luni/src/main/native/java_net_InetAddress.cpp
+++ b/libcore/luni/src/main/native/java_net_InetAddress.cpp
@@ -64,9 +64,7 @@
 }
 #endif
 
-static jobjectArray getAllByNameUsingDns(JNIEnv* env, const char* name, 
-                                         jboolean preferIPv4Stack)
-{
+static jobjectArray InetAddress_getaddrinfoImpl(JNIEnv* env, const char* name) {
     struct addrinfo hints, *addressList = NULL, *addrInfo;
     jobjectArray addressArray = NULL;
 
@@ -95,7 +93,7 @@
         addressArray = env->NewObjectArray(addressCount, byteArrayClass, NULL);
         if (addressArray == NULL) {
             // Appropriate exception will be thrown.
-            LOGE("getAllByNameUsingDns: could not allocate output array");
+            LOGE("getaddrinfo: could not allocate array of size %i", addressCount);
             freeaddrinfo(addrInfo);
             return NULL;
         }
@@ -123,7 +121,7 @@
                     break;
                 default:
                     // Unknown address family. Skip this address.
-                    LOGE("getAllByNameUsingDns: Unknown address family %d",
+                    LOGE("getaddrinfo: Unknown address family %d",
                          addrInfo->ai_family);
                     continue;
             }
@@ -132,7 +130,7 @@
             jbyteArray bytearray = env->NewByteArray(addressLength);
             if (bytearray == NULL) {
                 // Out of memory error will be thrown on return.
-                LOGE("getAllByNameUsingDns: Can't allocate %d-byte array",
+                LOGE("getaddrinfo: Can't allocate %d-byte array",
                      addressLength);
                 addressArray = NULL;
                 break;
@@ -160,17 +158,13 @@
     return addressArray;
 }
 
-jobjectArray InetAddress_getallbyname(JNIEnv* env, jobject obj,
-                                      jstring javaName,
-                                      jboolean preferIPv4Stack)
-{
+jobjectArray InetAddress_getaddrinfo(JNIEnv* env, jobject obj, jstring javaName) {
     if (javaName == NULL) {
         jniThrowException(env, "java/lang/NullPointerException", NULL);
         return NULL;
     }
-
     const char* name = env->GetStringUTFChars(javaName, NULL);
-    jobjectArray out = getAllByNameUsingDns(env, name, preferIPv4Stack);
+    jobjectArray out = InetAddress_getaddrinfoImpl(env, name);
     env->ReleaseStringUTFChars(javaName, name);
     return out;
 }
@@ -184,7 +178,7 @@
  * @return the hostname.
  * @throws UnknownHostException: the IP address has no associated hostname.
  */
-static jstring InetAddress_gethostbyaddr(JNIEnv* env, jobject obj,
+static jstring InetAddress_getnameinfo(JNIEnv* env, jobject obj,
                                          jbyteArray javaAddress)
 {
     if (javaAddress == NULL) {
@@ -233,16 +227,12 @@
  */
 static JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
-    { "gethostbyaddr",    "([B)Ljava/lang/String;",
-      (void*) InetAddress_gethostbyaddr },
-    { "getallbyname",     "(Ljava/lang/String;Z)[[B",
-      (void*) InetAddress_getallbyname },
-    { "gethostname",      "()Ljava/lang/String;",
-      (void*) InetAddress_gethostname  },
+    { "getaddrinfo", "(Ljava/lang/String;)[[B", (void*) InetAddress_getaddrinfo },
+    { "gethostname", "()Ljava/lang/String;", (void*) InetAddress_gethostname  },
+    { "getnameinfo", "([B)Ljava/lang/String;", (void*) InetAddress_getnameinfo },
 };
 
-extern "C" int register_java_net_InetAddress(JNIEnv* env)
-{
+extern "C" int register_java_net_InetAddress(JNIEnv* env) {
     jclass tempClass = env->FindClass("[B");
     if (tempClass) {
         byteArrayClass = (jclass) env->NewGlobalRef(tempClass);