content: /*
   * Copyright (C) 2019 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.shared;
  
  import android.annotation.Nullable;
  import android.net.INetd;
  import android.net.Network;
  import android.net.ProvisioningConfigurationParcelable;
  import android.net.StaticIpConfiguration;
  import android.net.apf.ApfCapabilities;
  import android.net.ip.IIpClient;
  
  import java.util.Objects;
  import java.util.StringJoiner;
  
  /**
   * This class encapsulates parameters to be passed to
   * IpClient#startProvisioning(). A defensive copy is made by IpClient
   * and the values specified herein are in force until IpClient#stop()
   * is called.
   *
   * Example use:
   *
   *     final ProvisioningConfiguration config =
   *             new ProvisioningConfiguration.Builder()
   *                     .withPreDhcpAction()
   *                     .withProvisioningTimeoutMs(36 * 1000)
   *                     .build();
   *     mIpClient.startProvisioning(config.toStableParcelable());
   *     ...
   *     mIpClient.stop();
   *
   * The specified provisioning configuration will only be active until
   * IIpClient#stop() is called. Future calls to IIpClient#startProvisioning()
   * must specify the configuration again.
   * @hide
   */
  public class ProvisioningConfiguration {
      // TODO: Delete this default timeout once those callers that care are
      // fixed to pass in their preferred timeout.
      //
      // We pick 36 seconds so we can send DHCP requests at
      //
      //     t=0, t=2, t=6, t=14, t=30
      //
      // allowing for 10% jitter.
      private static final int DEFAULT_TIMEOUT_MS = 36 * 1000;
  
      /**
       * Builder to create a {@link ProvisioningConfiguration}.
       */
      public static class Builder {
          protected ProvisioningConfiguration mConfig = new ProvisioningConfiguration();
  
          /**
           * Specify that the configuration should not enable IPv4. It is enabled by default.
           */
          public Builder withoutIPv4() {
              mConfig.mEnableIPv4 = false;
              return this;
          }
  
          /**
           * Specify that the configuration should not enable IPv6. It is enabled by default.
           */
          public Builder withoutIPv6() {
              mConfig.mEnableIPv6 = false;
              return this;
          }
  
          /**
           * Specify that the configuration should not use a MultinetworkPolicyTracker. It is used
           * by default.
           */
          public Builder withoutMultinetworkPolicyTracker() {
              mConfig.mUsingMultinetworkPolicyTracker = false;
              return this;
          }
  
          /**
           * Specify that the configuration should not use a IpReachabilityMonitor. It is used by
           * default.
           */
          public Builder withoutIpReachabilityMonitor() {
              mConfig.mUsingIpReachabilityMonitor = false;
              return this;
          }
  
          /**
           * Identical to {@link #withPreDhcpAction(int)}, using a default timeout.
           * @see #withPreDhcpAction(int)
           */
          public Builder withPreDhcpAction() {
              mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS;
              return this;
          }
  
          /**
           * Specify that {@link IpClientCallbacks#onPreDhcpAction()} should be called. Clients must
           * call {@link IIpClient#completedPreDhcpAction()} when the callback called. This behavior
           * is disabled by default.
           * @param dhcpActionTimeoutMs Timeout for clients to call completedPreDhcpAction().
           */
          public Builder withPreDhcpAction(int dhcpActionTimeoutMs) {
              mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs;
              return this;
          }
  
          /**
           * Specify the initial provisioning configuration.
           */
          public Builder withInitialConfiguration(InitialConfiguration initialConfig) {
              mConfig.mInitialConfig = initialConfig;
              return this;
          }
  
          /**
           * Specify a static configuration for provisioning.
           */
          public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
              mConfig.mStaticIpConfig = staticConfig;
              return this;
          }
  
          /**
           * Specify ApfCapabilities.
           */
          public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
              mConfig.mApfCapabilities = apfCapabilities;
              return this;
          }
  
          /**
           * Specify the timeout to use for provisioning.
           */
          public Builder withProvisioningTimeoutMs(int timeoutMs) {
              mConfig.mProvisioningTimeoutMs = timeoutMs;
              return this;
          }
  
          /**
           * Specify that IPv6 address generation should use a random MAC address.
           */
          public Builder withRandomMacAddress() {
              mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_EUI64;
              return this;
          }
  
          /**
           * Specify that IPv6 address generation should use a stable MAC address.
           */
          public Builder withStableMacAddress() {
              mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
              return this;
          }
  
          /**
           * Specify the network to use for provisioning.
           */
          public Builder withNetwork(Network network) {
              mConfig.mNetwork = network;
              return this;
          }
  
          /**
           * Specify the display name that the IpClient should use.
           */
          public Builder withDisplayName(String displayName) {
              mConfig.mDisplayName = displayName;
              return this;
          }
  
          /**
           * Build the configuration using previously specified parameters.
           */
          public ProvisioningConfiguration build() {
              return new ProvisioningConfiguration(mConfig);
          }
      }
  
      public boolean mEnableIPv4 = true;
      public boolean mEnableIPv6 = true;
      public boolean mUsingMultinetworkPolicyTracker = true;
      public boolean mUsingIpReachabilityMonitor = true;
      public int mRequestedPreDhcpActionMs;
      public InitialConfiguration mInitialConfig;
      public StaticIpConfiguration mStaticIpConfig;
      public ApfCapabilities mApfCapabilities;
      public int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS;
      public int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
      public Network mNetwork = null;
      public String mDisplayName = null;
  
      public ProvisioningConfiguration() {} // used by Builder
  
      public ProvisioningConfiguration(ProvisioningConfiguration other) {
          mEnableIPv4 = other.mEnableIPv4;
          mEnableIPv6 = other.mEnableIPv6;
          mUsingMultinetworkPolicyTracker = other.mUsingMultinetworkPolicyTracker;
          mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
          mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs;
          mInitialConfig = InitialConfiguration.copy(other.mInitialConfig);
          mStaticIpConfig = other.mStaticIpConfig == null
                  ? null
                  : new StaticIpConfiguration(other.mStaticIpConfig);
          mApfCapabilities = other.mApfCapabilities;
          mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
          mIPv6AddrGenMode = other.mIPv6AddrGenMode;
          mNetwork = other.mNetwork;
          mDisplayName = other.mDisplayName;
      }
  
      /**
       * Create a ProvisioningConfigurationParcelable from this ProvisioningConfiguration.
       */
      public ProvisioningConfigurationParcelable toStableParcelable() {
          final ProvisioningConfigurationParcelable p = new ProvisioningConfigurationParcelable();
          p.enableIPv4 = mEnableIPv4;
          p.enableIPv6 = mEnableIPv6;
          p.usingMultinetworkPolicyTracker = mUsingMultinetworkPolicyTracker;
          p.usingIpReachabilityMonitor = mUsingIpReachabilityMonitor;
          p.requestedPreDhcpActionMs = mRequestedPreDhcpActionMs;
          p.initialConfig = mInitialConfig == null ? null : mInitialConfig.toStableParcelable();
          p.staticIpConfig = mStaticIpConfig == null
                  ? null
                  : new StaticIpConfiguration(mStaticIpConfig);
          p.apfCapabilities = mApfCapabilities; // ApfCapabilities is immutable
          p.provisioningTimeoutMs = mProvisioningTimeoutMs;
          p.ipv6AddrGenMode = mIPv6AddrGenMode;
          p.network = mNetwork;
          p.displayName = mDisplayName;
          return p;
      }
  
      /**
       * Create a ProvisioningConfiguration from a ProvisioningConfigurationParcelable.
       */
      public static ProvisioningConfiguration fromStableParcelable(
              @Nullable ProvisioningConfigurationParcelable p) {
          if (p == null) return null;
          final ProvisioningConfiguration config = new ProvisioningConfiguration();
          config.mEnableIPv4 = p.enableIPv4;
          config.mEnableIPv6 = p.enableIPv6;
          config.mUsingMultinetworkPolicyTracker = p.usingMultinetworkPolicyTracker;
          config.mUsingIpReachabilityMonitor = p.usingIpReachabilityMonitor;
          config.mRequestedPreDhcpActionMs = p.requestedPreDhcpActionMs;
          config.mInitialConfig = InitialConfiguration.fromStableParcelable(p.initialConfig);
          config.mStaticIpConfig = p.staticIpConfig == null
                  ? null
                  : new StaticIpConfiguration(p.staticIpConfig);
          config.mApfCapabilities = p.apfCapabilities; // ApfCapabilities is immutable
          config.mProvisioningTimeoutMs = p.provisioningTimeoutMs;
          config.mIPv6AddrGenMode = p.ipv6AddrGenMode;
          config.mNetwork = p.network;
          config.mDisplayName = p.displayName;
          return config;
      }
  
      @Override
      public String toString() {
          return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
                  .add("mEnableIPv4: " + mEnableIPv4)
                  .add("mEnableIPv6: " + mEnableIPv6)
                  .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker)
                  .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
                  .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs)
                  .add("mInitialConfig: " + mInitialConfig)
                  .add("mStaticIpConfig: " + mStaticIpConfig)
                  .add("mApfCapabilities: " + mApfCapabilities)
                  .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
                  .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode)
                  .add("mNetwork: " + mNetwork)
                  .add("mDisplayName: " + mDisplayName)
                  .toString();
      }
  
      @Override
      public boolean equals(Object obj) {
          if (!(obj instanceof ProvisioningConfiguration)) return false;
          final ProvisioningConfiguration other = (ProvisioningConfiguration) obj;
          return mEnableIPv4 == other.mEnableIPv4
                  && mEnableIPv6 == other.mEnableIPv6
                  && mUsingMultinetworkPolicyTracker == other.mUsingMultinetworkPolicyTracker
                  && mUsingIpReachabilityMonitor == other.mUsingIpReachabilityMonitor
                  && mRequestedPreDhcpActionMs == other.mRequestedPreDhcpActionMs
                  && Objects.equals(mInitialConfig, other.mInitialConfig)
                  && Objects.equals(mStaticIpConfig, other.mStaticIpConfig)
                  && Objects.equals(mApfCapabilities, other.mApfCapabilities)
                  && mProvisioningTimeoutMs == other.mProvisioningTimeoutMs
                  && mIPv6AddrGenMode == other.mIPv6AddrGenMode
                  && Objects.equals(mNetwork, other.mNetwork)
                  && Objects.equals(mDisplayName, other.mDisplayName);
      }
  
      public boolean isValid() {
          return (mInitialConfig == null) || mInitialConfig.isValid();
      }
  }
  