package com.android.server.wifi.anqp; import java.net.ProtocolException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; /** * The Civic Location ANQP Element, IEEE802.11-2012 section 8.4.4.13 */ public class CivicLocationElement extends ANQPElement { public enum LocationType {DHCPServer, NwkElement, Client} private static final int GEOCONF_CIVIC4 = 99; private static final int RFC4776 = 0; // Table 8-77, 1=vendor specific private final LocationType mLocationType; private final Locale mLocale; private final Map<CAType, String> mValues; public CivicLocationElement(Constants.ANQPElementType infoID, ByteBuffer payload) throws ProtocolException { super(infoID); if (payload.remaining() < 6) { throw new ProtocolException("Runt civic location:" + payload.remaining()); } int locType = payload.get() & Constants.BYTE_MASK; if (locType != RFC4776) { throw new ProtocolException("Bad Civic location type: " + locType); } int locSubType = payload.get() & Constants.BYTE_MASK; if (locSubType != GEOCONF_CIVIC4) { throw new ProtocolException("Unexpected Civic location sub-type: " + locSubType + " (cannot handle sub elements)"); } int length = payload.get() & Constants.BYTE_MASK; if (length > payload.remaining()) { throw new ProtocolException("Invalid CA type length: " + length); } int what = payload.get() & Constants.BYTE_MASK; mLocationType = what < LocationType.values().length ? LocationType.values()[what] : null; mLocale = Locale.forLanguageTag(Constants.getString(payload, 2, StandardCharsets.US_ASCII)); mValues = new HashMap<CAType, String>(); while (payload.hasRemaining()) { int caTypeNumber = payload.get() & Constants.BYTE_MASK; CAType caType = s_caTypes.get(caTypeNumber); int caValLen = payload.get() & Constants.BYTE_MASK; if (caValLen > payload.remaining()) { throw new ProtocolException("Bad CA value length: " + caValLen); } byte[] caValOctets = new byte[caValLen]; payload.get(caValOctets); if (caType != null) { mValues.put(caType, new String(caValOctets, StandardCharsets.UTF_8)); } } } public LocationType getLocationType() { return mLocationType; } public Locale getLocale() { return mLocale; } public Map<CAType, String> getValues() { return Collections.unmodifiableMap(mValues); } @Override public String toString() { return "CivicLocation{" + "mLocationType=" + mLocationType + ", mLocale=" + mLocale + ", mValues=" + mValues + '}'; } private static final Map<Integer, CAType> s_caTypes = new HashMap<Integer, CAType>(); public static final int LANGUAGE = 0; public static final int STATE_PROVINCE = 1; public static final int COUNTY_DISTRICT = 2; public static final int CITY = 3; public static final int DIVISION_BOROUGH = 4; public static final int BLOCK = 5; public static final int STREET_GROUP = 6; public static final int STREET_DIRECTION = 16; public static final int LEADING_STREET_SUFFIX = 17; public static final int STREET_SUFFIX = 18; public static final int HOUSE_NUMBER = 19; public static final int HOUSE_NUMBER_SUFFIX = 20; public static final int LANDMARK = 21; public static final int ADDITIONAL_LOCATION = 22; public static final int NAME = 23; public static final int POSTAL_ZIP = 24; public static final int BUILDING = 25; public static final int UNIT = 26; public static final int FLOOR = 27; public static final int ROOM = 28; public static final int TYPE = 29; public static final int POSTAL_COMMUNITY = 30; public static final int PO_BOX = 31; public static final int ADDITIONAL_CODE = 32; public static final int SEAT_DESK = 33; public static final int PRIMARY_ROAD = 34; public static final int ROAD_SECTION = 35; public static final int BRANCH_ROAD = 36; public static final int SUB_BRANCH_ROAD = 37; public static final int STREET_NAME_PRE_MOD = 38; public static final int STREET_NAME_POST_MOD = 39; public static final int SCRIPT = 128; public static final int RESERVED = 255; public enum CAType { Language, StateProvince, CountyDistrict, City, DivisionBorough, Block, StreetGroup, StreetDirection, LeadingStreetSuffix, StreetSuffix, HouseNumber, HouseNumberSuffix, Landmark, AdditionalLocation, Name, PostalZIP, Building, Unit, Floor, Room, Type, PostalCommunity, POBox, AdditionalCode, SeatDesk, PrimaryRoad, RoadSection, BranchRoad, SubBranchRoad, StreetNamePreMod, StreetNamePostMod, Script, Reserved } static { s_caTypes.put(LANGUAGE, CAType.Language); s_caTypes.put(STATE_PROVINCE, CAType.StateProvince); s_caTypes.put(COUNTY_DISTRICT, CAType.CountyDistrict); s_caTypes.put(CITY, CAType.City); s_caTypes.put(DIVISION_BOROUGH, CAType.DivisionBorough); s_caTypes.put(BLOCK, CAType.Block); s_caTypes.put(STREET_GROUP, CAType.StreetGroup); s_caTypes.put(STREET_DIRECTION, CAType.StreetDirection); s_caTypes.put(LEADING_STREET_SUFFIX, CAType.LeadingStreetSuffix); s_caTypes.put(STREET_SUFFIX, CAType.StreetSuffix); s_caTypes.put(HOUSE_NUMBER, CAType.HouseNumber); s_caTypes.put(HOUSE_NUMBER_SUFFIX, CAType.HouseNumberSuffix); s_caTypes.put(LANDMARK, CAType.Landmark); s_caTypes.put(ADDITIONAL_LOCATION, CAType.AdditionalLocation); s_caTypes.put(NAME, CAType.Name); s_caTypes.put(POSTAL_ZIP, CAType.PostalZIP); s_caTypes.put(BUILDING, CAType.Building); s_caTypes.put(UNIT, CAType.Unit); s_caTypes.put(FLOOR, CAType.Floor); s_caTypes.put(ROOM, CAType.Room); s_caTypes.put(TYPE, CAType.Type); s_caTypes.put(POSTAL_COMMUNITY, CAType.PostalCommunity); s_caTypes.put(PO_BOX, CAType.POBox); s_caTypes.put(ADDITIONAL_CODE, CAType.AdditionalCode); s_caTypes.put(SEAT_DESK, CAType.SeatDesk); s_caTypes.put(PRIMARY_ROAD, CAType.PrimaryRoad); s_caTypes.put(ROAD_SECTION, CAType.RoadSection); s_caTypes.put(BRANCH_ROAD, CAType.BranchRoad); s_caTypes.put(SUB_BRANCH_ROAD, CAType.SubBranchRoad); s_caTypes.put(STREET_NAME_PRE_MOD, CAType.StreetNamePreMod); s_caTypes.put(STREET_NAME_POST_MOD, CAType.StreetNamePostMod); s_caTypes.put(SCRIPT, CAType.Script); s_caTypes.put(RESERVED, CAType.Reserved); } }