diff options
12 files changed, 927 insertions, 105 deletions
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointMatchInfo.java b/service/java/com/android/server/wifi/hotspot2/PasspointMatchInfo.java index 9f26675ad..dccd2fd8a 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointMatchInfo.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointMatchInfo.java @@ -6,6 +6,7 @@ import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType; import com.android.server.wifi.hotspot2.anqp.HSConnectionCapabilityElement; import com.android.server.wifi.hotspot2.anqp.HSWanMetricsElement; import com.android.server.wifi.hotspot2.anqp.IPAddressTypeAvailabilityElement; +import com.android.server.wifi.hotspot2.anqp.ProtocolPortTuple; import java.util.HashMap; import java.util.Map; @@ -119,12 +120,12 @@ public class PasspointMatchInfo implements Comparable<PasspointMatchInfo> { HSWanMetricsElement wm = (HSWanMetricsElement) anqp.get(ANQPElementType.HSWANMetrics); if (wm != null) { - if (wm.getStatus() != HSWanMetricsElement.LinkStatus.Up || wm.isCapped()) { + if (wm.getStatus() != HSWanMetricsElement.LINK_STATUS_UP || wm.isCapped()) { score -= 1000; } else { long scaledSpeed = - wm.getDlSpeed() * (255 - wm.getDlLoad()) * 8 + - wm.getUlSpeed() * (255 - wm.getUlLoad()) * 2; + wm.getDownlinkSpeed() * (255 - wm.getDownlinkLoad()) * 8 + + wm.getUplinkSpeed() * (255 - wm.getUplinkLoad()) * 2; score += Math.min(scaledSpeed, 255000000L) >>> 23; // Max value is 30 capped at 100Mb/s } @@ -176,9 +177,9 @@ public class PasspointMatchInfo implements Comparable<PasspointMatchInfo> { private static int protoScore(HSConnectionCapabilityElement cce) { int score = 0; - for (HSConnectionCapabilityElement.ProtocolTuple tuple : cce.getStatusList()) { - int sign = tuple.getStatus() == HSConnectionCapabilityElement.ProtoStatus.Open ? - 1 : -1; + for (ProtocolPortTuple tuple : cce.getStatusList()) { + int sign = tuple.getStatus() == ProtocolPortTuple.PROTO_STATUS_OPEN + ? 1 : -1; int elementScore = 1; if (tuple.getProtocol() == IPPROTO_ICMP) { diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/ANQPFactory.java b/service/java/com/android/server/wifi/hotspot2/anqp/ANQPFactory.java index 6944a6e00..8ef75a600 100644 --- a/service/java/com/android/server/wifi/hotspot2/anqp/ANQPFactory.java +++ b/service/java/com/android/server/wifi/hotspot2/anqp/ANQPFactory.java @@ -98,7 +98,7 @@ public class ANQPFactory { case ANQP3GPPNetwork: return new ThreeGPPNetworkElement(infoID, payload); case ANQPDomName: - return new DomainNameElement(infoID, payload); + return DomainNameElement.parse(payload); case ANQPVendorSpec: if (payload.remaining() > 5) { int oi = payload.getInt(); @@ -135,11 +135,11 @@ public class ANQPFactory { case HSFriendlyName: return HSFriendlyNameElement.parse(payload); case HSWANMetrics: - return new HSWanMetricsElement(infoID, payload); + return HSWanMetricsElement.parse(payload); case HSConnCapability: - return new HSConnectionCapabilityElement(infoID, payload); + return HSConnectionCapabilityElement.parse(payload); case HSOSUProviders: - return new RawByteElement(infoID, payload); + return RawByteElement.parse(infoID, payload); default: throw new ProtocolException("Unknown element ID: " + infoID); } 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 0b5352eb8..35b39565e 100644 --- a/service/java/com/android/server/wifi/hotspot2/anqp/DomainNameElement.java +++ b/service/java/com/android/server/wifi/hotspot2/anqp/DomainNameElement.java @@ -1,8 +1,25 @@ +/* + * 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. + */ + package com.android.server.wifi.hotspot2.anqp; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.wifi.ByteBufferReader; -import java.net.ProtocolException; +import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -10,21 +27,40 @@ import java.util.Collections; import java.util.List; /** - * The Domain Name ANQP Element, IEEE802.11-2012 section 8.4.4.15 + * The Domain Name ANQP Element, IEEE802.11-2012 section 8.4.4.15. + * + * Format: + * | Domain Name Field #1 (optional) | ... + * variable + * + * Domain Name Field Format: + * | Length | Domain Name | + * 1 variable */ public class DomainNameElement extends ANQPElement { private final List<String> mDomains; - public DomainNameElement(Constants.ANQPElementType infoID, ByteBuffer payload) - throws ProtocolException { - super(infoID); - mDomains = new ArrayList<>(); + @VisibleForTesting + public DomainNameElement(List<String> domains) { + super(Constants.ANQPElementType.ANQPDomName); + mDomains = domains; + } + /** + * Parse a DomainNameElement from the given buffer. + * + * @param payload The byte buffer to read from + * @return {@link DomainNameElement} + * @throws BufferUnderflowException + */ + public static DomainNameElement parse(ByteBuffer payload) { + List<String> domains = new ArrayList<>(); while (payload.hasRemaining()) { // Use latin-1 to decode for now - safe for ASCII and retains encoding - mDomains.add(ByteBufferReader.readStringWithByteLength( + domains.add(ByteBufferReader.readStringWithByteLength( payload, StandardCharsets.ISO_8859_1)); } + return new DomainNameElement(domains); } public List<String> getDomains() { @@ -32,6 +68,23 @@ public class DomainNameElement extends ANQPElement { } @Override + public boolean equals(Object thatObject) { + if (this == thatObject) { + return true; + } + if (!(thatObject instanceof DomainNameElement)) { + return false; + } + DomainNameElement that = (DomainNameElement) thatObject; + return mDomains.equals(that.mDomains); + } + + @Override + public int hashCode() { + return mDomains.hashCode(); + } + + @Override public String toString() { return "DomainName{" + "mDomains=" + mDomains + diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElement.java index d4b55dcb6..2c9a2b30c 100644 --- a/service/java/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElement.java +++ b/service/java/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElement.java @@ -1,6 +1,24 @@ +/* + * 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. + */ + package com.android.server.wifi.hotspot2.anqp; -import java.net.ProtocolException; +import com.android.internal.annotations.VisibleForTesting; + +import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; @@ -10,64 +28,54 @@ import java.util.List; * The Connection Capability vendor specific ANQP Element, * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00, * section 4.5 + * + * Format: + * | ProtoPort Tuple #1 (optiional) | .... + * 4 */ public class HSConnectionCapabilityElement extends ANQPElement { + private final List<ProtocolPortTuple> mStatusList; - public enum ProtoStatus {Closed, Open, Unknown} - - private final List<ProtocolTuple> mStatusList; - - public static class ProtocolTuple { - private final int mProtocol; - private final int mPort; - private final ProtoStatus mStatus; - - private ProtocolTuple(ByteBuffer payload) throws ProtocolException { - if (payload.remaining() < 4) { - throw new ProtocolException("Runt protocol tuple: " + payload.remaining()); - } - mProtocol = payload.get() & Constants.BYTE_MASK; - mPort = payload.getShort() & Constants.SHORT_MASK; - int statusNumber = payload.get() & Constants.BYTE_MASK; - mStatus = statusNumber < ProtoStatus.values().length ? - ProtoStatus.values()[statusNumber] : - null; - } - - public int getProtocol() { - return mProtocol; - } - - public int getPort() { - return mPort; - } - - public ProtoStatus getStatus() { - return mStatus; - } + @VisibleForTesting + public HSConnectionCapabilityElement(List<ProtocolPortTuple> statusList) { + super(Constants.ANQPElementType.HSConnCapability); + mStatusList = statusList; + } - @Override - public String toString() { - return "ProtocolTuple{" + - "mProtocol=" + mProtocol + - ", mPort=" + mPort + - ", mStatus=" + mStatus + - '}'; + /** + * Parse a HSConnectionCapabilityElement from the given buffer. + * + * @param payload The byte buffer to read from + * @return {@link HSConnectionCapabilityElement} + * @throws BufferUnderflowException + */ + public static HSConnectionCapabilityElement parse(ByteBuffer payload) { + List<ProtocolPortTuple> statusList = new ArrayList<>(); + while (payload.hasRemaining()) { + statusList.add(ProtocolPortTuple.parse(payload)); } + return new HSConnectionCapabilityElement(statusList); } - public HSConnectionCapabilityElement(Constants.ANQPElementType infoID, ByteBuffer payload) - throws ProtocolException { - super(infoID); + public List<ProtocolPortTuple> getStatusList() { + return Collections.unmodifiableList(mStatusList); + } - mStatusList = new ArrayList<>(); - while (payload.hasRemaining()) { - mStatusList.add(new ProtocolTuple(payload)); + @Override + public boolean equals(Object thatObject) { + if (this == thatObject) { + return true; + } + if (!(thatObject instanceof HSConnectionCapabilityElement)) { + return false; } + HSConnectionCapabilityElement that = (HSConnectionCapabilityElement) thatObject; + return mStatusList.equals(that.mStatusList); } - public List<ProtocolTuple> getStatusList() { - return Collections.unmodifiableList(mStatusList); + @Override + public int hashCode() { + return mStatusList.hashCode(); } @Override diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElement.java index d76bfaf18..b55fefb9b 100644 --- a/service/java/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElement.java +++ b/service/java/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElement.java @@ -1,50 +1,113 @@ +/* + * 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. + */ + package com.android.server.wifi.hotspot2.anqp; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.wifi.ByteBufferReader; + import java.net.ProtocolException; import java.nio.ByteBuffer; - -import static com.android.server.wifi.hotspot2.anqp.Constants.BYTE_MASK; -import static com.android.server.wifi.hotspot2.anqp.Constants.INT_MASK; -import static com.android.server.wifi.hotspot2.anqp.Constants.SHORT_MASK; +import java.nio.ByteOrder; /** * The WAN Metrics vendor specific ANQP Element, * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00, * section 4.4 + * + * Format: + * | WAN Info | Downlink Speed | Uplink Speed | Downlink Load | Uplink Load | LMD | + * 1 4 4 1 1 2 + * + * WAN Info Format: + * | Link Status | Symmetric Link | At Capacity | Reserved | + * B0 B1 B2 B3 B4 - B7 */ public class HSWanMetricsElement extends ANQPElement { + public static final int LINK_STATUS_RESERVED = 0; + public static final int LINK_STATUS_UP = 1; + public static final int LINK_STATUS_DOWN = 2; + public static final int LINK_STATUS_TEST = 3; + + @VisibleForTesting + public static final int EXPECTED_BUFFER_SIZE = 13; - public enum LinkStatus {Reserved, Up, Down, Test} + @VisibleForTesting + public static final int LINK_STATUS_MASK = (1 << 0 | 1 << 1); - private final LinkStatus mStatus; + @VisibleForTesting + public static final int SYMMETRIC_LINK_MASK = 1 << 2; + + @VisibleForTesting + public static final int AT_CAPACITY_MASK = 1 << 3; + + private static final int MAX_LOAD = 256; + + private final int mStatus; private final boolean mSymmetric; private final boolean mCapped; - private final long mDlSpeed; - private final long mUlSpeed; - private final int mDlLoad; - private final int mUlLoad; - private final int mLMD; + private final long mDownlinkSpeed; + private final long mUplinkSpeed; + private final int mDownlinkLoad; + private final int mUplinkLoad; + private final int mLMD; // Load Measurement Duration. + + @VisibleForTesting + public HSWanMetricsElement(int status, boolean symmetric, boolean capped, long downlinkSpeed, + long uplinkSpeed, int downlinkLoad, int uplinkLoad, int lmd) { + super(Constants.ANQPElementType.HSWANMetrics); + mStatus = status; + mSymmetric = symmetric; + mCapped = capped; + mDownlinkSpeed = downlinkSpeed; + mUplinkSpeed = uplinkSpeed; + mDownlinkLoad = downlinkLoad; + mUplinkLoad = uplinkLoad; + mLMD = lmd; + } - public HSWanMetricsElement(Constants.ANQPElementType infoID, ByteBuffer payload) + /** + * Parse a HSWanMetricsElement from the given buffer. + * + * @param payload The byte buffer to read from + * @return {@link HSWanMetricsElement} + * @throws ProtocolException + */ + public static HSWanMetricsElement parse(ByteBuffer payload) throws ProtocolException { - super(infoID); - - if (payload.remaining() != 13) { - throw new ProtocolException("Bad WAN metrics length: " + payload.remaining()); + if (payload.remaining() != EXPECTED_BUFFER_SIZE) { + throw new ProtocolException("Unexpected buffer size: " + payload.remaining()); } - int status = payload.get() & BYTE_MASK; - mStatus = LinkStatus.values()[status & 0x03]; - mSymmetric = (status & 0x04) != 0; - mCapped = (status & 0x08) != 0; - mDlSpeed = payload.getInt() & INT_MASK; - mUlSpeed = payload.getInt() & INT_MASK; - mDlLoad = payload.get() & BYTE_MASK; - mUlLoad = payload.get() & BYTE_MASK; - mLMD = payload.getShort() & SHORT_MASK; + int wanInfo = payload.get() & 0xFF; + int status = wanInfo & LINK_STATUS_MASK; + boolean symmetric = (wanInfo & SYMMETRIC_LINK_MASK) != 0; + boolean capped = (wanInfo & AT_CAPACITY_MASK) != 0; + long downlinkSpeed = ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 4) + & 0xFFFFFFFFL; + long uplinkSpeed = ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 4) + & 0xFFFFFFFFL; + int downlinkLoad = payload.get() & 0xFF; + int uplinkLoad = payload.get() & 0xFF; + int lmd = (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2) & 0xFFFF; + return new HSWanMetricsElement(status, symmetric, capped, downlinkSpeed, uplinkSpeed, + downlinkLoad, uplinkLoad, lmd); } - public LinkStatus getStatus() { + public int getStatus() { return mStatus; } @@ -56,20 +119,20 @@ public class HSWanMetricsElement extends ANQPElement { return mCapped; } - public long getDlSpeed() { - return mDlSpeed; + public long getDownlinkSpeed() { + return mDownlinkSpeed; } - public long getUlSpeed() { - return mUlSpeed; + public long getUplinkSpeed() { + return mUplinkSpeed; } - public int getDlLoad() { - return mDlLoad; + public int getDownlinkLoad() { + return mDownlinkLoad; } - public int getUlLoad() { - return mUlLoad; + public int getUplinkLoad() { + return mUplinkLoad; } public int getLMD() { @@ -77,13 +140,38 @@ public class HSWanMetricsElement extends ANQPElement { } @Override + public boolean equals(Object thatObject) { + if (this == thatObject) { + return true; + } + if (!(thatObject instanceof HSWanMetricsElement)) { + return false; + } + HSWanMetricsElement that = (HSWanMetricsElement) thatObject; + return mStatus == that.mStatus + && mSymmetric == that.mSymmetric + && mCapped == that.mCapped + && mDownlinkSpeed == that.mDownlinkSpeed + && mUplinkSpeed == that.mUplinkSpeed + && mDownlinkLoad == that.mDownlinkLoad + && mUplinkLoad == that.mUplinkLoad + && mLMD == that.mLMD; + } + + @Override + public int hashCode() { + return (int) (mStatus + mDownlinkSpeed + mUplinkSpeed + mDownlinkLoad + + mUplinkLoad + mLMD); + } + + @Override public String toString() { return String.format("HSWanMetrics{mStatus=%s, mSymmetric=%s, mCapped=%s, " + "mDlSpeed=%d, mUlSpeed=%d, mDlLoad=%f, mUlLoad=%f, mLMD=%d}", mStatus, mSymmetric, mCapped, - mDlSpeed, mUlSpeed, - (double)mDlLoad * 100.0 / 256.0, - (double)mUlLoad * 100.0 / 256.0, + mDownlinkSpeed, mUplinkSpeed, + mDownlinkLoad * 100.0 / MAX_LOAD, + mUplinkLoad * 100.0 / MAX_LOAD, mLMD); } } diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/ProtocolPortTuple.java b/service/java/com/android/server/wifi/hotspot2/anqp/ProtocolPortTuple.java new file mode 100644 index 000000000..c097ad349 --- /dev/null +++ b/service/java/com/android/server/wifi/hotspot2/anqp/ProtocolPortTuple.java @@ -0,0 +1,108 @@ +/* + * 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. + */ + +package com.android.server.wifi.hotspot2.anqp; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.wifi.ByteBufferReader; + +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * The ProtoPort Tuple used by Connection Capability vendor specific ANQP Element, + * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00, + * section 4.5 + * + * Format: + * | IP Procotol | Port Number | Status | + * 1 2 1 + */ +public class ProtocolPortTuple { + /** + * Number of raw bytes needed for the tuple. + */ + @VisibleForTesting + public static final int RAW_BYTE_SIZE = 4; + + public static final int PROTO_STATUS_CLOSED = 0; + public static final int PROTO_STATUS_OPEN = 1; + public static final int PROTO_STATUS_UNKNOWN = 2; + + private final int mProtocol; + private final int mPort; + private final int mStatus; + + @VisibleForTesting + public ProtocolPortTuple(int protocol, int port, int status) { + mProtocol = protocol; + mPort = port; + mStatus = status; + } + + /** + * Parse a ProtocolPortTuple from the given buffer. + * + * @param payload The byte buffer to read from + * @return {@link ProtocolPortTuple} + * @throws BufferUnderflowException + */ + public static ProtocolPortTuple parse(ByteBuffer payload) { + int protocol = payload.get(); + int port = (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2) + & 0xFFFF; + int status = payload.get() & 0xFF; + return new ProtocolPortTuple(protocol, port, status); + } + + public int getProtocol() { + return mProtocol; + } + + public int getPort() { + return mPort; + } + + public int getStatus() { + return mStatus; + } + + @Override + public boolean equals(Object thatObject) { + if (this == thatObject) { + return true; + } + if (!(thatObject instanceof ProtocolPortTuple)) { + return false; + } + ProtocolPortTuple that = (ProtocolPortTuple) thatObject; + return mProtocol == that.mProtocol + && mPort == that.mPort + && mStatus == that.mStatus; + } + + @Override + public int hashCode() { + return (mProtocol * 31 + mPort) * 31 + mStatus; + } + + @Override + public String toString() { + return "ProtocolTuple{" + "mProtocol=" + mProtocol + ", mPort=" + mPort + + ", mStatus=" + mStatus + '}'; + } +} diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/RawByteElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/RawByteElement.java index ae301c425..633147d45 100644 --- a/service/java/com/android/server/wifi/hotspot2/anqp/RawByteElement.java +++ b/service/java/com/android/server/wifi/hotspot2/anqp/RawByteElement.java @@ -1,6 +1,25 @@ +/* + * 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. + */ + package com.android.server.wifi.hotspot2.anqp; +import com.android.internal.annotations.VisibleForTesting; + import java.nio.ByteBuffer; +import java.util.Arrays; /** * An object holding the raw octets of an ANQP element as provided by the wpa_supplicant. @@ -8,13 +27,44 @@ import java.nio.ByteBuffer; public class RawByteElement extends ANQPElement { private final byte[] mPayload; - public RawByteElement(Constants.ANQPElementType infoID, ByteBuffer payload) { + @VisibleForTesting + public RawByteElement(Constants.ANQPElementType infoID, byte[] payload) { super(infoID); - mPayload = new byte[payload.remaining()]; - payload.get(mPayload); + mPayload = payload; + } + + /** + * Parse a RawByteElement from the given buffer. + * + * @param payload The byte buffer to read from + * @return {@link HSConnectionCapabilityElement} + */ + public static RawByteElement parse(Constants.ANQPElementType infoID, ByteBuffer payload) { + byte[] rawBytes = new byte[payload.remaining()]; + if (payload.hasRemaining()) { + payload.get(rawBytes); + } + return new RawByteElement(infoID, rawBytes); } public byte[] getPayload() { return mPayload; } + + @Override + public boolean equals(Object thatObject) { + if (this == thatObject) { + return true; + } + if (!(thatObject instanceof RawByteElement)) { + return false; + } + RawByteElement that = (RawByteElement) thatObject; + return getID() == that.getID() && Arrays.equals(mPayload, that.mPayload); + } + + @Override + public int hashCode() { + return Arrays.hashCode(mPayload); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/DomainNameElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/DomainNameElementTest.java new file mode 100644 index 000000000..d17a7fa1d --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/DomainNameElementTest.java @@ -0,0 +1,112 @@ +/* + * 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. + */ + +package com.android.server.wifi.hotspot2.anqp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.DomainNameElement}. + */ +@SmallTest +public class DomainNameElementTest { + private static final String TEST_DOMAIN_NAME1 = "test1.com"; + private static final String TEST_DOMAIN_NAME2 = "test2.com"; + + /** + * Helper function for appending a Domain Name to an output stream. + * + * @param stream Stream to write to + * @param domain The domain name string + * @throws IOException + */ + private void appendDomain(ByteArrayOutputStream stream, String domain) throws IOException { + byte[] domainBytes = domain.getBytes(StandardCharsets.ISO_8859_1); + stream.write((byte) domainBytes.length); + stream.write(domainBytes); + } + + /** + * Helper function for generating test data. + * + * @return byte[] of data + * @throws IOException + */ + private byte[] getTestData(String[] domains) throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + for (String domain : domains) { + appendDomain(stream, domain); + } + return stream.toByteArray(); + } + + /** + * Verify that a DomainNameElement with empty domain list will be returned when parsing an + * empty buffer. + * + * @throws Exception + */ + @Test + public void parseEmptyBuffer() throws Exception { + assertTrue(DomainNameElement.parse(ByteBuffer.allocate(0)).getDomains().isEmpty()); + } + + /** + * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer + * (missing a byte at the end). + * + * @throws Exception + */ + @Test(expected = BufferUnderflowException.class) + public void parseTruncatedBuffer() throws Exception { + ByteBuffer buffer = ByteBuffer.wrap(getTestData(new String[] {TEST_DOMAIN_NAME1})); + buffer.limit(buffer.remaining() - 1); + DomainNameElement.parse(buffer); + } + + /** + * Verify that a DomainNameElement with expected domain list will be returned when parsing a + * buffer contained valid domain name list. + * + * @throws Exception + */ + @Test + public void parseBufferWithValidDomainNames() throws Exception { + byte[] testData = getTestData(new String[] {TEST_DOMAIN_NAME1, TEST_DOMAIN_NAME2}); + ByteBuffer buffer = ByteBuffer.wrap(testData); + + // Setup expected element. + List<String> domainList = new ArrayList<>(); + domainList.add(TEST_DOMAIN_NAME1); + domainList.add(TEST_DOMAIN_NAME2); + DomainNameElement expectedElement = new DomainNameElement(domainList); + + assertEquals(expectedElement, DomainNameElement.parse(buffer)); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElementTest.java new file mode 100644 index 000000000..aef2b86d1 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElementTest.java @@ -0,0 +1,131 @@ +/* + * 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. + */ + +package com.android.server.wifi.hotspot2.anqp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; + +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; + +/** + * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.HSConnectionCapabilityElement}. + */ +@SmallTest +public class HSConnectionCapabilityElementTest { + private static final ProtocolPortTuple TEST_TUPLE1 = + new ProtocolPortTuple(1, 2, ProtocolPortTuple.PROTO_STATUS_CLOSED); + private static final ProtocolPortTuple TEST_TUPLE2 = + new ProtocolPortTuple(3, 4, ProtocolPortTuple.PROTO_STATUS_OPEN); + + /** + * Helper function for writing a ProtocolPortTuple into a buffer. + * + * @param buffer The buffer to write to + * @param tuple The tuple to write + */ + private void appendProtocolPortTuple(ByteBuffer buffer, ProtocolPortTuple tuple) { + buffer.put((byte) tuple.getProtocol()); + buffer.putShort((short) tuple.getPort()); + buffer.put((byte) tuple.getStatus()); + } + + /** + * Helper function for generating a buffer with test data. + * + * @param tuples Tuples to put in the buffer + * @return {@link ByteBuffer} + */ + private ByteBuffer getTestBuffer(ProtocolPortTuple[] tuples) { + ByteBuffer buffer = ByteBuffer.allocate(tuples.length * ProtocolPortTuple.RAW_BYTE_SIZE) + .order(ByteOrder.LITTLE_ENDIAN); + for (ProtocolPortTuple tuple : tuples) { + appendProtocolPortTuple(buffer, tuple); + } + buffer.position(0); + return buffer; + } + + /** + * Verify that a HSConnectionCapabilityElement with an empty status list will be returned + * when parsing an empty buffer. + * + * @throws Exception + */ + @Test + public void parseEmptyBuffer() throws Exception { + HSConnectionCapabilityElement element = + HSConnectionCapabilityElement.parse(ByteBuffer.allocate(0)); + assertTrue(element.getStatusList().isEmpty()); + } + + /** + * Verify that BufferUnderflowException will be thrown when parsing a buffer without + * the complete tuple data (missing status field). + * + * @throws Exception + */ + @Test(expected = BufferUnderflowException.class) + public void parseBufferWithLessThanMinimumSize() throws Exception { + ByteBuffer buffer = ByteBuffer.allocate(ProtocolPortTuple.RAW_BYTE_SIZE - 1); + buffer.put(new byte[ProtocolPortTuple.RAW_BYTE_SIZE - 1]); + buffer.position(0); + HSConnectionCapabilityElement.parse(buffer); + } + + /** + * Verify that BufferUnderflowException will be thrown when parsing a buffer that contained + * incomplete bytes for a tuple. + * + * @throws Exception + */ + @Test(expected = BufferUnderflowException.class) + public void parseBufferWithIncompleteTupleBytes() throws Exception { + // Construct a buffer which will contained a tuple and an extra byte at the end. + ByteBuffer buffer = ByteBuffer.allocate(ProtocolPortTuple.RAW_BYTE_SIZE + 1); + appendProtocolPortTuple(buffer, TEST_TUPLE1); + buffer.put((byte) 0); + buffer.position(0); + HSConnectionCapabilityElement.parse(buffer); + } + + /** + * Verify that the expected HSConnectionCapabilityElement is returned when parsing + * a buffer containing the test data. + * + * @throws Exception + */ + @Test + public void parseBufferWithTestData() throws Exception { + ByteBuffer buffer = getTestBuffer(new ProtocolPortTuple[] {TEST_TUPLE1, TEST_TUPLE2}); + + // Setup expected element. + List<ProtocolPortTuple> tupleList = new ArrayList<>(); + tupleList.add(TEST_TUPLE1); + tupleList.add(TEST_TUPLE2); + HSConnectionCapabilityElement expected = new HSConnectionCapabilityElement(tupleList); + + assertEquals(expected, HSConnectionCapabilityElement.parse(buffer)); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElementTest.java new file mode 100644 index 000000000..8c53fe3a7 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElementTest.java @@ -0,0 +1,117 @@ +/* + * 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. + */ + +package com.android.server.wifi.hotspot2.anqp; + +import static org.junit.Assert.assertEquals; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; + +import java.net.ProtocolException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.HSWanMetricsElement}. + */ +@SmallTest +public class HSWanMetricsElementTest { + private static final int TEST_LINK_STATUS = HSWanMetricsElement.LINK_STATUS_UP; + private static final boolean TEST_SYMMETRIC_LINK = true; + private static final boolean TEST_AT_CAPACITY = true; + private static final long TEST_DOWNLINK_SPEED = 0x1234556L; + private static final long TEST_UPLINK_SPEED = 0x342343L; + private static final int TEST_DOWNLINK_LOAD = 0x23; + private static final int TEST_UPLINK_LOAD = 0x45; + private static final int TEST_LMD = 0x2132; + + /** + * Helper function for generating a ByteBuffer with the test data. + * + * @return {@link ByteBuffer} + */ + private ByteBuffer getTestBuffer() { + ByteBuffer buffer = ByteBuffer.allocate(HSWanMetricsElement.EXPECTED_BUFFER_SIZE) + .order(ByteOrder.LITTLE_ENDIAN); + int wanInfo = TEST_LINK_STATUS & HSWanMetricsElement.LINK_STATUS_MASK; + if (TEST_SYMMETRIC_LINK) wanInfo |= HSWanMetricsElement.SYMMETRIC_LINK_MASK; + if (TEST_AT_CAPACITY) wanInfo |= HSWanMetricsElement.AT_CAPACITY_MASK; + buffer.put((byte) wanInfo); + buffer.putInt((int) (TEST_DOWNLINK_SPEED & 0xFFFFFFFFL)); + buffer.putInt((int) (TEST_UPLINK_SPEED & 0xFFFFFFFFL)); + buffer.put((byte) (TEST_DOWNLINK_LOAD & 0xFF)); + buffer.put((byte) (TEST_UPLINK_LOAD & 0xFF)); + buffer.putShort((short) (TEST_LMD & 0xFFFF)); + buffer.position(0); + return buffer; + } + + /** + * Verify that ProtocolException will be thrown when parsing an empty buffer. + * + * @throws Exception + */ + @Test(expected = ProtocolException.class) + public void parseEmptyBuffer() throws Exception { + HSWanMetricsElement.parse(ByteBuffer.allocate(0)); + } + + /** + * Verify that ProtocolException will be thrown when a buffer with size less than the + * expected. + * + * @throws Exception + */ + @Test(expected = ProtocolException.class) + public void parseBufferWithLessThanExpectedSize() throws Exception { + ByteBuffer buffer = ByteBuffer.allocate(HSWanMetricsElement.EXPECTED_BUFFER_SIZE - 1); + buffer.put(new byte[HSWanMetricsElement.EXPECTED_BUFFER_SIZE - 1]); + buffer.position(0); + HSWanMetricsElement.parse(buffer); + } + + /** + * Verify that ProtocolException will be thrown when a buffer with size more than the + * expected. + * + * @throws Exception + */ + @Test(expected = ProtocolException.class) + public void parseBufferWithMoreThanExpectedSize() throws Exception { + ByteBuffer buffer = ByteBuffer.allocate(HSWanMetricsElement.EXPECTED_BUFFER_SIZE + 1); + buffer.put(new byte[HSWanMetricsElement.EXPECTED_BUFFER_SIZE + 1]); + buffer.position(0); + HSWanMetricsElement.parse(buffer); + } + + /** + * Verify that the expected HSWanMetricsElement is returned when parsing + * a buffer containing the test data. + * + * @throws Exception + */ + @Test + public void parseBufferWithTestData() throws Exception { + ByteBuffer buffer = getTestBuffer(); + HSWanMetricsElement expectedElement = new HSWanMetricsElement( + TEST_LINK_STATUS, TEST_SYMMETRIC_LINK, TEST_AT_CAPACITY, + TEST_DOWNLINK_SPEED, TEST_UPLINK_SPEED, TEST_DOWNLINK_LOAD, + TEST_UPLINK_LOAD, TEST_LMD); + assertEquals(expectedElement, HSWanMetricsElement.parse(buffer)); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ProtocolPortTupleTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ProtocolPortTupleTest.java new file mode 100644 index 000000000..b4bfaf1c5 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ProtocolPortTupleTest.java @@ -0,0 +1,92 @@ +/* + * 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. + */ + +package com.android.server.wifi.hotspot2.anqp; + +import static org.junit.Assert.assertEquals; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; + +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.ProtocolPortTuple}. + */ +@SmallTest +public class ProtocolPortTupleTest { + private static final int TEST_PROTOCOL = 1; + private static final int TEST_PORT = 2; + private static final int TEST_STATUS = ProtocolPortTuple.PROTO_STATUS_CLOSED; + + /** + * Helper function for generating a buffer with test data. + * + * @param protocol Protocol value + * @param port Port value + * @param status Status value + * @return {@link ByteBuffer} + */ + private ByteBuffer getTestBuffer(int protocol, int port, int status) { + ByteBuffer buffer = ByteBuffer.allocate(ProtocolPortTuple.RAW_BYTE_SIZE) + .order(ByteOrder.LITTLE_ENDIAN); + buffer.put((byte) protocol); + buffer.putShort((short) port); + buffer.put((byte) status); + buffer.position(0); + return buffer; + } + + /** + * Verify that BufferUnderflowException will be thrown when parsing an empty buffer. + * + * @throws Exception + */ + @Test(expected = BufferUnderflowException.class) + public void parseEmptyBuffer() throws Exception { + ProtocolPortTuple.parse(ByteBuffer.allocate(0)); + } + + /** + * Verify that BufferUnderflowException will be thrown when parsing a buffer without + * the complete tuple data (missing status field). + * + * @throws Exception + */ + @Test(expected = BufferUnderflowException.class) + public void parseBufferWithIncompleteData() throws Exception { + ByteBuffer buffer = ByteBuffer.allocate(ProtocolPortTuple.RAW_BYTE_SIZE - 1); + buffer.put(new byte[ProtocolPortTuple.RAW_BYTE_SIZE - 1]); + buffer.position(0); + ProtocolPortTuple.parse(buffer); + } + + /** + * Verify that the expected ProtocolPortTuple is returned when parsing a buffer contained + * the test data. + * + * @throws Exception + */ + @Test + public void parseBufferWithTestData() throws Exception { + ByteBuffer buffer = getTestBuffer(TEST_PROTOCOL, TEST_PORT, TEST_STATUS); + ProtocolPortTuple expected = new ProtocolPortTuple(TEST_PROTOCOL, TEST_PORT, TEST_STATUS); + assertEquals(expected, ProtocolPortTuple.parse(buffer)); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RawByteElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RawByteElementTest.java new file mode 100644 index 000000000..e61797a87 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RawByteElementTest.java @@ -0,0 +1,62 @@ +/* + * 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. + */ + +package com.android.server.wifi.hotspot2.anqp; + +import static org.junit.Assert.assertEquals; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; + +import java.nio.ByteBuffer; + +/** + * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.RawByteElement}. + */ +@SmallTest +public class RawByteElementTest { + private static final Constants.ANQPElementType TEST_ELEMENT_ID = + Constants.ANQPElementType.HSOSUProviders; + + /** + * Verify that a RawByteElement with an empty payload will be returned when parsing + * an empty buffer. + * + * @throws Exception + */ + @Test + public void parseEmptyBuffer() throws Exception { + byte[] data = new byte[0]; + RawByteElement actual = RawByteElement.parse(TEST_ELEMENT_ID, ByteBuffer.wrap(data)); + RawByteElement expected = new RawByteElement(TEST_ELEMENT_ID, data); + assertEquals(expected, actual); + } + + /** + * Verify that the expected RawByteElement will be returned when parsing a non-empty + * buffer. + * + * @throws Exception + */ + @Test + public void parseNonEmptyBuffer() throws Exception { + byte[] data = new byte[10]; + RawByteElement actual = RawByteElement.parse(TEST_ELEMENT_ID, ByteBuffer.wrap(data)); + RawByteElement expected = new RawByteElement(TEST_ELEMENT_ID, data); + assertEquals(expected, actual); + } +} |