InformationElementUtil.java revision b86089a48fae8878b5a27533a116c97b0be6d0e7
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 {
335d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
345d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static InformationElement[] parseInformationElements(byte[] bytes) {
35da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein        if (bytes == null) {
36da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein            return new InformationElement[0];
37da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein        }
385d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        ByteBuffer data = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
395d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
405d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        ArrayList<InformationElement> infoElements = new ArrayList<>();
415d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        boolean found_ssid = false;
425d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        while (data.remaining() > 1) {
435d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int eid = data.get() & Constants.BYTE_MASK;
445d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int elementLength = data.get() & Constants.BYTE_MASK;
455d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
463571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (elementLength > data.remaining() || (eid == InformationElement.EID_SSID
473571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    && found_ssid)) {
48da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein                // APs often pad the data with bytes that happen to match that of the EID_SSID
49da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein                // marker.  This is not due to a known issue for APs to incorrectly send the SSID
50da51c12f413d54517aea317e37ba82e45d1e4a30Rebecca Silberstein                // name multiple times.
515d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                break;
525d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
533571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (eid == InformationElement.EID_SSID) {
545d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                found_ssid = true;
555d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
565d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
575d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            InformationElement ie = new InformationElement();
585d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ie.id = eid;
595d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ie.bytes = new byte[elementLength];
605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            data.get(ie.bytes);
615d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            infoElements.add(ie);
625d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
635d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        return infoElements.toArray(new InformationElement[infoElements.size()]);
645d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
655d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
665d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
675d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class BssLoad {
685d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int stationCount = 0;
695d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int channelUtilization = 0;
705d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int capacity = 0;
715d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
725d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
733571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_BSS_LOAD) {
745d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not BSS_LOAD, : " + ie.id);
755d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
765d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length != 5) {
773571366ac36c70746b9f013ec2b54482861c9292Randy Pan                throw new IllegalArgumentException("BSS Load element length is not 5: "
783571366ac36c70746b9f013ec2b54482861c9292Randy Pan                                                   + ie.bytes.length);
795d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
805d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
815d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            stationCount = data.getShort() & Constants.SHORT_MASK;
825d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            channelUtilization = data.get() & Constants.BYTE_MASK;
835d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            capacity = data.getShort() & Constants.SHORT_MASK;
845d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
855d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
865d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
875d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class HtOperation {
885d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int secondChannelOffset = 0;
895d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
905d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getChannelWidth() {
913571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (secondChannelOffset != 0) {
925d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 1;
933571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } else {
945d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 0;
955d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
965d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
975d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
985d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getCenterFreq0(int primaryFrequency) {
999f743918a412fec9ad5a0386fbf6cf0361313f58xinhe            //40 MHz
1009f743918a412fec9ad5a0386fbf6cf0361313f58xinhe            if (secondChannelOffset != 0) {
1015d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if (secondChannelOffset == 1) {
1029f743918a412fec9ad5a0386fbf6cf0361313f58xinhe                    return primaryFrequency + 10;
1035d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                } else if (secondChannelOffset == 3) {
1049f743918a412fec9ad5a0386fbf6cf0361313f58xinhe                    return primaryFrequency - 10;
1055d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                } else {
1065d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    Log.e("HtOperation", "Error on secondChannelOffset: " + secondChannelOffset);
1075d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    return 0;
1085d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
1095d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            } else {
1105d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 0;
1115d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1125d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1135d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1145d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
1153571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_HT_OPERATION) {
1165d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not HT_OPERATION, : " + ie.id);
1175d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1185d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            secondChannelOffset = ie.bytes[1] & 0x3;
1195d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1205d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
1215d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1225d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class VhtOperation {
1235d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int channelMode = 0;
1245d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int centerFreqIndex1 = 0;
1255d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int centerFreqIndex2 = 0;
1265d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1275d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public boolean isValid() {
1285d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return channelMode != 0;
1295d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1305d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1315d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getChannelWidth() {
1325d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return channelMode + 1;
1335d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1345d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1355d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getCenterFreq0() {
1365d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            //convert channel index to frequency in MHz, channel 36 is 5180MHz
1375d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            return (centerFreqIndex1 - 36) * 5 + 5180;
1385d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1395d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1405d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int getCenterFreq1() {
1413571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (channelMode > 1) { //160MHz
1425d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return (centerFreqIndex2 - 36) * 5 + 5180;
1435d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            } else {
1445d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                return 0;
1455d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1465d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1475d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1485d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
1493571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_VHT_OPERATION) {
1505d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not VHT_OPERATION, : " + ie.id);
1515d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1525d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            channelMode = ie.bytes[0] & Constants.BYTE_MASK;
1535d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            centerFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK;
1545d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            centerFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK;
1555d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1565d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
1575d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1585d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class Interworking {
1595d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public NetworkDetail.Ant ant = null;
1605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public boolean internet = false;
1615d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public long hessid = 0L;
1625d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1635d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
1643571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_INTERWORKING) {
1655d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                throw new IllegalArgumentException("Element id is not INTERWORKING, : " + ie.id);
1665d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1675d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1685d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int anOptions = data.get() & Constants.BYTE_MASK;
1695d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ant = NetworkDetail.Ant.values()[anOptions & 0x0f];
1705d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            internet = (anOptions & 0x10) != 0;
171e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan            // There are only three possible lengths for the Interworking IE:
172e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan            // Len 1: Access Network Options only
173e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan            // Len 3: Access Network Options & Venue Info
174e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan            // Len 7: Access Network Options & HESSID
175e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan            // Len 9: Access Network Options, Venue Info, & HESSID
176e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan            if (ie.bytes.length != 1
177e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan                    && ie.bytes.length != 3
178e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan                    && ie.bytes.length != 7
179e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan                    && ie.bytes.length != 9) {
180e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan                throw new IllegalArgumentException(
181e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan                        "Bad Interworking element length: " + ie.bytes.length);
1825d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
183e7399556522efdd3f137aba31c49cbb8d95c59d6Samuel Tan
1845d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length == 7 || ie.bytes.length == 9) {
185fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiu                hessid = ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 6);
1865d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1875d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
1885d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
1895d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1905d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class RoamingConsortium {
1915d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int anqpOICount = 0;
1925d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public long[] roamingConsortiums = null;
1935d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
1945d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
1953571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ie.id != InformationElement.EID_ROAMING_CONSORTIUM) {
1963571366ac36c70746b9f013ec2b54482861c9292Randy Pan                throw new IllegalArgumentException("Element id is not ROAMING_CONSORTIUM, : "
1973571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        + ie.id);
1985d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
1995d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2005d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            anqpOICount = data.get() & Constants.BYTE_MASK;
2015d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2025d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi12Length = data.get() & Constants.BYTE_MASK;
2035d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi1Length = oi12Length & Constants.NIBBLE_MASK;
2045d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK;
2055d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oi3Length = ie.bytes.length - 2 - oi1Length - oi2Length;
2065d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            int oiCount = 0;
2075d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi1Length > 0) {
2085d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                oiCount++;
2095d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if (oi2Length > 0) {
2105d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    oiCount++;
2115d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    if (oi3Length > 0) {
2125d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        oiCount++;
2135d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    }
2145d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
2155d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2165d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            roamingConsortiums = new long[oiCount];
2175d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi1Length > 0 && roamingConsortiums.length > 0) {
2185d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[0] =
219fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiu                        ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi1Length);
2205d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2215d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi2Length > 0 && roamingConsortiums.length > 1) {
2225d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[1] =
223fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiu                        ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi2Length);
2245d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2255d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (oi3Length > 0 && roamingConsortiums.length > 2) {
2265d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                roamingConsortiums[2] =
227fa04a81daf829e6e5c099c9a249b8dd8dd112102Peter Qiu                        ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi3Length);
2285d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2295d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2305d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
2315d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2325d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class Vsa {
2335d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        private static final int ANQP_DOMID_BIT = 0x04;
2345d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2355d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public NetworkDetail.HSRelease hsRelease = null;
2365d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public int anqpDomainID = 0;    // No domain ID treated the same as a 0; unique info per AP.
2375d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
2385d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        public void from(InformationElement ie) {
2395d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2405d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            if (ie.bytes.length >= 5 && data.getInt() == Constants.HS20_FRAME_PREFIX) {
2415d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                int hsConf = data.get() & Constants.BYTE_MASK;
2425d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                switch ((hsConf >> 4) & Constants.NIBBLE_MASK) {
2435d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    case 0:
2445d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.R1;
2455d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
2465d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    case 1:
2475d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.R2;
2485d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
2495d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    default:
2505d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        hsRelease = NetworkDetail.HSRelease.Unknown;
2515d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        break;
2525d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
2535d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                if ((hsConf & ANQP_DOMID_BIT) != 0) {
2545d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    if (ie.bytes.length < 7) {
2555d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                        throw new IllegalArgumentException(
2565d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                                "HS20 indication element too short: " + ie.bytes.length);
2575d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    }
2585d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                    anqpDomainID = data.getShort() & Constants.SHORT_MASK;
2595d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills                }
2605d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills            }
2615d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2625d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
2635d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
264b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu    /**
265b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * This IE contained a bit field indicating the capabilities being advertised by the STA.
266b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * The size of the bit field (number of bytes) is indicated by the |Length| field in the IE.
267b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     *
268b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * Refer to Section 8.4.2.29 in IEEE 802.11-2012 Spec for capability associated with each
269b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * bit.
270b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     *
271b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * Here is the wire format of this IE:
272b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     * | Element ID | Length | Capabilities |
273b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     *       1           1          n
274b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu     */
2755d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    public static class ExtendedCapabilities {
2765d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        private static final int RTT_RESP_ENABLE_BIT = 70;
277b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        private static final int SSID_UTF8_BIT = 48;
2785d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
279b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public BitSet capabilitiesBitSet;
2805d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
281b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        /**
282b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         * @return true if SSID should be interpreted using UTF-8 encoding
283b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         */
284b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public boolean isStrictUtf8() {
285b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            return capabilitiesBitSet.get(SSID_UTF8_BIT);
2865d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2875d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
288b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        /**
289b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         * @return true if 802.11 MC RTT Response is enabled
290b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         */
291b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public boolean is80211McRTTResponder() {
292b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            return capabilitiesBitSet.get(RTT_RESP_ENABLE_BIT);
2935d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2945d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
295b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public ExtendedCapabilities() {
296b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            capabilitiesBitSet = new BitSet();
2975d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
2985d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
299b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public ExtendedCapabilities(ExtendedCapabilities other) {
300b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            capabilitiesBitSet = other.capabilitiesBitSet;
301b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        }
3025d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills
303b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        /**
304b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         * Parse an ExtendedCapabilities from the IE containing raw bytes.
305b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         *
306b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         * @param ie The Information element data
307b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu         */
308b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu        public void from(InformationElement ie) {
309b86089a48fae8878b5a27533a116c97b0be6d0e7Peter Qiu            capabilitiesBitSet = BitSet.valueOf(ie.bytes);
3105d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills        }
3115d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills    }
3123571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3133571366ac36c70746b9f013ec2b54482861c9292Randy Pan    /**
3143571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * parse beacon to build the capabilities
3153571366ac36c70746b9f013ec2b54482861c9292Randy Pan     *
3163571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * This class is used to build the capabilities string of the scan results coming
3173571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * from HAL. It parses the ieee beacon's capability field, WPA and RSNE IE as per spec,
3183571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * and builds the ScanResult.capabilities String in a way that mirrors the values returned
3193571366ac36c70746b9f013ec2b54482861c9292Randy Pan     * by wpa_supplicant.
3203571366ac36c70746b9f013ec2b54482861c9292Randy Pan     */
3213571366ac36c70746b9f013ec2b54482861c9292Randy Pan    public static class Capabilities {
3229b5773d2805e8c6141ca75de272921a84941546bRandy Pan        private static final int CAP_ESS_BIT_OFFSET = 0;
3233571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int CAP_PRIVACY_BIT_OFFSET = 4;
3243571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3253571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA_VENDOR_OUI_TYPE_ONE = 0x01f25000;
3263571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final short WPA_VENDOR_OUI_VERSION = 0x0001;
3273571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final short RSNE_VERSION = 0x0001;
3283571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3293571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA_AKM_EAP = 0x01f25000;
3303571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA_AKM_PSK = 0x02f25000;
3313571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3323571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_EAP = 0x01ac0f00;
3333571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_PSK = 0x02ac0f00;
3343571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_FT_EAP = 0x03ac0f00;
3353571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_FT_PSK = 0x04ac0f00;
3363571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_EAP_SHA256 = 0x05ac0f00;
3373571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static final int WPA2_AKM_PSK_SHA256 = 0x06ac0f00;
3383571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3394d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int WPA_CIPHER_NONE = 0x00f25000;
3404d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int WPA_CIPHER_TKIP = 0x02f25000;
3414d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int WPA_CIPHER_CCMP = 0x04f25000;
3424d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
3434d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int RSN_CIPHER_NONE = 0x00ac0f00;
3444d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int RSN_CIPHER_TKIP = 0x02ac0f00;
3454d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int RSN_CIPHER_CCMP = 0x04ac0f00;
3464d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private static final int RSN_CIPHER_NO_GROUP_ADDRESSED = 0x07ac0f00;
3474d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
348fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        public int protocol;
349fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        public ArrayList<Integer> keyManagement;
350fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        public ArrayList<Integer> pairwiseCipher;
351fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        public int groupCipher;
3524d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        public boolean isESS;
3534d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        public boolean isPrivacy;
3544d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
3553571366ac36c70746b9f013ec2b54482861c9292Randy Pan        public Capabilities() {
3563571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
3573571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3583571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // RSNE format (size unit: byte)
3593571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
3603571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Element ID | Length | Version | Group Data Cipher Suite |
3613571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //      1           1         2                 4
3623571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
3633571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //              2                            4 * m
3643571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | AKM Suite Count | AKM Suite List | RSN Capabilities |
3653571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //          2               4 * n               2
3663571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | PMKID Count | PMKID List | Group Management Cipher Suite |
3673571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //        2          16 * s                 4
3683571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
3693571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // Note: InformationElement.bytes has 'Element ID' and 'Length'
3703571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //       stripped off already
3714d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private void parseRsnElement(InformationElement ie) {
3723571366ac36c70746b9f013ec2b54482861c9292Randy Pan            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
3733571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3743571366ac36c70746b9f013ec2b54482861c9292Randy Pan            try {
3753571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // version
3763571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (buf.getShort() != RSNE_VERSION) {
3773571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    // incorrect version
3784d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                    return;
3793571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
3803571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3813571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // found the RSNE IE, hence start building the capability string
382fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                protocol = ScanResult.PROTOCOL_WPA2;
3834d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
3844d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                // group data cipher suite
385fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                groupCipher = parseRsnCipher(buf.getInt());
3863571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3873571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise cipher suite count
3883571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short cipherCount = buf.getShort();
3893571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise cipher suite list
3903571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < cipherCount; i++) {
391fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    pairwiseCipher.add(parseRsnCipher(buf.getInt()));
3923571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
3933571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3943571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM
3953571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM suite count
3963571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short akmCount = buf.getShort();
3973571366ac36c70746b9f013ec2b54482861c9292Randy Pan
3983571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < akmCount; i++) {
3993571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    int akm = buf.getInt();
4003571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    switch (akm) {
4013571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_EAP:
402fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            keyManagement.add(ScanResult.KEY_MGMT_EAP);
4033571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4043571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_PSK:
405fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            keyManagement.add(ScanResult.KEY_MGMT_PSK);
4063571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4073571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_FT_EAP:
408fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            keyManagement.add(ScanResult.KEY_MGMT_FT_EAP);
4093571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4103571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_FT_PSK:
411fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            keyManagement.add(ScanResult.KEY_MGMT_FT_PSK);
4123571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4133571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_EAP_SHA256:
414fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            keyManagement.add(ScanResult.KEY_MGMT_EAP_SHA256);
4153571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4163571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA2_AKM_PSK_SHA256:
417fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            keyManagement.add(ScanResult.KEY_MGMT_PSK_SHA256);
4183571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4193571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        default:
4203571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            // do nothing
4213571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
4223571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    }
4233571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
4244d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                // Default AKM
4254d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                if (keyManagement.isEmpty()) {
426fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    keyManagement.add(ScanResult.KEY_MGMT_EAP);
4274d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                }
4283571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } catch (BufferUnderflowException e) {
4293571366ac36c70746b9f013ec2b54482861c9292Randy Pan                Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow");
4304d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            }
4314d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        }
4324d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
433fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private static int parseWpaCipher(int cipher) {
4344d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            switch (cipher) {
4354d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case WPA_CIPHER_NONE:
436fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NONE;
4374d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case WPA_CIPHER_TKIP:
438fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_TKIP;
4394d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case WPA_CIPHER_CCMP:
440fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_CCMP;
4414d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                default:
442fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    Log.w("IE_Capabilities", "Unknown WPA cipher suite: "
443fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            + Integer.toHexString(cipher));
444fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NONE;
4454d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            }
4464d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        }
4474d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
448fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private static int parseRsnCipher(int cipher) {
4494d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            switch (cipher) {
4504d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case RSN_CIPHER_NONE:
451fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NONE;
4524d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case RSN_CIPHER_TKIP:
453fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_TKIP;
4544d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case RSN_CIPHER_CCMP:
455fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_CCMP;
4564d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                case RSN_CIPHER_NO_GROUP_ADDRESSED:
457fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NO_GROUP_ADDRESSED;
4584d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                default:
459fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    Log.w("IE_Capabilities", "Unknown RSN cipher suite: "
460fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            + Integer.toHexString(cipher));
461fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return ScanResult.CIPHER_NONE;
4623571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
4633571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
4643571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4653571366ac36c70746b9f013ec2b54482861c9292Randy Pan        private static boolean isWpaOneElement(InformationElement ie) {
4663571366ac36c70746b9f013ec2b54482861c9292Randy Pan            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
4673571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4683571366ac36c70746b9f013ec2b54482861c9292Randy Pan            try {
4693571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // WPA OUI and type
4703571366ac36c70746b9f013ec2b54482861c9292Randy Pan                return (buf.getInt() == WPA_VENDOR_OUI_TYPE_ONE);
4713571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } catch (BufferUnderflowException e) {
4723571366ac36c70746b9f013ec2b54482861c9292Randy Pan                Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
4733571366ac36c70746b9f013ec2b54482861c9292Randy Pan                return false;
4743571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
4753571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
4763571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4773571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // WPA type 1 format (size unit: byte)
4783571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
4793571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Element ID | Length | OUI | Type | Version |
4803571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //      1           1       3     1        2
4814d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        // | Group Data Cipher Suite |
4824d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        //             4
4833571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
4843571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //              2                            4 * m
4853571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // | AKM Suite Count | AKM Suite List |
4863571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //          2               4 * n
4873571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
4883571366ac36c70746b9f013ec2b54482861c9292Randy Pan        // Note: InformationElement.bytes has 'Element ID' and 'Length'
4893571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //       stripped off already
4903571366ac36c70746b9f013ec2b54482861c9292Randy Pan        //
4914d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        private void parseWpaOneElement(InformationElement ie) {
4923571366ac36c70746b9f013ec2b54482861c9292Randy Pan            ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
4933571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4943571366ac36c70746b9f013ec2b54482861c9292Randy Pan            try {
4953571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // skip WPA OUI and type parsing. isWpaOneElement() should have
4963571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // been called for verification before we reach here.
4973571366ac36c70746b9f013ec2b54482861c9292Randy Pan                buf.getInt();
4983571366ac36c70746b9f013ec2b54482861c9292Randy Pan
4993571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // version
5003571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (buf.getShort() != WPA_VENDOR_OUI_VERSION)  {
5013571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    // incorrect version
5024d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                    return;
5033571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
5043571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5054d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                // start building the string
506fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                protocol = ScanResult.PROTOCOL_WPA;
5074d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
5083571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // group data cipher suite
509fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                groupCipher = parseWpaCipher(buf.getInt());
5103571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5113571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise cipher suite count
5123571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short cipherCount = buf.getShort();
5133571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // pairwise chipher suite list
5143571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < cipherCount; i++) {
515fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    pairwiseCipher.add(parseWpaCipher(buf.getInt()));
5163571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
5173571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5183571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM
5193571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM suite count
5203571366ac36c70746b9f013ec2b54482861c9292Randy Pan                short akmCount = buf.getShort();
5213571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5223571366ac36c70746b9f013ec2b54482861c9292Randy Pan                // AKM suite list
5233571366ac36c70746b9f013ec2b54482861c9292Randy Pan                for (int i = 0; i < akmCount; i++) {
5243571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    int akm = buf.getInt();
5253571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    switch (akm) {
5263571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA_AKM_EAP:
527fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            keyManagement.add(ScanResult.KEY_MGMT_EAP);
5283571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
5293571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        case WPA_AKM_PSK:
530fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                            keyManagement.add(ScanResult.KEY_MGMT_PSK);
5313571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
5323571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        default:
5333571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            // do nothing
5343571366ac36c70746b9f013ec2b54482861c9292Randy Pan                            break;
5353571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    }
5363571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
5374d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                // Default AKM
5384d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                if (keyManagement.isEmpty()) {
539fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    keyManagement.add(ScanResult.KEY_MGMT_EAP);
5404d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                }
5413571366ac36c70746b9f013ec2b54482861c9292Randy Pan            } catch (BufferUnderflowException e) {
5423571366ac36c70746b9f013ec2b54482861c9292Randy Pan                Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow");
5433571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
5443571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
5453571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5463571366ac36c70746b9f013ec2b54482861c9292Randy Pan        /**
5473571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * Parse the Information Element and the 16-bit Capability Information field
5484d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         * to build the InformationElemmentUtil.capabilities object.
5493571366ac36c70746b9f013ec2b54482861c9292Randy Pan         *
5503571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * @param ies -- Information Element array
5513571366ac36c70746b9f013ec2b54482861c9292Randy Pan         * @param beaconCap -- 16-bit Beacon Capability Information field
5523571366ac36c70746b9f013ec2b54482861c9292Randy Pan         */
5534d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang
5544d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        public void from(InformationElement[] ies, BitSet beaconCap) {
555fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            protocol = ScanResult.PROTOCOL_NONE;
556fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            keyManagement = new ArrayList<Integer>();
557fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            groupCipher = ScanResult.CIPHER_NONE;
558fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            pairwiseCipher = new ArrayList<Integer>();
5593571366ac36c70746b9f013ec2b54482861c9292Randy Pan            boolean rsneFound = false;
5603571366ac36c70746b9f013ec2b54482861c9292Randy Pan            boolean wpaFound = false;
5613571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5623571366ac36c70746b9f013ec2b54482861c9292Randy Pan            if (ies == null || beaconCap == null) {
5634d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                return;
5643571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
5654d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            isESS = beaconCap.get(CAP_ESS_BIT_OFFSET);
5664d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            isPrivacy = beaconCap.get(CAP_PRIVACY_BIT_OFFSET);
5673571366ac36c70746b9f013ec2b54482861c9292Randy Pan            for (InformationElement ie : ies) {
5683571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (ie.id == InformationElement.EID_RSN) {
5693571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    rsneFound = true;
5704d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                    parseRsnElement(ie);
5713571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
5723571366ac36c70746b9f013ec2b54482861c9292Randy Pan
5733571366ac36c70746b9f013ec2b54482861c9292Randy Pan                if (ie.id == InformationElement.EID_VSA) {
5743571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    if (isWpaOneElement(ie)) {
5753571366ac36c70746b9f013ec2b54482861c9292Randy Pan                        wpaFound = true;
5764d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                        parseWpaOneElement(ie);
5773571366ac36c70746b9f013ec2b54482861c9292Randy Pan                    }
5783571366ac36c70746b9f013ec2b54482861c9292Randy Pan                }
5793571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
580fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        }
581fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang
582fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private String protocolToString(int protocol) {
583fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            switch (protocol) {
584fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.PROTOCOL_NONE:
585fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "None";
586fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.PROTOCOL_WPA:
587fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "WPA";
588fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.PROTOCOL_WPA2:
589fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "WPA2";
590fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                default:
591fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "?";
592fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            }
593fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        }
594fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang
595fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private String keyManagementToString(int akm) {
596fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            switch (akm) {
597fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_NONE:
598fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "None";
599fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_PSK:
600fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "PSK";
601fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_EAP:
602fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "EAP";
603fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_FT_EAP:
604fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "FT/EAP";
605fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_FT_PSK:
606fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "FT/PSK";
607fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_EAP_SHA256:
608fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "EAP-SHA256";
609fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.KEY_MGMT_PSK_SHA256:
610fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "PSK-SHA256";
611fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                default:
612fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "?";
613fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            }
614fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        }
615fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang
616fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang        private String cipherToString(int cipher) {
617fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            switch (cipher) {
618fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.CIPHER_NONE:
619fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "None";
620fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.CIPHER_CCMP:
621fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "CCMP";
622fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                case ScanResult.CIPHER_TKIP:
623fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "TKIP";
624fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                default:
625fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    return "?";
6263571366ac36c70746b9f013ec2b54482861c9292Randy Pan            }
6274d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        }
6283571366ac36c70746b9f013ec2b54482861c9292Randy Pan
6294d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        /**
6304d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         * Build the ScanResult.capabilities String.
6314d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         *
6324d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         * @return security string that mirrors what wpa_supplicant generates
6334d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang         */
6344d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang        public String generateCapabilitiesString() {
6354d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            String capabilities = "";
636fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            // private Beacon without an RSNE or WPA IE, hence WEP0
637fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            boolean isWEP = (protocol == ScanResult.PROTOCOL_NONE) && isPrivacy;
638fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang
639fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang            if (protocol != ScanResult.PROTOCOL_NONE || isWEP) {
640fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                capabilities += "[" + (isWEP ? "WEP" : protocolToString(protocol));
6414d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                for (int i = 0; i < keyManagement.size(); i++) {
642fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    capabilities += ((i == 0) ? "-" : "+")
643fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                        + keyManagementToString(keyManagement.get(i));
6444d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                }
6454d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                for (int i = 0; i < pairwiseCipher.size(); i++) {
646fef4b474b74c838edf9d810bf13df757012571a3Ningyuan Wang                    capabilities += ((i == 0) ? "-" : "+") + cipherToString(pairwiseCipher.get(i));
6474d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                }
6484d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang                capabilities += "]";
6494d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            }
6504d11585ede6636fee294ffb89e832e2f7f271c12Ningyuan Wang            if (isESS) {
6519b5773d2805e8c6141ca75de272921a84941546bRandy Pan                capabilities += "[ESS]";
6529b5773d2805e8c6141ca75de272921a84941546bRandy Pan            }
6533571366ac36c70746b9f013ec2b54482861c9292Randy Pan            return capabilities;
6543571366ac36c70746b9f013ec2b54482861c9292Randy Pan        }
6553571366ac36c70746b9f013ec2b54482861c9292Randy Pan    }
656947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne
657947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne    /**
658947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     * Parser for the Traffic Indication Map (TIM) Information Element (EID 5). This element will
659947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     * only be present in scan results that are derived from a Beacon Frame, not from the more
660947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     * plentiful probe responses. Call 'isValid()' after parsing, to ensure the results are correct.
661947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne     */
662947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne    public static class TrafficIndicationMap {
663947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        private static final int MAX_TIM_LENGTH = 254;
664947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        private boolean mValid = false;
665947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mLength = 0;
666947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mDtimCount = -1;
667947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //Negative DTIM Period means no TIM element was given this frame.
668947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mDtimPeriod = -1;
669947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public int mBitmapControl = 0;
670947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne
671947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        /**
672947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne         * Is this a valid TIM information element.
673947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne         */
674947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public boolean isValid() {
675947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            return mValid;
676947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        }
677947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne
678947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        // Traffic Indication Map format (size unit: byte)
679947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //
680947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //| ElementID | Length | DTIM Count | DTIM Period | BitmapControl | Partial Virtual Bitmap |
681947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //      1          1          1            1               1                1 - 251
682947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //
683947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        // Note: InformationElement.bytes has 'Element ID' and 'Length'
684947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //       stripped off already
685947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        //
686947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        public void from(InformationElement ie) {
687e367514651d7f888dda870fb472eea6bcfd9ec5dPaul Stewart            mValid = false;
688947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            if (ie == null || ie.bytes == null) return;
689947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            mLength = ie.bytes.length;
690947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
691947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            try {
692947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mDtimCount = data.get() & Constants.BYTE_MASK;
693947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mDtimPeriod = data.get() & Constants.BYTE_MASK;
694947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mBitmapControl = data.get() & Constants.BYTE_MASK;
695947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                //A valid TIM element must have atleast one more byte
696947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                data.get();
697947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            } catch (BufferUnderflowException e) {
698947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                return;
699947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            }
70059f9a74676831ba4634b35d56a1e2bbe9bf4e322Glen Kuhne            if (mLength <= MAX_TIM_LENGTH && mDtimPeriod > 0) {
701947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne                mValid = true;
702947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne            }
703947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne        }
704947e55415eab3989f2f5cede0c03745cf9268309Glen Kuhne    }
705f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
706f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    /**
707f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     * This util class determines the 802.11 standard (a/b/g/n/ac) being used
708f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     */
709f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    public static class WifiMode {
710f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_UNDEFINED = 0; // Unknown/undefined
711f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11A = 1;       // 802.11a
712f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11B = 2;       // 802.11b
713f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11G = 3;       // 802.11g
714f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11N = 4;       // 802.11n
715f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MODE_11AC = 5;      // 802.11ac
716f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //<TODO> add support for 802.11ad and be more selective instead of defaulting to 11A
717f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
718f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
719f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Use frequency, max supported rate, and the existence of VHT, HT & ERP fields in scan
720f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * scan result to determine the 802.11 Wifi standard being used.
721f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
722f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static int determineMode(int frequency, int maxRate, boolean foundVht,
723f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                boolean foundHt, boolean foundErp) {
724f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            if (foundVht) {
725f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11AC;
726f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else if (foundHt) {
727f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11N;
728f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else if (foundErp) {
729f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11G;
730f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else if (frequency < 3000) {
731f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                if (maxRate < 24000000) {
732f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return MODE_11B;
733f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                } else {
734f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return MODE_11G;
735f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                }
736f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } else {
737f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return MODE_11A;
738f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
739f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
740f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
741f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
742f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Map the wifiMode integer to its type, and output as String MODE_11<A/B/G/N/AC>
743f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
744f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static String toString(int mode) {
745f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            switch(mode) {
746f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11A:
747f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11A";
748f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11B:
749f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11B";
750f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11G:
751f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11G";
752f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11N:
753f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11N";
754f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case MODE_11AC:
755f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_11AC";
756f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                default:
757f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return "MODE_UNDEFINED";
758f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
759f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
760f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    }
761f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
762f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    /**
763f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     * Parser for both the Supported Rates & Extended Supported Rates Information Elements
764f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne     */
765f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    public static class SupportedRates {
766f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static final int MASK = 0x7F; // 0111 1111
767f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public boolean mValid = false;
768f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public ArrayList<Integer> mRates;
769f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
770f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public SupportedRates() {
771f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            mRates = new ArrayList<Integer>();
772f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
773f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
774f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
775f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Is this a valid Supported Rates information element.
776f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
777f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public boolean isValid() {
778f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            return mValid;
779f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
780f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
781f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
782f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * get the Rate in bits/s from associated byteval
783f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
784f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public static int getRateFromByte(int byteVal) {
785f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            byteVal &= MASK;
786f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            switch(byteVal) {
787f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 2:
788f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 1000000;
789f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 4:
790f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 2000000;
791f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 11:
792f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 5500000;
793f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 12:
794f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 6000000;
795f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 18:
796f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 9000000;
797f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 22:
798f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 11000000;
799f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 24:
800f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 12000000;
801f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 36:
802f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 18000000;
803f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 44:
804f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 22000000;
805f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 48:
806f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 24000000;
807f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 66:
808f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 33000000;
809f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 72:
810f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 36000000;
811f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 96:
812f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 48000000;
813f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                case 108:
814f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return 54000000;
815f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                default:
816f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    //ERROR UNKNOWN RATE
817f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    return -1;
818f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
819f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
820f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
821f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        // Supported Rates format (size unit: byte)
822f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //
823f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //| ElementID | Length | Supported Rates  [7 Little Endian Info bits - 1 Flag bit]
824f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //      1          1          1 - 8
825f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //
826f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        // Note: InformationElement.bytes has 'Element ID' and 'Length'
827f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //       stripped off already
828f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        //
829f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public void from(InformationElement ie) {
830e367514651d7f888dda870fb472eea6bcfd9ec5dPaul Stewart            mValid = false;
831e367514651d7f888dda870fb472eea6bcfd9ec5dPaul Stewart            if (ie == null || ie.bytes == null || ie.bytes.length > 8 || ie.bytes.length < 1)  {
832f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return;
833f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
834f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
835f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            try {
836f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                for (int i = 0; i < ie.bytes.length; i++) {
837f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    int rate = getRateFromByte(data.get());
838f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    if (rate > 0) {
839f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                        mRates.add(rate);
840f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    } else {
841f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                        return;
842f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                    }
843f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                }
844f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            } catch (BufferUnderflowException e) {
845f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                return;
846f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
847f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            mValid = true;
848f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            return;
849f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
850f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne
851f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        /**
852f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         * Lists the rates in a human readable string
853f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne         */
854f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        public String toString() {
855f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            StringBuilder sbuf = new StringBuilder();
856f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            for (Integer rate : mRates) {
857f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne                sbuf.append(String.format("%.1f", (double) rate / 1000000) + ", ");
858f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            }
859f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne            return sbuf.toString();
860f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne        }
861f5cc6a0c7ede374b33de1cf5156bf149e2e76c13Glen Kuhne    }
8625d31cedf4024e0f038b4dfc2081016c8631ee8feMitchell Wills}
863