NetworkInterface.java revision 693eacca9fa67ad79d1b35dbaad61c5ac1ac457c
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.net;
19
20import java.util.ArrayList;
21import java.util.Collections;
22import java.util.Enumeration;
23import java.util.LinkedHashMap;
24import java.util.LinkedList;
25import java.util.List;
26import java.util.Map;
27import libcore.base.EmptyArray;
28
29/**
30 * This class is used to represent a network interface of the local device. An
31 * interface is defined by its address and a platform dependent name. The class
32 * provides methods to get all information about the available interfaces of the
33 * system or to identify the local interface of a joined multicast group.
34 */
35public final class NetworkInterface extends Object {
36
37    private static final int CHECK_CONNECT_NO_PORT = -1;
38
39    static final int NO_INTERFACE_INDEX = 0;
40    static final int UNSET_INTERFACE_INDEX = -1;
41
42    private final String name;
43    private final String displayName;
44    private final List<InterfaceAddress> interfaceAddresses = new LinkedList<InterfaceAddress>();
45
46    private final List<InetAddress> addresses = new LinkedList<InetAddress>();
47
48    // The interface index is a positive integer which is non-negative. Where
49    // value is zero then we do not have an index for the interface (which
50    // occurs in systems which only support IPV4)
51    private int interfaceIndex;
52
53    private NetworkInterface parent = null;
54
55    private final List<NetworkInterface> children = new LinkedList<NetworkInterface>();
56
57    private static NetworkInterface[] getNetworkInterfacesImpl() throws SocketException {
58        Map<String, NetworkInterface> networkInterfaces = new LinkedHashMap<String, NetworkInterface>();
59        for (InterfaceAddress ia : getAllInterfaceAddressesImpl()) {
60            if (ia != null) { // The array may contain harmless null elements.
61                String name = ia.name;
62                NetworkInterface ni = networkInterfaces.get(name);
63                if (ni == null) {
64                    ni = new NetworkInterface(name, name, new InetAddress[] { ia.address }, ia.index);
65                    ni.interfaceAddresses.add(ia);
66                    networkInterfaces.put(name, ni);
67                } else {
68                    ni.addresses.add(ia.address);
69                    ni.interfaceAddresses.add(ia);
70                }
71            }
72        }
73        return networkInterfaces.values().toArray(new NetworkInterface[networkInterfaces.size()]);
74    }
75    private static native InterfaceAddress[] getAllInterfaceAddressesImpl() throws SocketException;
76
77    /**
78     * This constructor is used by the native method in order to construct the
79     * NetworkInterface objects in the array that it returns.
80     *
81     * @param name
82     *            internal name associated with the interface.
83     * @param displayName
84     *            a user interpretable name for the interface.
85     * @param addresses
86     *            the Internet addresses associated with the interface.
87     * @param interfaceIndex
88     *            an index for the interface. Only set for platforms that
89     *            support IPV6.
90     */
91    NetworkInterface(String name, String displayName, InetAddress[] addresses,
92            int interfaceIndex) {
93        this.name = name;
94        this.displayName = displayName;
95        this.interfaceIndex = interfaceIndex;
96        if (addresses != null) {
97            for (InetAddress address : addresses) {
98                this.addresses.add(address);
99            }
100        }
101    }
102
103    /**
104     * Returns the index for the network interface. Unless the system supports
105     * IPV6 this will be 0.
106     *
107     * @return the index
108     */
109    int getIndex() {
110        return interfaceIndex;
111    }
112
113    /**
114     * Returns the first address for the network interface. This is used in the
115     * natives when we need one of the addresses for the interface and any one
116     * will do
117     *
118     * @return the first address if one exists, otherwise null.
119     */
120    InetAddress getFirstAddress() {
121        if (addresses.size() >= 1) {
122            return addresses.get(0);
123        }
124        return null;
125    }
126
127    /**
128     * Gets the name associated with this network interface.
129     *
130     * @return the name of this {@code NetworkInterface} instance.
131     */
132    public String getName() {
133        return name;
134    }
135
136    /**
137     * Gets a list of addresses bound to this network interface.
138     *
139     * @return the address list of the represented network interface.
140     */
141    public Enumeration<InetAddress> getInetAddresses() {
142        SecurityManager sm = System.getSecurityManager();
143        if (sm == null || addresses.isEmpty()) {
144            return Collections.enumeration(addresses);
145        }
146        // TODO: Android should ditch SecurityManager and the associated pollution.
147        List<InetAddress> result = new ArrayList<InetAddress>(addresses.size());
148        for (InetAddress address : addresses) {
149            try {
150                sm.checkConnect(address.getHostName(), CHECK_CONNECT_NO_PORT);
151            } catch (SecurityException e) {
152                continue;
153            }
154            result.add(address);
155        }
156        return Collections.enumeration(result);
157    }
158
159    /**
160     * Gets the human-readable name associated with this network interface.
161     *
162     * @return the display name of this network interface or the name if the
163     *         display name is not available.
164     */
165    public String getDisplayName() {
166        /*
167         * we should return the display name unless it is blank in this case
168         * return the name so that something is displayed.
169         */
170        return displayName.isEmpty() ? name : displayName;
171    }
172
173    /**
174     * Gets the specific network interface according to a given name.
175     *
176     * @param interfaceName
177     *            the name to identify the searched network interface.
178     * @return the network interface with the specified name if one exists or
179     *         {@code null} otherwise.
180     * @throws SocketException
181     *             if an error occurs while getting the network interface
182     *             information.
183     * @throws NullPointerException
184     *             if the given interface's name is {@code null}.
185     */
186    public static NetworkInterface getByName(String interfaceName) throws SocketException {
187        if (interfaceName == null) {
188            throw new NullPointerException();
189        }
190        for (NetworkInterface networkInterface : getNetworkInterfacesList()) {
191            if (networkInterface.name.equals(interfaceName)) {
192                return networkInterface;
193            }
194        }
195        return null;
196    }
197
198    /**
199     * Gets the specific network interface according to the given address.
200     *
201     * @param address
202     *            the address to identify the searched network interface.
203     * @return the network interface with the specified address if one exists or
204     *         {@code null} otherwise.
205     * @throws SocketException
206     *             if an error occurs while getting the network interface
207     *             information.
208     * @throws NullPointerException
209     *             if the given interface address is invalid.
210     */
211    public static NetworkInterface getByInetAddress(InetAddress address) throws SocketException {
212        if (address == null) {
213            throw new NullPointerException("address == null");
214        }
215        for (NetworkInterface networkInterface : getNetworkInterfacesList()) {
216            if (networkInterface.addresses.contains(address)) {
217                return networkInterface;
218            }
219        }
220        return null;
221    }
222
223    /**
224     * Gets a list of all network interfaces available on the local system or
225     * {@code null} if no interface is available.
226     *
227     * @return the list of {@code NetworkInterface} instances representing the
228     *         available interfaces.
229     * @throws SocketException
230     *             if an error occurs while getting the network interface
231     *             information.
232     */
233    public static Enumeration<NetworkInterface> getNetworkInterfaces() throws SocketException {
234        return Collections.enumeration(getNetworkInterfacesList());
235    }
236
237    private static List<NetworkInterface> getNetworkInterfacesList() throws SocketException {
238        NetworkInterface[] interfaces = getNetworkInterfacesImpl();
239
240        for (NetworkInterface netif : interfaces) {
241            // Ensure that current NetworkInterface is bound to at least
242            // one InetAddress before processing
243            for (InetAddress addr : netif.addresses) {
244                if (addr.ipaddress.length == 16) {
245                    if (addr.isLinkLocalAddress() || addr.isSiteLocalAddress()) {
246                        ((Inet6Address) addr).scopedIf = netif;
247                        ((Inet6Address) addr).ifname = netif.name;
248                        ((Inet6Address) addr).scope_ifname_set = true;
249                    }
250                }
251            }
252        }
253
254        List<NetworkInterface> result = new ArrayList<NetworkInterface>();
255        boolean[] peeked = new boolean[interfaces.length];
256        for (int counter = 0; counter < interfaces.length; counter++) {
257            // If this interface has been touched, continue.
258            if (peeked[counter]) {
259                continue;
260            }
261            int counter2 = counter;
262            // Checks whether the following interfaces are children.
263            for (; counter2 < interfaces.length; counter2++) {
264                if (peeked[counter2]) {
265                    continue;
266                }
267                if (interfaces[counter2].name.startsWith(interfaces[counter].name + ":")) {
268                    // Tagged as peeked
269                    peeked[counter2] = true;
270                    interfaces[counter].children.add(interfaces[counter2]);
271                    interfaces[counter2].parent = interfaces[counter];
272                    interfaces[counter].addresses.addAll(interfaces[counter2].addresses);
273                }
274            }
275            // Tagged as peeked
276            result.add(interfaces[counter]);
277            peeked[counter] = true;
278        }
279        return result;
280    }
281
282    /**
283     * Compares the specified object to this {@code NetworkInterface} and
284     * returns whether they are equal or not. The object must be an instance of
285     * {@code NetworkInterface} with the same name, {@code displayName} and list
286     * of network interfaces to be equal.
287     *
288     * @param obj
289     *            the object to compare with this instance.
290     * @return {@code true} if the specified object is equal to this {@code
291     *         NetworkInterface}, {@code false} otherwise.
292     * @see #hashCode()
293     */
294    @Override
295    public boolean equals(Object obj) {
296        if (obj == this) {
297            return true;
298        }
299        if (!(obj instanceof NetworkInterface)) {
300            return false;
301        }
302        NetworkInterface rhs = (NetworkInterface) obj;
303        // TODO: should the order of the addresses matter (we use List.equals)?
304        return interfaceIndex == rhs.interfaceIndex &&
305                name.equals(rhs.name) && displayName.equals(rhs.displayName) &&
306                addresses.equals(rhs.addresses);
307    }
308
309    /**
310     * Returns the hash code for this {@code NetworkInterface}. Since the
311     * name should be unique for each network interface the hash code is
312     * generated using this name.
313     */
314    @Override
315    public int hashCode() {
316        return name.hashCode();
317    }
318
319    /**
320     * Gets a string containing a concise, human-readable description of this
321     * network interface.
322     *
323     * @return the textual representation for this network interface.
324     */
325    @Override
326    public String toString() {
327        StringBuilder string = new StringBuilder(25);
328        string.append("[");
329        string.append(name);
330        string.append("][");
331        string.append(displayName);
332        string.append("][");
333        string.append(interfaceIndex);
334        string.append("]");
335
336        /*
337         * get the addresses through this call to make sure we only reveal those
338         * that we should
339         */
340        Enumeration<InetAddress> theAddresses = getInetAddresses();
341        if (theAddresses != null) {
342            while (theAddresses.hasMoreElements()) {
343                InetAddress nextAddress = theAddresses.nextElement();
344                string.append("[");
345                string.append(nextAddress.toString());
346                string.append("]");
347            }
348        }
349        return string.toString();
350    }
351
352    /**
353     * Returns a List the InterfaceAddresses for this network interface.
354     * <p>
355     * If there is a security manager, its checkConnect method is called with
356     * the InetAddress for each InterfaceAddress. Only InterfaceAddresses where
357     * the checkConnect doesn't throw a SecurityException will be returned.
358     *
359     * @return a List of the InterfaceAddresses for this network interface.
360     * @since 1.6
361     */
362    public List<InterfaceAddress> getInterfaceAddresses() {
363        SecurityManager sm = System.getSecurityManager();
364        if (sm == null) {
365            return Collections.unmodifiableList(interfaceAddresses);
366        }
367        // TODO: Android should ditch SecurityManager and the associated pollution.
368        List<InterfaceAddress> result = new ArrayList<InterfaceAddress>(interfaceAddresses.size());
369        for (InterfaceAddress ia : interfaceAddresses) {
370            try {
371                sm.checkConnect(ia.getAddress().getHostName(), CHECK_CONNECT_NO_PORT);
372            } catch (SecurityException e) {
373                continue;
374            }
375            result.add(ia);
376        }
377        return result;
378    }
379
380    /**
381     * Returns an {@code Enumeration} of all the sub-interfaces of this network interface.
382     * Sub-interfaces are also known as virtual interfaces.
383     * <p>
384     * For example, {@code eth0:1} would be a sub-interface of {@code eth0}.
385     *
386     * @return an Enumeration of all the sub-interfaces of this network interface
387     * @since 1.6
388     */
389    public Enumeration<NetworkInterface> getSubInterfaces() {
390        return Collections.enumeration(children);
391    }
392
393    /**
394     * Returns the parent NetworkInterface of this interface if this is a
395     * sub-interface, or null if it's a physical (non virtual) interface.
396     *
397     * @return the NetworkInterface this interface is attached to.
398     * @since 1.6
399     */
400    public NetworkInterface getParent() {
401        return parent;
402    }
403
404    /**
405     * Returns true if this network interface is up.
406     *
407     * @return true if the interface is up.
408     * @throws SocketException if an I/O error occurs.
409     * @since 1.6
410     */
411    public boolean isUp() throws SocketException {
412        if (addresses.isEmpty()) {
413            return false;
414        }
415        return isUpImpl(name);
416    }
417    private static native boolean isUpImpl(String n) throws SocketException;
418
419    /**
420     * Returns true if this network interface is a loopback interface.
421     *
422     * @return true if the interface is a loopback interface.
423     * @throws SocketException if an I/O error occurs.
424     * @since 1.6
425     */
426    public boolean isLoopback() throws SocketException {
427        if (addresses.isEmpty()) {
428            return false;
429        }
430        return isLoopbackImpl(name);
431    }
432    private static native boolean isLoopbackImpl(String n) throws SocketException;
433
434    /**
435     * Returns true if this network interface is a point-to-point interface.
436     * (For example, a PPP connection using a modem.)
437     *
438     * @return true if the interface is point-to-point.
439     * @throws SocketException if an I/O error occurs.
440     * @since 1.6
441     */
442    public boolean isPointToPoint() throws SocketException {
443        if (addresses.isEmpty()) {
444            return false;
445        }
446        return isPointToPointImpl(name);
447    }
448    private static native boolean isPointToPointImpl(String n) throws SocketException;
449
450    /**
451     * Returns true if this network interface supports multicast.
452     *
453     * @throws SocketException if an I/O error occurs.
454     * @since 1.6
455     */
456    public boolean supportsMulticast() throws SocketException {
457        if (addresses.isEmpty()) {
458            return false;
459        }
460        return supportsMulticastImpl(name);
461    }
462    private static native boolean supportsMulticastImpl(String n) throws SocketException;
463
464    /**
465     * Returns the hardware address of the interface, if it has one, and the
466     * user has the necessary privileges to access the address.
467     *
468     * @return a byte array containing the address or null if the address
469     *         doesn't exist or is not accessible.
470     * @throws SocketException if an I/O error occurs.
471     * @since 1.6
472     */
473    public byte[] getHardwareAddress() throws SocketException {
474        if (addresses.isEmpty()) {
475            return EmptyArray.BYTE;
476        }
477        return getHardwareAddressImpl(name);
478    }
479    private static native byte[] getHardwareAddressImpl(String n) throws SocketException;
480
481    /**
482     * Returns the Maximum Transmission Unit (MTU) of this interface.
483     *
484     * @return the value of the MTU for the interface.
485     * @throws SocketException if an I/O error occurs.
486     * @since 1.6
487     */
488    public int getMTU() throws SocketException {
489        if (addresses.isEmpty()) {
490            return 0;
491        }
492        return getMTUImpl(name);
493    }
494    private static native int getMTUImpl(String n) throws SocketException;
495
496    /**
497     * Returns true if this interface is a virtual interface (also called
498     * a sub-interface). Virtual interfaces are, on some systems, interfaces
499     * created as a child of a physical interface and given different settings
500     * (like address or MTU). Usually the name of the interface will the name of
501     * the parent followed by a colon (:) and a number identifying the child,
502     * since there can be several virtual interfaces attached to a single
503     * physical interface.
504     *
505     * @return true if this interface is a virtual interface.
506     * @since 1.6
507     */
508    public boolean isVirtual() {
509        return parent != null;
510    }
511}
512