blob: 115e98ac557fbbc0b3155a50a15c830785f4d8e4 [file] [log] [blame]
/*
* 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.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* This class is used to represent a network interface of the local device. An
* interface is defined by its address and a platform dependent name. The class
* provides methods to get all information about the available interfaces of the
* system or to identify the local interface of a joined multicast group.
*/
public final class NetworkInterface extends Object {
private static final int CHECK_CONNECT_NO_PORT = -1;
static final int NO_INTERFACE_INDEX = 0;
static final int UNSET_INTERFACE_INDEX = -1;
private final String name;
private final String displayName;
private final List<InterfaceAddress> interfaceAddresses = new LinkedList<InterfaceAddress>();
private final List<InetAddress> addresses = new LinkedList<InetAddress>();
// The interface index is a positive integer which is non-negative. Where
// value is zero then we do not have an index for the interface (which
// occurs in systems which only support IPV4)
private int interfaceIndex;
private NetworkInterface parent = null;
private final List<NetworkInterface> children = new LinkedList<NetworkInterface>();
// BEGIN android-changed: we pay this extra complexity on the Java side
// in return for vastly simpler native code.
private static native InterfaceAddress[] getAllInterfaceAddressesImpl() throws SocketException;
private static NetworkInterface[] getNetworkInterfacesImpl() throws SocketException {
Map<String, NetworkInterface> networkInterfaces = new LinkedHashMap<String, NetworkInterface>();
for (InterfaceAddress ia : getAllInterfaceAddressesImpl()) {
if (ia != null) { // The array may contain harmless null elements.
String name = ia.name;
NetworkInterface ni = networkInterfaces.get(name);
if (ni == null) {
ni = new NetworkInterface(name, name, new InetAddress[] { ia.address }, ia.index);
ni.interfaceAddresses.add(ia);
networkInterfaces.put(name, ni);
} else {
ni.addresses.add(ia.address);
ni.interfaceAddresses.add(ia);
}
}
}
return networkInterfaces.values().toArray(new NetworkInterface[networkInterfaces.size()]);
}
// END android-changed
/**
* This constructor is used by the native method in order to construct the
* NetworkInterface objects in the array that it returns.
*
* @param name
* internal name associated with the interface.
* @param displayName
* a user interpretable name for the interface.
* @param addresses
* the Internet addresses associated with the interface.
* @param interfaceIndex
* an index for the interface. Only set for platforms that
* support IPV6.
*/
NetworkInterface(String name, String displayName, InetAddress[] addresses,
int interfaceIndex) {
this.name = name;
this.displayName = displayName;
this.interfaceIndex = interfaceIndex;
if (addresses != null) {
for (InetAddress address : addresses) {
this.addresses.add(address);
}
}
}
/**
* Returns the index for the network interface. Unless the system supports
* IPV6 this will be 0.
*
* @return the index
*/
int getIndex() {
return interfaceIndex;
}
/**
* Returns the first address for the network interface. This is used in the
* natives when we need one of the addresses for the interface and any one
* will do
*
* @return the first address if one exists, otherwise null.
*/
InetAddress getFirstAddress() {
if (addresses.size() >= 1) {
return addresses.get(0);
}
return null;
}
/**
* Gets the name associated with this network interface.
*
* @return the name of this {@code NetworkInterface} instance.
*/
public String getName() {
return name;
}
/**
* Gets a list of addresses bound to this network interface.
*
* @return the address list of the represented network interface.
*/
public Enumeration<InetAddress> getInetAddresses() {
SecurityManager sm = System.getSecurityManager();
if (sm == null || addresses.isEmpty()) {
return Collections.enumeration(addresses);
}
// TODO: Android should ditch SecurityManager and the associated pollution.
List<InetAddress> result = new ArrayList<InetAddress>(addresses.size());
for (InetAddress address : addresses) {
try {
sm.checkConnect(address.getHostName(), CHECK_CONNECT_NO_PORT);
} catch (SecurityException e) {
continue;
}
result.add(address);
}
return Collections.enumeration(result);
}
/**
* Gets the human-readable name associated with this network interface.
*
* @return the display name of this network interface or the name if the
* display name is not available.
*/
public String getDisplayName() {
/*
* we should return the display name unless it is blank in this case
* return the name so that something is displayed.
*/
return displayName.isEmpty() ? name : displayName;
}
/**
* Gets the specific network interface according to a given name.
*
* @param interfaceName
* the name to identify the searched network interface.
* @return the network interface with the specified name if one exists or
* {@code null} otherwise.
* @throws SocketException
* if an error occurs while getting the network interface
* information.
* @throws NullPointerException
* if the given interface's name is {@code null}.
*/
public static NetworkInterface getByName(String interfaceName) throws SocketException {
if (interfaceName == null) {
throw new NullPointerException();
}
for (NetworkInterface networkInterface : getNetworkInterfacesList()) {
if (networkInterface.name.equals(interfaceName)) {
return networkInterface;
}
}
return null;
}
/**
* Gets the specific network interface according to the given address.
*
* @param address
* the address to identify the searched network interface.
* @return the network interface with the specified address if one exists or
* {@code null} otherwise.
* @throws SocketException
* if an error occurs while getting the network interface
* information.
* @throws NullPointerException
* if the given interface address is invalid.
*/
public static NetworkInterface getByInetAddress(InetAddress address) throws SocketException {
if (address == null) {
throw new NullPointerException("address == null");
}
for (NetworkInterface networkInterface : getNetworkInterfacesList()) {
if (networkInterface.addresses.contains(address)) {
return networkInterface;
}
}
return null;
}
/**
* Gets a list of all network interfaces available on the local system or
* {@code null} if no interface is available.
*
* @return the list of {@code NetworkInterface} instances representing the
* available interfaces.
* @throws SocketException
* if an error occurs while getting the network interface
* information.
*/
public static Enumeration<NetworkInterface> getNetworkInterfaces() throws SocketException {
return Collections.enumeration(getNetworkInterfacesList());
}
private static List<NetworkInterface> getNetworkInterfacesList() throws SocketException {
NetworkInterface[] interfaces = getNetworkInterfacesImpl();
for (NetworkInterface netif : interfaces) {
// Ensure that current NetworkInterface is bound to at least
// one InetAddress before processing
for (InetAddress addr : netif.addresses) {
if (addr.ipaddress.length == 16) {
if (addr.isLinkLocalAddress() || addr.isSiteLocalAddress()) {
((Inet6Address) addr).scopedIf = netif;
((Inet6Address) addr).ifname = netif.name;
((Inet6Address) addr).scope_ifname_set = true;
}
}
}
}
List<NetworkInterface> result = new ArrayList<NetworkInterface>();
boolean[] peeked = new boolean[interfaces.length];
for (int counter = 0; counter < interfaces.length; counter++) {
// If this interface has been touched, continue.
if (peeked[counter]) {
continue;
}
int counter2 = counter;
// Checks whether the following interfaces are children.
for (; counter2 < interfaces.length; counter2++) {
if (peeked[counter2]) {
continue;
}
if (interfaces[counter2].name.startsWith(interfaces[counter].name + ":")) {
// Tagged as peeked
peeked[counter2] = true;
interfaces[counter].children.add(interfaces[counter2]);
interfaces[counter2].parent = interfaces[counter];
interfaces[counter].addresses.addAll(interfaces[counter2].addresses);
}
}
// Tagged as peeked
result.add(interfaces[counter]);
peeked[counter] = true;
}
return result;
}
/**
* Compares the specified object to this {@code NetworkInterface} and
* returns whether they are equal or not. The object must be an instance of
* {@code NetworkInterface} with the same name, {@code displayName} and list
* of network interfaces to be equal.
*
* @param obj
* the object to compare with this instance.
* @return {@code true} if the specified object is equal to this {@code
* NetworkInterface}, {@code false} otherwise.
* @see #hashCode()
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof NetworkInterface)) {
return false;
}
NetworkInterface rhs = (NetworkInterface) obj;
// TODO: should the order of the addresses matter (we use List.equals)?
return interfaceIndex == rhs.interfaceIndex &&
name.equals(rhs.name) && displayName.equals(rhs.displayName) &&
addresses.equals(rhs.addresses);
}
/**
* Returns the hash code for this {@code NetworkInterface}. Since the
* name should be unique for each network interface the hash code is
* generated using this name.
*/
@Override
public int hashCode() {
return name.hashCode();
}
/**
* Gets a string containing a concise, human-readable description of this
* network interface.
*
* @return the textual representation for this network interface.
*/
@Override
public String toString() {
StringBuilder string = new StringBuilder(25);
string.append("[");
string.append(name);
string.append("][");
string.append(displayName);
// BEGIN android-added: the RI shows this, and it's useful for IPv6 users.
string.append("][");
string.append(interfaceIndex);
// END android-added
string.append("]");
/*
* get the addresses through this call to make sure we only reveal those
* that we should
*/
Enumeration<InetAddress> theAddresses = getInetAddresses();
if (theAddresses != null) {
while (theAddresses.hasMoreElements()) {
InetAddress nextAddress = theAddresses.nextElement();
string.append("[");
string.append(nextAddress.toString());
string.append("]");
}
}
return string.toString();
}
/**
* Returns a List the InterfaceAddresses for this network interface.
* <p>
* If there is a security manager, its checkConnect method is called with
* the InetAddress for each InterfaceAddress. Only InterfaceAddresses where
* the checkConnect doesn't throw a SecurityException will be returned.
*
* @return a List of the InterfaceAddresses for this network interface.
* @since 1.6
*/
public List<InterfaceAddress> getInterfaceAddresses() {
SecurityManager sm = System.getSecurityManager();
if (sm == null) {
return Collections.unmodifiableList(interfaceAddresses);
}
// TODO: Android should ditch SecurityManager and the associated pollution.
List<InterfaceAddress> result = new ArrayList<InterfaceAddress>(interfaceAddresses.size());
for (InterfaceAddress ia : interfaceAddresses) {
try {
sm.checkConnect(ia.getAddress().getHostName(), CHECK_CONNECT_NO_PORT);
} catch (SecurityException e) {
continue;
}
result.add(ia);
}
return result;
}
/**
* Returns an {@code Enumeration} of all the sub-interfaces of this network interface.
* Sub-interfaces are also known as virtual interfaces.
* <p>
* For example, {@code eth0:1} would be a sub-interface of {@code eth0}.
*
* @return an Enumeration of all the sub-interfaces of this network interface
* @since 1.6
*/
public Enumeration<NetworkInterface> getSubInterfaces() {
return Collections.enumeration(children);
}
/**
* Returns the parent NetworkInterface of this interface if this is a
* sub-interface, or null if it's a physical (non virtual) interface.
*
* @return the NetworkInterface this interface is attached to.
* @since 1.6
*/
public NetworkInterface getParent() {
return parent;
}
/**
* Returns true if this network interface is up.
*
* @return true if the interface is up.
* @throws SocketException if an I/O error occurs.
* @since 1.6
*/
public boolean isUp() throws SocketException {
if (addresses.isEmpty()) {
return false;
}
return isUpImpl(name);
}
private static native boolean isUpImpl(String n) throws SocketException;
/**
* Returns true if this network interface is a loopback interface.
*
* @return true if the interface is a loopback interface.
* @throws SocketException if an I/O error occurs.
* @since 1.6
*/
public boolean isLoopback() throws SocketException {
if (addresses.isEmpty()) {
return false;
}
return isLoopbackImpl(name);
}
private static native boolean isLoopbackImpl(String n) throws SocketException;
/**
* Returns true if this network interface is a point-to-point interface.
* (For example, a PPP connection using a modem.)
*
* @return true if the interface is point-to-point.
* @throws SocketException if an I/O error occurs.
* @since 1.6
*/
public boolean isPointToPoint() throws SocketException {
if (addresses.isEmpty()) {
return false;
}
return isPointToPointImpl(name);
}
private static native boolean isPointToPointImpl(String n) throws SocketException;
/**
* Returns true if this network interface supports multicast.
*
* @throws SocketException if an I/O error occurs.
* @since 1.6
*/
public boolean supportsMulticast() throws SocketException {
if (addresses.isEmpty()) {
return false;
}
return supportsMulticastImpl(name);
}
private static native boolean supportsMulticastImpl(String n) throws SocketException;
/**
* Returns the hardware address of the interface, if it has one, and the
* user has the necessary privileges to access the address.
*
* @return a byte array containing the address or null if the address
* doesn't exist or is not accessible.
* @throws SocketException if an I/O error occurs.
* @since 1.6
*/
public byte[] getHardwareAddress() throws SocketException {
if (addresses.isEmpty()) {
return new byte[0];
}
return getHardwareAddressImpl(name);
}
private static native byte[] getHardwareAddressImpl(String n) throws SocketException;
/**
* Returns the Maximum Transmission Unit (MTU) of this interface.
*
* @return the value of the MTU for the interface.
* @throws SocketException if an I/O error occurs.
* @since 1.6
*/
public int getMTU() throws SocketException {
if (addresses.isEmpty()) {
return 0;
}
return getMTUImpl(name);
}
private static native int getMTUImpl(String n) throws SocketException;
/**
* Returns true if this interface is a virtual interface (also called
* a sub-interface). Virtual interfaces are, on some systems, interfaces
* created as a child of a physical interface and given different settings
* (like address or MTU). Usually the name of the interface will the name of
* the parent followed by a colon (:) and a number identifying the child,
* since there can be several virtual interfaces attached to a single
* physical interface.
*
* @return true if this interface is a virtual interface.
* @since 1.6
*/
public boolean isVirtual() {
return parent != null;
}
}