1package com.android.server.wifi.anqp;
2
3import java.net.ProtocolException;
4import java.nio.ByteBuffer;
5import java.nio.charset.StandardCharsets;
6import java.util.Collections;
7import java.util.HashMap;
8import java.util.Locale;
9import java.util.Map;
10
11/**
12 * The Civic Location ANQP Element, IEEE802.11-2012 section 8.4.4.13
13 */
14public class CivicLocationElement extends ANQPElement {
15    public enum LocationType {DHCPServer, NwkElement, Client}
16
17    private static final int GEOCONF_CIVIC4 = 99;
18    private static final int RFC4776 = 0;       // Table 8-77, 1=vendor specific
19
20    private final LocationType mLocationType;
21    private final Locale mLocale;
22    private final Map<CAType, String> mValues;
23
24    public CivicLocationElement(Constants.ANQPElementType infoID, ByteBuffer payload)
25            throws ProtocolException {
26        super(infoID);
27
28        if (payload.remaining() < 6) {
29            throw new ProtocolException("Runt civic location:" + payload.remaining());
30        }
31
32        int locType = payload.get() & Constants.BYTE_MASK;
33        if (locType != RFC4776) {
34            throw new ProtocolException("Bad Civic location type: " + locType);
35        }
36
37        int locSubType = payload.get() & Constants.BYTE_MASK;
38        if (locSubType != GEOCONF_CIVIC4) {
39            throw new ProtocolException("Unexpected Civic location sub-type: " + locSubType +
40                    " (cannot handle sub elements)");
41        }
42
43        int length = payload.get() & Constants.BYTE_MASK;
44        if (length > payload.remaining()) {
45            throw new ProtocolException("Invalid CA type length: " + length);
46        }
47
48        int what = payload.get() & Constants.BYTE_MASK;
49        mLocationType = what < LocationType.values().length ? LocationType.values()[what] : null;
50
51        mLocale = Locale.forLanguageTag(Constants.getString(payload, 2, StandardCharsets.US_ASCII));
52
53        mValues = new HashMap<CAType, String>();
54        while (payload.hasRemaining()) {
55            int caTypeNumber = payload.get() & Constants.BYTE_MASK;
56            CAType caType = s_caTypes.get(caTypeNumber);
57
58            int caValLen = payload.get() & Constants.BYTE_MASK;
59            if (caValLen > payload.remaining()) {
60                throw new ProtocolException("Bad CA value length: " + caValLen);
61            }
62            byte[] caValOctets = new byte[caValLen];
63            payload.get(caValOctets);
64
65            if (caType != null) {
66                mValues.put(caType, new String(caValOctets, StandardCharsets.UTF_8));
67            }
68        }
69    }
70
71    public LocationType getLocationType() {
72        return mLocationType;
73    }
74
75    public Locale getLocale() {
76        return mLocale;
77    }
78
79    public Map<CAType, String> getValues() {
80        return Collections.unmodifiableMap(mValues);
81    }
82
83    @Override
84    public String toString() {
85        return "CivicLocation{" +
86                "mLocationType=" + mLocationType +
87                ", mLocale=" + mLocale +
88                ", mValues=" + mValues +
89                '}';
90    }
91
92    private static final Map<Integer, CAType> s_caTypes = new HashMap<Integer, CAType>();
93
94    public static final int LANGUAGE = 0;
95    public static final int STATE_PROVINCE = 1;
96    public static final int COUNTY_DISTRICT = 2;
97    public static final int CITY = 3;
98    public static final int DIVISION_BOROUGH = 4;
99    public static final int BLOCK = 5;
100    public static final int STREET_GROUP = 6;
101    public static final int STREET_DIRECTION = 16;
102    public static final int LEADING_STREET_SUFFIX = 17;
103    public static final int STREET_SUFFIX = 18;
104    public static final int HOUSE_NUMBER = 19;
105    public static final int HOUSE_NUMBER_SUFFIX = 20;
106    public static final int LANDMARK = 21;
107    public static final int ADDITIONAL_LOCATION = 22;
108    public static final int NAME = 23;
109    public static final int POSTAL_ZIP = 24;
110    public static final int BUILDING = 25;
111    public static final int UNIT = 26;
112    public static final int FLOOR = 27;
113    public static final int ROOM = 28;
114    public static final int TYPE = 29;
115    public static final int POSTAL_COMMUNITY = 30;
116    public static final int PO_BOX = 31;
117    public static final int ADDITIONAL_CODE = 32;
118    public static final int SEAT_DESK = 33;
119    public static final int PRIMARY_ROAD = 34;
120    public static final int ROAD_SECTION = 35;
121    public static final int BRANCH_ROAD = 36;
122    public static final int SUB_BRANCH_ROAD = 37;
123    public static final int STREET_NAME_PRE_MOD = 38;
124    public static final int STREET_NAME_POST_MOD = 39;
125    public static final int SCRIPT = 128;
126    public static final int RESERVED = 255;
127
128    public enum CAType {
129        Language,
130        StateProvince,
131        CountyDistrict,
132        City,
133        DivisionBorough,
134        Block,
135        StreetGroup,
136        StreetDirection,
137        LeadingStreetSuffix,
138        StreetSuffix,
139        HouseNumber,
140        HouseNumberSuffix,
141        Landmark,
142        AdditionalLocation,
143        Name,
144        PostalZIP,
145        Building,
146        Unit,
147        Floor,
148        Room,
149        Type,
150        PostalCommunity,
151        POBox,
152        AdditionalCode,
153        SeatDesk,
154        PrimaryRoad,
155        RoadSection,
156        BranchRoad,
157        SubBranchRoad,
158        StreetNamePreMod,
159        StreetNamePostMod,
160        Script,
161        Reserved
162    }
163
164    static {
165        s_caTypes.put(LANGUAGE, CAType.Language);
166        s_caTypes.put(STATE_PROVINCE, CAType.StateProvince);
167        s_caTypes.put(COUNTY_DISTRICT, CAType.CountyDistrict);
168        s_caTypes.put(CITY, CAType.City);
169        s_caTypes.put(DIVISION_BOROUGH, CAType.DivisionBorough);
170        s_caTypes.put(BLOCK, CAType.Block);
171        s_caTypes.put(STREET_GROUP, CAType.StreetGroup);
172        s_caTypes.put(STREET_DIRECTION, CAType.StreetDirection);
173        s_caTypes.put(LEADING_STREET_SUFFIX, CAType.LeadingStreetSuffix);
174        s_caTypes.put(STREET_SUFFIX, CAType.StreetSuffix);
175        s_caTypes.put(HOUSE_NUMBER, CAType.HouseNumber);
176        s_caTypes.put(HOUSE_NUMBER_SUFFIX, CAType.HouseNumberSuffix);
177        s_caTypes.put(LANDMARK, CAType.Landmark);
178        s_caTypes.put(ADDITIONAL_LOCATION, CAType.AdditionalLocation);
179        s_caTypes.put(NAME, CAType.Name);
180        s_caTypes.put(POSTAL_ZIP, CAType.PostalZIP);
181        s_caTypes.put(BUILDING, CAType.Building);
182        s_caTypes.put(UNIT, CAType.Unit);
183        s_caTypes.put(FLOOR, CAType.Floor);
184        s_caTypes.put(ROOM, CAType.Room);
185        s_caTypes.put(TYPE, CAType.Type);
186        s_caTypes.put(POSTAL_COMMUNITY, CAType.PostalCommunity);
187        s_caTypes.put(PO_BOX, CAType.POBox);
188        s_caTypes.put(ADDITIONAL_CODE, CAType.AdditionalCode);
189        s_caTypes.put(SEAT_DESK, CAType.SeatDesk);
190        s_caTypes.put(PRIMARY_ROAD, CAType.PrimaryRoad);
191        s_caTypes.put(ROAD_SECTION, CAType.RoadSection);
192        s_caTypes.put(BRANCH_ROAD, CAType.BranchRoad);
193        s_caTypes.put(SUB_BRANCH_ROAD, CAType.SubBranchRoad);
194        s_caTypes.put(STREET_NAME_PRE_MOD, CAType.StreetNamePreMod);
195        s_caTypes.put(STREET_NAME_POST_MOD, CAType.StreetNamePostMod);
196        s_caTypes.put(SCRIPT, CAType.Script);
197        s_caTypes.put(RESERVED, CAType.Reserved);
198    }
199}
200