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
18fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wangimport android.net.wifi.ScanResult;
195d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport android.net.wifi.ScanResult.InformationElement;
205d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport android.util.Log;
2159f9a74676831ba4634b35d56a1e2bbe9bf4e322Glen Kuhne
22fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiuimport com.android.server.wifi.ByteBufferReader;
235d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport com.android.server.wifi.hotspot2.NetworkDetail;
24fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiuimport com.android.server.wifi.hotspot2.anqp.Constants;
2559f9a74676831ba4634b35d56a1e2bbe9bf4e322Glen Kuhne
263571366ac36c70746b9f013ec2b54482861c9292Randy Panimport java.nio.BufferUnderflowException;
275d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport java.nio.ByteBuffer;
285d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport java.nio.ByteOrder;
295d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willsimport java.util.ArrayList;
303571366ac36c70746b9f013ec2b54482861c9292Randy Panimport java.util.BitSet;
315d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
325d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Willspublic class InformationElementUtil {
339e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu    private static final String TAG = "InformationElementUtil";
345d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
355d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static InformationElement[] parseInformationElements(byte[] bytes) {
36da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein        if (bytes == null) {
37da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein            return new InformationElement[0];
38da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein        }
395d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        ByteBuffer data = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
405d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
415d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        ArrayList<InformationElement> infoElements = new ArrayList<>();
425d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        boolean found_ssid = false;
435d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        while (data.remaining() > 1) {
445d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int eid = data.get() & Constants.BYTE_MASK;
455d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int elementLength = data.get() & Constants.BYTE_MASK;
465d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
473571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (elementLength > data.remaining() || (eid == InformationElement.EID_SSID
483571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    && found_ssid)) {
49da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein                // APs often pad the data with bytes that happen to match that of the EID_SSID
50da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein                // marker.  This is not due to a known issue for APs to incorrectly send the SSID
51da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein                // name multiple times.
525d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                break;
535d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
543571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (eid == InformationElement.EID_SSID) {
555d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                found_ssid = true;
565d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
575d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
585d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            InformationElement ie = new InformationElement();
595d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ie.id = eid;
605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ie.bytes = new byte[elementLength];
615d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            data.get(ie.bytes);
625d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            infoElements.add(ie);
635d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
645d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        return infoElements.toArray(new InformationElement[infoElements.size()]);
655d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
665d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
679e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu    /**
689e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     * Parse and retrieve the Roaming Consortium Information Element from the list of IEs.
699e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     *
709e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     * @param ies List of IEs to retrieve from
719e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     * @return {@link RoamingConsortium}
729e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     */
739e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu    public static RoamingConsortium getRoamingConsortiumIE(InformationElement[] ies) {
749e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu        RoamingConsortium roamingConsortium = new RoamingConsortium();
759e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu        if (ies != null) {
769e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu            for (InformationElement ie : ies) {
779e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                if (ie.id == InformationElement.EID_ROAMING_CONSORTIUM) {
789e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                    try {
799e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                        roamingConsortium.from(ie);
809e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                    } catch (RuntimeException e) {
819e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                        Log.e(TAG, "Failed to parse Roaming Consortium IE: " + e.getMessage());
829e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                    }
839e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                }
849e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu            }
859e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu        }
869e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu        return roamingConsortium;
879e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu    }
889e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu
899e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu    /**
909e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     * Parse and retrieve the Hotspot 2.0 Vendor Specific Information Element from the list of IEs.
919e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     *
929e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     * @param ies List of IEs to retrieve from
939e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     * @return {@link Vsa}
949e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     */
959e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu    public static Vsa getHS2VendorSpecificIE(InformationElement[] ies) {
969e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu        Vsa vsa = new Vsa();
979e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu        if (ies != null) {
989e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu            for (InformationElement ie : ies) {
999e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                if (ie.id == InformationElement.EID_VSA) {
1009e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                    try {
1019e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                        vsa.from(ie);
1029e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                    } catch (RuntimeException e) {
1039e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                        Log.e(TAG, "Failed to parse Vendor Specific IE: " + e.getMessage());
1049e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                    }
1059e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                }
1069e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu            }
1079e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu        }
1089e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu        return vsa;
1099e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu    }
1109e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu
1119e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu    /**
1129e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     * Parse and retrieve the Interworking information element from the list of IEs.
1139e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     *
1149e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     * @param ies List of IEs to retrieve from
1159e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     * @return {@link Interworking}
1169e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu     */
1179e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu    public static Interworking getInterworkingIE(InformationElement[] ies) {
1189e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu        Interworking interworking = new Interworking();
1199e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu        if (ies != null) {
1209e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu            for (InformationElement ie : ies) {
1219e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                if (ie.id == InformationElement.EID_INTERWORKING) {
1229e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                    try {
1239e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                        interworking.from(ie);
1249e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                    } catch (RuntimeException e) {
1259e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                        Log.e(TAG, "Failed to parse Interworking IE: " + e.getMessage());
1269e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                    }
1279e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu                }
1289e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu            }
1299e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu        }
1309e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu        return interworking;
1319e83e33d444446e59aa59fc355972ff804d8eafaPeter Qiu    }
1325d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1335d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class BssLoad {
1345d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int stationCount = 0;
1355d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int channelUtilization = 0;
1365d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int capacity = 0;
1375d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1385d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
1393571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_BSS_LOAD) {
1405d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not BSS_LOAD, : " + ie.id);
1415d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1425d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length != 5) {
1433571366ac36c70746b9f013ec2b54482861c9292Randy Pan                throw new IllegalArgumentException("BSS Load element length is not 5: "
1443571366ac36c70746b9f013ec2b54482861c9292Randy Pan                                                   + ie.bytes.length);
1455d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1465d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1475d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            stationCount = data.getShort() & Constants.SHORT_MASK;
1485d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            channelUtilization = data.get() & Constants.BYTE_MASK;
1495d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            capacity = data.getShort() & Constants.SHORT_MASK;
1505d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1515d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
1525d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1535d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class HtOperation {
1545d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int secondChannelOffset = 0;
1555d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1565d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getChannelWidth() {
1573571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (secondChannelOffset != 0) {
1585d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 1;
1593571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } else {
1605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 0;
1615d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1625d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1635d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1645d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getCenterFreq0(int primaryFrequency) {
1659f743918a412fec9ad5a0386fbf6cf0361313f58xinhe            //40 MHz
1669f743918a412fec9ad5a0386fbf6cf0361313f58xinhe            if (secondChannelOffset != 0) {
1675d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if (secondChannelOffset == 1) {
1689f743918a412fec9ad5a0386fbf6cf0361313f58xinhe                    return primaryFrequency + 10;
1695d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                } else if (secondChannelOffset == 3) {
1709f743918a412fec9ad5a0386fbf6cf0361313f58xinhe                    return primaryFrequency - 10;
1715d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                } else {
1725d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    Log.e("HtOperation", "Error on secondChannelOffset: " + secondChannelOffset);
1735d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    return 0;
1745d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
1755d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            } else {
1765d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 0;
1775d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1785d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1795d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1805d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
1813571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_HT_OPERATION) {
1825d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not HT_OPERATION, : " + ie.id);
1835d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1845d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            secondChannelOffset = ie.bytes[1] & 0x3;
1855d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1865d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
1875d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1885d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class VhtOperation {
1895d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int channelMode = 0;
1905d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int centerFreqIndex1 = 0;
1915d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int centerFreqIndex2 = 0;
1925d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1935d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public boolean isValid() {
1945d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return channelMode != 0;
1955d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1965d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1975d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getChannelWidth() {
1985d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return channelMode + 1;
1995d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2005d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2015d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getCenterFreq0() {
2025d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            //convert channel index to frequency in MHz, channel 36 is 5180MHz
2035d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return (centerFreqIndex1 - 36) * 5 + 5180;
2045d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2055d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2065d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getCenterFreq1() {
2073571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (channelMode > 1) { //160MHz
2085d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return (centerFreqIndex2 - 36) * 5 + 5180;
2095d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            } else {
2105d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 0;
2115d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2125d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2135d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2145d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
2153571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_VHT_OPERATION) {
2165d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not VHT_OPERATION, : " + ie.id);
2175d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2185d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            channelMode = ie.bytes[0] & Constants.BYTE_MASK;
2195d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            centerFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK;
2205d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            centerFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK;
2215d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2225d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
2235d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2245d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class Interworking {
2255d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public NetworkDetail.Ant ant = null;
2265d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public boolean internet = false;
2275d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public long hessid = 0L;
2285d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2295d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
2303571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_INTERWORKING) {
2315d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not INTERWORKING, : " + ie.id);
2325d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2335d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2345d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int anOptions = data.get() & Constants.BYTE_MASK;
2355d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ant = NetworkDetail.Ant.values()[anOptions & 0x0f];
2365d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            internet = (anOptions & 0x10) != 0;
237e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan            // There are only three possible lengths for the Interworking IE:
238e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan            // Len 1: Access Network Options only
239e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan            // Len 3: Access Network Options & Venue Info
240e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan            // Len 7: Access Network Options & HESSID
241e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan            // Len 9: Access Network Options, Venue Info, & HESSID
242e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan            if (ie.bytes.length != 1
243e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan                    && ie.bytes.length != 3
244e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan                    && ie.bytes.length != 7
245e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan                    && ie.bytes.length != 9) {
246e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan                throw new IllegalArgumentException(
247e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan                        "Bad Interworking element length: " + ie.bytes.length);
2485d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
249e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan
250749ceba56556c36337dd0081b68bb6da43ae9758Etan Cohen            if (ie.bytes.length == 3 || ie.bytes.length == 9) {
251749ceba56556c36337dd0081b68bb6da43ae9758Etan Cohen                int venueInfo = (int) ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 2);
252749ceba56556c36337dd0081b68bb6da43ae9758Etan Cohen            }
253749ceba56556c36337dd0081b68bb6da43ae9758Etan Cohen
2545d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length == 7 || ie.bytes.length == 9) {
255fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiu                hessid = ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 6);
2565d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2575d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2585d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
2595d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class RoamingConsortium {
2615d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int anqpOICount = 0;
2622e889dc0b3f348f45cc5f5006d8784bed30583a9Masashi Honma
2632e889dc0b3f348f45cc5f5006d8784bed30583a9Masashi Honma        private long[] roamingConsortiums = null;
2642e889dc0b3f348f45cc5f5006d8784bed30583a9Masashi Honma
2652e889dc0b3f348f45cc5f5006d8784bed30583a9Masashi Honma        public long[] getRoamingConsortiums() {
2662e889dc0b3f348f45cc5f5006d8784bed30583a9Masashi Honma            return roamingConsortiums;
2672e889dc0b3f348f45cc5f5006d8784bed30583a9Masashi Honma        }
2685d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2695d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
2703571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_ROAMING_CONSORTIUM) {
2713571366ac36c70746b9f013ec2b54482861c9292Randy Pan                throw new IllegalArgumentException("Element id is not ROAMING_CONSORTIUM, : "
2723571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        + ie.id);
2735d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2745d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2755d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            anqpOICount = data.get() & Constants.BYTE_MASK;
2765d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2775d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi12Length = data.get() & Constants.BYTE_MASK;
2785d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi1Length = oi12Length & Constants.NIBBLE_MASK;
2795d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK;
2805d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi3Length = ie.bytes.length - 2 - oi1Length - oi2Length;
2815d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oiCount = 0;
2825d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi1Length > 0) {
2835d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                oiCount++;
2845d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if (oi2Length > 0) {
2855d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    oiCount++;
2865d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    if (oi3Length > 0) {
2875d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        oiCount++;
2885d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    }
2895d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
2905d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2915d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            roamingConsortiums = new long[oiCount];
2925d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi1Length > 0 && roamingConsortiums.length > 0) {
2935d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[0] =
294fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiu                        ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi1Length);
2955d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2965d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi2Length > 0 && roamingConsortiums.length > 1) {
2975d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[1] =
298fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiu                        ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi2Length);
2995d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
3005d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi3Length > 0 && roamingConsortiums.length > 2) {
3015d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[2] =
302fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiu                        ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi3Length);
3035d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
3045d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
3055d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
3065d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
3075d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class Vsa {
3085d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        private static final int ANQP_DOMID_BIT = 0x04;
3095d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
3105d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public NetworkDetail.HSRelease hsRelease = null;
3115d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int anqpDomainID = 0;    // No domain ID treated the same as a 0; unique info per AP.
3125d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
3135d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
3145d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
3155d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length >= 5 && data.getInt() == Constants.HS20_FRAME_PREFIX) {
3165d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                int hsConf = data.get() & Constants.BYTE_MASK;
3175d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                switch ((hsConf >> 4) & Constants.NIBBLE_MASK) {
3185d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    case 0:
3195d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.R1;
3205d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
3215d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    case 1:
3225d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.R2;
3235d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
3245d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    default:
3255d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.Unknown;
3265d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
3275d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
3285d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if ((hsConf & ANQP_DOMID_BIT) != 0) {
3295d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    if (ie.bytes.length < 7) {
3305d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        throw new IllegalArgumentException(
3315d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                                "HS20 indication element too short: " + ie.bytes.length);
3325d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    }
3335d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    anqpDomainID = data.getShort() & Constants.SHORT_MASK;
3345d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
3355d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
3365d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
3375d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
3385d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
339b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu    /**
340b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * This IE contained a bit field indicating the capabilities being advertised by the STA.
341b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * The size of the bit field (number of bytes) is indicated by the |Length| field in the IE.
342b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     *
343b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * Refer to Section 8.4.2.29 in IEEE 802.11-2012 Spec for capability associated with each
344b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * bit.
345b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     *
346b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * Here is the wire format of this IE:
347b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * | Element ID | Length | Capabilities |
348b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     *       1           1          n
349b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     */
3505d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class ExtendedCapabilities {
3515d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        private static final int RTT_RESP_ENABLE_BIT = 70;
352b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        private static final int SSID_UTF8_BIT = 48;
3535d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
354b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public BitSet capabilitiesBitSet;
3555d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
356b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        /**
357b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         * @return true if SSID should be interpreted using UTF-8 encoding
358b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         */
359b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public boolean isStrictUtf8() {
360b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            return capabilitiesBitSet.get(SSID_UTF8_BIT);
3615d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
3625d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
363b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        /**
364b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         * @return true if 802.11 MC RTT Response is enabled
365b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         */
366b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public boolean is80211McRTTResponder() {
367b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            return capabilitiesBitSet.get(RTT_RESP_ENABLE_BIT);
3685d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
3695d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
370b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public ExtendedCapabilities() {
371b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            capabilitiesBitSet = new BitSet();
3725d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
3735d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
374b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public ExtendedCapabilities(ExtendedCapabilities other) {
375b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            capabilitiesBitSet = other.capabilitiesBitSet;
376b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        }
3775d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
378b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        /**
379b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         * Parse an ExtendedCapabilities from the IE containing raw bytes.
380b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         *
381b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         * @param ie The Information element data
382b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         */
383b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public void from(InformationElement ie) {
384b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            capabilitiesBitSet = BitSet.valueOf(ie.bytes);
3855d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
3865d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
3873571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3883571366ac36c70746b9f013ec2b54482861c9292Randy Pan    /**
3893571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * parse beacon to build the capabilities
3903571366ac36c70746b9f013ec2b54482861c9292Randy Pan     *
3913571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * This class is used to build the capabilities string of the scan results coming
3923571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * from HAL. It parses the ieee beacon's capability field, WPA and RSNE IE as per spec,
3933571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * and builds the ScanResult.capabilities String in a way that mirrors the values returned
3943571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * by wpa_supplicant.
3953571366ac36c70746b9f013ec2b54482861c9292Randy Pan     */
3963571366ac36c70746b9f013ec2b54482861c9292Randy Pan    public static class Capabilities {
3979b5773d2805e8c6141ca75de272921a84941546bRandy Pan        private static final int CAP_ESS_BIT_OFFSET = 0;
3983571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int CAP_PRIVACY_BIT_OFFSET = 4;
3993571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4003571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA_VENDOR_OUI_TYPE_ONE = 0x01f25000;
401f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang        private static final int WPS_VENDOR_OUI_TYPE = 0x04f25000;
4023571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final short WPA_VENDOR_OUI_VERSION = 0x0001;
4033571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final short RSNE_VERSION = 0x0001;
4043571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4053571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA_AKM_EAP = 0x01f25000;
4063571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA_AKM_PSK = 0x02f25000;
4073571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4083571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_EAP = 0x01ac0f00;
4093571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_PSK = 0x02ac0f00;
4103571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_FT_EAP = 0x03ac0f00;
4113571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_FT_PSK = 0x04ac0f00;
4123571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_EAP_SHA256 = 0x05ac0f00;
4133571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_PSK_SHA256 = 0x06ac0f00;
4143571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4154d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int WPA_CIPHER_NONE = 0x00f25000;
4164d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int WPA_CIPHER_TKIP = 0x02f25000;
4174d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int WPA_CIPHER_CCMP = 0x04f25000;
4184d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
4194d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int RSN_CIPHER_NONE = 0x00ac0f00;
4204d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int RSN_CIPHER_TKIP = 0x02ac0f00;
4214d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int RSN_CIPHER_CCMP = 0x04ac0f00;
4224d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int RSN_CIPHER_NO_GROUP_ADDRESSED = 0x07ac0f00;
4234d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
424c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang        public ArrayList<Integer> protocol;
425c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang        public ArrayList<ArrayList<Integer>> keyManagement;
426c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang        public ArrayList<ArrayList<Integer>> pairwiseCipher;
427c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang        public ArrayList<Integer> groupCipher;
4284d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        public boolean isESS;
4294d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        public boolean isPrivacy;
430f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang        public boolean isWPS;
4314d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
4323571366ac36c70746b9f013ec2b54482861c9292Randy Pan        public Capabilities() {
4333571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
4343571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4353571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // RSNE format (size unit: byte)
4363571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
4373571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Element ID | Length | Version | Group Data Cipher Suite |
4383571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //      1           1         2                 4
4393571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
4403571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //              2                            4 * m
4413571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | AKM Suite Count | AKM Suite List | RSN Capabilities |
4423571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //          2               4 * n               2
4433571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | PMKID Count | PMKID List | Group Management Cipher Suite |
4443571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //        2          16 * s                 4
4453571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
4463571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // Note: InformationElement.bytes has 'Element ID' and 'Length'
4473571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //       stripped off already
4484d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private void parseRsnElement(InformationElement ie) {
4493571366ac36c70746b9f013ec2b54482861c9292Randy Pan            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
4503571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4513571366ac36c70746b9f013ec2b54482861c9292Randy Pan            try {
4523571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // version
4533571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (buf.getShort() != RSNE_VERSION) {
4543571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    // incorrect version
4554d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                    return;
4563571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
4573571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4583571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // found the RSNE IE, hence start building the capability string
459c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                protocol.add(ScanResult.PROTOCOL_WPA2);
4604d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
4614d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                // group data cipher suite
462c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                groupCipher.add(parseRsnCipher(buf.getInt()));
4633571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4643571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise cipher suite count
4653571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short cipherCount = buf.getShort();
466c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                ArrayList<Integer> rsnPairwiseCipher = new ArrayList<>();
4673571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise cipher suite list
4683571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < cipherCount; i++) {
469c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                    rsnPairwiseCipher.add(parseRsnCipher(buf.getInt()));
4703571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
471c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                pairwiseCipher.add(rsnPairwiseCipher);
4723571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4733571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM
4743571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM suite count
4753571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short akmCount = buf.getShort();
476c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                ArrayList<Integer> rsnKeyManagement = new ArrayList<>();
4773571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4783571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < akmCount; i++) {
4793571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    int akm = buf.getInt();
4803571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    switch (akm) {
4813571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_EAP:
482c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP);
4833571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4843571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_PSK:
485c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK);
4863571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4873571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_FT_EAP:
488c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_EAP);
4893571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4903571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_FT_PSK:
491c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK);
4923571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4933571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_EAP_SHA256:
494c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SHA256);
4953571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4963571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_PSK_SHA256:
497c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK_SHA256);
4983571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4993571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        default:
5003571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            // do nothing
5013571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
5023571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    }
5033571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
5044d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                // Default AKM
505c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                if (rsnKeyManagement.isEmpty()) {
506c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                    rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP);
5074d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                }
508c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                keyManagement.add(rsnKeyManagement);
5093571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } catch (BufferUnderflowException e) {
5103571366ac36c70746b9f013ec2b54482861c9292Randy Pan                Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow");
5114d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            }
5124d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        }
5134d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
514fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private static int parseWpaCipher(int cipher) {
5154d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            switch (cipher) {
5164d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case WPA_CIPHER_NONE:
517fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NONE;
5184d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case WPA_CIPHER_TKIP:
519fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_TKIP;
5204d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case WPA_CIPHER_CCMP:
521fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_CCMP;
5224d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                default:
523fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    Log.w("IE_Capabilities", "Unknown WPA cipher suite: "
524fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            + Integer.toHexString(cipher));
525fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NONE;
5264d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            }
5274d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        }
5284d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
529fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private static int parseRsnCipher(int cipher) {
5304d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            switch (cipher) {
5314d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case RSN_CIPHER_NONE:
532fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NONE;
5334d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case RSN_CIPHER_TKIP:
534fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_TKIP;
5354d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case RSN_CIPHER_CCMP:
536fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_CCMP;
5374d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case RSN_CIPHER_NO_GROUP_ADDRESSED:
538fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NO_GROUP_ADDRESSED;
5394d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                default:
540fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    Log.w("IE_Capabilities", "Unknown RSN cipher suite: "
541fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            + Integer.toHexString(cipher));
542fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NONE;
5433571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
5443571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
5453571366ac36c70746b9f013ec2b54482861c9292Randy Pan
546f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang        private static boolean isWpsElement(InformationElement ie) {
547f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
548f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang            try {
549f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                // WPS OUI and type
550f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                return (buf.getInt() == WPS_VENDOR_OUI_TYPE);
551f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang            } catch (BufferUnderflowException e) {
552f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
553f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                return false;
554f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang            }
555f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang        }
556f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang
5573571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static boolean isWpaOneElement(InformationElement ie) {
5583571366ac36c70746b9f013ec2b54482861c9292Randy Pan            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
5593571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5603571366ac36c70746b9f013ec2b54482861c9292Randy Pan            try {
5613571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // WPA OUI and type
5623571366ac36c70746b9f013ec2b54482861c9292Randy Pan                return (buf.getInt() == WPA_VENDOR_OUI_TYPE_ONE);
5633571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } catch (BufferUnderflowException e) {
5643571366ac36c70746b9f013ec2b54482861c9292Randy Pan                Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
5653571366ac36c70746b9f013ec2b54482861c9292Randy Pan                return false;
5663571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
5673571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
5683571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5693571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // WPA type 1 format (size unit: byte)
5703571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
5713571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Element ID | Length | OUI | Type | Version |
5723571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //      1           1       3     1        2
5734d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        // | Group Data Cipher Suite |
5744d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        //             4
5753571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
5763571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //              2                            4 * m
5773571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | AKM Suite Count | AKM Suite List |
5783571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //          2               4 * n
5793571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
5803571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // Note: InformationElement.bytes has 'Element ID' and 'Length'
5813571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //       stripped off already
5823571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
5834d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private void parseWpaOneElement(InformationElement ie) {
5843571366ac36c70746b9f013ec2b54482861c9292Randy Pan            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
5853571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5863571366ac36c70746b9f013ec2b54482861c9292Randy Pan            try {
5873571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // skip WPA OUI and type parsing. isWpaOneElement() should have
5883571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // been called for verification before we reach here.
5893571366ac36c70746b9f013ec2b54482861c9292Randy Pan                buf.getInt();
5903571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5913571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // version
5923571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (buf.getShort() != WPA_VENDOR_OUI_VERSION)  {
5933571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    // incorrect version
5944d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                    return;
5953571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
5963571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5974d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                // start building the string
598c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                protocol.add(ScanResult.PROTOCOL_WPA);
5994d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
6003571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // group data cipher suite
601c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                groupCipher.add(parseWpaCipher(buf.getInt()));
6023571366ac36c70746b9f013ec2b54482861c9292Randy Pan
6033571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise cipher suite count
6043571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short cipherCount = buf.getShort();
605c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                ArrayList<Integer> wpaPairwiseCipher = new ArrayList<>();
6063571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise chipher suite list
6073571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < cipherCount; i++) {
608c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                    wpaPairwiseCipher.add(parseWpaCipher(buf.getInt()));
6093571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
610c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                pairwiseCipher.add(wpaPairwiseCipher);
6113571366ac36c70746b9f013ec2b54482861c9292Randy Pan
6123571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM
6133571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM suite count
6143571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short akmCount = buf.getShort();
615c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                ArrayList<Integer> wpaKeyManagement = new ArrayList<>();
6163571366ac36c70746b9f013ec2b54482861c9292Randy Pan
6173571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM suite list
6183571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < akmCount; i++) {
6193571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    int akm = buf.getInt();
6203571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    switch (akm) {
6213571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA_AKM_EAP:
622c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP);
6233571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
6243571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA_AKM_PSK:
625c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            wpaKeyManagement.add(ScanResult.KEY_MGMT_PSK);
6263571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
6273571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        default:
6283571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            // do nothing
6293571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
6303571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    }
6313571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
6324d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                // Default AKM
633c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                if (wpaKeyManagement.isEmpty()) {
634c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                    wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP);
6354d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                }
636c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                keyManagement.add(wpaKeyManagement);
6373571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } catch (BufferUnderflowException e) {
6383571366ac36c70746b9f013ec2b54482861c9292Randy Pan                Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow");
6393571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
6403571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
6413571366ac36c70746b9f013ec2b54482861c9292Randy Pan
6423571366ac36c70746b9f013ec2b54482861c9292Randy Pan        /**
6433571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * Parse the Information Element and the 16-bit Capability Information field
6444d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         * to build the InformationElemmentUtil.capabilities object.
6453571366ac36c70746b9f013ec2b54482861c9292Randy Pan         *
6463571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * @param ies -- Information Element array
6473571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * @param beaconCap -- 16-bit Beacon Capability Information field
6483571366ac36c70746b9f013ec2b54482861c9292Randy Pan         */
6494d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
6504d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        public void from(InformationElement[] ies, BitSet beaconCap) {
651c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            protocol = new ArrayList<Integer>();
652c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            keyManagement = new ArrayList<ArrayList<Integer>>();
653c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            groupCipher = new ArrayList<Integer>();
654c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            pairwiseCipher = new ArrayList<ArrayList<Integer>>();
6553571366ac36c70746b9f013ec2b54482861c9292Randy Pan
6563571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ies == null || beaconCap == null) {
6574d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                return;
6583571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
6594d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            isESS = beaconCap.get(CAP_ESS_BIT_OFFSET);
6604d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            isPrivacy = beaconCap.get(CAP_PRIVACY_BIT_OFFSET);
6613571366ac36c70746b9f013ec2b54482861c9292Randy Pan            for (InformationElement ie : ies) {
6623571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (ie.id == InformationElement.EID_RSN) {
6634d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                    parseRsnElement(ie);
6643571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
6653571366ac36c70746b9f013ec2b54482861c9292Randy Pan
6663571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (ie.id == InformationElement.EID_VSA) {
6673571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    if (isWpaOneElement(ie)) {
6684d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                        parseWpaOneElement(ie);
6693571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    }
670f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                    if (isWpsElement(ie)) {
671f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                        // TODO(b/62134557): parse WPS IE to provide finer granularity information.
672f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                        isWPS = true;
673f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                    }
6743571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
6753571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
676fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        }
677fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang
678fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private String protocolToString(int protocol) {
679fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            switch (protocol) {
680fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.PROTOCOL_NONE:
681fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "None";
682fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.PROTOCOL_WPA:
683fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "WPA";
684fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.PROTOCOL_WPA2:
685fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "WPA2";
686fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                default:
687fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "?";
688fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            }
689fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        }
690fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang
691fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private String keyManagementToString(int akm) {
692fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            switch (akm) {
693fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_NONE:
694fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "None";
695fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_PSK:
696fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "PSK";
697fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_EAP:
698fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "EAP";
699fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_FT_EAP:
700fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "FT/EAP";
701fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_FT_PSK:
702fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "FT/PSK";
703fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_EAP_SHA256:
704fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "EAP-SHA256";
705fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_PSK_SHA256:
706fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "PSK-SHA256";
707fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                default:
708fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "?";
709fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            }
710fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        }
711fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang
712fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private String cipherToString(int cipher) {
713fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            switch (cipher) {
714fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.CIPHER_NONE:
715fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "None";
716fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.CIPHER_CCMP:
717fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "CCMP";
718fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.CIPHER_TKIP:
719fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "TKIP";
720fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                default:
721fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "?";
7223571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
7234d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        }
7243571366ac36c70746b9f013ec2b54482861c9292Randy Pan
7254d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        /**
7264d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         * Build the ScanResult.capabilities String.
7274d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         *
7284d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         * @return security string that mirrors what wpa_supplicant generates
7294d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         */
7304d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        public String generateCapabilitiesString() {
7314d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            String capabilities = "";
732fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            // private Beacon without an RSNE or WPA IE, hence WEP0
733c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            boolean isWEP = (protocol.isEmpty()) && isPrivacy;
734fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang
735c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            if (isWEP) {
736c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                capabilities += "[WEP]";
737c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            }
738c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            for (int i = 0; i < protocol.size(); i++) {
7393e80f5fa9e07c935c328fd06de555cfd5f43ed26Roshan Pius                capabilities += "[" + protocolToString(protocol.get(i));
7403e80f5fa9e07c935c328fd06de555cfd5f43ed26Roshan Pius                if (i < keyManagement.size()) {
7413e80f5fa9e07c935c328fd06de555cfd5f43ed26Roshan Pius                    for (int j = 0; j < keyManagement.get(i).size(); j++) {
7423e80f5fa9e07c935c328fd06de555cfd5f43ed26Roshan Pius                        capabilities += ((j == 0) ? "-" : "+")
7433e80f5fa9e07c935c328fd06de555cfd5f43ed26Roshan Pius                                + keyManagementToString(keyManagement.get(i).get(j));
7443e80f5fa9e07c935c328fd06de555cfd5f43ed26Roshan Pius                    }
7454d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                }
7463e80f5fa9e07c935c328fd06de555cfd5f43ed26Roshan Pius                if (i < pairwiseCipher.size()) {
7473e80f5fa9e07c935c328fd06de555cfd5f43ed26Roshan Pius                    for (int j = 0; j < pairwiseCipher.get(i).size(); j++) {
7483e80f5fa9e07c935c328fd06de555cfd5f43ed26Roshan Pius                        capabilities += ((j == 0) ? "-" : "+")
7493e80f5fa9e07c935c328fd06de555cfd5f43ed26Roshan Pius                                + cipherToString(pairwiseCipher.get(i).get(j));
7503e80f5fa9e07c935c328fd06de555cfd5f43ed26Roshan Pius                    }
7514d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                }
7524d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                capabilities += "]";
7534d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            }
7544d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            if (isESS) {
7559b5773d2805e8c6141ca75de272921a84941546bRandy Pan                capabilities += "[ESS]";
7569b5773d2805e8c6141ca75de272921a84941546bRandy Pan            }
757f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang            if (isWPS) {
758f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                capabilities += "[WPS]";
759f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang            }
760f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang
7613571366ac36c70746b9f013ec2b54482861c9292Randy Pan            return capabilities;
7623571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
7633571366ac36c70746b9f013ec2b54482861c9292Randy Pan    }
764947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne
765947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne    /**
766947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     * Parser for the Traffic Indication Map (TIM) Information Element (EID 5). This element will
767947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     * only be present in scan results that are derived from a Beacon Frame, not from the more
768947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     * plentiful probe responses. Call 'isValid()' after parsing, to ensure the results are correct.
769947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     */
770947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne    public static class TrafficIndicationMap {
771947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        private static final int MAX_TIM_LENGTH = 254;
772947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        private boolean mValid = false;
773947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mLength = 0;
774947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mDtimCount = -1;
775947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //Negative DTIM Period means no TIM element was given this frame.
776947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mDtimPeriod = -1;
777947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mBitmapControl = 0;
778947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne
779947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        /**
780947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne         * Is this a valid TIM information element.
781947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne         */
782947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public boolean isValid() {
783947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            return mValid;
784947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        }
785947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne
786947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        // Traffic Indication Map format (size unit: byte)
787947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //
788947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //| ElementID | Length | DTIM Count | DTIM Period | BitmapControl | Partial Virtual Bitmap |
789947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //      1          1          1            1               1                1 - 251
790947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //
791947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        // Note: InformationElement.bytes has 'Element ID' and 'Length'
792947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //       stripped off already
793947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //
794947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public void from(InformationElement ie) {
795e367514651d7f888dda870fb472eea6bcfd9ec5dPaul Stewart            mValid = false;
796947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            if (ie == null || ie.bytes == null) return;
797947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            mLength = ie.bytes.length;
798947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
799947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            try {
800947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mDtimCount = data.get() & Constants.BYTE_MASK;
801947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mDtimPeriod = data.get() & Constants.BYTE_MASK;
802947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mBitmapControl = data.get() & Constants.BYTE_MASK;
803947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                //A valid TIM element must have atleast one more byte
804947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                data.get();
805947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            } catch (BufferUnderflowException e) {
806947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                return;
807947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            }
80859f9a74676831ba4634b35d56a1e2bbe9bf4e322Glen Kuhne            if (mLength <= MAX_TIM_LENGTH && mDtimPeriod > 0) {
809947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mValid = true;
810947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            }
811947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        }
812947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne    }
813f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
814f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    /**
815f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     * This util class determines the 802.11 standard (a/b/g/n/ac) being used
816f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     */
817f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    public static class WifiMode {
818f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_UNDEFINED = 0; // Unknown/undefined
819f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11A = 1;       // 802.11a
820f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11B = 2;       // 802.11b
821f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11G = 3;       // 802.11g
822f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11N = 4;       // 802.11n
823f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11AC = 5;      // 802.11ac
824f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //<TODO> add support for 802.11ad and be more selective instead of defaulting to 11A
825f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
826f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
827f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Use frequency, max supported rate, and the existence of VHT, HT & ERP fields in scan
828f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * scan result to determine the 802.11 Wifi standard being used.
829f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
830f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static int determineMode(int frequency, int maxRate, boolean foundVht,
831f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                boolean foundHt, boolean foundErp) {
832f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            if (foundVht) {
833f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11AC;
834f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else if (foundHt) {
835f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11N;
836f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else if (foundErp) {
837f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11G;
838f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else if (frequency < 3000) {
839f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                if (maxRate < 24000000) {
840f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return MODE_11B;
841f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                } else {
842f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return MODE_11G;
843f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                }
844f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else {
845f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11A;
846f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
847f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
848f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
849f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
850f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Map the wifiMode integer to its type, and output as String MODE_11<A/B/G/N/AC>
851f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
852f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static String toString(int mode) {
853f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            switch(mode) {
854f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11A:
855f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11A";
856f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11B:
857f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11B";
858f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11G:
859f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11G";
860f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11N:
861f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11N";
862f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11AC:
863f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11AC";
864f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                default:
865f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_UNDEFINED";
866f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
867f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
868f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    }
869f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
870f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    /**
871f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     * Parser for both the Supported Rates & Extended Supported Rates Information Elements
872f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     */
873f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    public static class SupportedRates {
874f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MASK = 0x7F; // 0111 1111
875f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public boolean mValid = false;
876f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public ArrayList<Integer> mRates;
877f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
878f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public SupportedRates() {
879f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            mRates = new ArrayList<Integer>();
880f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
881f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
882f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
883f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Is this a valid Supported Rates information element.
884f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
885f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public boolean isValid() {
886f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            return mValid;
887f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
888f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
889f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
890f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * get the Rate in bits/s from associated byteval
891f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
892f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static int getRateFromByte(int byteVal) {
893f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            byteVal &= MASK;
894f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            switch(byteVal) {
895f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 2:
896f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 1000000;
897f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 4:
898f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 2000000;
899f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 11:
900f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 5500000;
901f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 12:
902f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 6000000;
903f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 18:
904f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 9000000;
905f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 22:
906f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 11000000;
907f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 24:
908f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 12000000;
909f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 36:
910f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 18000000;
911f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 44:
912f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 22000000;
913f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 48:
914f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 24000000;
915f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 66:
916f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 33000000;
917f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 72:
918f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 36000000;
919f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 96:
920f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 48000000;
921f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 108:
922f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 54000000;
923f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                default:
924f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    //ERROR UNKNOWN RATE
925f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return -1;
926f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
927f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
928f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
929f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        // Supported Rates format (size unit: byte)
930f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //
931f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //| ElementID | Length | Supported Rates  [7 Little Endian Info bits - 1 Flag bit]
932f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //      1          1          1 - 8
933f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //
934f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        // Note: InformationElement.bytes has 'Element ID' and 'Length'
935f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //       stripped off already
936f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //
937f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public void from(InformationElement ie) {
938e367514651d7f888dda870fb472eea6bcfd9ec5dPaul Stewart            mValid = false;
939e367514651d7f888dda870fb472eea6bcfd9ec5dPaul Stewart            if (ie == null || ie.bytes == null || ie.bytes.length > 8 || ie.bytes.length < 1)  {
940f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return;
941f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
942f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
943f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            try {
944f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                for (int i = 0; i < ie.bytes.length; i++) {
945f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    int rate = getRateFromByte(data.get());
946f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    if (rate > 0) {
947f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                        mRates.add(rate);
948f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    } else {
949f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                        return;
950f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    }
951f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                }
952f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } catch (BufferUnderflowException e) {
953f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return;
954f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
955f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            mValid = true;
956f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            return;
957f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
958f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
959f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
960f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Lists the rates in a human readable string
961f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
962f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public String toString() {
963f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            StringBuilder sbuf = new StringBuilder();
964f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            for (Integer rate : mRates) {
965f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                sbuf.append(String.format("%.1f", (double) rate / 1000000) + ", ");
966f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
967f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            return sbuf.toString();
968f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
969f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    }
9705d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills}
971