InformationElementUtil.java revision f7364b4baea06722022b7c6519ace0556481cc06
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
2505d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length == 7 || ie.bytes.length == 9) {
251fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiu                hessid = ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 6);
2525d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2535d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2545d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
2555d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2565d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class RoamingConsortium {
2575d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int anqpOICount = 0;
2585d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public long[] roamingConsortiums = null;
2595d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
2613571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_ROAMING_CONSORTIUM) {
2623571366ac36c70746b9f013ec2b54482861c9292Randy Pan                throw new IllegalArgumentException("Element id is not ROAMING_CONSORTIUM, : "
2633571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        + ie.id);
2645d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2655d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2665d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            anqpOICount = data.get() & Constants.BYTE_MASK;
2675d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2685d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi12Length = data.get() & Constants.BYTE_MASK;
2695d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi1Length = oi12Length & Constants.NIBBLE_MASK;
2705d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK;
2715d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi3Length = ie.bytes.length - 2 - oi1Length - oi2Length;
2725d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oiCount = 0;
2735d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi1Length > 0) {
2745d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                oiCount++;
2755d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if (oi2Length > 0) {
2765d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    oiCount++;
2775d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    if (oi3Length > 0) {
2785d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        oiCount++;
2795d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    }
2805d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
2815d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2825d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            roamingConsortiums = new long[oiCount];
2835d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi1Length > 0 && roamingConsortiums.length > 0) {
2845d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[0] =
285fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiu                        ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi1Length);
2865d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2875d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi2Length > 0 && roamingConsortiums.length > 1) {
2885d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[1] =
289fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiu                        ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi2Length);
2905d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2915d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi3Length > 0 && roamingConsortiums.length > 2) {
2925d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[2] =
293fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiu                        ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi3Length);
2945d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2955d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2965d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
2975d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2985d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class Vsa {
2995d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        private static final int ANQP_DOMID_BIT = 0x04;
3005d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
3015d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public NetworkDetail.HSRelease hsRelease = null;
3025d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int anqpDomainID = 0;    // No domain ID treated the same as a 0; unique info per AP.
3035d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
3045d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
3055d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
3065d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length >= 5 && data.getInt() == Constants.HS20_FRAME_PREFIX) {
3075d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                int hsConf = data.get() & Constants.BYTE_MASK;
3085d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                switch ((hsConf >> 4) & Constants.NIBBLE_MASK) {
3095d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    case 0:
3105d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.R1;
3115d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
3125d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    case 1:
3135d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.R2;
3145d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
3155d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    default:
3165d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.Unknown;
3175d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
3185d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
3195d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if ((hsConf & ANQP_DOMID_BIT) != 0) {
3205d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    if (ie.bytes.length < 7) {
3215d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        throw new IllegalArgumentException(
3225d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                                "HS20 indication element too short: " + ie.bytes.length);
3235d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    }
3245d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    anqpDomainID = data.getShort() & Constants.SHORT_MASK;
3255d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
3265d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
3275d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
3285d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
3295d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
330b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu    /**
331b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * This IE contained a bit field indicating the capabilities being advertised by the STA.
332b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * The size of the bit field (number of bytes) is indicated by the |Length| field in the IE.
333b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     *
334b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * Refer to Section 8.4.2.29 in IEEE 802.11-2012 Spec for capability associated with each
335b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * bit.
336b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     *
337b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * Here is the wire format of this IE:
338b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * | Element ID | Length | Capabilities |
339b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     *       1           1          n
340b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     */
3415d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class ExtendedCapabilities {
3425d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        private static final int RTT_RESP_ENABLE_BIT = 70;
343b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        private static final int SSID_UTF8_BIT = 48;
3445d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
345b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public BitSet capabilitiesBitSet;
3465d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
347b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        /**
348b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         * @return true if SSID should be interpreted using UTF-8 encoding
349b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         */
350b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public boolean isStrictUtf8() {
351b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            return capabilitiesBitSet.get(SSID_UTF8_BIT);
3525d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
3535d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
354b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        /**
355b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         * @return true if 802.11 MC RTT Response is enabled
356b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         */
357b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public boolean is80211McRTTResponder() {
358b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            return capabilitiesBitSet.get(RTT_RESP_ENABLE_BIT);
3595d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
3605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
361b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public ExtendedCapabilities() {
362b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            capabilitiesBitSet = new BitSet();
3635d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
3645d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
365b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public ExtendedCapabilities(ExtendedCapabilities other) {
366b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            capabilitiesBitSet = other.capabilitiesBitSet;
367b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        }
3685d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
369b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        /**
370b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         * Parse an ExtendedCapabilities from the IE containing raw bytes.
371b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         *
372b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         * @param ie The Information element data
373b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         */
374b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public void from(InformationElement ie) {
375b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            capabilitiesBitSet = BitSet.valueOf(ie.bytes);
3765d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
3775d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
3783571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3793571366ac36c70746b9f013ec2b54482861c9292Randy Pan    /**
3803571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * parse beacon to build the capabilities
3813571366ac36c70746b9f013ec2b54482861c9292Randy Pan     *
3823571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * This class is used to build the capabilities string of the scan results coming
3833571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * from HAL. It parses the ieee beacon's capability field, WPA and RSNE IE as per spec,
3843571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * and builds the ScanResult.capabilities String in a way that mirrors the values returned
3853571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * by wpa_supplicant.
3863571366ac36c70746b9f013ec2b54482861c9292Randy Pan     */
3873571366ac36c70746b9f013ec2b54482861c9292Randy Pan    public static class Capabilities {
3889b5773d2805e8c6141ca75de272921a84941546bRandy Pan        private static final int CAP_ESS_BIT_OFFSET = 0;
3893571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int CAP_PRIVACY_BIT_OFFSET = 4;
3903571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3913571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA_VENDOR_OUI_TYPE_ONE = 0x01f25000;
392f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang        private static final int WPS_VENDOR_OUI_TYPE = 0x04f25000;
3933571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final short WPA_VENDOR_OUI_VERSION = 0x0001;
3943571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final short RSNE_VERSION = 0x0001;
3953571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3963571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA_AKM_EAP = 0x01f25000;
3973571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA_AKM_PSK = 0x02f25000;
3983571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3993571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_EAP = 0x01ac0f00;
4003571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_PSK = 0x02ac0f00;
4013571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_FT_EAP = 0x03ac0f00;
4023571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_FT_PSK = 0x04ac0f00;
4033571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_EAP_SHA256 = 0x05ac0f00;
4043571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_PSK_SHA256 = 0x06ac0f00;
4053571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4064d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int WPA_CIPHER_NONE = 0x00f25000;
4074d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int WPA_CIPHER_TKIP = 0x02f25000;
4084d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int WPA_CIPHER_CCMP = 0x04f25000;
4094d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
4104d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int RSN_CIPHER_NONE = 0x00ac0f00;
4114d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int RSN_CIPHER_TKIP = 0x02ac0f00;
4124d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int RSN_CIPHER_CCMP = 0x04ac0f00;
4134d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int RSN_CIPHER_NO_GROUP_ADDRESSED = 0x07ac0f00;
4144d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
415c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang        public ArrayList<Integer> protocol;
416c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang        public ArrayList<ArrayList<Integer>> keyManagement;
417c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang        public ArrayList<ArrayList<Integer>> pairwiseCipher;
418c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang        public ArrayList<Integer> groupCipher;
4194d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        public boolean isESS;
4204d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        public boolean isPrivacy;
421f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang        public boolean isWPS;
4224d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
4233571366ac36c70746b9f013ec2b54482861c9292Randy Pan        public Capabilities() {
4243571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
4253571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4263571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // RSNE format (size unit: byte)
4273571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
4283571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Element ID | Length | Version | Group Data Cipher Suite |
4293571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //      1           1         2                 4
4303571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
4313571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //              2                            4 * m
4323571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | AKM Suite Count | AKM Suite List | RSN Capabilities |
4333571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //          2               4 * n               2
4343571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | PMKID Count | PMKID List | Group Management Cipher Suite |
4353571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //        2          16 * s                 4
4363571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
4373571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // Note: InformationElement.bytes has 'Element ID' and 'Length'
4383571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //       stripped off already
4394d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private void parseRsnElement(InformationElement ie) {
4403571366ac36c70746b9f013ec2b54482861c9292Randy Pan            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
4413571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4423571366ac36c70746b9f013ec2b54482861c9292Randy Pan            try {
4433571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // version
4443571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (buf.getShort() != RSNE_VERSION) {
4453571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    // incorrect version
4464d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                    return;
4473571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
4483571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4493571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // found the RSNE IE, hence start building the capability string
450c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                protocol.add(ScanResult.PROTOCOL_WPA2);
4514d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
4524d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                // group data cipher suite
453c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                groupCipher.add(parseRsnCipher(buf.getInt()));
4543571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4553571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise cipher suite count
4563571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short cipherCount = buf.getShort();
457c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                ArrayList<Integer> rsnPairwiseCipher = new ArrayList<>();
4583571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise cipher suite list
4593571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < cipherCount; i++) {
460c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                    rsnPairwiseCipher.add(parseRsnCipher(buf.getInt()));
4613571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
462c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                pairwiseCipher.add(rsnPairwiseCipher);
4633571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4643571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM
4653571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM suite count
4663571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short akmCount = buf.getShort();
467c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                ArrayList<Integer> rsnKeyManagement = new ArrayList<>();
4683571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4693571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < akmCount; i++) {
4703571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    int akm = buf.getInt();
4713571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    switch (akm) {
4723571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_EAP:
473c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP);
4743571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4753571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_PSK:
476c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK);
4773571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4783571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_FT_EAP:
479c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_EAP);
4803571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4813571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_FT_PSK:
482c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK);
4833571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4843571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_EAP_SHA256:
485c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SHA256);
4863571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4873571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_PSK_SHA256:
488c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK_SHA256);
4893571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4903571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        default:
4913571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            // do nothing
4923571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4933571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    }
4943571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
4954d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                // Default AKM
496c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                if (rsnKeyManagement.isEmpty()) {
497c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                    rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP);
4984d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                }
499c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                keyManagement.add(rsnKeyManagement);
5003571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } catch (BufferUnderflowException e) {
5013571366ac36c70746b9f013ec2b54482861c9292Randy Pan                Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow");
5024d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            }
5034d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        }
5044d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
505fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private static int parseWpaCipher(int cipher) {
5064d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            switch (cipher) {
5074d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case WPA_CIPHER_NONE:
508fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NONE;
5094d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case WPA_CIPHER_TKIP:
510fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_TKIP;
5114d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case WPA_CIPHER_CCMP:
512fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_CCMP;
5134d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                default:
514fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    Log.w("IE_Capabilities", "Unknown WPA cipher suite: "
515fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            + Integer.toHexString(cipher));
516fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NONE;
5174d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            }
5184d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        }
5194d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
520fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private static int parseRsnCipher(int cipher) {
5214d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            switch (cipher) {
5224d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case RSN_CIPHER_NONE:
523fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NONE;
5244d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case RSN_CIPHER_TKIP:
525fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_TKIP;
5264d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case RSN_CIPHER_CCMP:
527fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_CCMP;
5284d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case RSN_CIPHER_NO_GROUP_ADDRESSED:
529fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NO_GROUP_ADDRESSED;
5304d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                default:
531fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    Log.w("IE_Capabilities", "Unknown RSN cipher suite: "
532fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            + Integer.toHexString(cipher));
533fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NONE;
5343571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
5353571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
5363571366ac36c70746b9f013ec2b54482861c9292Randy Pan
537f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang        private static boolean isWpsElement(InformationElement ie) {
538f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
539f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang            try {
540f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                // WPS OUI and type
541f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                return (buf.getInt() == WPS_VENDOR_OUI_TYPE);
542f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang            } catch (BufferUnderflowException e) {
543f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
544f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                return false;
545f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang            }
546f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang        }
547f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang
5483571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static boolean isWpaOneElement(InformationElement ie) {
5493571366ac36c70746b9f013ec2b54482861c9292Randy Pan            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
5503571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5513571366ac36c70746b9f013ec2b54482861c9292Randy Pan            try {
5523571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // WPA OUI and type
5533571366ac36c70746b9f013ec2b54482861c9292Randy Pan                return (buf.getInt() == WPA_VENDOR_OUI_TYPE_ONE);
5543571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } catch (BufferUnderflowException e) {
5553571366ac36c70746b9f013ec2b54482861c9292Randy Pan                Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
5563571366ac36c70746b9f013ec2b54482861c9292Randy Pan                return false;
5573571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
5583571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
5593571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5603571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // WPA type 1 format (size unit: byte)
5613571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
5623571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Element ID | Length | OUI | Type | Version |
5633571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //      1           1       3     1        2
5644d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        // | Group Data Cipher Suite |
5654d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        //             4
5663571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
5673571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //              2                            4 * m
5683571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | AKM Suite Count | AKM Suite List |
5693571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //          2               4 * n
5703571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
5713571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // Note: InformationElement.bytes has 'Element ID' and 'Length'
5723571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //       stripped off already
5733571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
5744d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private void parseWpaOneElement(InformationElement ie) {
5753571366ac36c70746b9f013ec2b54482861c9292Randy Pan            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
5763571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5773571366ac36c70746b9f013ec2b54482861c9292Randy Pan            try {
5783571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // skip WPA OUI and type parsing. isWpaOneElement() should have
5793571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // been called for verification before we reach here.
5803571366ac36c70746b9f013ec2b54482861c9292Randy Pan                buf.getInt();
5813571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5823571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // version
5833571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (buf.getShort() != WPA_VENDOR_OUI_VERSION)  {
5843571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    // incorrect version
5854d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                    return;
5863571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
5873571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5884d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                // start building the string
589c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                protocol.add(ScanResult.PROTOCOL_WPA);
5904d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
5913571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // group data cipher suite
592c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                groupCipher.add(parseWpaCipher(buf.getInt()));
5933571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5943571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise cipher suite count
5953571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short cipherCount = buf.getShort();
596c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                ArrayList<Integer> wpaPairwiseCipher = new ArrayList<>();
5973571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise chipher suite list
5983571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < cipherCount; i++) {
599c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                    wpaPairwiseCipher.add(parseWpaCipher(buf.getInt()));
6003571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
601c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                pairwiseCipher.add(wpaPairwiseCipher);
6023571366ac36c70746b9f013ec2b54482861c9292Randy Pan
6033571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM
6043571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM suite count
6053571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short akmCount = buf.getShort();
606c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                ArrayList<Integer> wpaKeyManagement = new ArrayList<>();
6073571366ac36c70746b9f013ec2b54482861c9292Randy Pan
6083571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM suite list
6093571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < akmCount; i++) {
6103571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    int akm = buf.getInt();
6113571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    switch (akm) {
6123571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA_AKM_EAP:
613c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP);
6143571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
6153571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA_AKM_PSK:
616c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            wpaKeyManagement.add(ScanResult.KEY_MGMT_PSK);
6173571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
6183571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        default:
6193571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            // do nothing
6203571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
6213571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    }
6223571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
6234d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                // Default AKM
624c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                if (wpaKeyManagement.isEmpty()) {
625c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                    wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP);
6264d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                }
627c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                keyManagement.add(wpaKeyManagement);
6283571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } catch (BufferUnderflowException e) {
6293571366ac36c70746b9f013ec2b54482861c9292Randy Pan                Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow");
6303571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
6313571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
6323571366ac36c70746b9f013ec2b54482861c9292Randy Pan
6333571366ac36c70746b9f013ec2b54482861c9292Randy Pan        /**
6343571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * Parse the Information Element and the 16-bit Capability Information field
6354d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         * to build the InformationElemmentUtil.capabilities object.
6363571366ac36c70746b9f013ec2b54482861c9292Randy Pan         *
6373571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * @param ies -- Information Element array
6383571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * @param beaconCap -- 16-bit Beacon Capability Information field
6393571366ac36c70746b9f013ec2b54482861c9292Randy Pan         */
6404d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
6414d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        public void from(InformationElement[] ies, BitSet beaconCap) {
642c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            protocol = new ArrayList<Integer>();
643c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            keyManagement = new ArrayList<ArrayList<Integer>>();
644c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            groupCipher = new ArrayList<Integer>();
645c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            pairwiseCipher = new ArrayList<ArrayList<Integer>>();
6463571366ac36c70746b9f013ec2b54482861c9292Randy Pan
6473571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ies == null || beaconCap == null) {
6484d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                return;
6493571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
6504d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            isESS = beaconCap.get(CAP_ESS_BIT_OFFSET);
6514d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            isPrivacy = beaconCap.get(CAP_PRIVACY_BIT_OFFSET);
6523571366ac36c70746b9f013ec2b54482861c9292Randy Pan            for (InformationElement ie : ies) {
6533571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (ie.id == InformationElement.EID_RSN) {
6544d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                    parseRsnElement(ie);
6553571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
6563571366ac36c70746b9f013ec2b54482861c9292Randy Pan
6573571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (ie.id == InformationElement.EID_VSA) {
6583571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    if (isWpaOneElement(ie)) {
6594d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                        parseWpaOneElement(ie);
6603571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    }
661f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                    if (isWpsElement(ie)) {
662f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                        // TODO(b/62134557): parse WPS IE to provide finer granularity information.
663f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                        isWPS = true;
664f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                    }
6653571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
6663571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
667fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        }
668fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang
669fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private String protocolToString(int protocol) {
670fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            switch (protocol) {
671fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.PROTOCOL_NONE:
672fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "None";
673fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.PROTOCOL_WPA:
674fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "WPA";
675fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.PROTOCOL_WPA2:
676fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "WPA2";
677fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                default:
678fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "?";
679fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            }
680fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        }
681fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang
682fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private String keyManagementToString(int akm) {
683fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            switch (akm) {
684fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_NONE:
685fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "None";
686fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_PSK:
687fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "PSK";
688fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_EAP:
689fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "EAP";
690fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_FT_EAP:
691fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "FT/EAP";
692fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_FT_PSK:
693fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "FT/PSK";
694fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_EAP_SHA256:
695fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "EAP-SHA256";
696fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_PSK_SHA256:
697fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "PSK-SHA256";
698fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                default:
699fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "?";
700fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            }
701fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        }
702fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang
703fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private String cipherToString(int cipher) {
704fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            switch (cipher) {
705fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.CIPHER_NONE:
706fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "None";
707fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.CIPHER_CCMP:
708fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "CCMP";
709fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.CIPHER_TKIP:
710fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "TKIP";
711fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                default:
712fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "?";
7133571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
7144d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        }
7153571366ac36c70746b9f013ec2b54482861c9292Randy Pan
7164d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        /**
7174d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         * Build the ScanResult.capabilities String.
7184d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         *
7194d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         * @return security string that mirrors what wpa_supplicant generates
7204d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         */
7214d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        public String generateCapabilitiesString() {
7224d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            String capabilities = "";
723fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            // private Beacon without an RSNE or WPA IE, hence WEP0
724c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            boolean isWEP = (protocol.isEmpty()) && isPrivacy;
725fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang
726c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            if (isWEP) {
727c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                capabilities += "[WEP]";
728c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            }
729c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang            for (int i = 0; i < protocol.size(); i++) {
730c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                capabilities += "[" +  protocolToString(protocol.get(i));
731c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                for (int j = 0; j < keyManagement.get(i).size(); j++) {
732c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                    capabilities += ((j == 0) ? "-" : "+")
733c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                        + keyManagementToString(keyManagement.get(i).get(j));
7344d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                }
735c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                for (int j = 0; j < pairwiseCipher.get(i).size(); j++) {
736c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                    capabilities += ((j == 0) ? "-" : "+")
737c66d00cefbd32ec2fbefcf1fd54c1aaf50b5ce5aNingyuan Wang                            + cipherToString(pairwiseCipher.get(i).get(j));
7384d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                }
7394d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                capabilities += "]";
7404d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            }
7414d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            if (isESS) {
7429b5773d2805e8c6141ca75de272921a84941546bRandy Pan                capabilities += "[ESS]";
7439b5773d2805e8c6141ca75de272921a84941546bRandy Pan            }
744f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang            if (isWPS) {
745f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang                capabilities += "[WPS]";
746f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang            }
747f7364b4baea06722022b7c6519ace0556481cc06Ningyuan Wang
7483571366ac36c70746b9f013ec2b54482861c9292Randy Pan            return capabilities;
7493571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
7503571366ac36c70746b9f013ec2b54482861c9292Randy Pan    }
751947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne
752947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne    /**
753947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     * Parser for the Traffic Indication Map (TIM) Information Element (EID 5). This element will
754947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     * only be present in scan results that are derived from a Beacon Frame, not from the more
755947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     * plentiful probe responses. Call 'isValid()' after parsing, to ensure the results are correct.
756947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     */
757947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne    public static class TrafficIndicationMap {
758947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        private static final int MAX_TIM_LENGTH = 254;
759947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        private boolean mValid = false;
760947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mLength = 0;
761947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mDtimCount = -1;
762947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //Negative DTIM Period means no TIM element was given this frame.
763947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mDtimPeriod = -1;
764947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mBitmapControl = 0;
765947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne
766947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        /**
767947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne         * Is this a valid TIM information element.
768947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne         */
769947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public boolean isValid() {
770947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            return mValid;
771947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        }
772947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne
773947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        // Traffic Indication Map format (size unit: byte)
774947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //
775947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //| ElementID | Length | DTIM Count | DTIM Period | BitmapControl | Partial Virtual Bitmap |
776947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //      1          1          1            1               1                1 - 251
777947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //
778947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        // Note: InformationElement.bytes has 'Element ID' and 'Length'
779947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //       stripped off already
780947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //
781947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public void from(InformationElement ie) {
782e367514651d7f888dda870fb472eea6bcfd9ec5dPaul Stewart            mValid = false;
783947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            if (ie == null || ie.bytes == null) return;
784947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            mLength = ie.bytes.length;
785947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
786947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            try {
787947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mDtimCount = data.get() & Constants.BYTE_MASK;
788947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mDtimPeriod = data.get() & Constants.BYTE_MASK;
789947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mBitmapControl = data.get() & Constants.BYTE_MASK;
790947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                //A valid TIM element must have atleast one more byte
791947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                data.get();
792947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            } catch (BufferUnderflowException e) {
793947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                return;
794947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            }
79559f9a74676831ba4634b35d56a1e2bbe9bf4e322Glen Kuhne            if (mLength <= MAX_TIM_LENGTH && mDtimPeriod > 0) {
796947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mValid = true;
797947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            }
798947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        }
799947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne    }
800f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
801f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    /**
802f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     * This util class determines the 802.11 standard (a/b/g/n/ac) being used
803f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     */
804f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    public static class WifiMode {
805f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_UNDEFINED = 0; // Unknown/undefined
806f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11A = 1;       // 802.11a
807f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11B = 2;       // 802.11b
808f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11G = 3;       // 802.11g
809f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11N = 4;       // 802.11n
810f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11AC = 5;      // 802.11ac
811f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //<TODO> add support for 802.11ad and be more selective instead of defaulting to 11A
812f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
813f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
814f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Use frequency, max supported rate, and the existence of VHT, HT & ERP fields in scan
815f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * scan result to determine the 802.11 Wifi standard being used.
816f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
817f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static int determineMode(int frequency, int maxRate, boolean foundVht,
818f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                boolean foundHt, boolean foundErp) {
819f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            if (foundVht) {
820f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11AC;
821f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else if (foundHt) {
822f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11N;
823f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else if (foundErp) {
824f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11G;
825f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else if (frequency < 3000) {
826f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                if (maxRate < 24000000) {
827f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return MODE_11B;
828f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                } else {
829f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return MODE_11G;
830f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                }
831f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else {
832f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11A;
833f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
834f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
835f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
836f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
837f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Map the wifiMode integer to its type, and output as String MODE_11<A/B/G/N/AC>
838f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
839f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static String toString(int mode) {
840f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            switch(mode) {
841f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11A:
842f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11A";
843f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11B:
844f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11B";
845f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11G:
846f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11G";
847f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11N:
848f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11N";
849f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11AC:
850f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11AC";
851f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                default:
852f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_UNDEFINED";
853f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
854f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
855f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    }
856f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
857f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    /**
858f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     * Parser for both the Supported Rates & Extended Supported Rates Information Elements
859f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     */
860f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    public static class SupportedRates {
861f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MASK = 0x7F; // 0111 1111
862f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public boolean mValid = false;
863f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public ArrayList<Integer> mRates;
864f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
865f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public SupportedRates() {
866f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            mRates = new ArrayList<Integer>();
867f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
868f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
869f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
870f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Is this a valid Supported Rates information element.
871f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
872f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public boolean isValid() {
873f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            return mValid;
874f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
875f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
876f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
877f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * get the Rate in bits/s from associated byteval
878f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
879f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static int getRateFromByte(int byteVal) {
880f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            byteVal &= MASK;
881f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            switch(byteVal) {
882f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 2:
883f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 1000000;
884f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 4:
885f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 2000000;
886f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 11:
887f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 5500000;
888f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 12:
889f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 6000000;
890f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 18:
891f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 9000000;
892f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 22:
893f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 11000000;
894f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 24:
895f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 12000000;
896f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 36:
897f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 18000000;
898f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 44:
899f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 22000000;
900f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 48:
901f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 24000000;
902f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 66:
903f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 33000000;
904f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 72:
905f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 36000000;
906f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 96:
907f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 48000000;
908f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 108:
909f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 54000000;
910f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                default:
911f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    //ERROR UNKNOWN RATE
912f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return -1;
913f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
914f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
915f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
916f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        // Supported Rates format (size unit: byte)
917f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //
918f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //| ElementID | Length | Supported Rates  [7 Little Endian Info bits - 1 Flag bit]
919f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //      1          1          1 - 8
920f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //
921f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        // Note: InformationElement.bytes has 'Element ID' and 'Length'
922f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //       stripped off already
923f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //
924f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public void from(InformationElement ie) {
925e367514651d7f888dda870fb472eea6bcfd9ec5dPaul Stewart            mValid = false;
926e367514651d7f888dda870fb472eea6bcfd9ec5dPaul Stewart            if (ie == null || ie.bytes == null || ie.bytes.length > 8 || ie.bytes.length < 1)  {
927f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return;
928f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
929f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
930f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            try {
931f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                for (int i = 0; i < ie.bytes.length; i++) {
932f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    int rate = getRateFromByte(data.get());
933f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    if (rate > 0) {
934f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                        mRates.add(rate);
935f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    } else {
936f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                        return;
937f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    }
938f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                }
939f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } catch (BufferUnderflowException e) {
940f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return;
941f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
942f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            mValid = true;
943f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            return;
944f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
945f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
946f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
947f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Lists the rates in a human readable string
948f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
949f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public String toString() {
950f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            StringBuilder sbuf = new StringBuilder();
951f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            for (Integer rate : mRates) {
952f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                sbuf.append(String.format("%.1f", (double) rate / 1000000) + ", ");
953f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
954f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            return sbuf.toString();
955f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
956f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    }
9575d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills}
958