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