Home | History | Annotate | Download | only in anqp
      1 package com.android.server.wifi.anqp;
      2 
      3 import java.net.ProtocolException;
      4 import java.nio.ByteBuffer;
      5 import java.nio.ByteOrder;
      6 import java.nio.charset.StandardCharsets;
      7 import java.util.ArrayList;
      8 import java.util.Collections;
      9 import java.util.List;
     10 import java.util.ListIterator;
     11 import java.util.Set;
     12 
     13 /**
     14  * Factory to build a collection of 802.11u ANQP elements from a byte buffer.
     15  */
     16 public class ANQPFactory {
     17 
     18     private static final Constants.ANQPElementType[] BaseANQPSet = new Constants.ANQPElementType[]{
     19             Constants.ANQPElementType.ANQPVenueName,
     20             Constants.ANQPElementType.ANQPNwkAuthType,
     21             Constants.ANQPElementType.ANQPRoamingConsortium,
     22             Constants.ANQPElementType.ANQPIPAddrAvailability,
     23             Constants.ANQPElementType.ANQPNAIRealm,
     24             Constants.ANQPElementType.ANQP3GPPNetwork,
     25             Constants.ANQPElementType.ANQPDomName
     26     };
     27 
     28     private static final Constants.ANQPElementType[] HS20ANQPSet = new Constants.ANQPElementType[]{
     29             Constants.ANQPElementType.HSFriendlyName,
     30             Constants.ANQPElementType.HSWANMetrics,
     31             Constants.ANQPElementType.HSConnCapability
     32     };
     33 
     34     public static Constants.ANQPElementType[] getBaseANQPSet() {
     35         return BaseANQPSet;
     36     }
     37 
     38     public static Constants.ANQPElementType[] getHS20ANQPSet() {
     39         return HS20ANQPSet;
     40     }
     41 
     42     public static ByteBuffer buildQueryRequest(Set<Constants.ANQPElementType> elements,
     43                                                ByteBuffer target) {
     44         List<Constants.ANQPElementType> list = new ArrayList<Constants.ANQPElementType>(elements);
     45         Collections.sort(list);
     46 
     47         ListIterator<Constants.ANQPElementType> elementIterator = list.listIterator();
     48 
     49         target.order(ByteOrder.LITTLE_ENDIAN);
     50         target.putShort((short) Constants.ANQP_QUERY_LIST);
     51         int lenPos = target.position();
     52         target.putShort((short) 0);
     53 
     54         while (elementIterator.hasNext()) {
     55             Integer id = Constants.getANQPElementID(elementIterator.next());
     56             if (id != null) {
     57                 target.putShort(id.shortValue());
     58             } else {
     59                 elementIterator.previous();
     60                 break;
     61             }
     62         }
     63         target.putShort(lenPos, (short) (target.position() - lenPos - Constants.BYTES_IN_SHORT));
     64 
     65         // Start a new vendor specific element for HS2.0 elements:
     66         if (elementIterator.hasNext()) {
     67             target.putShort((short) Constants.ANQP_VENDOR_SPEC);
     68             int vsLenPos = target.position();
     69             target.putShort((short) 0);
     70 
     71             target.putInt(Constants.HS20_PREFIX);
     72             target.put((byte) Constants.HS_QUERY_LIST);
     73             target.put((byte) 0);
     74 
     75             while (elementIterator.hasNext()) {
     76                 Constants.ANQPElementType elementType = elementIterator.next();
     77                 Integer id = Constants.getHS20ElementID(elementType);
     78                 if (id == null) {
     79                     throw new RuntimeException("Unmapped ANQPElementType: " + elementType);
     80                 } else {
     81                     target.put(id.byteValue());
     82                 }
     83             }
     84             target.putShort(vsLenPos,
     85                     (short) (target.position() - vsLenPos - Constants.BYTES_IN_SHORT));
     86         }
     87 
     88         target.flip();
     89         return target;
     90     }
     91 
     92     public static ByteBuffer buildHomeRealmRequest(List<String> realmNames, ByteBuffer target) {
     93         target.order(ByteOrder.LITTLE_ENDIAN);
     94         target.putShort((short) Constants.ANQP_VENDOR_SPEC);
     95         int lenPos = target.position();
     96         target.putShort((short) 0);
     97 
     98         target.putInt(Constants.HS20_PREFIX);
     99         target.put((byte) Constants.HS_NAI_HOME_REALM_QUERY);
    100         target.put((byte) 0);
    101 
    102         target.put((byte) realmNames.size());
    103         for (String realmName : realmNames) {
    104             target.put((byte) Constants.UTF8_INDICATOR);
    105             byte[] octets = realmName.getBytes(StandardCharsets.UTF_8);
    106             target.put((byte) octets.length);
    107             target.put(octets);
    108         }
    109         target.putShort(lenPos, (short) (target.position() - lenPos - Constants.BYTES_IN_SHORT));
    110 
    111         target.flip();
    112         return target;
    113     }
    114 
    115     public static ByteBuffer buildIconRequest(String fileName, ByteBuffer target) {
    116         target.order(ByteOrder.LITTLE_ENDIAN);
    117         target.putShort((short) Constants.ANQP_VENDOR_SPEC);
    118         int lenPos = target.position();
    119         target.putShort((short) 0);
    120 
    121         target.putInt(Constants.HS20_PREFIX);
    122         target.put((byte) Constants.HS_ICON_REQUEST);
    123         target.put((byte) 0);
    124 
    125         target.put(fileName.getBytes(StandardCharsets.UTF_8));
    126         target.putShort(lenPos, (short) (target.position() - lenPos - Constants.BYTES_IN_SHORT));
    127 
    128         target.flip();
    129         return target;
    130     }
    131 
    132     public static List<ANQPElement> parsePayload(ByteBuffer payload) throws ProtocolException {
    133         payload.order(ByteOrder.LITTLE_ENDIAN);
    134         List<ANQPElement> elements = new ArrayList<ANQPElement>();
    135         while (payload.hasRemaining()) {
    136             elements.add(buildElement(payload));
    137         }
    138         return elements;
    139     }
    140 
    141     private static ANQPElement buildElement(ByteBuffer payload) throws ProtocolException {
    142         if (payload.remaining() < 4)
    143             throw new ProtocolException("Runt payload: " + payload.remaining());
    144 
    145         int infoIDNumber = payload.getShort() & Constants.SHORT_MASK;
    146         Constants.ANQPElementType infoID = Constants.mapANQPElement(infoIDNumber);
    147         if (infoID == null) {
    148             throw new ProtocolException("Bad info ID: " + infoIDNumber);
    149         }
    150         int length = payload.getShort() & Constants.SHORT_MASK;
    151 
    152         if (payload.remaining() < length) {
    153             throw new ProtocolException("Truncated payload: " +
    154                     payload.remaining() + " vs " + length);
    155         }
    156         return buildElement(payload, infoID, length);
    157     }
    158 
    159     public static ANQPElement buildElement(ByteBuffer payload, Constants.ANQPElementType infoID,
    160                                             int length) throws ProtocolException {
    161         ByteBuffer elementPayload = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
    162         payload.position(payload.position() + length);
    163         elementPayload.limit(elementPayload.position() + length);
    164 
    165         switch (infoID) {
    166             case ANQPCapabilityList:
    167                 return new CapabilityListElement(infoID, elementPayload);
    168             case ANQPVenueName:
    169                 return new VenueNameElement(infoID, elementPayload);
    170             case ANQPEmergencyNumber:
    171                 return new EmergencyNumberElement(infoID, elementPayload);
    172             case ANQPNwkAuthType:
    173                 return new NetworkAuthenticationTypeElement(infoID, elementPayload);
    174             case ANQPRoamingConsortium:
    175                 return new RoamingConsortiumElement(infoID, elementPayload);
    176             case ANQPIPAddrAvailability:
    177                 return new IPAddressTypeAvailabilityElement(infoID, elementPayload);
    178             case ANQPNAIRealm:
    179                 return new NAIRealmElement(infoID, elementPayload);
    180             case ANQP3GPPNetwork:
    181                 return new ThreeGPPNetworkElement(infoID, elementPayload);
    182             case ANQPGeoLoc:
    183                 return new GEOLocationElement(infoID, elementPayload);
    184             case ANQPCivicLoc:
    185                 return new CivicLocationElement(infoID, elementPayload);
    186             case ANQPLocURI:
    187                 return new GenericStringElement(infoID, elementPayload);
    188             case ANQPDomName:
    189                 return new DomainNameElement(infoID, elementPayload);
    190             case ANQPEmergencyAlert:
    191                 return new GenericStringElement(infoID, elementPayload);
    192             case ANQPTDLSCap:
    193                 return new GenericBlobElement(infoID, elementPayload);
    194             case ANQPEmergencyNAI:
    195                 return new GenericStringElement(infoID, elementPayload);
    196             case ANQPNeighborReport:
    197                 return new GenericBlobElement(infoID, elementPayload);
    198             case ANQPVendorSpec:
    199                 if (elementPayload.remaining() > 5) {
    200                     int oi = elementPayload.getInt();
    201                     if (oi != Constants.HS20_PREFIX) {
    202                         return null;
    203                     }
    204                     int subType = elementPayload.get() & Constants.BYTE_MASK;
    205                     Constants.ANQPElementType hs20ID = Constants.mapHS20Element(subType);
    206                     if (hs20ID == null) {
    207                         throw new ProtocolException("Bad HS20 info ID: " + subType);
    208                     }
    209                     elementPayload.get();   // Skip the reserved octet
    210                     return buildHS20Element(hs20ID, elementPayload);
    211                 } else {
    212                     return new GenericBlobElement(infoID, elementPayload);
    213                 }
    214             default:
    215                 throw new ProtocolException("Unknown element ID: " + infoID);
    216         }
    217     }
    218 
    219     public static ANQPElement buildHS20Element(Constants.ANQPElementType infoID,
    220                                                 ByteBuffer payload) throws ProtocolException {
    221         switch (infoID) {
    222             case HSCapabilityList:
    223                 return new HSCapabilityListElement(infoID, payload);
    224             case HSFriendlyName:
    225                 return new HSFriendlyNameElement(infoID, payload);
    226             case HSWANMetrics:
    227                 return new HSWanMetricsElement(infoID, payload);
    228             case HSConnCapability:
    229                 return new HSConnectionCapabilityElement(infoID, payload);
    230             case HSOperatingclass:
    231                 return new GenericBlobElement(infoID, payload);
    232             case HSOSUProviders:
    233                 return new HSOsuProvidersElement(infoID, payload);
    234             case HSIconFile:
    235                 return new HSIconFileElement(infoID, payload);
    236             default:
    237                 return null;
    238         }
    239     }
    240 }
    241