InformationElementUtil.java revision 9f743918a412fec9ad5a0386fbf6cf0361313f58
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi.util;
18
19import android.net.wifi.ScanResult.InformationElement;
20import android.util.Log;
21
22import com.android.server.wifi.anqp.Constants;
23import com.android.server.wifi.anqp.VenueNameElement;
24import com.android.server.wifi.hotspot2.NetworkDetail;
25
26import java.net.ProtocolException;
27import java.nio.ByteBuffer;
28import java.nio.ByteOrder;
29import java.util.ArrayList;
30
31import static com.android.server.wifi.anqp.Constants.getInteger;
32
33public class InformationElementUtil {
34
35    public static InformationElement[] parseInformationElements(byte[] bytes) {
36        ByteBuffer data = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
37
38        ArrayList<InformationElement> infoElements = new ArrayList<>();
39        boolean found_ssid = false;
40        while (data.remaining() > 1) {
41            int eid = data.get() & Constants.BYTE_MASK;
42            int elementLength = data.get() & Constants.BYTE_MASK;
43
44            if (elementLength > data.remaining() || (eid == InformationElement.EID_SSID &&
45                    found_ssid)) {
46                break;
47            }
48            if(eid == InformationElement.EID_SSID) {
49                found_ssid = true;
50            }
51
52            InformationElement ie = new InformationElement();
53            ie.id = eid;
54            ie.bytes = new byte[elementLength];
55            data.get(ie.bytes);
56            infoElements.add(ie);
57        }
58        return infoElements.toArray(new InformationElement[infoElements.size()]);
59    }
60
61
62    public static class BssLoad {
63        public int stationCount = 0;
64        public int channelUtilization = 0;
65        public int capacity = 0;
66
67        public void from(InformationElement ie) {
68            if(ie.id != InformationElement.EID_BSS_LOAD) {
69                throw new IllegalArgumentException("Element id is not BSS_LOAD, : " + ie.id);
70            }
71            if (ie.bytes.length != 5) {
72                throw new IllegalArgumentException("BSS Load element length is not 5: " +
73                                                   ie.bytes.length);
74            }
75            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
76            stationCount = data.getShort() & Constants.SHORT_MASK;
77            channelUtilization = data.get() & Constants.BYTE_MASK;
78            capacity = data.getShort() & Constants.SHORT_MASK;
79        }
80    }
81
82    public static class HtOperation {
83        public int secondChannelOffset = 0;
84
85        public int getChannelWidth() {
86            if(secondChannelOffset != 0) {
87                return 1;
88            }
89            else {
90                return 0;
91            }
92        }
93
94        public int getCenterFreq0(int primaryFrequency) {
95            //40 MHz
96            if (secondChannelOffset != 0) {
97                if (secondChannelOffset == 1) {
98                    return primaryFrequency + 10;
99                } else if (secondChannelOffset == 3) {
100                    return primaryFrequency - 10;
101                } else {
102                    Log.e("HtOperation", "Error on secondChannelOffset: " + secondChannelOffset);
103                    return 0;
104                }
105            } else {
106                return 0;
107            }
108        }
109
110        public void from(InformationElement ie) {
111            if(ie.id != InformationElement.EID_HT_OPERATION) {
112                throw new IllegalArgumentException("Element id is not HT_OPERATION, : " + ie.id);
113            }
114            secondChannelOffset = ie.bytes[1] & 0x3;
115        }
116    }
117
118    public static class VhtOperation {
119        public int channelMode = 0;
120        public int centerFreqIndex1 = 0;
121        public int centerFreqIndex2 = 0;
122
123        public boolean isValid() {
124            return channelMode != 0;
125        }
126
127        public int getChannelWidth() {
128            return channelMode + 1;
129        }
130
131        public int getCenterFreq0() {
132            //convert channel index to frequency in MHz, channel 36 is 5180MHz
133            return (centerFreqIndex1 - 36) * 5 + 5180;
134        }
135
136        public int getCenterFreq1() {
137            if(channelMode > 1) { //160MHz
138                return (centerFreqIndex2 - 36) * 5 + 5180;
139            } else {
140                return 0;
141            }
142        }
143
144        public void from(InformationElement ie) {
145            if(ie.id != InformationElement.EID_VHT_OPERATION) {
146                throw new IllegalArgumentException("Element id is not VHT_OPERATION, : " + ie.id);
147            }
148            channelMode = ie.bytes[0] & Constants.BYTE_MASK;
149            centerFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK;
150            centerFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK;
151        }
152    }
153
154    public static class Interworking {
155        public NetworkDetail.Ant ant = null;
156        public boolean internet = false;
157        public VenueNameElement.VenueGroup venueGroup = null;
158        public VenueNameElement.VenueType venueType = null;
159        public long hessid = 0L;
160
161        public void from(InformationElement ie) {
162            if(ie.id != InformationElement.EID_INTERWORKING) {
163                throw new IllegalArgumentException("Element id is not INTERWORKING, : " + ie.id);
164            }
165            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
166            int anOptions = data.get() & Constants.BYTE_MASK;
167            ant = NetworkDetail.Ant.values()[anOptions & 0x0f];
168            internet = (anOptions & 0x10) != 0;
169            // Len 1 none, 3 venue-info, 7 HESSID, 9 venue-info & HESSID
170            if (ie.bytes.length == 3 || ie.bytes.length == 9) {
171                try {
172                    ByteBuffer vinfo = data.duplicate();
173                    vinfo.limit(vinfo.position() + 2);
174                    VenueNameElement vne = new VenueNameElement(
175                            Constants.ANQPElementType.ANQPVenueName, vinfo);
176                    venueGroup = vne.getGroup();
177                    venueType = vne.getType();
178                } catch (ProtocolException pe) {
179                    /*Cannot happen*/
180                }
181            } else if (ie.bytes.length != 1 && ie.bytes.length != 7) {
182                throw new IllegalArgumentException("Bad Interworking element length: " +
183                        ie.bytes.length);
184            }
185            if (ie.bytes.length == 7 || ie.bytes.length == 9) {
186                hessid = getInteger(data, ByteOrder.BIG_ENDIAN, 6);
187            }
188        }
189    }
190
191    public static class RoamingConsortium {
192        public int anqpOICount = 0;
193        public long[] roamingConsortiums = null;
194
195        public void from(InformationElement ie) {
196            if(ie.id != InformationElement.EID_ROAMING_CONSORTIUM) {
197                throw new IllegalArgumentException("Element id is not ROAMING_CONSORTIUM, : " +
198                        ie.id);
199            }
200            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
201            anqpOICount = data.get() & Constants.BYTE_MASK;
202
203            int oi12Length = data.get() & Constants.BYTE_MASK;
204            int oi1Length = oi12Length & Constants.NIBBLE_MASK;
205            int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK;
206            int oi3Length = ie.bytes.length - 2 - oi1Length - oi2Length;
207            int oiCount = 0;
208            if (oi1Length > 0) {
209                oiCount++;
210                if (oi2Length > 0) {
211                    oiCount++;
212                    if (oi3Length > 0) {
213                        oiCount++;
214                    }
215                }
216            }
217            roamingConsortiums = new long[oiCount];
218            if (oi1Length > 0 && roamingConsortiums.length > 0) {
219                roamingConsortiums[0] =
220                        getInteger(data, ByteOrder.BIG_ENDIAN, oi1Length);
221            }
222            if (oi2Length > 0 && roamingConsortiums.length > 1) {
223                roamingConsortiums[1] =
224                        getInteger(data, ByteOrder.BIG_ENDIAN, oi2Length);
225            }
226            if (oi3Length > 0 && roamingConsortiums.length > 2) {
227                roamingConsortiums[2] =
228                        getInteger(data, ByteOrder.BIG_ENDIAN, oi3Length);
229            }
230        }
231    }
232
233    public static class Vsa {
234        private static final int ANQP_DOMID_BIT = 0x04;
235
236        public NetworkDetail.HSRelease hsRelease = null;
237        public int anqpDomainID = 0;    // No domain ID treated the same as a 0; unique info per AP.
238
239        public void from(InformationElement ie) {
240            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
241            if (ie.bytes.length >= 5 && data.getInt() == Constants.HS20_FRAME_PREFIX) {
242                int hsConf = data.get() & Constants.BYTE_MASK;
243                switch ((hsConf >> 4) & Constants.NIBBLE_MASK) {
244                    case 0:
245                        hsRelease = NetworkDetail.HSRelease.R1;
246                        break;
247                    case 1:
248                        hsRelease = NetworkDetail.HSRelease.R2;
249                        break;
250                    default:
251                        hsRelease = NetworkDetail.HSRelease.Unknown;
252                        break;
253                }
254                if ((hsConf & ANQP_DOMID_BIT) != 0) {
255                    if (ie.bytes.length < 7) {
256                        throw new IllegalArgumentException(
257                                "HS20 indication element too short: " + ie.bytes.length);
258                    }
259                    anqpDomainID = data.getShort() & Constants.SHORT_MASK;
260                }
261            }
262        }
263    }
264
265    public static class ExtendedCapabilities {
266        private static final int RTT_RESP_ENABLE_BIT = 70;
267        private static final long SSID_UTF8_BIT = 0x0001000000000000L;
268
269        public Long extendedCapabilities = null;
270        public boolean is80211McRTTResponder = false;
271
272        public ExtendedCapabilities() {
273        }
274
275        public ExtendedCapabilities(ExtendedCapabilities other) {
276            extendedCapabilities = other.extendedCapabilities;
277            is80211McRTTResponder = other.is80211McRTTResponder;
278        }
279
280        public boolean isStrictUtf8() {
281            return extendedCapabilities != null && (extendedCapabilities & SSID_UTF8_BIT) != 0;
282        }
283
284        public void from(InformationElement ie) {
285            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
286            extendedCapabilities =
287                    Constants.getInteger(data, ByteOrder.LITTLE_ENDIAN, ie.bytes.length);
288
289            int index = RTT_RESP_ENABLE_BIT / 8;
290            byte offset = RTT_RESP_ENABLE_BIT % 8;
291            if(ie.bytes.length < index + 1) {
292                is80211McRTTResponder = false;
293            } else {
294                is80211McRTTResponder = (ie.bytes[index] & ((byte)0x1 << offset)) != 0;
295            }
296        }
297    }
298}
299