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.BufferUnderflowException;
  import java.nio.ByteBuffer;
  import java.nio.ByteOrder;
  
  /**
   * Factory to build a collection of 802.11u ANQP elements from a byte buffer.
   */
  public class ANQPParser {
      /**
       * The OI value for Hotspot 2.0 ANQP-element.
       */
      @VisibleForTesting
      public static final int VENDOR_SPECIFIC_HS20_OI = 0x506F9A;
  
      /**
       * The Type value for Hotspot 2.0 ANQP-element.
       */
      @VisibleForTesting
      public static final int VENDOR_SPECIFIC_HS20_TYPE = 0x11;
  
      /**
       * Parse an ANQP element from the pass-in byte buffer.
       *
       * Note: Each Hotspot 2.0 Release 2 element will be wrapped inside a Vendor Specific element
       * in the ANQP response from the AP.  However, the lower layer (e.g. wpa_supplicant) should
       * already take care of parsing those elements out of Vendor Specific elements.  To be safe,
       * we will parse the Vendor Specific elements for non-Hotspot 2.0 Release elements or in
       * the case they're not parsed by the lower layer.
       *
       * @param infoID The ANQP element type
       * @param payload The buffer to read from
       * @return {@link com.android.server.wifi.hotspot2.anqp.ANQPElement}
       * @throws BufferUnderflowException
       * @throws ProtocolException
       */
      public static ANQPElement parseElement(Constants.ANQPElementType infoID, ByteBuffer payload)
              throws ProtocolException {
          switch (infoID) {
              case ANQPVenueName:
                  return VenueNameElement.parse(payload);
              case ANQPRoamingConsortium:
                  return RoamingConsortiumElement.parse(payload);
              case ANQPIPAddrAvailability:
                  return IPAddressTypeAvailabilityElement.parse(payload);
              case ANQPNAIRealm:
                  return NAIRealmElement.parse(payload);
              case ANQP3GPPNetwork:
                  return ThreeGPPNetworkElement.parse(payload);
              case ANQPDomName:
                  return DomainNameElement.parse(payload);
              case ANQPVendorSpec:
                  return parseVendorSpecificElement(payload);
              default:
                  throw new ProtocolException("Unknown element ID: " + infoID);
          }
      }
  
      /**
       * Parse a Hotspot 2.0 Release 2 ANQP element from the pass-in byte buffer.
       *
       * @param infoID The ANQP element ID
       * @param payload The buffer to read from
       * @return {@link com.android.server.wifi.hotspot2.anqp.ANQPElement}
       * @throws BufferUnderflowException
       * @throws ProtocolException
       */
      public static ANQPElement parseHS20Element(Constants.ANQPElementType infoID,
              ByteBuffer payload) throws ProtocolException {
          switch (infoID) {
              case HSFriendlyName:
                  return HSFriendlyNameElement.parse(payload);
              case HSWANMetrics:
                  return HSWanMetricsElement.parse(payload);
              case HSConnCapability:
                  return HSConnectionCapabilityElement.parse(payload);
              case HSOSUProviders:
                  return HSOsuProvidersElement.parse(payload);
              default:
                  throw new ProtocolException("Unknown element ID: " + infoID);
          }
      }
  
      /**
       * Parse the ANQP vendor specific element.  Currently only supports the vendor specific
       * element that contained Hotspot 2.0 ANQP-element.
       *
       * Format of a ANQP Vendor Specific element:
       * | OI | Type | Subtype | Reserved | Payload |
       *   3     1        1         1       variable
       *
       * @param payload The buffer to read from
       * @return {@link ANQPElement}
       * @throws BufferUnderflowException
       * @throws ProtocolException
       */
      private static ANQPElement parseVendorSpecificElement(ByteBuffer payload)
              throws ProtocolException {
          int oi = (int) ByteBufferReader.readInteger(payload, ByteOrder.BIG_ENDIAN, 3);
          int type = payload.get() & 0xFF;
  
          if (oi != VENDOR_SPECIFIC_HS20_OI || type != VENDOR_SPECIFIC_HS20_TYPE) {
              throw new ProtocolException("Unsupported vendor specific OI=" + oi + " type=" + type);
          }
  
          int subType = payload.get() & 0xFF;
          Constants.ANQPElementType hs20ID = Constants.mapHS20Element(subType);
          if (hs20ID == null) {
              throw new ProtocolException("Unsupported subtype: " + subType);
          }
          payload.get();     // Skip the reserved byte
          return parseHS20Element(hs20ID, payload);
      }
  }
  