InformationElementUtil.java revision 5d31cedf4024e0f038b4dfc2081016c8631ee8fe
15d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills/*
25d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills * Copyright (C) 2015 The Android Open Source Project
35d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills *
45d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills * Licensed under the Apache License, Version 2.0 (the "License");
55d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills * you may not use this file except in compliance with the License.
65d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills * You may obtain a copy of the License at
75d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills *
85d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills *      http://www.apache.org/licenses/LICENSE-2.0
95d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills *
105d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills * Unless required by applicable law or agreed to in writing, software
115d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills * distributed under the License is distributed on an "AS IS" BASIS,
125d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills * See the License for the specific language governing permissions and
145d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills * limitations under the License.
155d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills */
165d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
175d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willspackage com.android.server.wifi.util;
185d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
195d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport android.net.wifi.ScanResult.InformationElement;
205d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport android.util.Log;
215d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
225d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport com.android.server.wifi.anqp.Constants;
235d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport com.android.server.wifi.anqp.VenueNameElement;
245d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport com.android.server.wifi.hotspot2.NetworkDetail;
255d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
265d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport java.net.ProtocolException;
275d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport java.nio.ByteBuffer;
285d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport java.nio.ByteOrder;
295d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport java.util.ArrayList;
305d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
315d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport static com.android.server.wifi.anqp.Constants.getInteger;
325d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
335d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willspublic class InformationElementUtil {
345d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
355d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static InformationElement[] parseInformationElements(byte[] bytes) {
365d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        ByteBuffer data = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
375d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
385d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        ArrayList<InformationElement> infoElements = new ArrayList<>();
395d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        boolean found_ssid = false;
405d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        while (data.remaining() > 1) {
415d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int eid = data.get() & Constants.BYTE_MASK;
425d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int elementLength = data.get() & Constants.BYTE_MASK;
435d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
445d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (elementLength > data.remaining() || (eid == InformationElement.EID_SSID &&
455d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    found_ssid)) {
465d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                break;
475d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
485d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if(eid == InformationElement.EID_SSID) {
495d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                found_ssid = true;
505d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
515d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
525d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            InformationElement ie = new InformationElement();
535d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ie.id = eid;
545d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ie.bytes = new byte[elementLength];
555d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            data.get(ie.bytes);
565d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            infoElements.add(ie);
575d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
585d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        return infoElements.toArray(new InformationElement[infoElements.size()]);
595d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
615d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
625d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class BssLoad {
635d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int stationCount = 0;
645d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int channelUtilization = 0;
655d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int capacity = 0;
665d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
675d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
685d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if(ie.id != InformationElement.EID_BSS_LOAD) {
695d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not BSS_LOAD, : " + ie.id);
705d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
715d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length != 5) {
725d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("BSS Load element length is not 5: " +
735d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                                                   ie.bytes.length);
745d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
755d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
765d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            stationCount = data.getShort() & Constants.SHORT_MASK;
775d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            channelUtilization = data.get() & Constants.BYTE_MASK;
785d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            capacity = data.getShort() & Constants.SHORT_MASK;
795d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
805d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
815d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
825d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class HtOperation {
835d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int secondChannelOffset = 0;
845d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
855d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getChannelWidth() {
865d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if(secondChannelOffset != 0) {
875d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 1;
885d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
895d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            else {
905d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 0;
915d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
925d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
935d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
945d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getCenterFreq0(int primaryFrequency) {
955d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            //20 or 40 MHz
965d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (secondChannelOffset != 0) {//40MHz
975d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if (secondChannelOffset == 1) {
985d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    return primaryFrequency + 20;
995d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                } else if (secondChannelOffset == 3) {
1005d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    return primaryFrequency - 20;
1015d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                } else {
1025d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    Log.e("HtOperation", "Error on secondChannelOffset: " + secondChannelOffset);
1035d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    return 0;
1045d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
1055d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            } else {
1065d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 0;
1075d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1085d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1095d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1105d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
1115d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if(ie.id != InformationElement.EID_HT_OPERATION) {
1125d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not HT_OPERATION, : " + ie.id);
1135d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1145d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            secondChannelOffset = ie.bytes[1] & 0x3;
1155d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1165d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
1175d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1185d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class VhtOperation {
1195d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int channelMode = 0;
1205d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int centerFreqIndex1 = 0;
1215d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int centerFreqIndex2 = 0;
1225d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1235d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public boolean isValid() {
1245d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return channelMode != 0;
1255d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1265d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1275d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getChannelWidth() {
1285d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return channelMode + 1;
1295d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1305d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1315d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getCenterFreq0() {
1325d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            //convert channel index to frequency in MHz, channel 36 is 5180MHz
1335d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return (centerFreqIndex1 - 36) * 5 + 5180;
1345d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1355d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1365d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getCenterFreq1() {
1375d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if(channelMode > 1) { //160MHz
1385d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return (centerFreqIndex2 - 36) * 5 + 5180;
1395d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            } else {
1405d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 0;
1415d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1425d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1435d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1445d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
1455d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if(ie.id != InformationElement.EID_VHT_OPERATION) {
1465d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not VHT_OPERATION, : " + ie.id);
1475d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1485d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            channelMode = ie.bytes[0] & Constants.BYTE_MASK;
1495d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            centerFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK;
1505d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            centerFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK;
1515d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1525d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
1535d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1545d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class Interworking {
1555d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public NetworkDetail.Ant ant = null;
1565d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public boolean internet = false;
1575d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public VenueNameElement.VenueGroup venueGroup = null;
1585d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public VenueNameElement.VenueType venueType = null;
1595d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public long hessid = 0L;
1605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1615d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
1625d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if(ie.id != InformationElement.EID_INTERWORKING) {
1635d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not INTERWORKING, : " + ie.id);
1645d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1655d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1665d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int anOptions = data.get() & Constants.BYTE_MASK;
1675d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ant = NetworkDetail.Ant.values()[anOptions & 0x0f];
1685d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            internet = (anOptions & 0x10) != 0;
1695d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            // Len 1 none, 3 venue-info, 7 HESSID, 9 venue-info & HESSID
1705d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length == 3 || ie.bytes.length == 9) {
1715d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                try {
1725d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    ByteBuffer vinfo = data.duplicate();
1735d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    vinfo.limit(vinfo.position() + 2);
1745d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    VenueNameElement vne = new VenueNameElement(
1755d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                            Constants.ANQPElementType.ANQPVenueName, vinfo);
1765d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    venueGroup = vne.getGroup();
1775d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    venueType = vne.getType();
1785d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                } catch (ProtocolException pe) {
1795d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    /*Cannot happen*/
1805d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
1815d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            } else if (ie.bytes.length != 1 && ie.bytes.length != 7) {
1825d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Bad Interworking element length: " +
1835d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        ie.bytes.length);
1845d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1855d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length == 7 || ie.bytes.length == 9) {
1865d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                hessid = getInteger(data, ByteOrder.BIG_ENDIAN, 6);
1875d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1885d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1895d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
1905d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1915d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class RoamingConsortium {
1925d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int anqpOICount = 0;
1935d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public long[] roamingConsortiums = null;
1945d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1955d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
1965d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if(ie.id != InformationElement.EID_ROAMING_CONSORTIUM) {
1975d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not ROAMING_CONSORTIUM, : " +
1985d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        ie.id);
1995d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2005d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2015d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            anqpOICount = data.get() & Constants.BYTE_MASK;
2025d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2035d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi12Length = data.get() & Constants.BYTE_MASK;
2045d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi1Length = oi12Length & Constants.NIBBLE_MASK;
2055d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK;
2065d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi3Length = ie.bytes.length - 2 - oi1Length - oi2Length;
2075d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oiCount = 0;
2085d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi1Length > 0) {
2095d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                oiCount++;
2105d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if (oi2Length > 0) {
2115d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    oiCount++;
2125d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    if (oi3Length > 0) {
2135d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        oiCount++;
2145d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    }
2155d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
2165d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2175d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            roamingConsortiums = new long[oiCount];
2185d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi1Length > 0 && roamingConsortiums.length > 0) {
2195d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[0] =
2205d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        getInteger(data, ByteOrder.BIG_ENDIAN, oi1Length);
2215d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2225d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi2Length > 0 && roamingConsortiums.length > 1) {
2235d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[1] =
2245d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        getInteger(data, ByteOrder.BIG_ENDIAN, oi2Length);
2255d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2265d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi3Length > 0 && roamingConsortiums.length > 2) {
2275d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[2] =
2285d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        getInteger(data, ByteOrder.BIG_ENDIAN, oi3Length);
2295d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2305d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2315d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
2325d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2335d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class Vsa {
2345d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        private static final int ANQP_DOMID_BIT = 0x04;
2355d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2365d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public NetworkDetail.HSRelease hsRelease = null;
2375d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int anqpDomainID = 0;    // No domain ID treated the same as a 0; unique info per AP.
2385d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2395d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
2405d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2415d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length >= 5 && data.getInt() == Constants.HS20_FRAME_PREFIX) {
2425d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                int hsConf = data.get() & Constants.BYTE_MASK;
2435d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                switch ((hsConf >> 4) & Constants.NIBBLE_MASK) {
2445d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    case 0:
2455d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.R1;
2465d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
2475d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    case 1:
2485d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.R2;
2495d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
2505d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    default:
2515d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.Unknown;
2525d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
2535d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
2545d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if ((hsConf & ANQP_DOMID_BIT) != 0) {
2555d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    if (ie.bytes.length < 7) {
2565d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        throw new IllegalArgumentException(
2575d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                                "HS20 indication element too short: " + ie.bytes.length);
2585d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    }
2595d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    anqpDomainID = data.getShort() & Constants.SHORT_MASK;
2605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
2615d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2625d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2635d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
2645d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2655d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class ExtendedCapabilities {
2665d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        private static final int RTT_RESP_ENABLE_BIT = 70;
2675d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        private static final long SSID_UTF8_BIT = 0x0001000000000000L;
2685d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2695d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public Long extendedCapabilities = null;
2705d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public boolean is80211McRTTResponder = false;
2715d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2725d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public ExtendedCapabilities() {
2735d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2745d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2755d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public ExtendedCapabilities(ExtendedCapabilities other) {
2765d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            extendedCapabilities = other.extendedCapabilities;
2775d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            is80211McRTTResponder = other.is80211McRTTResponder;
2785d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2795d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2805d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public boolean isStrictUtf8() {
2815d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return extendedCapabilities != null && (extendedCapabilities & SSID_UTF8_BIT) != 0;
2825d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2835d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2845d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
2855d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2865d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            extendedCapabilities =
2875d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    Constants.getInteger(data, ByteOrder.LITTLE_ENDIAN, ie.bytes.length);
2885d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2895d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int index = RTT_RESP_ENABLE_BIT / 8;
2905d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            byte offset = RTT_RESP_ENABLE_BIT % 8;
2915d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if(ie.bytes.length < index + 1) {
2925d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                is80211McRTTResponder = false;
2935d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            } else {
2945d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                is80211McRTTResponder = (ie.bytes[index] & ((byte)0x1 << offset)) != 0;
2955d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2965d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2975d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
2985d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills}
299