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 getBaseANQPSet(boolean includeRC)49 public static List<Constants.ANQPElementType> getBaseANQPSet(boolean includeRC) { 50 return includeRC ? BaseANQPSet1 : BaseANQPSet2; 51 } 52 getHS20ANQPSet(boolean includeOSU)53 public static List<Constants.ANQPElementType> getHS20ANQPSet(boolean includeOSU) { 54 return includeOSU ? HS20ANQPSetwOSU : HS20ANQPSet; 55 } 56 buildQueryList(NetworkDetail networkDetail, boolean matchSet, boolean osu)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 buildQueryRequest(Set<Constants.ANQPElementType> elements, ByteBuffer target)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 buildHomeRealmRequest(List<String> realmNames, ByteBuffer target)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 buildIconRequest(String fileName, ByteBuffer target)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 parsePayload(ByteBuffer payload)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 buildElement(ByteBuffer payload)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 buildElement(ByteBuffer payload, Constants.ANQPElementType infoID, int length)195 public static ANQPElement buildElement(ByteBuffer payload, Constants.ANQPElementType infoID, 196 int length) throws ProtocolException { 197 ByteBuffer elementPayload = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN); 198 payload.position(payload.position() + length); 199 elementPayload.limit(elementPayload.position() + length); 200 201 switch (infoID) { 202 case ANQPCapabilityList: 203 return new CapabilityListElement(infoID, elementPayload); 204 case ANQPVenueName: 205 return new VenueNameElement(infoID, elementPayload); 206 case ANQPEmergencyNumber: 207 return new EmergencyNumberElement(infoID, elementPayload); 208 case ANQPNwkAuthType: 209 return new NetworkAuthenticationTypeElement(infoID, elementPayload); 210 case ANQPRoamingConsortium: 211 return new RoamingConsortiumElement(infoID, elementPayload); 212 case ANQPIPAddrAvailability: 213 return new IPAddressTypeAvailabilityElement(infoID, elementPayload); 214 case ANQPNAIRealm: 215 return new NAIRealmElement(infoID, elementPayload); 216 case ANQP3GPPNetwork: 217 return new ThreeGPPNetworkElement(infoID, elementPayload); 218 case ANQPGeoLoc: 219 return new GEOLocationElement(infoID, elementPayload); 220 case ANQPCivicLoc: 221 return new CivicLocationElement(infoID, elementPayload); 222 case ANQPLocURI: 223 return new GenericStringElement(infoID, elementPayload); 224 case ANQPDomName: 225 return new DomainNameElement(infoID, elementPayload); 226 case ANQPEmergencyAlert: 227 return new GenericStringElement(infoID, elementPayload); 228 case ANQPTDLSCap: 229 return new GenericBlobElement(infoID, elementPayload); 230 case ANQPEmergencyNAI: 231 return new GenericStringElement(infoID, elementPayload); 232 case ANQPNeighborReport: 233 return new GenericBlobElement(infoID, elementPayload); 234 case ANQPVendorSpec: 235 if (elementPayload.remaining() > 5) { 236 int oi = elementPayload.getInt(); 237 if (oi != Constants.HS20_PREFIX) { 238 return null; 239 } 240 int subType = elementPayload.get() & Constants.BYTE_MASK; 241 Constants.ANQPElementType hs20ID = Constants.mapHS20Element(subType); 242 if (hs20ID == null) { 243 throw new ProtocolException("Bad HS20 info ID: " + subType); 244 } 245 elementPayload.get(); // Skip the reserved octet 246 return buildHS20Element(hs20ID, elementPayload); 247 } else { 248 return new GenericBlobElement(infoID, elementPayload); 249 } 250 default: 251 throw new ProtocolException("Unknown element ID: " + infoID); 252 } 253 } 254 buildHS20Element(Constants.ANQPElementType infoID, ByteBuffer payload)255 public static ANQPElement buildHS20Element(Constants.ANQPElementType infoID, 256 ByteBuffer payload) throws ProtocolException { 257 switch (infoID) { 258 case HSCapabilityList: 259 return new HSCapabilityListElement(infoID, payload); 260 case HSFriendlyName: 261 return new HSFriendlyNameElement(infoID, payload); 262 case HSWANMetrics: 263 return new HSWanMetricsElement(infoID, payload); 264 case HSConnCapability: 265 return new HSConnectionCapabilityElement(infoID, payload); 266 case HSOperatingclass: 267 return new GenericBlobElement(infoID, payload); 268 case HSOSUProviders: 269 return new RawByteElement(infoID, payload); 270 case HSIconFile: 271 return new HSIconFileElement(infoID, payload); 272 default: 273 return null; 274 } 275 } 276 } 277