diff options
author | Peter Qiu <zqiu@google.com> | 2016-12-02 10:47:24 -0800 |
---|---|---|
committer | Peter Qiu <zqiu@google.com> | 2016-12-15 15:37:19 -0800 |
commit | fa04a81daf829e6e5c099c9a249b8dd8dd112102 (patch) | |
tree | 3873a014d906c85d2ca98251e1d250580924d471 /service | |
parent | 3d42402e0d282dc75f9c65f29d0f9e0eea753100 (diff) |
Cleanup support for parsing various length integer and string from ByteBuffer
Move and cleanup the parsing functions from
com.android.server.wifi.hotspot2.anqp.Constants to the newly created
ByteBufferReader, since these are generic parsing functions (not
specific to ANQP).
An unchecked runtime exception will be thrown when an error is
encountered. The possible exceptions are documented for each
API. It is the caller's responsibility to handle those
exceptions appropriately (e.g. propagate the exceptions upwards
with appropriate documentation or catch the exception).
The handling of unchecked runtime exceptions for ANQP elements
parsing will be added in the follow-on CL as part of the ANQP cleanup.
b/33296974 is filed for tracking handling of unchecked runtime
exceptions for parsing information elements.
Bug: 33000864
Test: frameworks/opt/net/wifi/tests/wifitests/runtests.sh
Change-Id: I6964520e7cc86071b2096408d6cb0503e0f618ff
Diffstat (limited to 'service')
8 files changed, 126 insertions, 84 deletions
diff --git a/service/java/com/android/server/wifi/ByteBufferReader.java b/service/java/com/android/server/wifi/ByteBufferReader.java new file mode 100644 index 000000000..91598af5f --- /dev/null +++ b/service/java/com/android/server/wifi/ByteBufferReader.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License.Wifi + */ + +package com.android.server.wifi; + +import com.android.internal.annotations.VisibleForTesting; + +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; + +/** + * Utility class for reading generic data (e.g. various length integer, string) from ByteBuffer. + */ +public class ByteBufferReader { + @VisibleForTesting + public static final int MINIMUM_INTEGER_SIZE = Byte.BYTES; + + @VisibleForTesting + public static final int MAXIMUM_INTEGER_SIZE = Long.BYTES; + + /** + * Read an integer value from a buffer. + * + * @param payload The buffer to read from + * @param byteOrder Byte order of the buffer + * @param size The number of bytes to read from the buffer + * @return The integer value + * @throws BufferUnderflowException + * @throws IllegalArgumentException + */ + public static long readInteger(ByteBuffer payload, ByteOrder byteOrder, int size) { + if (size < MINIMUM_INTEGER_SIZE || size > MAXIMUM_INTEGER_SIZE) { + throw new IllegalArgumentException("Invalid size " + size); + } + + // Read the necessary bytes. + byte[] octets = new byte[size]; + payload.get(octets); + + // Format the value based on byte order. + long value = 0; + if (byteOrder == ByteOrder.LITTLE_ENDIAN) { + for (int n = octets.length - 1; n >= 0; n--) { + value = (value << Byte.SIZE) | (octets[n] & 0xFF); + } + } else { + for (byte octet : octets) { + value = (value << Byte.SIZE) | (octet & 0xFF); + } + } + return value; + } + + /** + * Read a string from a buffer. An empty String will be returned for a String with 0 length. + * + * @param payload The buffer to read from + * @param length Number of bytes to read from the buffer + * @param charset The character set of the string + * @return {@link String} + * @throws BufferUnderflowException + * @throws NegativeArraySizeException + */ + public static String readString(ByteBuffer payload, int length, Charset charset) { + byte[] octets = new byte[length]; + payload.get(octets); + return new String(octets, charset); + } + + /** + * Read a string from a buffer where the string value is preceded by the length of the string + * (1 byte) in the buffer. + * + * @param payload The buffer to read from + * @param charset The character set of the string + * @return {@link String} + * @throws BufferUnderflowException + */ + public static String readStringWithByteLength(ByteBuffer payload, Charset charset) { + int length = payload.get() & 0xFF; + return readString(payload, length, charset); + } +} diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/Constants.java b/service/java/com/android/server/wifi/hotspot2/anqp/Constants.java index 08b5563b9..7cf34c75d 100644 --- a/service/java/com/android/server/wifi/hotspot2/anqp/Constants.java +++ b/service/java/com/android/server/wifi/hotspot2/anqp/Constants.java @@ -131,66 +131,4 @@ public class Constants { public static boolean hasR2Elements(List<ANQPElementType> elements) { return elements.contains(ANQPElementType.HSOSUProviders); } - - public static long getInteger(ByteBuffer payload, ByteOrder bo, int size) { - byte[] octets = new byte[size]; - payload.get(octets); - long value = 0; - if (bo == ByteOrder.LITTLE_ENDIAN) { - for (int n = octets.length - 1; n >= 0; n--) { - value = (value << Byte.SIZE) | (octets[n] & BYTE_MASK); - } - } - else { - for (byte octet : octets) { - value = (value << Byte.SIZE) | (octet & BYTE_MASK); - } - } - return value; - } - - public static String getPrefixedString(ByteBuffer payload, int lengthLength, Charset charset) - throws ProtocolException { - return getPrefixedString(payload, lengthLength, charset, false); - } - - public static String getPrefixedString(ByteBuffer payload, int lengthLength, Charset charset, - boolean useNull) throws ProtocolException { - if (payload.remaining() < lengthLength) { - throw new ProtocolException("Runt string: " + payload.remaining()); - } - return getString(payload, (int) getInteger(payload, ByteOrder.LITTLE_ENDIAN, - lengthLength), charset, useNull); - } - - public static String getTrimmedString(ByteBuffer payload, int length, Charset charset) - throws ProtocolException { - String s = getString(payload, length, charset, false); - int zero = length - 1; - while (zero >= 0) { - if (s.charAt(zero) != 0) { - break; - } - zero--; - } - return zero < length - 1 ? s.substring(0, zero + 1) : s; - } - - public static String getString(ByteBuffer payload, int length, Charset charset) - throws ProtocolException { - return getString(payload, length, charset, false); - } - - public static String getString(ByteBuffer payload, int length, Charset charset, boolean useNull) - throws ProtocolException { - if (length > payload.remaining()) { - throw new ProtocolException("Bad string length: " + length); - } - if (useNull && length == 0) { - return null; - } - byte[] octets = new byte[length]; - payload.get(octets); - return new String(octets, charset); - } } diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/DomainNameElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/DomainNameElement.java index cb0872e73..0b5352eb8 100644 --- a/service/java/com/android/server/wifi/hotspot2/anqp/DomainNameElement.java +++ b/service/java/com/android/server/wifi/hotspot2/anqp/DomainNameElement.java @@ -1,5 +1,7 @@ package com.android.server.wifi.hotspot2.anqp; +import com.android.server.wifi.ByteBufferReader; + import java.net.ProtocolException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -20,7 +22,8 @@ public class DomainNameElement extends ANQPElement { while (payload.hasRemaining()) { // Use latin-1 to decode for now - safe for ASCII and retains encoding - mDomains.add(Constants.getPrefixedString(payload, 1, StandardCharsets.ISO_8859_1)); + mDomains.add(ByteBufferReader.readStringWithByteLength( + payload, StandardCharsets.ISO_8859_1)); } } diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/I18Name.java b/service/java/com/android/server/wifi/hotspot2/anqp/I18Name.java index f97610fbc..81ebdfa2a 100644 --- a/service/java/com/android/server/wifi/hotspot2/anqp/I18Name.java +++ b/service/java/com/android/server/wifi/hotspot2/anqp/I18Name.java @@ -19,6 +19,7 @@ package com.android.server.wifi.hotspot2.anqp; import static com.android.server.wifi.hotspot2.anqp.Constants.BYTE_MASK; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.wifi.ByteBufferReader; import java.net.ProtocolException; import java.nio.ByteBuffer; @@ -48,11 +49,11 @@ public class I18Name { if (length < Constants.LANG_CODE_LENGTH || length > payload.remaining()) { throw new ProtocolException("Invalid I18Name length field value " + length); } - mLanguage = Constants.getTrimmedString(payload, - Constants.LANG_CODE_LENGTH, StandardCharsets.US_ASCII); + mLanguage = ByteBufferReader.readString( + payload, Constants.LANG_CODE_LENGTH, StandardCharsets.US_ASCII).trim(); mLocale = Locale.forLanguageTag(mLanguage); - mText = Constants.getString(payload, length - - Constants.LANG_CODE_LENGTH, StandardCharsets.UTF_8); + mText = ByteBufferReader.readString(payload, length - Constants.LANG_CODE_LENGTH, + StandardCharsets.UTF_8); } @VisibleForTesting diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/IconInfo.java b/service/java/com/android/server/wifi/hotspot2/anqp/IconInfo.java index c89c4429c..c961bbe79 100644 --- a/service/java/com/android/server/wifi/hotspot2/anqp/IconInfo.java +++ b/service/java/com/android/server/wifi/hotspot2/anqp/IconInfo.java @@ -7,6 +7,8 @@ import java.util.Locale; import static com.android.server.wifi.hotspot2.anqp.Constants.SHORT_MASK; +import com.android.server.wifi.ByteBufferReader; + /** * The Icons available OSU Providers sub field, as specified in * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00, @@ -26,10 +28,10 @@ public class IconInfo { mWidth = payload.getShort() & SHORT_MASK; mHeight = payload.getShort() & SHORT_MASK; - mLanguage = Constants.getTrimmedString(payload, - Constants.LANG_CODE_LENGTH, StandardCharsets.US_ASCII); - mIconType = Constants.getPrefixedString(payload, 1, StandardCharsets.US_ASCII); - mFileName = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8); + mLanguage = ByteBufferReader.readString( + payload, Constants.LANG_CODE_LENGTH, StandardCharsets.US_ASCII).trim(); + mIconType = ByteBufferReader.readStringWithByteLength(payload, StandardCharsets.US_ASCII); + mFileName = ByteBufferReader.readStringWithByteLength(payload, StandardCharsets.UTF_8); } public int getWidth() { diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/NAIRealmData.java b/service/java/com/android/server/wifi/hotspot2/anqp/NAIRealmData.java index 95fbc893c..6acdbc535 100644 --- a/service/java/com/android/server/wifi/hotspot2/anqp/NAIRealmData.java +++ b/service/java/com/android/server/wifi/hotspot2/anqp/NAIRealmData.java @@ -1,6 +1,7 @@ package com.android.server.wifi.hotspot2.anqp; import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod; +import com.android.server.wifi.ByteBufferReader; import com.android.server.wifi.hotspot2.AuthMatch; import com.android.server.wifi.hotspot2.DomainMatcher; import com.android.server.wifi.hotspot2.Utils; @@ -30,9 +31,8 @@ public class NAIRealmData { } boolean utf8 = (payload.get() & 1) == Constants.UTF8_INDICATOR; - String realm = Constants.getPrefixedString(payload, 1, utf8 ? - StandardCharsets.UTF_8 : - StandardCharsets.US_ASCII); + String realm = ByteBufferReader.readStringWithByteLength( + payload, utf8 ? StandardCharsets.UTF_8 : StandardCharsets.US_ASCII); String[] realms = realm.split(";"); mRealms = new ArrayList<>(); for (String realmElement : realms) { diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElement.java index 7230c910d..430126070 100644 --- a/service/java/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElement.java +++ b/service/java/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElement.java @@ -10,7 +10,8 @@ import java.util.Collections; import java.util.List; import static com.android.server.wifi.hotspot2.anqp.Constants.BYTE_MASK; -import static com.android.server.wifi.hotspot2.anqp.Constants.getInteger; + +import com.android.server.wifi.ByteBufferReader; /** * The Roaming Consortium ANQP Element, IEEE802.11-2012 section 8.4.4.7 @@ -30,7 +31,7 @@ public class RoamingConsortiumElement extends ANQPElement { if (length > payload.remaining()) { throw new ProtocolException("Bad OI length: " + length); } - mOis.add(getInteger(payload, ByteOrder.BIG_ENDIAN, length)); + mOis.add(ByteBufferReader.readInteger(payload, ByteOrder.BIG_ENDIAN, length)); } } diff --git a/service/java/com/android/server/wifi/util/InformationElementUtil.java b/service/java/com/android/server/wifi/util/InformationElementUtil.java index 5bb116e3b..268ddce23 100644 --- a/service/java/com/android/server/wifi/util/InformationElementUtil.java +++ b/service/java/com/android/server/wifi/util/InformationElementUtil.java @@ -15,14 +15,13 @@ */ package com.android.server.wifi.util; -import static com.android.server.wifi.hotspot2.anqp.Constants.getInteger; - import android.net.wifi.ScanResult; import android.net.wifi.ScanResult.InformationElement; import android.util.Log; -import com.android.server.wifi.hotspot2.anqp.Constants; +import com.android.server.wifi.ByteBufferReader; import com.android.server.wifi.hotspot2.NetworkDetail; +import com.android.server.wifi.hotspot2.anqp.Constants; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; @@ -183,7 +182,7 @@ public class InformationElementUtil { } if (ie.bytes.length == 7 || ie.bytes.length == 9) { - hessid = getInteger(data, ByteOrder.BIG_ENDIAN, 6); + hessid = ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 6); } } } @@ -217,15 +216,15 @@ public class InformationElementUtil { roamingConsortiums = new long[oiCount]; if (oi1Length > 0 && roamingConsortiums.length > 0) { roamingConsortiums[0] = - getInteger(data, ByteOrder.BIG_ENDIAN, oi1Length); + ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi1Length); } if (oi2Length > 0 && roamingConsortiums.length > 1) { roamingConsortiums[1] = - getInteger(data, ByteOrder.BIG_ENDIAN, oi2Length); + ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi2Length); } if (oi3Length > 0 && roamingConsortiums.length > 2) { roamingConsortiums[2] = - getInteger(data, ByteOrder.BIG_ENDIAN, oi3Length); + ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi3Length); } } } @@ -284,7 +283,7 @@ public class InformationElementUtil { public void from(InformationElement ie) { ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); extendedCapabilities = - Constants.getInteger(data, ByteOrder.LITTLE_ENDIAN, ie.bytes.length); + ByteBufferReader.readInteger(data, ByteOrder.LITTLE_ENDIAN, ie.bytes.length); int index = RTT_RESP_ENABLE_BIT / 8; byte offset = RTT_RESP_ENABLE_BIT % 8; |