content: /*
   * Copyright (C) 2017 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.wifi.rtt;
  
  import static android.net.wifi.ScanResult.InformationElement.EID_HT_CAPABILITIES;
  import static android.net.wifi.ScanResult.InformationElement.EID_VHT_CAPABILITIES;
  
  import android.annotation.IntDef;
  import android.annotation.NonNull;
  import android.annotation.SystemApi;
  import android.net.MacAddress;
  import android.net.wifi.ScanResult;
  import android.net.wifi.aware.PeerHandle;
  import android.os.Parcel;
  import android.os.Parcelable;
  import android.util.Log;
  
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.util.Objects;
  
  /**
   * Defines the configuration of an IEEE 802.11mc Responder. The Responder may be an Access Point
   * (AP), a Wi-Fi Aware device, or a manually configured Responder.
   * <p>
   * A Responder configuration may be constructed from a {@link ScanResult} or manually (with the
   * data obtained out-of-band from a peer).
   *
   * @hide
   */
  @SystemApi
  public final class ResponderConfig implements Parcelable {
      private static final String TAG = "ResponderConfig";
      private static final int AWARE_BAND_2_DISCOVERY_CHANNEL = 2437;
  
      /** @hide */
      @IntDef({RESPONDER_AP, RESPONDER_STA, RESPONDER_P2P_GO, RESPONDER_P2P_CLIENT, RESPONDER_AWARE})
      @Retention(RetentionPolicy.SOURCE)
      public @interface ResponderType {
      }
  
      /**
       * Responder is an AP.
       */
      public static final int RESPONDER_AP = 0;
      /**
       * Responder is a STA.
       */
      public static final int RESPONDER_STA = 1;
      /**
       * Responder is a Wi-Fi Direct Group Owner (GO).
       */
      public static final int RESPONDER_P2P_GO = 2;
      /**
       * Responder is a Wi-Fi Direct Group Client.
       */
      public static final int RESPONDER_P2P_CLIENT = 3;
      /**
       * Responder is a Wi-Fi Aware device.
       */
      public static final int RESPONDER_AWARE = 4;
  
  
      /** @hide */
      @IntDef({
              CHANNEL_WIDTH_20MHZ, CHANNEL_WIDTH_40MHZ, CHANNEL_WIDTH_80MHZ, CHANNEL_WIDTH_160MHZ,
              CHANNEL_WIDTH_80MHZ_PLUS_MHZ})
      @Retention(RetentionPolicy.SOURCE)
      public @interface ChannelWidth {
      }
  
      /**
       * Channel bandwidth is 20 MHZ
       */
      public static final int CHANNEL_WIDTH_20MHZ = 0;
      /**
       * Channel bandwidth is 40 MHZ
       */
      public static final int CHANNEL_WIDTH_40MHZ = 1;
      /**
       * Channel bandwidth is 80 MHZ
       */
      public static final int CHANNEL_WIDTH_80MHZ = 2;
      /**
       * Channel bandwidth is 160 MHZ
       */
      public static final int CHANNEL_WIDTH_160MHZ = 3;
      /**
       * Channel bandwidth is 160 MHZ, but 80MHZ + 80MHZ
       */
      public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4;
  
      /** @hide */
      @IntDef({PREAMBLE_LEGACY, PREAMBLE_HT, PREAMBLE_VHT})
      @Retention(RetentionPolicy.SOURCE)
      public @interface PreambleType {
      }
  
      /**
       * Preamble type: Legacy.
       */
      public static final int PREAMBLE_LEGACY = 0;
  
      /**
       * Preamble type: HT.
       */
      public static final int PREAMBLE_HT = 1;
  
      /**
       * Preamble type: VHT.
       */
      public static final int PREAMBLE_VHT = 2;
  
  
      /**
       * The MAC address of the Responder. Will be null if a Wi-Fi Aware peer identifier (the
       * peerHandle field) ise used to identify the Responder.
       */
      public final MacAddress macAddress;
  
      /**
       * The peer identifier of a Wi-Fi Aware Responder. Will be null if a MAC Address (the macAddress
       * field) is used to identify the Responder.
       */
      public final PeerHandle peerHandle;
  
      /**
       * The device type of the Responder.
       */
      public final int responderType;
  
      /**
       * Indicates whether the Responder device supports IEEE 802.11mc.
       */
      public final boolean supports80211mc;
  
      /**
       * Responder channel bandwidth, specified using {@link ChannelWidth}.
       */
      public final int channelWidth;
  
      /**
       * The primary 20 MHz frequency (in MHz) of the channel of the Responder.
       */
      public final int frequency;
  
      /**
       * Not used if the {@link #channelWidth} is 20 MHz. If the Responder uses 40, 80 or 160 MHz,
       * this is the center frequency (in MHz), if the Responder uses 80 + 80 MHz, this is the
       * center frequency of the first segment (in MHz).
       */
      public final int centerFreq0;
  
      /**
       * Only used if the {@link #channelWidth} is 80 + 80 MHz. If the Responder uses 80 + 80 MHz,
       * this is the center frequency of the second segment (in MHz).
       */
      public final int centerFreq1;
  
      /**
       * The preamble used by the Responder, specified using {@link PreambleType}.
       */
      public final int preamble;
  
      /**
       * Constructs Responder configuration, using a MAC address to identify the Responder.
       *
       * @param macAddress      The MAC address of the Responder.
       * @param responderType   The type of the responder device, specified using
       *                        {@link ResponderType}.
       * @param supports80211mc Indicates whether the responder supports IEEE 802.11mc.
       * @param channelWidth    Responder channel bandwidth, specified using {@link ChannelWidth}.
       * @param frequency       The primary 20 MHz frequency (in MHz) of the channel of the Responder.
       * @param centerFreq0     Not used if the {@code channelWidth} is 20 MHz. If the Responder uses
       *                        40, 80 or 160 MHz, this is the center frequency (in MHz), if the
       *                        Responder uses 80 + 80 MHz, this is the center frequency of the first
       *                        segment (in MHz).
       * @param centerFreq1     Only used if the {@code channelWidth} is 80 + 80 MHz. If the
       *                        Responder
       *                        uses 80 + 80 MHz, this is the center frequency of the second segment
       *                        (in
       *                        MHz).
       * @param preamble        The preamble used by the Responder, specified using
       *                        {@link PreambleType}.
       */
      public ResponderConfig(@NonNull MacAddress macAddress, @ResponderType int responderType,
              boolean supports80211mc, @ChannelWidth int channelWidth, int frequency, int centerFreq0,
              int centerFreq1, @PreambleType int preamble) {
          if (macAddress == null) {
              throw new IllegalArgumentException(
                      "Invalid ResponderConfig - must specify a MAC address");
          }
          this.macAddress = macAddress;
          this.peerHandle = null;
          this.responderType = responderType;
          this.supports80211mc = supports80211mc;
          this.channelWidth = channelWidth;
          this.frequency = frequency;
          this.centerFreq0 = centerFreq0;
          this.centerFreq1 = centerFreq1;
          this.preamble = preamble;
      }
  
      /**
       * Constructs Responder configuration, using a Wi-Fi Aware PeerHandle to identify the Responder.
       *
       * @param peerHandle      The Wi-Fi Aware peer identifier of the Responder.
       * @param responderType   The type of the responder device, specified using
       *                        {@link ResponderType}.
       * @param supports80211mc Indicates whether the responder supports IEEE 802.11mc.
       * @param channelWidth    Responder channel bandwidth, specified using {@link ChannelWidth}.
       * @param frequency       The primary 20 MHz frequency (in MHz) of the channel of the Responder.
       * @param centerFreq0     Not used if the {@code channelWidth} is 20 MHz. If the Responder uses
       *                        40, 80 or 160 MHz, this is the center frequency (in MHz), if the
       *                        Responder uses 80 + 80 MHz, this is the center frequency of the first
       *                        segment (in MHz).
       * @param centerFreq1     Only used if the {@code channelWidth} is 80 + 80 MHz. If the
       *                        Responder
       *                        uses 80 + 80 MHz, this is the center frequency of the second segment
       *                        (in
       *                        MHz).
       * @param preamble        The preamble used by the Responder, specified using
       *                        {@link PreambleType}.
       */
      public ResponderConfig(@NonNull PeerHandle peerHandle, @ResponderType int responderType,
              boolean supports80211mc, @ChannelWidth int channelWidth, int frequency, int centerFreq0,
              int centerFreq1, @PreambleType int preamble) {
          this.macAddress = null;
          this.peerHandle = peerHandle;
          this.responderType = responderType;
          this.supports80211mc = supports80211mc;
          this.channelWidth = channelWidth;
          this.frequency = frequency;
          this.centerFreq0 = centerFreq0;
          this.centerFreq1 = centerFreq1;
          this.preamble = preamble;
      }
  
      /**
       * Constructs Responder configuration. This is an internal-only constructor which specifies both
       * a MAC address and a Wi-Fi PeerHandle to identify the Responder.
       *
       * @param macAddress      The MAC address of the Responder.
       * @param peerHandle      The Wi-Fi Aware peer identifier of the Responder.
       * @param responderType   The type of the responder device, specified using
       *                        {@link ResponderType}.
       * @param supports80211mc Indicates whether the responder supports IEEE 802.11mc.
       * @param channelWidth    Responder channel bandwidth, specified using {@link ChannelWidth}.
       * @param frequency       The primary 20 MHz frequency (in MHz) of the channel of the Responder.
       * @param centerFreq0     Not used if the {@code channelWidth} is 20 MHz. If the Responder uses
       *                        40, 80 or 160 MHz, this is the center frequency (in MHz), if the
       *                        Responder uses 80 + 80 MHz, this is the center frequency of the first
       *                        segment (in MHz).
       * @param centerFreq1     Only used if the {@code channelWidth} is 80 + 80 MHz. If the
       *                        Responder
       *                        uses 80 + 80 MHz, this is the center frequency of the second segment
       *                        (in
       *                        MHz).
       * @param preamble        The preamble used by the Responder, specified using
       *                        {@link PreambleType}.
       * @hide
       */
      public ResponderConfig(@NonNull MacAddress macAddress, @NonNull PeerHandle peerHandle,
              @ResponderType int responderType, boolean supports80211mc,
              @ChannelWidth int channelWidth, int frequency, int centerFreq0, int centerFreq1,
              @PreambleType int preamble) {
          this.macAddress = macAddress;
          this.peerHandle = peerHandle;
          this.responderType = responderType;
          this.supports80211mc = supports80211mc;
          this.channelWidth = channelWidth;
          this.frequency = frequency;
          this.centerFreq0 = centerFreq0;
          this.centerFreq1 = centerFreq1;
          this.preamble = preamble;
      }
  
      /**
       * Creates a Responder configuration from a {@link ScanResult} corresponding to an Access
       * Point (AP), which can be obtained from {@link android.net.wifi.WifiManager#getScanResults()}.
       */
      public static ResponderConfig fromScanResult(ScanResult scanResult) {
          MacAddress macAddress = MacAddress.fromString(scanResult.BSSID);
          int responderType = RESPONDER_AP;
          boolean supports80211mc = scanResult.is80211mcResponder();
          int channelWidth = translateScanResultChannelWidth(scanResult.channelWidth);
          int frequency = scanResult.frequency;
          int centerFreq0 = scanResult.centerFreq0;
          int centerFreq1 = scanResult.centerFreq1;
  
          int preamble;
          if (scanResult.informationElements != null && scanResult.informationElements.length != 0) {
              boolean htCapabilitiesPresent = false;
              boolean vhtCapabilitiesPresent = false;
              for (ScanResult.InformationElement ie : scanResult.informationElements) {
                  if (ie.id == EID_HT_CAPABILITIES) {
                      htCapabilitiesPresent = true;
                  } else if (ie.id == EID_VHT_CAPABILITIES) {
                      vhtCapabilitiesPresent = true;
                  }
              }
              if (vhtCapabilitiesPresent) {
                  preamble = PREAMBLE_VHT;
              } else if (htCapabilitiesPresent) {
                  preamble = PREAMBLE_HT;
              } else {
                  preamble = PREAMBLE_LEGACY;
              }
          } else {
              Log.e(TAG, "Scan Results do not contain IEs - using backup method to select preamble");
              if (channelWidth == CHANNEL_WIDTH_80MHZ || channelWidth == CHANNEL_WIDTH_160MHZ) {
                  preamble = PREAMBLE_VHT;
              } else {
                  preamble = PREAMBLE_HT;
              }
          }
  
          return new ResponderConfig(macAddress, responderType, supports80211mc, channelWidth,
                  frequency, centerFreq0, centerFreq1, preamble);
      }
  
      /**
       * Creates a Responder configuration from a MAC address corresponding to a Wi-Fi Aware
       * Responder. The Responder parameters are set to defaults.
       */
      public static ResponderConfig fromWifiAwarePeerMacAddressWithDefaults(MacAddress macAddress) {
          /* Note: the parameters are those of the Aware discovery channel (channel 6). A Responder
           * is expected to be brought up and available to negotiate a maximum accuracy channel
           * (i.e. Band 5 @ 80MHz). A Responder is brought up on the peer by starting an Aware
           * Unsolicited Publisher with Ranging enabled.
           */
          return new ResponderConfig(macAddress, RESPONDER_AWARE, true, CHANNEL_WIDTH_20MHZ,
                  AWARE_BAND_2_DISCOVERY_CHANNEL, 0, 0, PREAMBLE_HT);
      }
  
      /**
       * Creates a Responder configuration from a {@link PeerHandle} corresponding to a Wi-Fi Aware
       * Responder. The Responder parameters are set to defaults.
       */
      public static ResponderConfig fromWifiAwarePeerHandleWithDefaults(PeerHandle peerHandle) {
          /* Note: the parameters are those of the Aware discovery channel (channel 6). A Responder
           * is expected to be brought up and available to negotiate a maximum accuracy channel
           * (i.e. Band 5 @ 80MHz). A Responder is brought up on the peer by starting an Aware
           * Unsolicited Publisher with Ranging enabled.
           */
          return new ResponderConfig(peerHandle, RESPONDER_AWARE, true, CHANNEL_WIDTH_20MHZ,
                  AWARE_BAND_2_DISCOVERY_CHANNEL, 0, 0, PREAMBLE_HT);
      }
  
      /**
       * Check whether the Responder configuration is valid.
       *
       * @return true if valid, false otherwise.
       * @hide
       */
      public boolean isValid(boolean awareSupported) {
          if (macAddress == null && peerHandle == null || macAddress != null && peerHandle != null) {
              return false;
          }
          if (!awareSupported && responderType == RESPONDER_AWARE) {
              return false;
          }
  
          return true;
      }
  
      @Override
      public int describeContents() {
          return 0;
      }
  
      @Override
      public void writeToParcel(Parcel dest, int flags) {
          if (macAddress == null) {
              dest.writeBoolean(false);
          } else {
              dest.writeBoolean(true);
              macAddress.writeToParcel(dest, flags);
          }
          if (peerHandle == null) {
              dest.writeBoolean(false);
          } else {
              dest.writeBoolean(true);
              dest.writeInt(peerHandle.peerId);
          }
          dest.writeInt(responderType);
          dest.writeInt(supports80211mc ? 1 : 0);
          dest.writeInt(channelWidth);
          dest.writeInt(frequency);
          dest.writeInt(centerFreq0);
          dest.writeInt(centerFreq1);
          dest.writeInt(preamble);
      }
  
      public static final @android.annotation.NonNull Creator<ResponderConfig> CREATOR = new Creator<ResponderConfig>() {
          @Override
          public ResponderConfig[] newArray(int size) {
              return new ResponderConfig[size];
          }
  
          @Override
          public ResponderConfig createFromParcel(Parcel in) {
              boolean macAddressPresent = in.readBoolean();
              MacAddress macAddress = null;
              if (macAddressPresent) {
                  macAddress = MacAddress.CREATOR.createFromParcel(in);
              }
              boolean peerHandlePresent = in.readBoolean();
              PeerHandle peerHandle = null;
              if (peerHandlePresent) {
                  peerHandle = new PeerHandle(in.readInt());
              }
              int responderType = in.readInt();
              boolean supports80211mc = in.readInt() == 1;
              int channelWidth = in.readInt();
              int frequency = in.readInt();
              int centerFreq0 = in.readInt();
              int centerFreq1 = in.readInt();
              int preamble = in.readInt();
  
              if (peerHandle == null) {
                  return new ResponderConfig(macAddress, responderType, supports80211mc, channelWidth,
                          frequency, centerFreq0, centerFreq1, preamble);
              } else {
                  return new ResponderConfig(peerHandle, responderType, supports80211mc, channelWidth,
                          frequency, centerFreq0, centerFreq1, preamble);
              }
          }
      };
  
      @Override
      public boolean equals(Object o) {
          if (this == o) {
              return true;
          }
  
          if (!(o instanceof ResponderConfig)) {
              return false;
          }
  
          ResponderConfig lhs = (ResponderConfig) o;
  
          return Objects.equals(macAddress, lhs.macAddress) && Objects.equals(peerHandle,
                  lhs.peerHandle) && responderType == lhs.responderType
                  && supports80211mc == lhs.supports80211mc && channelWidth == lhs.channelWidth
                  && frequency == lhs.frequency && centerFreq0 == lhs.centerFreq0
                  && centerFreq1 == lhs.centerFreq1 && preamble == lhs.preamble;
      }
  
      @Override
      public int hashCode() {
          return Objects.hash(macAddress, peerHandle, responderType, supports80211mc, channelWidth,
                  frequency, centerFreq0, centerFreq1, preamble);
      }
  
      /** @hide */
      @Override
      public String toString() {
          return new StringBuffer("ResponderConfig: macAddress=").append(macAddress).append(
                  ", peerHandle=").append(peerHandle == null ? "<null>" : peerHandle.peerId).append(
                  ", responderType=").append(responderType).append(", supports80211mc=").append(
                  supports80211mc).append(", channelWidth=").append(channelWidth).append(
                  ", frequency=").append(frequency).append(", centerFreq0=").append(
                  centerFreq0).append(", centerFreq1=").append(centerFreq1).append(
                  ", preamble=").append(preamble).toString();
      }
  
      /** @hide */
      static int translateScanResultChannelWidth(int scanResultChannelWidth) {
          switch (scanResultChannelWidth) {
              case ScanResult.CHANNEL_WIDTH_20MHZ:
                  return CHANNEL_WIDTH_20MHZ;
              case ScanResult.CHANNEL_WIDTH_40MHZ:
                  return CHANNEL_WIDTH_40MHZ;
              case ScanResult.CHANNEL_WIDTH_80MHZ:
                  return CHANNEL_WIDTH_80MHZ;
              case ScanResult.CHANNEL_WIDTH_160MHZ:
                  return CHANNEL_WIDTH_160MHZ;
              case ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ:
                  return CHANNEL_WIDTH_80MHZ_PLUS_MHZ;
              default:
                  throw new IllegalArgumentException(
                          "translateScanResultChannelWidth: bad " + scanResultChannelWidth);
          }
      }
  }
  