content: /*
   * Copyright (C) 2014 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 android.net;
  
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.annotation.SystemApi;
  import android.annotation.TestApi;
  import android.annotation.UnsupportedAppUsage;
  import android.net.shared.InetAddressUtils;
  import android.os.Parcel;
  import android.os.Parcelable;
  
  import java.net.InetAddress;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Objects;
  
  /**
   * Class that describes static IP configuration.
   *
   * <p>This class is different from {@link LinkProperties} because it represents
   * configuration intent. The general contract is that if we can represent
   * a configuration here, then we should be able to configure it on a network.
   * The intent is that it closely match the UI we have for configuring networks.
   *
   * <p>In contrast, {@link LinkProperties} represents current state. It is much more
   * expressive. For example, it supports multiple IP addresses, multiple routes,
   * stacked interfaces, and so on. Because LinkProperties is so expressive,
   * using it to represent configuration intent as well as current state causes
   * problems. For example, we could unknowingly save a configuration that we are
   * not in fact capable of applying, or we could save a configuration that the
   * UI cannot display, which has the potential for malicious code to hide
   * hostile or unexpected configuration from the user.
   *
   * @hide
   */
  @SystemApi
  @TestApi
  public final class StaticIpConfiguration implements Parcelable {
      /** @hide */
      @UnsupportedAppUsage
      @Nullable
      public LinkAddress ipAddress;
      /** @hide */
      @UnsupportedAppUsage
      @Nullable
      public InetAddress gateway;
      /** @hide */
      @UnsupportedAppUsage
      @NonNull
      public final ArrayList<InetAddress> dnsServers;
      /** @hide */
      @UnsupportedAppUsage
      @Nullable
      public String domains;
  
      public StaticIpConfiguration() {
          dnsServers = new ArrayList<>();
      }
  
      public StaticIpConfiguration(@Nullable StaticIpConfiguration source) {
          this();
          if (source != null) {
              // All of these except dnsServers are immutable, so no need to make copies.
              ipAddress = source.ipAddress;
              gateway = source.gateway;
              dnsServers.addAll(source.dnsServers);
              domains = source.domains;
          }
      }
  
      public void clear() {
          ipAddress = null;
          gateway = null;
          dnsServers.clear();
          domains = null;
      }
  
      /**
       * Get the static IP address included in the configuration.
       */
      public @Nullable LinkAddress getIpAddress() {
          return ipAddress;
      }
  
      /**
       * Get the gateway included in the configuration.
       */
      public @Nullable InetAddress getGateway() {
          return gateway;
      }
  
      /**
       * Get the DNS servers included in the configuration.
       */
      public @NonNull List<InetAddress> getDnsServers() {
          return dnsServers;
      }
  
      /**
       * Get a {@link String} listing in priority order of the comma separated domains to search when
       * resolving host names on the link.
       */
      public @Nullable String getDomains() {
          return domains;
      }
  
      /**
       * Helper class to build a new instance of {@link StaticIpConfiguration}.
       */
      public static final class Builder {
          private LinkAddress mIpAddress;
          private InetAddress mGateway;
          private Iterable<InetAddress> mDnsServers;
          private String mDomains;
  
          /**
           * Set the IP address to be included in the configuration; null by default.
           * @return The {@link Builder} for chaining.
           */
          public @NonNull Builder setIpAddress(@Nullable LinkAddress ipAddress) {
              mIpAddress = ipAddress;
              return this;
          }
  
          /**
           * Set the address of the gateway to be included in the configuration; null by default.
           * @return The {@link Builder} for chaining.
           */
          public @NonNull Builder setGateway(@Nullable InetAddress gateway) {
              mGateway = gateway;
              return this;
          }
  
          /**
           * Set the addresses of the DNS servers included in the configuration; empty by default.
           * @return The {@link Builder} for chaining.
           */
          public @NonNull Builder setDnsServers(@NonNull Iterable<InetAddress> dnsServers) {
              mDnsServers = dnsServers;
              return this;
          }
  
          /**
           * Sets the DNS domain search path to be used on the link; null by default.
           * @param newDomains A {@link String} containing the comma separated domains to search when
           *                   resolving host names on this link, in priority order.
           * @return The {@link Builder} for chaining.
           */
          public @NonNull Builder setDomains(@Nullable String newDomains) {
              mDomains = newDomains;
              return this;
          }
  
          /**
           * Create a {@link StaticIpConfiguration} from the parameters in this {@link Builder}.
           * @return The newly created StaticIpConfiguration.
           */
          public @NonNull StaticIpConfiguration build() {
              final StaticIpConfiguration config = new StaticIpConfiguration();
              config.ipAddress = mIpAddress;
              config.gateway = mGateway;
              for (InetAddress server : mDnsServers) {
                  config.dnsServers.add(server);
              }
              config.domains = mDomains;
              return config;
          }
      }
  
      /**
       * Add a DNS server to this configuration.
       */
      public void addDnsServer(@NonNull InetAddress server) {
          dnsServers.add(server);
      }
  
      /**
       * Returns the network routes specified by this object. Will typically include a
       * directly-connected route for the IP address's local subnet and a default route.
       * @param iface Interface to include in the routes.
       */
      public @NonNull List<RouteInfo> getRoutes(@Nullable String iface) {
          List<RouteInfo> routes = new ArrayList<RouteInfo>(3);
          if (ipAddress != null) {
              RouteInfo connectedRoute = new RouteInfo(ipAddress, null, iface);
              routes.add(connectedRoute);
              // If the default gateway is not covered by the directly-connected route, also add a
              // host route to the gateway as well. This configuration is arguably invalid, but it
              // used to work in K and earlier, and other OSes appear to accept it.
              if (gateway != null && !connectedRoute.matches(gateway)) {
                  routes.add(RouteInfo.makeHostRoute(gateway, iface));
              }
          }
          if (gateway != null) {
              routes.add(new RouteInfo((IpPrefix) null, gateway, iface));
          }
          return routes;
      }
  
      /**
       * Returns a LinkProperties object expressing the data in this object. Note that the information
       * contained in the LinkProperties will not be a complete picture of the link's configuration,
       * because any configuration information that is obtained dynamically by the network (e.g.,
       * IPv6 configuration) will not be included.
       * @hide
       */
      public @NonNull LinkProperties toLinkProperties(String iface) {
          LinkProperties lp = new LinkProperties();
          lp.setInterfaceName(iface);
          if (ipAddress != null) {
              lp.addLinkAddress(ipAddress);
          }
          for (RouteInfo route : getRoutes(iface)) {
              lp.addRoute(route);
          }
          for (InetAddress dns : dnsServers) {
              lp.addDnsServer(dns);
          }
          lp.setDomains(domains);
          return lp;
      }
  
      @Override
      public String toString() {
          StringBuffer str = new StringBuffer();
  
          str.append("IP address ");
          if (ipAddress != null ) str.append(ipAddress).append(" ");
  
          str.append("Gateway ");
          if (gateway != null) str.append(gateway.getHostAddress()).append(" ");
  
          str.append(" DNS servers: [");
          for (InetAddress dnsServer : dnsServers) {
              str.append(" ").append(dnsServer.getHostAddress());
          }
  
          str.append(" ] Domains ");
          if (domains != null) str.append(domains);
          return str.toString();
      }
  
      @Override
      public int hashCode() {
          int result = 13;
          result = 47 * result + (ipAddress == null ? 0 : ipAddress.hashCode());
          result = 47 * result + (gateway == null ? 0 : gateway.hashCode());
          result = 47 * result + (domains == null ? 0 : domains.hashCode());
          result = 47 * result + dnsServers.hashCode();
          return result;
      }
  
      @Override
      public boolean equals(Object obj) {
          if (this == obj) return true;
  
          if (!(obj instanceof StaticIpConfiguration)) return false;
  
          StaticIpConfiguration other = (StaticIpConfiguration) obj;
  
          return other != null &&
                  Objects.equals(ipAddress, other.ipAddress) &&
                  Objects.equals(gateway, other.gateway) &&
                  dnsServers.equals(other.dnsServers) &&
                  Objects.equals(domains, other.domains);
      }
  
      /** Implement the Parcelable interface */
      public static final @android.annotation.NonNull Creator<StaticIpConfiguration> CREATOR =
          new Creator<StaticIpConfiguration>() {
              public StaticIpConfiguration createFromParcel(Parcel in) {
                  return readFromParcel(in);
              }
  
              public StaticIpConfiguration[] newArray(int size) {
                  return new StaticIpConfiguration[size];
              }
          };
  
      /** Implement the Parcelable interface */
      @Override
      public int describeContents() {
          return 0;
      }
  
      /** Implement the Parcelable interface */
      @Override
      public void writeToParcel(Parcel dest, int flags) {
          dest.writeParcelable(ipAddress, flags);
          InetAddressUtils.parcelInetAddress(dest, gateway, flags);
          dest.writeInt(dnsServers.size());
          for (InetAddress dnsServer : dnsServers) {
              InetAddressUtils.parcelInetAddress(dest, dnsServer, flags);
          }
          dest.writeString(domains);
      }
  
      /** @hide */
      public static StaticIpConfiguration readFromParcel(Parcel in) {
          final StaticIpConfiguration s = new StaticIpConfiguration();
          s.ipAddress = in.readParcelable(null);
          s.gateway = InetAddressUtils.unparcelInetAddress(in);
          s.dnsServers.clear();
          int size = in.readInt();
          for (int i = 0; i < size; i++) {
              s.dnsServers.add(InetAddressUtils.unparcelInetAddress(in));
          }
          s.domains = in.readString();
          return s;
      }
  }
  