1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package java.net; 18 19 import android.compat.annotation.UnsupportedAppUsage; 20 import libcore.util.BasicLruCache; 21 22 /** 23 * Implements caching for {@code InetAddress}. We use a unified cache for both positive and negative 24 * cache entries. 25 * 26 * TODO: benchmark and optimize InetAddress until we get to the point where we can just rely on 27 * the C library level caching. The main thing caching at this level buys us is avoiding repeated 28 * conversions from 'struct sockaddr's to InetAddress[]. 29 */ 30 class AddressCache { 31 /** 32 * When the cache contains more entries than this, we start dropping the oldest ones. 33 * This should be a power of two to avoid wasted space in our custom map. 34 */ 35 private static final int MAX_ENTRIES = 16; 36 37 // The TTL for the Java-level cache is short, just 2s. 38 private static final long TTL_NANOS = 2 * 1000000000L; 39 40 // The actual cache. 41 @UnsupportedAppUsage 42 private final BasicLruCache<AddressCacheKey, AddressCacheEntry> cache 43 = new BasicLruCache<AddressCacheKey, AddressCacheEntry>(MAX_ENTRIES); 44 45 static class AddressCacheKey { 46 @UnsupportedAppUsage 47 private final String mHostname; 48 private final int mNetId; 49 AddressCacheKey(String hostname, int netId)50 AddressCacheKey(String hostname, int netId) { 51 mHostname = hostname; 52 mNetId = netId; 53 } 54 equals(Object o)55 @Override public boolean equals(Object o) { 56 if (this == o) { 57 return true; 58 } 59 if (!(o instanceof AddressCacheKey)) { 60 return false; 61 } 62 AddressCacheKey lhs = (AddressCacheKey) o; 63 return mHostname.equals(lhs.mHostname) && mNetId == lhs.mNetId; 64 } 65 hashCode()66 @Override public int hashCode() { 67 int result = 17; 68 result = 31 * result + mNetId; 69 result = 31 * result + mHostname.hashCode(); 70 return result; 71 } 72 } 73 74 static class AddressCacheEntry { 75 // Either an InetAddress[] for a positive entry, 76 // or a String detail message for a negative entry. 77 @UnsupportedAppUsage 78 final Object value; 79 80 /** 81 * The absolute expiry time in nanoseconds. Nanoseconds from System.nanoTime is ideal 82 * because -- unlike System.currentTimeMillis -- it can never go backwards. 83 * 84 * We don't need to worry about overflow with a TTL_NANOS of 2s. 85 */ 86 @UnsupportedAppUsage 87 final long expiryNanos; 88 89 @UnsupportedAppUsage AddressCacheEntry(Object value)90 AddressCacheEntry(Object value) { 91 this.value = value; 92 this.expiryNanos = System.nanoTime() + TTL_NANOS; 93 } 94 } 95 96 /** 97 * Removes all entries from the cache. 98 */ clear()99 public void clear() { 100 cache.evictAll(); 101 } 102 103 /** 104 * Returns the cached InetAddress[] for 'hostname' on network 'netId'. Returns null 105 * if nothing is known about 'hostname'. Returns a String suitable for use as an 106 * UnknownHostException detail message if 'hostname' is known not to exist. 107 */ get(String hostname, int netId)108 public Object get(String hostname, int netId) { 109 AddressCacheEntry entry = cache.get(new AddressCacheKey(hostname, netId)); 110 // Do we have a valid cache entry? 111 if (entry != null && entry.expiryNanos >= System.nanoTime()) { 112 return entry.value; 113 } 114 // Either we didn't find anything, or it had expired. 115 // No need to remove expired entries: the caller will provide a replacement shortly. 116 return null; 117 } 118 119 /** 120 * Associates the given 'addresses' with 'hostname'. The association will expire after a 121 * certain length of time. 122 */ put(String hostname, int netId, InetAddress[] addresses)123 public void put(String hostname, int netId, InetAddress[] addresses) { 124 cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(addresses)); 125 } 126 127 /** 128 * Records that 'hostname' is known not to have any associated addresses. (I.e. insert a 129 * negative cache entry.) 130 */ putUnknownHost(String hostname, int netId, String detailMessage)131 public void putUnknownHost(String hostname, int netId, String detailMessage) { 132 cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(detailMessage)); 133 } 134 } 135