content: /*
   * Copyright (C) 2016 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 com.android.server.wifi.hotspot2.anqp;
  
  import com.android.internal.annotations.VisibleForTesting;
  import com.android.server.wifi.ByteBufferReader;
  
  import java.net.ProtocolException;
  import java.nio.ByteBuffer;
  import java.nio.ByteOrder;
  
  /**
   * The WAN Metrics vendor specific ANQP Element,
   * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
   * section 4.4
   *
   * Format:
   * | WAN Info | Downlink Speed | Uplink Speed | Downlink Load | Uplink Load | LMD |
   *      1             4               4               1              1         2
   *
   * WAN Info Format:
   * | Link Status | Symmetric Link | At Capacity | Reserved |
   *      B0 B1            B2             B3        B4 - B7
   */
  public class HSWanMetricsElement extends ANQPElement {
      public static final int LINK_STATUS_RESERVED = 0;
      public static final int LINK_STATUS_UP = 1;
      public static final int LINK_STATUS_DOWN = 2;
      public static final int LINK_STATUS_TEST = 3;
  
      @VisibleForTesting
      public static final int EXPECTED_BUFFER_SIZE = 13;
  
      @VisibleForTesting
      public static final int LINK_STATUS_MASK = (1 << 0 | 1 << 1);
  
      @VisibleForTesting
      public static final int SYMMETRIC_LINK_MASK = 1 << 2;
  
      @VisibleForTesting
      public static final int AT_CAPACITY_MASK = 1 << 3;
  
      private static final int MAX_LOAD = 256;
  
      private final int mStatus;
      private final boolean mSymmetric;
      private final boolean mCapped;
      private final long mDownlinkSpeed;
      private final long mUplinkSpeed;
      private final int mDownlinkLoad;
      private final int mUplinkLoad;
      private final int mLMD;     // Load Measurement Duration.
  
      @VisibleForTesting
      public HSWanMetricsElement(int status, boolean symmetric, boolean capped, long downlinkSpeed,
              long uplinkSpeed, int downlinkLoad, int uplinkLoad, int lmd) {
          super(Constants.ANQPElementType.HSWANMetrics);
          mStatus = status;
          mSymmetric = symmetric;
          mCapped = capped;
          mDownlinkSpeed = downlinkSpeed;
          mUplinkSpeed = uplinkSpeed;
          mDownlinkLoad = downlinkLoad;
          mUplinkLoad = uplinkLoad;
          mLMD = lmd;
      }
  
      /**
       * Parse a HSWanMetricsElement from the given buffer.
       *
       * @param payload The byte buffer to read from
       * @return {@link HSWanMetricsElement}
       * @throws ProtocolException
       */
      public static HSWanMetricsElement parse(ByteBuffer payload)
              throws ProtocolException {
          if (payload.remaining() != EXPECTED_BUFFER_SIZE) {
              throw new ProtocolException("Unexpected buffer size: " + payload.remaining());
          }
  
          int wanInfo = payload.get() & 0xFF;
          int status = wanInfo & LINK_STATUS_MASK;
          boolean symmetric = (wanInfo & SYMMETRIC_LINK_MASK) != 0;
          boolean capped = (wanInfo & AT_CAPACITY_MASK) != 0;
          long downlinkSpeed = ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 4)
                  & 0xFFFFFFFFL;
          long uplinkSpeed = ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 4)
                  & 0xFFFFFFFFL;
          int downlinkLoad = payload.get() & 0xFF;
          int uplinkLoad = payload.get() & 0xFF;
          int lmd = (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2) & 0xFFFF;
          return new HSWanMetricsElement(status, symmetric, capped, downlinkSpeed, uplinkSpeed,
                  downlinkLoad, uplinkLoad, lmd);
      }
  
      public int getStatus() {
          return mStatus;
      }
  
      public boolean isSymmetric() {
          return mSymmetric;
      }
  
      public boolean isCapped() {
          return mCapped;
      }
  
      public long getDownlinkSpeed() {
          return mDownlinkSpeed;
      }
  
      public long getUplinkSpeed() {
          return mUplinkSpeed;
      }
  
      public int getDownlinkLoad() {
          return mDownlinkLoad;
      }
  
      public int getUplinkLoad() {
          return mUplinkLoad;
      }
  
      public int getLMD() {
          return mLMD;
      }
  
      @Override
      public boolean equals(Object thatObject) {
          if (this == thatObject) {
              return true;
          }
          if (!(thatObject instanceof HSWanMetricsElement)) {
              return false;
          }
          HSWanMetricsElement that = (HSWanMetricsElement) thatObject;
          return mStatus == that.mStatus
                  && mSymmetric == that.mSymmetric
                  && mCapped == that.mCapped
                  && mDownlinkSpeed == that.mDownlinkSpeed
                  && mUplinkSpeed == that.mUplinkSpeed
                  && mDownlinkLoad == that.mDownlinkLoad
                  && mUplinkLoad == that.mUplinkLoad
                  && mLMD == that.mLMD;
      }
  
      @Override
      public int hashCode() {
          return (int) (mStatus + mDownlinkSpeed + mUplinkSpeed + mDownlinkLoad
                  + mUplinkLoad + mLMD);
      }
  
      @Override
      public String toString() {
          return String.format("HSWanMetrics{mStatus=%s, mSymmetric=%s, mCapped=%s, " +
                  "mDlSpeed=%d, mUlSpeed=%d, mDlLoad=%f, mUlLoad=%f, mLMD=%d}",
                  mStatus, mSymmetric, mCapped,
                  mDownlinkSpeed, mUplinkSpeed,
                  mDownlinkLoad * 100.0 / MAX_LOAD,
                  mUplinkLoad * 100.0 / MAX_LOAD,
                  mLMD);
      }
  }
  