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 Willspackage com.android.server.wifi.util;
175d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
183571366ac36c70746b9f013ec2b54482861c9292Randy Panimport static com.android.server.wifi.anqp.Constants.getInteger;
193571366ac36c70746b9f013ec2b54482861c9292Randy Pan
205d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport android.net.wifi.ScanResult.InformationElement;
215d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport android.util.Log;
225d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
235d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport com.android.server.wifi.anqp.Constants;
245d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport com.android.server.wifi.anqp.VenueNameElement;
255d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport com.android.server.wifi.hotspot2.NetworkDetail;
265d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
275d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport java.net.ProtocolException;
283571366ac36c70746b9f013ec2b54482861c9292Randy Panimport java.nio.BufferUnderflowException;
295d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport java.nio.ByteBuffer;
305d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport java.nio.ByteOrder;
315d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport java.util.ArrayList;
323571366ac36c70746b9f013ec2b54482861c9292Randy Panimport java.util.BitSet;
335d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
345d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willspublic class InformationElementUtil {
355d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
365d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static InformationElement[] parseInformationElements(byte[] bytes) {
37da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein        if (bytes == null) {
38da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein            return new InformationElement[0];
39da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein        }
405d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        ByteBuffer data = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
415d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
425d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        ArrayList<InformationElement> infoElements = new ArrayList<>();
435d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        boolean found_ssid = false;
445d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        while (data.remaining() > 1) {
455d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int eid = data.get() & Constants.BYTE_MASK;
465d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int elementLength = data.get() & Constants.BYTE_MASK;
475d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
483571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (elementLength > data.remaining() || (eid == InformationElement.EID_SSID
493571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    && found_ssid)) {
50da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein                // APs often pad the data with bytes that happen to match that of the EID_SSID
51da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein                // marker.  This is not due to a known issue for APs to incorrectly send the SSID
52da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein                // name multiple times.
535d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                break;
545d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
553571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (eid == InformationElement.EID_SSID) {
565d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                found_ssid = true;
575d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
585d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
595d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            InformationElement ie = new InformationElement();
605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ie.id = eid;
615d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ie.bytes = new byte[elementLength];
625d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            data.get(ie.bytes);
635d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            infoElements.add(ie);
645d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
655d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        return infoElements.toArray(new InformationElement[infoElements.size()]);
665d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
675d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
685d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
695d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class BssLoad {
705d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int stationCount = 0;
715d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int channelUtilization = 0;
725d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int capacity = 0;
735d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
745d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
753571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_BSS_LOAD) {
765d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not BSS_LOAD, : " + ie.id);
775d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
785d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length != 5) {
793571366ac36c70746b9f013ec2b54482861c9292Randy Pan                throw new IllegalArgumentException("BSS Load element length is not 5: "
803571366ac36c70746b9f013ec2b54482861c9292Randy Pan                                                   + ie.bytes.length);
815d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
825d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
835d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            stationCount = data.getShort() & Constants.SHORT_MASK;
845d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            channelUtilization = data.get() & Constants.BYTE_MASK;
855d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            capacity = data.getShort() & Constants.SHORT_MASK;
865d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
875d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
885d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
895d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class HtOperation {
905d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int secondChannelOffset = 0;
915d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
925d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getChannelWidth() {
933571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (secondChannelOffset != 0) {
945d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 1;
953571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } else {
965d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 0;
975d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
985d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
995d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1005d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getCenterFreq0(int primaryFrequency) {
1019f743918a412fec9ad5a0386fbf6cf0361313f58xinhe            //40 MHz
1029f743918a412fec9ad5a0386fbf6cf0361313f58xinhe            if (secondChannelOffset != 0) {
1035d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if (secondChannelOffset == 1) {
1049f743918a412fec9ad5a0386fbf6cf0361313f58xinhe                    return primaryFrequency + 10;
1055d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                } else if (secondChannelOffset == 3) {
1069f743918a412fec9ad5a0386fbf6cf0361313f58xinhe                    return primaryFrequency - 10;
1075d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                } else {
1085d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    Log.e("HtOperation", "Error on secondChannelOffset: " + secondChannelOffset);
1095d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    return 0;
1105d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
1115d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            } else {
1125d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 0;
1135d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1145d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1155d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1165d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
1173571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_HT_OPERATION) {
1185d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not HT_OPERATION, : " + ie.id);
1195d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1205d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            secondChannelOffset = ie.bytes[1] & 0x3;
1215d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1225d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
1235d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1245d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class VhtOperation {
1255d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int channelMode = 0;
1265d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int centerFreqIndex1 = 0;
1275d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int centerFreqIndex2 = 0;
1285d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1295d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public boolean isValid() {
1305d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return channelMode != 0;
1315d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1325d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1335d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getChannelWidth() {
1345d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return channelMode + 1;
1355d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1365d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1375d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getCenterFreq0() {
1385d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            //convert channel index to frequency in MHz, channel 36 is 5180MHz
1395d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return (centerFreqIndex1 - 36) * 5 + 5180;
1405d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1415d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1425d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getCenterFreq1() {
1433571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (channelMode > 1) { //160MHz
1445d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return (centerFreqIndex2 - 36) * 5 + 5180;
1455d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            } else {
1465d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 0;
1475d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1485d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1495d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1505d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
1513571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_VHT_OPERATION) {
1525d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not VHT_OPERATION, : " + ie.id);
1535d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1545d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            channelMode = ie.bytes[0] & Constants.BYTE_MASK;
1555d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            centerFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK;
1565d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            centerFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK;
1575d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1585d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
1595d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class Interworking {
1615d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public NetworkDetail.Ant ant = null;
1625d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public boolean internet = false;
1635d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public VenueNameElement.VenueGroup venueGroup = null;
1645d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public VenueNameElement.VenueType venueType = null;
1655d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public long hessid = 0L;
1665d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1675d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
1683571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_INTERWORKING) {
1695d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not INTERWORKING, : " + ie.id);
1705d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1715d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1725d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int anOptions = data.get() & Constants.BYTE_MASK;
1735d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ant = NetworkDetail.Ant.values()[anOptions & 0x0f];
1745d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            internet = (anOptions & 0x10) != 0;
1755d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            // Len 1 none, 3 venue-info, 7 HESSID, 9 venue-info & HESSID
1765d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length == 3 || ie.bytes.length == 9) {
1775d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                try {
1785d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    ByteBuffer vinfo = data.duplicate();
1795d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    vinfo.limit(vinfo.position() + 2);
1805d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    VenueNameElement vne = new VenueNameElement(
1815d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                            Constants.ANQPElementType.ANQPVenueName, vinfo);
1825d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    venueGroup = vne.getGroup();
1835d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    venueType = vne.getType();
1845d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                } catch (ProtocolException pe) {
1855d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    /*Cannot happen*/
1865d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
1875d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            } else if (ie.bytes.length != 1 && ie.bytes.length != 7) {
1883571366ac36c70746b9f013ec2b54482861c9292Randy Pan                throw new IllegalArgumentException("Bad Interworking element length: "
1893571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        + ie.bytes.length);
1905d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1915d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length == 7 || ie.bytes.length == 9) {
1925d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                hessid = getInteger(data, ByteOrder.BIG_ENDIAN, 6);
1935d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1945d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1955d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
1965d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1975d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class RoamingConsortium {
1985d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int anqpOICount = 0;
1995d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public long[] roamingConsortiums = null;
2005d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2015d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
2023571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_ROAMING_CONSORTIUM) {
2033571366ac36c70746b9f013ec2b54482861c9292Randy Pan                throw new IllegalArgumentException("Element id is not ROAMING_CONSORTIUM, : "
2043571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        + ie.id);
2055d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2065d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2075d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            anqpOICount = data.get() & Constants.BYTE_MASK;
2085d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2095d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi12Length = data.get() & Constants.BYTE_MASK;
2105d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi1Length = oi12Length & Constants.NIBBLE_MASK;
2115d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK;
2125d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi3Length = ie.bytes.length - 2 - oi1Length - oi2Length;
2135d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oiCount = 0;
2145d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi1Length > 0) {
2155d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                oiCount++;
2165d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if (oi2Length > 0) {
2175d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    oiCount++;
2185d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    if (oi3Length > 0) {
2195d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        oiCount++;
2205d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    }
2215d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
2225d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2235d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            roamingConsortiums = new long[oiCount];
2245d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi1Length > 0 && roamingConsortiums.length > 0) {
2255d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[0] =
2265d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        getInteger(data, ByteOrder.BIG_ENDIAN, oi1Length);
2275d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2285d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi2Length > 0 && roamingConsortiums.length > 1) {
2295d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[1] =
2305d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        getInteger(data, ByteOrder.BIG_ENDIAN, oi2Length);
2315d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2325d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi3Length > 0 && roamingConsortiums.length > 2) {
2335d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[2] =
2345d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        getInteger(data, ByteOrder.BIG_ENDIAN, oi3Length);
2355d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2365d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2375d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
2385d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2395d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class Vsa {
2405d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        private static final int ANQP_DOMID_BIT = 0x04;
2415d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2425d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public NetworkDetail.HSRelease hsRelease = null;
2435d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int anqpDomainID = 0;    // No domain ID treated the same as a 0; unique info per AP.
2445d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2455d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
2465d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2475d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length >= 5 && data.getInt() == Constants.HS20_FRAME_PREFIX) {
2485d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                int hsConf = data.get() & Constants.BYTE_MASK;
2495d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                switch ((hsConf >> 4) & Constants.NIBBLE_MASK) {
2505d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    case 0:
2515d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.R1;
2525d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
2535d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    case 1:
2545d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.R2;
2555d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
2565d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    default:
2575d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.Unknown;
2585d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
2595d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
2605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if ((hsConf & ANQP_DOMID_BIT) != 0) {
2615d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    if (ie.bytes.length < 7) {
2625d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        throw new IllegalArgumentException(
2635d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                                "HS20 indication element too short: " + ie.bytes.length);
2645d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    }
2655d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    anqpDomainID = data.getShort() & Constants.SHORT_MASK;
2665d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
2675d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2685d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2695d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
2705d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2715d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class ExtendedCapabilities {
2725d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        private static final int RTT_RESP_ENABLE_BIT = 70;
2735d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        private static final long SSID_UTF8_BIT = 0x0001000000000000L;
2745d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2755d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public Long extendedCapabilities = null;
2765d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public boolean is80211McRTTResponder = false;
2775d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2785d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public ExtendedCapabilities() {
2795d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2805d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2815d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public ExtendedCapabilities(ExtendedCapabilities other) {
2825d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            extendedCapabilities = other.extendedCapabilities;
2835d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            is80211McRTTResponder = other.is80211McRTTResponder;
2845d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2855d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2865d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public boolean isStrictUtf8() {
2875d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return extendedCapabilities != null && (extendedCapabilities & SSID_UTF8_BIT) != 0;
2885d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2895d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2905d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
2915d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2925d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            extendedCapabilities =
2935d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    Constants.getInteger(data, ByteOrder.LITTLE_ENDIAN, ie.bytes.length);
2945d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2955d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int index = RTT_RESP_ENABLE_BIT / 8;
2965d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            byte offset = RTT_RESP_ENABLE_BIT % 8;
2973571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.bytes.length < index + 1) {
2985d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                is80211McRTTResponder = false;
2995d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            } else {
3003571366ac36c70746b9f013ec2b54482861c9292Randy Pan                is80211McRTTResponder = (ie.bytes[index] & ((byte) 0x1 << offset)) != 0;
3015d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
3025d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
3035d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
3043571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3053571366ac36c70746b9f013ec2b54482861c9292Randy Pan    /**
3063571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * parse beacon to build the capabilities
3073571366ac36c70746b9f013ec2b54482861c9292Randy Pan     *
3083571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * This class is used to build the capabilities string of the scan results coming
3093571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * from HAL. It parses the ieee beacon's capability field, WPA and RSNE IE as per spec,
3103571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * and builds the ScanResult.capabilities String in a way that mirrors the values returned
3113571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * by wpa_supplicant.
3123571366ac36c70746b9f013ec2b54482861c9292Randy Pan     */
3133571366ac36c70746b9f013ec2b54482861c9292Randy Pan    public static class Capabilities {
3149b5773d2805e8c6141ca75de272921a84941546bRandy Pan        private static final int CAP_ESS_BIT_OFFSET = 0;
3153571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int CAP_PRIVACY_BIT_OFFSET = 4;
3163571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3173571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA_VENDOR_OUI_TYPE_ONE = 0x01f25000;
3183571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final short WPA_VENDOR_OUI_VERSION = 0x0001;
3193571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final short RSNE_VERSION = 0x0001;
3203571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3213571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA_AKM_EAP = 0x01f25000;
3223571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA_AKM_PSK = 0x02f25000;
3233571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3243571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_EAP = 0x01ac0f00;
3253571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_PSK = 0x02ac0f00;
3263571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_FT_EAP = 0x03ac0f00;
3273571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_FT_PSK = 0x04ac0f00;
3283571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_EAP_SHA256 = 0x05ac0f00;
3293571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_PSK_SHA256 = 0x06ac0f00;
3303571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3313571366ac36c70746b9f013ec2b54482861c9292Randy Pan        public Capabilities() {
3323571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
3333571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3343571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // RSNE format (size unit: byte)
3353571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
3363571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Element ID | Length | Version | Group Data Cipher Suite |
3373571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //      1           1         2                 4
3383571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
3393571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //              2                            4 * m
3403571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | AKM Suite Count | AKM Suite List | RSN Capabilities |
3413571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //          2               4 * n               2
3423571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | PMKID Count | PMKID List | Group Management Cipher Suite |
3433571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //        2          16 * s                 4
3443571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
3453571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // Note: InformationElement.bytes has 'Element ID' and 'Length'
3463571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //       stripped off already
3473571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static String parseRsnElement(InformationElement ie) {
3483571366ac36c70746b9f013ec2b54482861c9292Randy Pan            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
3493571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3503571366ac36c70746b9f013ec2b54482861c9292Randy Pan            try {
3513571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // version
3523571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (buf.getShort() != RSNE_VERSION) {
3533571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    // incorrect version
3543571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    return null;
3553571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
3563571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3573571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // group data cipher suite
3583571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // here we simply advance the buffer position
3593571366ac36c70746b9f013ec2b54482861c9292Randy Pan                buf.getInt();
3603571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3613571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // found the RSNE IE, hence start building the capability string
3623571366ac36c70746b9f013ec2b54482861c9292Randy Pan                String security = "[WPA2";
3633571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3643571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise cipher suite count
3653571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short cipherCount = buf.getShort();
3663571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3673571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise cipher suite list
3683571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < cipherCount; i++) {
3693571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    // here we simply advance the buffer position
3703571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    buf.getInt();
3713571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
3723571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3733571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM
3743571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM suite count
3753571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short akmCount = buf.getShort();
3763571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3773571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // parse AKM suite list
3783571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (akmCount == 0) {
3793571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    security += "-EAP"; //default AKM
3803571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
3813571366ac36c70746b9f013ec2b54482861c9292Randy Pan                boolean found = false;
3823571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < akmCount; i++) {
3833571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    int akm = buf.getInt();
3843571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    switch (akm) {
3853571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_EAP:
3863571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            security += (found ? "+" : "-") + "EAP";
3873571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            found = true;
3883571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
3893571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_PSK:
3903571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            security += (found ? "+" : "-") + "PSK";
3913571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            found = true;
3923571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
3933571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_FT_EAP:
3943571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            security += (found ? "+" : "-") + "FT/EAP";
3953571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            found = true;
3963571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
3973571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_FT_PSK:
3983571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            security += (found ? "+" : "-") + "FT/PSK";
3993571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            found = true;
4003571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4013571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_EAP_SHA256:
4023571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            security += (found ? "+" : "-") + "EAP-SHA256";
4033571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            found = true;
4043571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4053571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_PSK_SHA256:
4063571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            security += (found ? "+" : "-") + "PSK-SHA256";
4073571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            found = true;
4083571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4093571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        default:
4103571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            // do nothing
4113571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4123571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    }
4133571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
4143571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4153571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // we parsed what we want at this point
4163571366ac36c70746b9f013ec2b54482861c9292Randy Pan                security += "]";
4173571366ac36c70746b9f013ec2b54482861c9292Randy Pan                return security;
4183571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } catch (BufferUnderflowException e) {
4193571366ac36c70746b9f013ec2b54482861c9292Randy Pan                Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow");
4203571366ac36c70746b9f013ec2b54482861c9292Randy Pan                return null;
4213571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
4223571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
4233571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4243571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static boolean isWpaOneElement(InformationElement ie) {
4253571366ac36c70746b9f013ec2b54482861c9292Randy Pan            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
4263571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4273571366ac36c70746b9f013ec2b54482861c9292Randy Pan            try {
4283571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // WPA OUI and type
4293571366ac36c70746b9f013ec2b54482861c9292Randy Pan                return (buf.getInt() == WPA_VENDOR_OUI_TYPE_ONE);
4303571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } catch (BufferUnderflowException e) {
4313571366ac36c70746b9f013ec2b54482861c9292Randy Pan                Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
4323571366ac36c70746b9f013ec2b54482861c9292Randy Pan                return false;
4333571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
4343571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
4353571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4363571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // WPA type 1 format (size unit: byte)
4373571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
4383571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Element ID | Length | OUI | Type | Version |
4393571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //      1           1       3     1        2
4403571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
4413571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //              2                            4 * m
4423571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | AKM Suite Count | AKM Suite List |
4433571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //          2               4 * n
4443571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
4453571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // Note: InformationElement.bytes has 'Element ID' and 'Length'
4463571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //       stripped off already
4473571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
4483571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static String parseWpaOneElement(InformationElement ie) {
4493571366ac36c70746b9f013ec2b54482861c9292Randy Pan            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
4503571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4513571366ac36c70746b9f013ec2b54482861c9292Randy Pan            try {
4523571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // skip WPA OUI and type parsing. isWpaOneElement() should have
4533571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // been called for verification before we reach here.
4543571366ac36c70746b9f013ec2b54482861c9292Randy Pan                buf.getInt();
4553571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4563571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // start building the string
4573571366ac36c70746b9f013ec2b54482861c9292Randy Pan                String security = "[WPA";
4583571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4593571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // version
4603571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (buf.getShort() != WPA_VENDOR_OUI_VERSION)  {
4613571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    // incorrect version
4623571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    return null;
4633571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
4643571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4653571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // group data cipher suite
4663571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // here we simply advance buffer position
4673571366ac36c70746b9f013ec2b54482861c9292Randy Pan                buf.getInt();
4683571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4693571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise cipher suite count
4703571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short cipherCount = buf.getShort();
4713571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4723571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise chipher suite list
4733571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < cipherCount; i++) {
4743571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    // here we simply advance buffer position
4753571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    buf.getInt();
4763571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
4773571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4783571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM
4793571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM suite count
4803571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short akmCount = buf.getShort();
4813571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4823571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM suite list
4833571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (akmCount == 0) {
4843571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    security += "-EAP"; //default AKM
4853571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
4863571366ac36c70746b9f013ec2b54482861c9292Randy Pan                boolean found = false;
4873571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < akmCount; i++) {
4883571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    int akm = buf.getInt();
4893571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    switch (akm) {
4903571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA_AKM_EAP:
4913571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            security += (found ? "+" : "-") + "EAP";
4923571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            found = true;
4933571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4943571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA_AKM_PSK:
4953571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            security += (found ? "+" : "-") + "PSK";
4963571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            found = true;
4973571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4983571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        default:
4993571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            // do nothing
5003571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
5013571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    }
5023571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
5033571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5043571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // we parsed what we want at this point
5053571366ac36c70746b9f013ec2b54482861c9292Randy Pan                security += "]";
5063571366ac36c70746b9f013ec2b54482861c9292Randy Pan                return security;
5073571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } catch (BufferUnderflowException e) {
5083571366ac36c70746b9f013ec2b54482861c9292Randy Pan                Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow");
5093571366ac36c70746b9f013ec2b54482861c9292Randy Pan                return null;
5103571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
5113571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
5123571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5133571366ac36c70746b9f013ec2b54482861c9292Randy Pan        /**
5143571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * Parse the Information Element and the 16-bit Capability Information field
5153571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * to build the ScanResult.capabilities String.
5163571366ac36c70746b9f013ec2b54482861c9292Randy Pan         *
5173571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * @param ies -- Information Element array
5183571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * @param beaconCap -- 16-bit Beacon Capability Information field
5193571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * @return security string that mirrors what wpa_supplicant generates
5203571366ac36c70746b9f013ec2b54482861c9292Randy Pan         */
5213571366ac36c70746b9f013ec2b54482861c9292Randy Pan        public static String buildCapabilities(InformationElement[] ies, BitSet beaconCap) {
5223571366ac36c70746b9f013ec2b54482861c9292Randy Pan            String capabilities = "";
5233571366ac36c70746b9f013ec2b54482861c9292Randy Pan            boolean rsneFound = false;
5243571366ac36c70746b9f013ec2b54482861c9292Randy Pan            boolean wpaFound = false;
5253571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5263571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ies == null || beaconCap == null) {
5273571366ac36c70746b9f013ec2b54482861c9292Randy Pan                return capabilities;
5283571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
5293571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5309b5773d2805e8c6141ca75de272921a84941546bRandy Pan            boolean ess = beaconCap.get(CAP_ESS_BIT_OFFSET);
5313571366ac36c70746b9f013ec2b54482861c9292Randy Pan            boolean privacy = beaconCap.get(CAP_PRIVACY_BIT_OFFSET);
5323571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5333571366ac36c70746b9f013ec2b54482861c9292Randy Pan            for (InformationElement ie : ies) {
5343571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (ie.id == InformationElement.EID_RSN) {
5353571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    rsneFound = true;
5363571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    capabilities += parseRsnElement(ie);
5373571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
5383571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5393571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (ie.id == InformationElement.EID_VSA) {
5403571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    if (isWpaOneElement(ie)) {
5413571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        wpaFound = true;
5423571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        capabilities += parseWpaOneElement(ie);
5433571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    }
5443571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
5453571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
5463571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5473571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (!rsneFound && !wpaFound && privacy) {
5483571366ac36c70746b9f013ec2b54482861c9292Randy Pan                //private Beacon without an RSNE or WPA IE, hence WEP0
5493571366ac36c70746b9f013ec2b54482861c9292Randy Pan                capabilities += "[WEP]";
5503571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
5513571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5529b5773d2805e8c6141ca75de272921a84941546bRandy Pan            if (ess) {
5539b5773d2805e8c6141ca75de272921a84941546bRandy Pan                capabilities += "[ESS]";
5549b5773d2805e8c6141ca75de272921a84941546bRandy Pan            }
5559b5773d2805e8c6141ca75de272921a84941546bRandy Pan
5563571366ac36c70746b9f013ec2b54482861c9292Randy Pan            return capabilities;
5573571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
5583571366ac36c70746b9f013ec2b54482861c9292Randy Pan    }
559947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne
560947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne    /**
561947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     * Parser for the Traffic Indication Map (TIM) Information Element (EID 5). This element will
562947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     * only be present in scan results that are derived from a Beacon Frame, not from the more
563947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     * plentiful probe responses. Call 'isValid()' after parsing, to ensure the results are correct.
564947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     */
565947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne    public static class TrafficIndicationMap {
566947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        private static final int MAX_TIM_LENGTH = 254;
567947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        private boolean mValid = false;
568947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mLength = 0;
569947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mDtimCount = -1;
570947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //Negative DTIM Period means no TIM element was given this frame.
571947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mDtimPeriod = -1;
572947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mBitmapControl = 0;
573947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne
574947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        /**
575947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne         * Is this a valid TIM information element.
576947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne         */
577947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public boolean isValid() {
578947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            return mValid;
579947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        }
580947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne
581947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        // Traffic Indication Map format (size unit: byte)
582947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //
583947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //| ElementID | Length | DTIM Count | DTIM Period | BitmapControl | Partial Virtual Bitmap |
584947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //      1          1          1            1               1                1 - 251
585947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //
586947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        // Note: InformationElement.bytes has 'Element ID' and 'Length'
587947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //       stripped off already
588947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //
589947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public void from(InformationElement ie) {
590e367514651d7f888dda870fb472eea6bcfd9ec5dPaul Stewart            mValid = false;
591947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            if (ie == null || ie.bytes == null) return;
592947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            mLength = ie.bytes.length;
593947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
594947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            try {
595947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mDtimCount = data.get() & Constants.BYTE_MASK;
596947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mDtimPeriod = data.get() & Constants.BYTE_MASK;
597947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mBitmapControl = data.get() & Constants.BYTE_MASK;
598947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                //A valid TIM element must have atleast one more byte
599947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                data.get();
600947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            } catch (BufferUnderflowException e) {
601947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                return;
602947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            }
603947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            if (mLength <= MAX_TIM_LENGTH) {
604947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mValid = true;
605947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            }
606947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        }
607947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne    }
608f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
609f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    /**
610f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     * This util class determines the 802.11 standard (a/b/g/n/ac) being used
611f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     */
612f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    public static class WifiMode {
613f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_UNDEFINED = 0; // Unknown/undefined
614f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11A = 1;       // 802.11a
615f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11B = 2;       // 802.11b
616f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11G = 3;       // 802.11g
617f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11N = 4;       // 802.11n
618f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11AC = 5;      // 802.11ac
619f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //<TODO> add support for 802.11ad and be more selective instead of defaulting to 11A
620f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
621f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
622f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Use frequency, max supported rate, and the existence of VHT, HT & ERP fields in scan
623f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * scan result to determine the 802.11 Wifi standard being used.
624f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
625f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static int determineMode(int frequency, int maxRate, boolean foundVht,
626f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                boolean foundHt, boolean foundErp) {
627f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            if (foundVht) {
628f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11AC;
629f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else if (foundHt) {
630f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11N;
631f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else if (foundErp) {
632f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11G;
633f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else if (frequency < 3000) {
634f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                if (maxRate < 24000000) {
635f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return MODE_11B;
636f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                } else {
637f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return MODE_11G;
638f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                }
639f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else {
640f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11A;
641f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
642f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
643f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
644f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
645f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Map the wifiMode integer to its type, and output as String MODE_11<A/B/G/N/AC>
646f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
647f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static String toString(int mode) {
648f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            switch(mode) {
649f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11A:
650f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11A";
651f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11B:
652f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11B";
653f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11G:
654f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11G";
655f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11N:
656f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11N";
657f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11AC:
658f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11AC";
659f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                default:
660f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_UNDEFINED";
661f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
662f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
663f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    }
664f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
665f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    /**
666f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     * Parser for both the Supported Rates & Extended Supported Rates Information Elements
667f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     */
668f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    public static class SupportedRates {
669f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MASK = 0x7F; // 0111 1111
670f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public boolean mValid = false;
671f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public ArrayList<Integer> mRates;
672f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
673f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public SupportedRates() {
674f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            mRates = new ArrayList<Integer>();
675f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
676f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
677f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
678f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Is this a valid Supported Rates information element.
679f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
680f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public boolean isValid() {
681f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            return mValid;
682f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
683f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
684f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
685f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * get the Rate in bits/s from associated byteval
686f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
687f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static int getRateFromByte(int byteVal) {
688f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            byteVal &= MASK;
689f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            switch(byteVal) {
690f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 2:
691f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 1000000;
692f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 4:
693f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 2000000;
694f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 11:
695f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 5500000;
696f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 12:
697f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 6000000;
698f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 18:
699f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 9000000;
700f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 22:
701f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 11000000;
702f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 24:
703f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 12000000;
704f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 36:
705f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 18000000;
706f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 44:
707f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 22000000;
708f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 48:
709f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 24000000;
710f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 66:
711f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 33000000;
712f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 72:
713f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 36000000;
714f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 96:
715f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 48000000;
716f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 108:
717f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 54000000;
718f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                default:
719f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    //ERROR UNKNOWN RATE
720f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return -1;
721f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
722f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
723f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
724f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        // Supported Rates format (size unit: byte)
725f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //
726f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //| ElementID | Length | Supported Rates  [7 Little Endian Info bits - 1 Flag bit]
727f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //      1          1          1 - 8
728f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //
729f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        // Note: InformationElement.bytes has 'Element ID' and 'Length'
730f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //       stripped off already
731f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //
732f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public void from(InformationElement ie) {
733e367514651d7f888dda870fb472eea6bcfd9ec5dPaul Stewart            mValid = false;
734e367514651d7f888dda870fb472eea6bcfd9ec5dPaul Stewart            if (ie == null || ie.bytes == null || ie.bytes.length > 8 || ie.bytes.length < 1)  {
735f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return;
736f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
737f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
738f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            try {
739f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                for (int i = 0; i < ie.bytes.length; i++) {
740f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    int rate = getRateFromByte(data.get());
741f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    if (rate > 0) {
742f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                        mRates.add(rate);
743f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    } else {
744f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                        return;
745f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    }
746f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                }
747f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } catch (BufferUnderflowException e) {
748f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return;
749f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
750f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            mValid = true;
751f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            return;
752f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
753f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
754f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
755f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Lists the rates in a human readable string
756f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
757f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public String toString() {
758f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            StringBuilder sbuf = new StringBuilder();
759f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            for (Integer rate : mRates) {
760f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                sbuf.append(String.format("%.1f", (double) rate / 1000000) + ", ");
761f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
762f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            return sbuf.toString();
763f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
764f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    }
7655d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills}
766