diff options
14 files changed, 441 insertions, 233 deletions
diff --git a/apishim/29/com/android/networkstack/apishim/api29/ConstantsShim.java b/apishim/29/com/android/networkstack/apishim/api29/ConstantsShim.java index 29d8506..762a8b8 100644 --- a/apishim/29/com/android/networkstack/apishim/api29/ConstantsShim.java +++ b/apishim/29/com/android/networkstack/apishim/api29/ConstantsShim.java @@ -24,7 +24,7 @@ public class ConstantsShim { public static final int DETECTION_METHOD_DNS_EVENTS = 1; public static final int DETECTION_METHOD_TCP_METRICS = 2; public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts"; - public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttemped"; + public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttempted"; public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded"; public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult"; public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = diff --git a/common/moduleutils/src/android/net/shared/ProvisioningConfiguration.java b/common/moduleutils/src/android/net/shared/ProvisioningConfiguration.java index 00b4e19..7de376a 100644 --- a/common/moduleutils/src/android/net/shared/ProvisioningConfiguration.java +++ b/common/moduleutils/src/android/net/shared/ProvisioningConfiguration.java @@ -16,14 +16,25 @@ package android.net.shared; +import static android.net.shared.ParcelableUtil.fromParcelableArray; +import static android.net.shared.ParcelableUtil.toParcelableArray; + +import android.annotation.NonNull; import android.annotation.Nullable; import android.net.INetd; +import android.net.InformationElementParcelable; import android.net.Network; import android.net.ProvisioningConfigurationParcelable; +import android.net.ScanResultInfoParcelable; import android.net.StaticIpConfiguration; import android.net.apf.ApfCapabilities; import android.net.ip.IIpClient; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.StringJoiner; @@ -193,6 +204,17 @@ public class ProvisioningConfiguration { } /** + * Specify the information elements included in wifi scan result that was obtained + * prior to connecting to the access point, if this is a WiFi network. + * + * <p>The scan result can be used to infer whether the network is metered. + */ + public Builder withScanResultInfo(ScanResultInfo scanResultInfo) { + mConfig.mScanResultInfo = scanResultInfo; + return this; + } + + /** * Build the configuration using previously specified parameters. */ public ProvisioningConfiguration build() { @@ -200,6 +222,158 @@ public class ProvisioningConfiguration { } } + /** + * Class wrapper of {@link android.net.wifi.ScanResult} to encapsulate the SSID and + * InformationElements fields of ScanResult. + */ + public static class ScanResultInfo { + private final String mSsid; + private final List<InformationElement> mInformationElements; + + /** + * Class wrapper of {@link android.net.wifi.ScanResult.InformationElement} to encapsulate + * the specific IE id and payload fields. + */ + public static class InformationElement { + private final int mId; + private final byte[] mPayload; + + public InformationElement(int id, @NonNull ByteBuffer payload) { + mId = id; + mPayload = convertToByteArray(payload.asReadOnlyBuffer()); + } + + /** + * Get the element ID of the information element. + */ + public int getId() { + return mId; + } + + /** + * Get the specific content of the information element. + */ + public ByteBuffer getPayload() { + return ByteBuffer.wrap(mPayload).asReadOnlyBuffer(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof InformationElement)) return false; + InformationElement other = (InformationElement) o; + return mId == other.mId && Arrays.equals(mPayload, other.mPayload); + } + + @Override + public int hashCode() { + return Objects.hash(mId, mPayload); + } + + @Override + public String toString() { + return "ID: " + mId + ", " + Arrays.toString(mPayload); + } + + /** + * Convert this InformationElement to a {@link InformationElementParcelable}. + */ + public InformationElementParcelable toStableParcelable() { + final InformationElementParcelable p = new InformationElementParcelable(); + p.id = mId; + p.payload = mPayload.clone(); + return p; + } + + /** + * Create an instance of {@link InformationElement} based on the contents of the + * specified {@link InformationElementParcelable}. + */ + public static InformationElement fromStableParcelable(InformationElementParcelable p) { + if (p == null) return null; + return new InformationElement(p.id, + ByteBuffer.wrap(p.payload.clone()).asReadOnlyBuffer()); + } + } + + public ScanResultInfo(String ssid, @NonNull List<InformationElement> informationElements) { + mSsid = ssid; + mInformationElements = + Collections.unmodifiableList(new ArrayList<>(informationElements)); + } + + /** + * Get the scanned network name. + */ + public String getSsid() { + return mSsid; + } + + /** + * Get all information elements found in the beacon. + */ + public List<InformationElement> getInformationElements() { + return mInformationElements; + } + + @Override + public String toString() { + StringBuffer str = new StringBuffer(); + str.append("SSID: ").append(mSsid); + str.append(", Information Elements: {"); + for (InformationElement ie : mInformationElements) { + str.append("[").append(ie.toString()).append("]"); + } + str.append("}"); + return str.toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof ScanResultInfo)) return false; + ScanResultInfo other = (ScanResultInfo) o; + return Objects.equals(mSsid, other.mSsid) + && mInformationElements.equals(other.mInformationElements); + } + + @Override + public int hashCode() { + return Objects.hash(mSsid, mInformationElements); + } + + /** + * Convert this ScanResultInfo to a {@link ScanResultInfoParcelable}. + */ + public ScanResultInfoParcelable toStableParcelable() { + final ScanResultInfoParcelable p = new ScanResultInfoParcelable(); + p.ssid = mSsid; + p.informationElements = toParcelableArray(mInformationElements, + InformationElement::toStableParcelable, InformationElementParcelable.class); + return p; + } + + /** + * Create an instance of {@link ScanResultInfo} based on the contents of the specified + * {@link ScanResultInfoParcelable}. + */ + public static ScanResultInfo fromStableParcelable(ScanResultInfoParcelable p) { + if (p == null) return null; + final List<InformationElement> ies = new ArrayList<InformationElement>(); + ies.addAll(fromParcelableArray(p.informationElements, + InformationElement::fromStableParcelable)); + return new ScanResultInfo(p.ssid, ies); + } + + private static byte[] convertToByteArray(final ByteBuffer buffer) { + if (buffer == null) return null; + byte[] bytes = new byte[buffer.limit()]; + final ByteBuffer copy = buffer.asReadOnlyBuffer(); + copy.get(bytes); + return bytes; + } + } + public boolean mEnableIPv4 = true; public boolean mEnableIPv6 = true; public boolean mEnablePreconnection = false; @@ -213,6 +387,7 @@ public class ProvisioningConfiguration { public int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; public Network mNetwork = null; public String mDisplayName = null; + public ScanResultInfo mScanResultInfo; public ProvisioningConfiguration() {} // used by Builder @@ -232,6 +407,7 @@ public class ProvisioningConfiguration { mIPv6AddrGenMode = other.mIPv6AddrGenMode; mNetwork = other.mNetwork; mDisplayName = other.mDisplayName; + mScanResultInfo = other.mScanResultInfo; } /** @@ -254,6 +430,7 @@ public class ProvisioningConfiguration { p.ipv6AddrGenMode = mIPv6AddrGenMode; p.network = mNetwork; p.displayName = mDisplayName; + p.scanResultInfo = mScanResultInfo == null ? null : mScanResultInfo.toStableParcelable(); return p; } @@ -279,6 +456,7 @@ public class ProvisioningConfiguration { config.mIPv6AddrGenMode = p.ipv6AddrGenMode; config.mNetwork = p.network; config.mDisplayName = p.displayName; + config.mScanResultInfo = ScanResultInfo.fromStableParcelable(p.scanResultInfo); return config; } @@ -298,6 +476,7 @@ public class ProvisioningConfiguration { .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode) .add("mNetwork: " + mNetwork) .add("mDisplayName: " + mDisplayName) + .add("mScanResultInfo: " + mScanResultInfo) .toString(); } @@ -317,7 +496,8 @@ public class ProvisioningConfiguration { && mProvisioningTimeoutMs == other.mProvisioningTimeoutMs && mIPv6AddrGenMode == other.mIPv6AddrGenMode && Objects.equals(mNetwork, other.mNetwork) - && Objects.equals(mDisplayName, other.mDisplayName); + && Objects.equals(mDisplayName, other.mDisplayName) + && Objects.equals(mScanResultInfo, other.mScanResultInfo); } public boolean isValid() { diff --git a/common/networkstackclient/Android.bp b/common/networkstackclient/Android.bp index 31f3384..dbe8ff0 100644 --- a/common/networkstackclient/Android.bp +++ b/common/networkstackclient/Android.bp @@ -45,6 +45,7 @@ aidl_interface { include_dirs: [ "frameworks/base/core/java", // For framework parcelables. "frameworks/native/aidl/binder/android/os", // For PersistableBundle.aidl + "frameworks/base/wifi/aidl-export", // For wifi parcelables. ], srcs: [ "src/android/net/DhcpResultsParcelable.aidl", @@ -53,10 +54,12 @@ aidl_interface { "src/android/net/INetworkStackConnector.aidl", "src/android/net/INetworkStackStatusCallback.aidl", "src/android/net/InitialConfigurationParcelable.aidl", + "src/android/net/InformationElementParcelable.aidl", "src/android/net/Layer2PacketParcelable.aidl", "src/android/net/NattKeepalivePacketDataParcelable.aidl", "src/android/net/PrivateDnsConfigParcel.aidl", "src/android/net/ProvisioningConfigurationParcelable.aidl", + "src/android/net/ScanResultInfoParcelable.aidl", "src/android/net/TcpKeepalivePacketDataParcelable.aidl", "src/android/net/dhcp/DhcpServingParamsParcel.aidl", "src/android/net/dhcp/IDhcpServer.aidl", diff --git a/common/networkstackclient/src/android/net/InformationElementParcelable.aidl b/common/networkstackclient/src/android/net/InformationElementParcelable.aidl new file mode 100644 index 0000000..c70bf6f --- /dev/null +++ b/common/networkstackclient/src/android/net/InformationElementParcelable.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2020 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 android.net; + +parcelable InformationElementParcelable { + int id; + byte[] payload; +} diff --git a/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl b/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl index 0b6d7d5..9fcb036 100644 --- a/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl +++ b/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl @@ -19,6 +19,7 @@ package android.net; import android.net.InitialConfigurationParcelable; import android.net.Network; +import android.net.ScanResultInfoParcelable; import android.net.StaticIpConfiguration; import android.net.apf.ApfCapabilities; @@ -36,4 +37,5 @@ parcelable ProvisioningConfigurationParcelable { Network network; String displayName; boolean enablePreconnection; + ScanResultInfoParcelable scanResultInfo; } diff --git a/common/networkstackclient/src/android/net/ScanResultInfoParcelable.aidl b/common/networkstackclient/src/android/net/ScanResultInfoParcelable.aidl new file mode 100644 index 0000000..f5f101d --- /dev/null +++ b/common/networkstackclient/src/android/net/ScanResultInfoParcelable.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2020 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 android.net; + +import android.net.InformationElementParcelable; + +parcelable ScanResultInfoParcelable { + String ssid; + InformationElementParcelable[] informationElements; +} diff --git a/src/com/android/networkstack/netlink/TcpInfo.java b/src/com/android/networkstack/netlink/TcpInfo.java index e6036b5..31a408f 100644 --- a/src/com/android/networkstack/netlink/TcpInfo.java +++ b/src/com/android/networkstack/netlink/TcpInfo.java @@ -22,11 +22,9 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; import java.util.Objects; /** @@ -91,27 +89,39 @@ public class TcpInfo { } private static final String TAG = "TcpInfo"; - private final Map<Field, Number> mFieldsValues; + @VisibleForTesting + static final int LOST_OFFSET = getFieldOffset(Field.LOST); + @VisibleForTesting + static final int RETRANSMITS_OFFSET = getFieldOffset(Field.RETRANSMITS); + @VisibleForTesting + static final int SEGS_IN_OFFSET = getFieldOffset(Field.SEGS_IN); + @VisibleForTesting + static final int SEGS_OUT_OFFSET = getFieldOffset(Field.SEGS_OUT); + final int mSegsIn; + final int mSegsOut; + final int mLost; + final int mRetransmits; + + private static int getFieldOffset(@NonNull final Field needle) { + int offset = 0; + for (final Field field : Field.values()) { + if (field == needle) return offset; + offset += field.size; + } + throw new IllegalArgumentException("Unknown field"); + } private TcpInfo(@NonNull ByteBuffer bytes, int infolen) { - final int start = bytes.position(); - final LinkedHashMap<Field, Number> fields = new LinkedHashMap<>(); - for (final Field field : Field.values()) { - switch (field.size) { - case Byte.BYTES: - fields.put(field, getByte(bytes, start, infolen)); - break; - case Integer.BYTES: - fields.put(field, getInt(bytes, start, infolen)); - break; - case Long.BYTES: - fields.put(field, getLong(bytes, start, infolen)); - break; - default: - Log.e(TAG, "Unexpected size:" + field.size); - } + // SEGS_IN is the last required field in the buffer, so if the buffer is long enough for + // SEGS_IN it's long enough for everything + if (SEGS_IN_OFFSET + Field.SEGS_IN.size > infolen) { + throw new IllegalArgumentException("Length " + infolen + " is less than required."); } - mFieldsValues = Collections.unmodifiableMap(fields); + final int start = bytes.position(); + mSegsIn = bytes.getInt(start + SEGS_IN_OFFSET); + mSegsOut = bytes.getInt(start + SEGS_OUT_OFFSET); + mLost = bytes.getInt(start + LOST_OFFSET); + mRetransmits = bytes.get(start + RETRANSMITS_OFFSET); // tcp_info structure grows over time as new fields are added. Jump to the end of the // structure, as unknown fields might remain at the end of the structure if the tcp_info // struct was expanded. @@ -119,12 +129,11 @@ public class TcpInfo { } @VisibleForTesting - TcpInfo(@NonNull Map<Field, Number> info) { - final LinkedHashMap<Field, Number> fields = new LinkedHashMap<>(); - for (final Field field : Field.values()) { - fields.put(field, info.get(field)); - } - mFieldsValues = Collections.unmodifiableMap(fields); + TcpInfo(int retransmits, int lost, int segsOut, int segsIn) { + mRetransmits = retransmits; + mLost = lost; + mSegsOut = segsOut; + mSegsIn = segsIn; } /** Parse a TcpInfo from a giving ByteBuffer with a specific length. */ @@ -132,53 +141,13 @@ public class TcpInfo { public static TcpInfo parse(@NonNull ByteBuffer bytes, int infolen) { try { return new TcpInfo(bytes, infolen); - } catch (BufferUnderflowException | IllegalArgumentException e) { + } catch (BufferUnderflowException | BufferOverflowException | IllegalArgumentException + | IndexOutOfBoundsException e) { Log.e(TAG, "parsing error.", e); return null; } } - /** - * Helper function for handling different struct tcp_info versions in the kernel. - */ - private static boolean isValidTargetPosition(int start, int len, int pos, int targetBytes) - throws IllegalArgumentException { - // Equivalent to new Range(start, start + len).contains(new Range(pos, pos + targetBytes)) - if (len < 0 || targetBytes < 0) throw new IllegalArgumentException(); - // Check that start < pos < start + len - if (pos < start || pos > start + len) return false; - // Pos is inside the range and targetBytes is positive. Offset is valid if end of 2nd range - // is below end of 1st range. - return pos + targetBytes <= start + len; - } - - /** Get value for specific key. */ - @Nullable - public Number getValue(@NonNull Field key) { - return mFieldsValues.get(key); - } - - @Nullable - private static Byte getByte(@NonNull ByteBuffer buffer, int start, int len) { - if (!isValidTargetPosition(start, len, buffer.position(), Byte.BYTES)) return null; - - return buffer.get(); - } - - @Nullable - private static Integer getInt(@NonNull ByteBuffer buffer, int start, int len) { - if (!isValidTargetPosition(start, len, buffer.position(), Integer.BYTES)) return null; - - return buffer.getInt(); - } - - @Nullable - private static Long getLong(@NonNull ByteBuffer buffer, int start, int len) { - if (!isValidTargetPosition(start, len, buffer.position(), Long.BYTES)) return null; - - return buffer.getLong(); - } - private static String decodeWscale(byte num) { return String.valueOf((num >> 4) & 0x0f) + ":" + String.valueOf(num & 0x0f); } @@ -210,33 +179,18 @@ public class TcpInfo { if (!(obj instanceof TcpInfo)) return false; TcpInfo other = (TcpInfo) obj; - for (final Field key : mFieldsValues.keySet()) { - if (!Objects.equals(mFieldsValues.get(key), other.mFieldsValues.get(key))) { - return false; - } - } - return true; + return mSegsIn == other.mSegsIn && mSegsOut == other.mSegsOut + && mRetransmits == other.mRetransmits && mLost == other.mLost; } @Override public int hashCode() { - return Objects.hash(mFieldsValues.values().toArray()); + return Objects.hash(mLost, mRetransmits, mSegsIn, mSegsOut); } @Override public String toString() { - String str = "TcpInfo{ "; - for (final Field key : mFieldsValues.keySet()) { - str += key.name().toLowerCase() + "="; - if (key == Field.STATE) { - str += getTcpStateName(mFieldsValues.get(key).intValue()) + " "; - } else if (key == Field.WSCALE) { - str += decodeWscale(mFieldsValues.get(key).byteValue()) + " "; - } else { - str += mFieldsValues.get(key) + " "; - } - } - str += "}"; - return str; + return "TcpInfo{lost=" + mLost + ", retransmit=" + mRetransmits + ", received=" + mSegsIn + + ", sent=" + mSegsOut + "}"; } } diff --git a/src/com/android/networkstack/netlink/TcpSocketTracker.java b/src/com/android/networkstack/netlink/TcpSocketTracker.java index 78813bd..f660f81 100644 --- a/src/com/android/networkstack/netlink/TcpSocketTracker.java +++ b/src/com/android/networkstack/netlink/TcpSocketTracker.java @@ -340,16 +340,16 @@ public class TcpSocketTracker { return null; } - stat.sentCount = current.tcpInfo.getValue(TcpInfo.Field.SEGS_OUT).intValue(); - stat.receivedCount = current.tcpInfo.getValue(TcpInfo.Field.SEGS_IN).intValue(); - stat.lostCount = current.tcpInfo.getValue(TcpInfo.Field.LOST).intValue(); - stat.retransmitCount = current.tcpInfo.getValue(TcpInfo.Field.RETRANSMITS).intValue(); + stat.sentCount = current.tcpInfo.mSegsOut; + stat.receivedCount = current.tcpInfo.mSegsIn; + stat.lostCount = current.tcpInfo.mLost; + stat.retransmitCount = current.tcpInfo.mRetransmits; if (previous != null && previous.tcpInfo != null) { - stat.sentCount -= previous.tcpInfo.getValue(TcpInfo.Field.SEGS_OUT).intValue(); - stat.receivedCount -= previous.tcpInfo.getValue(TcpInfo.Field.SEGS_IN).intValue(); - stat.lostCount -= previous.tcpInfo.getValue(TcpInfo.Field.LOST).intValue(); - stat.retransmitCount -= previous.tcpInfo.getValue(TcpInfo.Field.RETRANSMITS).intValue(); + stat.sentCount -= previous.tcpInfo.mSegsOut; + stat.receivedCount -= previous.tcpInfo.mSegsIn; + stat.lostCount -= previous.tcpInfo.mLost; + stat.retransmitCount -= previous.tcpInfo.mRetransmits; } return stat; diff --git a/tests/integration/Android.bp b/tests/integration/Android.bp index b782efc..c4f057b 100644 --- a/tests/integration/Android.bp +++ b/tests/integration/Android.bp @@ -67,6 +67,7 @@ android_test { platform_apis: true, min_sdk_version: "29", test_suites: ["device-tests", "mts"], + test_config: "AndroidTest_Coverage.xml", defaults: ["NetworkStackIntegrationTestsJniDefaults"], static_libs: ["NetworkStackTestsLib", "NetworkStackIntegrationTestsLib"], compile_multilib: "both", diff --git a/tests/integration/AndroidTest_Coverage.xml b/tests/integration/AndroidTest_Coverage.xml new file mode 100644 index 0000000..e33fa87 --- /dev/null +++ b/tests/integration/AndroidTest_Coverage.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> +<configuration description="Runs coverage tests for NetworkStack"> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="NetworkStackCoverageTests.apk" /> + </target_preparer> + + <option name="test-tag" value="NetworkStackCoverageTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.server.networkstack.coverage" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java b/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java index e645a2c..e9384c8 100644 --- a/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java +++ b/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java @@ -28,6 +28,7 @@ import android.net.LinkAddress; import android.net.Network; import android.net.StaticIpConfiguration; import android.net.apf.ApfCapabilities; +import android.net.shared.ProvisioningConfiguration.ScanResultInfo; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -36,6 +37,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.nio.ByteBuffer; +import java.util.Collections; import java.util.function.Consumer; /** @@ -46,6 +49,17 @@ import java.util.function.Consumer; public class ProvisioningConfigurationTest { private ProvisioningConfiguration mConfig; + private ScanResultInfo makeScanResultInfo(final String ssid) { + final byte[] payload = new byte[] { + (byte) 0x00, (byte) 0x17, (byte) 0xF2, (byte) 0x06, (byte) 0x01, + (byte) 0x01, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x00, + }; + final ScanResultInfo.InformationElement ie = + new ScanResultInfo.InformationElement(0xdd /* vendor specific IE id */, + ByteBuffer.wrap(payload)); + return new ScanResultInfo(ssid, Collections.singletonList(ie)); + } + @Before public void setUp() { mConfig = new ProvisioningConfiguration(); @@ -67,8 +81,9 @@ public class ProvisioningConfigurationTest { mConfig.mNetwork = new Network(321); mConfig.mDisplayName = "test_config"; mConfig.mEnablePreconnection = false; + mConfig.mScanResultInfo = makeScanResultInfo("ssid"); // Any added field must be included in equals() to be tested properly - assertFieldCountEquals(13, ProvisioningConfiguration.class); + assertFieldCountEquals(14, ProvisioningConfiguration.class); } @Test @@ -101,6 +116,12 @@ public class ProvisioningConfigurationTest { } @Test + public void testParcelUnparcel_NullScanResultInfo() { + mConfig.mScanResultInfo = null; + doParcelUnparcelTest(); + } + + @Test public void testParcelUnparcel_WithPreDhcpConnection() { mConfig.mEnablePreconnection = true; doParcelUnparcelTest(); @@ -136,7 +157,9 @@ public class ProvisioningConfigurationTest { assertNotEqualsAfterChange(c -> c.mDisplayName = "other_test"); assertNotEqualsAfterChange(c -> c.mDisplayName = null); assertNotEqualsAfterChange(c -> c.mEnablePreconnection = true); - assertFieldCountEquals(13, ProvisioningConfiguration.class); + assertNotEqualsAfterChange(c -> c.mScanResultInfo = null); + assertNotEqualsAfterChange(c -> c.mScanResultInfo = makeScanResultInfo("another ssid")); + assertFieldCountEquals(14, ProvisioningConfiguration.class); } private void assertNotEqualsAfterChange(Consumer<ProvisioningConfiguration> mutator) { diff --git a/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java b/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java index f65de9c..ddab8c7 100644 --- a/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java +++ b/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java @@ -89,6 +89,8 @@ public class TcpInfoTest { "0000000000000000"; // sndBufLimited = 0 private static final byte[] TCP_INFO_BYTES = HexEncoding.decode(TCP_INFO_HEX.toCharArray(), false); + private static final TcpInfo TEST_TCPINFO = + new TcpInfo(0 /* retransmits */, 0 /* lost */, 2 /* segsOut */, 1 /* segsIn */); private static final String EXPANDED_TCP_INFO_HEX = TCP_INFO_HEX + "00000000" // tcpi_delivered @@ -100,40 +102,48 @@ public class TcpInfoTest { @Test public void testParseTcpInfo() { final ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES); - final Map<TcpInfo.Field, Number> expected = makeTestTcpInfoHash(); + // Length is less than required + final TcpInfo nullInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO); + assertEquals(nullInfo, null); + final TcpInfo parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1); + assertEquals(parsedInfo, TEST_TCPINFO); + + // Make a data that TcpInfo is not started from the begining of the buffer. + final ByteBuffer bufferWithHeader = + ByteBuffer.allocate(EXPANDED_TCP_INFO_BYTES.length + TCP_INFO_BYTES.length); + bufferWithHeader.put(EXPANDED_TCP_INFO_BYTES); + bufferWithHeader.put(TCP_INFO_BYTES); + final TcpInfo infoWithHeader = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1); + bufferWithHeader.position(EXPANDED_TCP_INFO_BYTES.length); + assertEquals(parsedInfo, TEST_TCPINFO); + } - assertEquals(parsedInfo, new TcpInfo(expected)); + @Test + public void testFieldOffset() { + assertEquals(TcpInfo.RETRANSMITS_OFFSET, 2); + assertEquals(TcpInfo.LOST_OFFSET, 32); + assertEquals(TcpInfo.SEGS_OUT_OFFSET, 136); + assertEquals(TcpInfo.SEGS_IN_OFFSET, 140); } @Test public void testParseTcpInfoExpanded() { final ByteBuffer buffer = ByteBuffer.wrap(EXPANDED_TCP_INFO_BYTES); - final Map<TcpInfo.Field, Number> expected = makeTestTcpInfoHash(); final TcpInfo parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1 + EXPANDED_TCP_INFO_LENGTH); - assertEquals(parsedInfo, new TcpInfo(expected)); + assertEquals(parsedInfo, TEST_TCPINFO); assertEquals(buffer.limit(), buffer.position()); // reset the index. buffer.position(0); final TcpInfo parsedInfoShorterLen = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1); - assertEquals(parsedInfoShorterLen, new TcpInfo(expected)); + assertEquals(parsedInfoShorterLen, TEST_TCPINFO); assertEquals(TCP_INFO_LENGTH_V1, buffer.position()); } @Test - public void testValidOffset() { - final ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES); - - final Map<TcpInfo.Field, Number> expected = makeShortTestTcpInfoHash(); - final TcpInfo parsedInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO); - - assertEquals(parsedInfo, new TcpInfo(expected)); - } - - @Test public void testTcpStateName() { assertEquals(TcpInfo.getTcpStateName(4), TCP_FIN_WAIT1); assertEquals(TcpInfo.getTcpStateName(1), TCP_ESTABLISHED); @@ -156,39 +166,14 @@ public class TcpInfoTest { @Test public void testMalformedTcpInfo() { final ByteBuffer buffer = ByteBuffer.wrap(MALFORMED_TCP_INFO_BYTES); - final Map<TcpInfo.Field, Number> expected = makeShortTestTcpInfoHash(); TcpInfo parsedInfo = TcpInfo.parse(buffer, SHORT_TEST_TCP_INFO); - assertEquals(parsedInfo, new TcpInfo(expected)); + assertEquals(parsedInfo, null); parsedInfo = TcpInfo.parse(buffer, TCP_INFO_LENGTH_V1); assertEquals(parsedInfo, null); } - @Test - public void testGetValue() { - ByteBuffer buffer = ByteBuffer.wrap(TCP_INFO_BYTES); - - final Map<TcpInfo.Field, Number> expected = makeShortTestTcpInfoHash(); - expected.put(TcpInfo.Field.MAX_PACING_RATE, 10_000L); - expected.put(TcpInfo.Field.FACKETS, 10); - - final TcpInfo expectedInfo = new TcpInfo(expected); - assertEquals((byte) 0x01, expectedInfo.getValue(TcpInfo.Field.STATE)); - assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.CASTATE)); - assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.RETRANSMITS)); - assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.PROBES)); - assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.BACKOFF)); - assertEquals((byte) 0x07, expectedInfo.getValue(TcpInfo.Field.OPTIONS)); - assertEquals((byte) 0x88, expectedInfo.getValue(TcpInfo.Field.WSCALE)); - assertEquals((byte) 0x00, expectedInfo.getValue(TcpInfo.Field.DELIVERY_RATE_APP_LIMITED)); - - assertEquals(10_000L, expectedInfo.getValue(TcpInfo.Field.MAX_PACING_RATE)); - assertEquals(10, expectedInfo.getValue(TcpInfo.Field.FACKETS)); - assertEquals(null, expectedInfo.getValue(TcpInfo.Field.RTT)); - - } - // Make a TcpInfo contains only first 8 bytes. private Map<TcpInfo.Field, Number> makeShortTestTcpInfoHash() { final Map<TcpInfo.Field, Number> info = new LinkedHashMap<>(); diff --git a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java index a21e7cf..6a09f12 100644 --- a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java +++ b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java @@ -61,7 +61,6 @@ import org.mockito.quality.Strictness; import java.io.FileDescriptor; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.HashMap; // TODO: Add more tests for missing coverage. @RunWith(AndroidJUnit4.class) @@ -174,6 +173,8 @@ public class TcpSocketTrackerTest { "0000000000000000"; // deliverRate = 0 private static final byte[] SOCK_DIAG_TCP_INET_BYTES = HexEncoding.decode(SOCK_DIAG_TCP_INET_HEX.toCharArray(), false); + private static final TcpInfo TEST_TCPINFO = + new TcpInfo(5 /* retransmits */, 0 /* lost */, 10 /* segsOut */, 0 /* segsIn */); private static final String TEST_RESPONSE_HEX = SOCK_DIAG_TCP_INET_HEX // struct nlmsghdr @@ -253,52 +254,8 @@ public class TcpSocketTrackerTest { buffer.position(SOCKDIAG_MSG_HEADER_SIZE); final TcpSocketTracker.SocketInfo parsed = tst.parseSockInfo(buffer, AF_INET, 276, 100L); - final HashMap<TcpInfo.Field, Number> expected = new HashMap<>(); - expected.put(TcpInfo.Field.STATE, (byte) 0x01); - expected.put(TcpInfo.Field.CASTATE, (byte) 0x00); - expected.put(TcpInfo.Field.RETRANSMITS, (byte) 0x05); - expected.put(TcpInfo.Field.PROBES, (byte) 0x00); - expected.put(TcpInfo.Field.BACKOFF, (byte) 0x00); - expected.put(TcpInfo.Field.OPTIONS, (byte) 0x07); - expected.put(TcpInfo.Field.WSCALE, (byte) 0x88); - expected.put(TcpInfo.Field.DELIVERY_RATE_APP_LIMITED, (byte) 0x00); - expected.put(TcpInfo.Field.RTO, 1806666); - expected.put(TcpInfo.Field.ATO, 0); - expected.put(TcpInfo.Field.SND_MSS, 1326); - expected.put(TcpInfo.Field.RCV_MSS, 536); - expected.put(TcpInfo.Field.UNACKED, 0); - expected.put(TcpInfo.Field.SACKED, 0); - expected.put(TcpInfo.Field.LOST, 0); - expected.put(TcpInfo.Field.RETRANS, 0); - expected.put(TcpInfo.Field.FACKETS, 0); - expected.put(TcpInfo.Field.LAST_DATA_SENT, 187); - expected.put(TcpInfo.Field.LAST_ACK_SENT, 0); - expected.put(TcpInfo.Field.LAST_DATA_RECV, 187); - expected.put(TcpInfo.Field.LAST_ACK_RECV, 187); - expected.put(TcpInfo.Field.PMTU, 1500); - expected.put(TcpInfo.Field.RCV_SSTHRESH, 87600); - expected.put(TcpInfo.Field.RTT, 601150); - expected.put(TcpInfo.Field.RTTVAR, 300575); - expected.put(TcpInfo.Field.SND_SSTHRESH, 1400); - expected.put(TcpInfo.Field.SND_CWND, 10); - expected.put(TcpInfo.Field.ADVMSS, 1448); - expected.put(TcpInfo.Field.REORDERING, 3); - expected.put(TcpInfo.Field.RCV_RTT, 0); - expected.put(TcpInfo.Field.RCV_SPACE, 87600); - expected.put(TcpInfo.Field.TOTAL_RETRANS, 0); - expected.put(TcpInfo.Field.PACING_RATE, 44115L); - expected.put(TcpInfo.Field.MAX_PACING_RATE, -1L); - expected.put(TcpInfo.Field.BYTES_ACKED, 1L); - expected.put(TcpInfo.Field.BYTES_RECEIVED, 0L); - expected.put(TcpInfo.Field.SEGS_OUT, 10); - expected.put(TcpInfo.Field.SEGS_IN, 0); - expected.put(TcpInfo.Field.NOTSENT_BYTES, 0); - expected.put(TcpInfo.Field.MIN_RTT, 601150); - expected.put(TcpInfo.Field.DATA_SEGS_IN, 0); - expected.put(TcpInfo.Field.DATA_SEGS_OUT, 0); - expected.put(TcpInfo.Field.DELIVERY_RATE, 0L); - - assertEquals(parsed.tcpInfo, new TcpInfo(expected)); + + assertEquals(parsed.tcpInfo, TEST_TCPINFO); assertEquals(parsed.fwmark, 789125); assertEquals(parsed.updateTime, 100); assertEquals(parsed.ipFamily, AF_INET); diff --git a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java index b3f7934..b376386 100644 --- a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java @@ -129,7 +129,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -228,6 +227,9 @@ public class NetworkMonitorTest { private static final NetworkCapabilities NO_INTERNET_CAPABILITIES = new NetworkCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + private static final int NOTIFY_NETWORK_TESTED_VALIDATION_RESULT_MASK = 0x3; + private static final int NOTIFY_NETWORK_TESTED_SUCCESSFUL_PROBES_MASK = 0xFFFC; + /** * Fakes DNS responses. * @@ -792,7 +794,7 @@ public class NetworkMonitorTest { makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); assertTrue(wrappedMonitor.isDataStall()); verify(mCallbacks).notifyDataStallSuspected(anyLong(), eq(DETECTION_METHOD_DNS_EVENTS), - argThat(getDataStallDnsBundleMatcher())); + bundleForDnsDataStall(DEFAULT_DNS_TIMEOUT_THRESHOLD)); } @Test @@ -805,7 +807,7 @@ public class NetworkMonitorTest { makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); assertTrue(wrappedMonitor.isDataStall()); verify(mCallbacks).notifyDataStallSuspected(anyLong(), eq(DETECTION_METHOD_DNS_EVENTS), - argThat(getDataStallDnsBundleMatcher())); + bundleForDnsDataStall(DEFAULT_DNS_TIMEOUT_THRESHOLD)); } @Test @@ -821,8 +823,10 @@ public class NetworkMonitorTest { makeDnsTimeoutEvent(wrappedMonitor, 3); assertTrue(wrappedMonitor.isDataStall()); + + // The expected timeout count is the previous 2 DNS timeouts + the most recent 3 timeouts verify(mCallbacks).notifyDataStallSuspected(anyLong(), eq(DETECTION_METHOD_DNS_EVENTS), - argThat(getDataStallDnsBundleMatcher())); + bundleForDnsDataStall(5)); // Set the value to larger than the default dns log size. setConsecutiveDnsTimeoutThreshold(51); @@ -833,8 +837,10 @@ public class NetworkMonitorTest { makeDnsTimeoutEvent(wrappedMonitor, 1); assertTrue(wrappedMonitor.isDataStall()); - verify(mCallbacks, times(2)).notifyDataStallSuspected(anyLong(), - eq(DETECTION_METHOD_DNS_EVENTS), argThat(getDataStallDnsBundleMatcher())); + + // The expected timeout count is the previous 50 DNS timeouts + the most recent timeout + verify(mCallbacks).notifyDataStallSuspected(anyLong(), eq(DETECTION_METHOD_DNS_EVENTS), + bundleForDnsDataStall(51)); } @Test @@ -860,7 +866,7 @@ public class NetworkMonitorTest { wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); assertTrue(wrappedMonitor.isDataStall()); verify(mCallbacks).notifyDataStallSuspected(anyLong(), eq(DETECTION_METHOD_DNS_EVENTS), - argThat(getDataStallDnsBundleMatcher())); + bundleForDnsDataStall(DEFAULT_DNS_TIMEOUT_THRESHOLD)); // Test dns events happened before valid dns time threshold. setValidDataStallDnsTimeThreshold(0); @@ -895,7 +901,7 @@ public class NetworkMonitorTest { HandlerUtilsKt.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS); assertTrue(wrappedMonitor.isDataStall()); verify(mCallbacks).notifyDataStallSuspected(anyLong(), eq(DETECTION_METHOD_TCP_METRICS), - argThat(getDataStallTcpBundleMatcher())); + bundleForTcpDataStall()); } @Test @@ -958,12 +964,13 @@ public class NetworkMonitorTest { setStatus(mHttpsConnection, 204); setStatus(mHttpConnection, 204); + final int expectedResult = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP + | NETWORK_VALIDATION_RESULT_VALID; resetCallbacks(); nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) - .notifyNetworkTestedWithExtras(eq(NETWORK_VALIDATION_PROBE_DNS - | NETWORK_VALIDATION_PROBE_HTTP | NETWORK_VALIDATION_RESULT_VALID), any(), - anyLong(), argThat(getNotifyNetworkTestedBundleMatcher())); + .notifyNetworkTestedWithExtras(eq(expectedResult), any(), anyLong(), + bundleForNotifyNetworkTested(expectedResult)); assertEquals(0, mRegisteredReceivers.size()); } @@ -972,6 +979,8 @@ public class NetworkMonitorTest { setStatus(mHttpsConnection, 204); setStatus(mHttpConnection, 204); + final int expectedResult = VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS; + // Verify dns query only get v6 address. mFakeDns.setAnswer("dns6.google", new String[]{"2001:db8::53"}, TYPE_AAAA); WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); @@ -979,9 +988,8 @@ public class NetworkMonitorTest { new InetAddress[0])); wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTestedWithExtras(eq(VALIDATION_RESULT_VALID - | NETWORK_VALIDATION_PROBE_PRIVDNS), eq(null), anyLong(), - argThat(getNotifyNetworkTestedBundleMatcher())); + .notifyNetworkTestedWithExtras(eq(expectedResult), eq(null), anyLong(), + bundleForNotifyNetworkTested(expectedResult)); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged( eq(VALIDATION_RESULT_PRIVDNS_VALID), eq(VALIDATION_RESULT_PRIVDNS_VALID)); @@ -991,9 +999,8 @@ public class NetworkMonitorTest { wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns4.google", new InetAddress[0])); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTestedWithExtras(eq(VALIDATION_RESULT_VALID - | NETWORK_VALIDATION_PROBE_PRIVDNS), eq(null), anyLong(), - argThat(getNotifyNetworkTestedBundleMatcher())); + .notifyNetworkTestedWithExtras(eq(expectedResult), eq(null), anyLong(), + bundleForNotifyNetworkTested(expectedResult)); // NetworkMonitor will check if the probes has changed or not, if the probes has not // changed, the callback won't be fired. verify(mCallbacks, never()).notifyProbeStatusChanged( @@ -1005,9 +1012,8 @@ public class NetworkMonitorTest { mFakeDns.setAnswer("dns.google", new String[]{"192.0.2.3"}, TYPE_A); wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTestedWithExtras(eq(VALIDATION_RESULT_VALID - | NETWORK_VALIDATION_PROBE_PRIVDNS), eq(null), anyLong(), - argThat(getNotifyNetworkTestedBundleMatcher())); + .notifyNetworkTestedWithExtras(eq(expectedResult), eq(null), anyLong(), + bundleForNotifyNetworkTested(expectedResult)); verify(mCallbacks, never()).notifyProbeStatusChanged( eq(VALIDATION_RESULT_PRIVDNS_VALID), eq(VALIDATION_RESULT_PRIVDNS_VALID)); } @@ -1023,7 +1029,8 @@ public class NetworkMonitorTest { wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTestedWithExtras( eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS), eq(null), - anyLong(), argThat(getNotifyNetworkTestedBundleMatcher())); + anyLong(), bundleForNotifyNetworkTested(NETWORK_VALIDATION_PROBE_DNS + | NETWORK_VALIDATION_PROBE_HTTPS)); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged( eq(VALIDATION_RESULT_PRIVDNS_VALID), eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS)); @@ -1038,7 +1045,8 @@ public class NetworkMonitorTest { verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) .notifyNetworkTestedWithExtras(eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS), eq(null), anyLong(), - argThat(getNotifyNetworkTestedBundleMatcher())); + bundleForNotifyNetworkTested(VALIDATION_RESULT_VALID + | NETWORK_VALIDATION_PROBE_PRIVDNS)); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged( eq(VALIDATION_RESULT_PRIVDNS_VALID), eq(VALIDATION_RESULT_PRIVDNS_VALID)); } @@ -1054,7 +1062,8 @@ public class NetworkMonitorTest { wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)).notifyNetworkTestedWithExtras( eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS), eq(null), - anyLong(), argThat(getNotifyNetworkTestedBundleMatcher())); + anyLong(), bundleForNotifyNetworkTested(NETWORK_VALIDATION_PROBE_DNS + | NETWORK_VALIDATION_PROBE_HTTPS)); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged( eq(VALIDATION_RESULT_PRIVDNS_VALID), eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS)); @@ -1067,7 +1076,8 @@ public class NetworkMonitorTest { verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) .notifyNetworkTestedWithExtras(eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS), eq(null), anyLong(), - argThat(getNotifyNetworkTestedBundleMatcher())); + bundleForNotifyNetworkTested(VALIDATION_RESULT_VALID + | NETWORK_VALIDATION_PROBE_PRIVDNS)); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged( eq(VALIDATION_RESULT_PRIVDNS_VALID), eq(VALIDATION_RESULT_PRIVDNS_VALID)); @@ -1081,7 +1091,8 @@ public class NetworkMonitorTest { verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)) .notifyNetworkTestedWithExtras(eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS), eq(null), anyLong(), - argThat(getNotifyNetworkTestedBundleMatcher())); + bundleForNotifyNetworkTested(NETWORK_VALIDATION_PROBE_DNS + | NETWORK_VALIDATION_PROBE_HTTPS)); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged( eq(VALIDATION_RESULT_PRIVDNS_VALID), eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS)); @@ -1094,7 +1105,8 @@ public class NetworkMonitorTest { new InetAddress[0])); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)).notifyNetworkTestedWithExtras( eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS), eq(null), - anyLong(), argThat(getNotifyNetworkTestedBundleMatcher())); + anyLong(), bundleForNotifyNetworkTested(NETWORK_VALIDATION_PROBE_DNS + | NETWORK_VALIDATION_PROBE_HTTPS)); // NetworkMonitor will check if the probes has changed or not, if the probes has not // changed, the callback won't be fired. verify(mCallbacks, never()).notifyProbeStatusChanged( @@ -1108,7 +1120,8 @@ public class NetworkMonitorTest { verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) .notifyNetworkTestedWithExtras( eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS), eq(null), - anyLong(), argThat(getNotifyNetworkTestedBundleMatcher())); + anyLong(), bundleForNotifyNetworkTested(VALIDATION_RESULT_VALID + | NETWORK_VALIDATION_PROBE_PRIVDNS)); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyProbeStatusChanged( eq(VALIDATION_RESULT_PRIVDNS_VALID), eq(VALIDATION_RESULT_PRIVDNS_VALID)); } @@ -1173,12 +1186,13 @@ public class NetworkMonitorTest { // Expect to send HTTP, HTTPS, FALLBACK probe and evaluation result notifications to CS. final NetworkMonitor nm = runNetworkTest(VALIDATION_RESULT_PARTIAL); + final int expectedResult = VALIDATION_RESULT_PARTIAL | NETWORK_VALIDATION_RESULT_VALID; resetCallbacks(); nm.setAcceptPartialConnectivity(); // Expect to update evaluation result notifications to CS. verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTestedWithExtras( - eq(VALIDATION_RESULT_PARTIAL | NETWORK_VALIDATION_RESULT_VALID), eq(null), - anyLong(), argThat(getNotifyNetworkTestedBundleMatcher())); + eq(expectedResult), eq(null), anyLong(), bundleForNotifyNetworkTested( + expectedResult)); } @Test @@ -1242,6 +1256,9 @@ public class NetworkMonitorTest { @Test public void testNotifyNetwork_WithforceReevaluation() throws Exception { + final int expectedResult = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK + | NETWORK_VALIDATION_RESULT_PARTIAL; + final NetworkMonitor nm = runValidatedNetworkTest(); // Verify forceReevalution will not reset the validation result but only probe result until // getting the validation result. @@ -1253,9 +1270,8 @@ public class NetworkMonitorTest { final ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class); // Expect to send HTTP, HTTPs, FALLBACK and evaluation results. verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)) - .notifyNetworkTestedWithExtras(eq(NETWORK_VALIDATION_PROBE_DNS - | NETWORK_VALIDATION_PROBE_FALLBACK | NETWORK_VALIDATION_RESULT_PARTIAL), - any(), anyLong(), argThat(getNotifyNetworkTestedBundleMatcher())); + .notifyNetworkTestedWithExtras(eq(expectedResult), any(), anyLong(), + bundleForNotifyNetworkTested(expectedResult)); } @Test @@ -1310,20 +1326,23 @@ public class NetworkMonitorTest { verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTestedWithExtras( eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS | NETWORK_VALIDATION_RESULT_PARTIAL), eq(null), anyLong(), - argThat(getNotifyNetworkTestedBundleMatcher())); + bundleForNotifyNetworkTested(NETWORK_VALIDATION_PROBE_DNS + | NETWORK_VALIDATION_PROBE_HTTPS | NETWORK_VALIDATION_RESULT_PARTIAL)); nm.getEvaluationState().reportEvaluationResult( NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL, null /* redirectUrl */); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTestedWithExtras( eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL), eq(null), - anyLong(), argThat(getNotifyNetworkTestedBundleMatcher())); + anyLong(), bundleForNotifyNetworkTested(VALIDATION_RESULT_VALID + | NETWORK_VALIDATION_RESULT_PARTIAL)); nm.getEvaluationState().reportEvaluationResult(VALIDATION_RESULT_INVALID, TEST_REDIRECT_URL); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTestedWithExtras( eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS), - eq(TEST_REDIRECT_URL), anyLong(), argThat(getNotifyNetworkTestedBundleMatcher())); + eq(TEST_REDIRECT_URL), anyLong(), bundleForNotifyNetworkTested( + NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS)); } private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) { @@ -1437,7 +1456,7 @@ public class NetworkMonitorTest { verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)) .notifyNetworkTestedWithExtras(eq(testResult), mNetworkTestedRedirectUrlCaptor.capture(), anyLong(), - argThat(getNotifyNetworkTestedBundleMatcher())); + bundleForNotifyNetworkTested(testResult)); } catch (RemoteException e) { fail("Unexpected exception: " + e); } @@ -1469,21 +1488,32 @@ public class NetworkMonitorTest { } } - private ArgumentMatcher<PersistableBundle> getNotifyNetworkTestedBundleMatcher() { - return bundle -> + private PersistableBundle bundleForNotifyNetworkTested(final int result) { + // result = KEY_NETWORK_PROBES_SUCCEEDED_BITMASK | KEY_NETWORK_VALIDATION_RESULT + // See NetworkMonitor.EvaluationState#getNetworkTestResult + final int validationResult = result & NOTIFY_NETWORK_TESTED_VALIDATION_RESULT_MASK; + final int probesSucceeded = result & NOTIFY_NETWORK_TESTED_SUCCESSFUL_PROBES_MASK; + + return argThat(bundle -> bundle.containsKey(KEY_NETWORK_PROBES_ATTEMPTED_BITMASK) && bundle.containsKey(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK) - && bundle.containsKey(KEY_NETWORK_VALIDATION_RESULT); + && (bundle.getInt(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK) & probesSucceeded) + == probesSucceeded + && bundle.containsKey(KEY_NETWORK_VALIDATION_RESULT) + && (bundle.getInt(KEY_NETWORK_VALIDATION_RESULT) & validationResult) + == validationResult); } - private ArgumentMatcher<PersistableBundle> getDataStallDnsBundleMatcher() { - return bundle -> bundle.containsKey(KEY_DNS_CONSECUTIVE_TIMEOUTS); + private PersistableBundle bundleForDnsDataStall(final int timeoutCount) { + return argThat(bundle -> + bundle.containsKey(KEY_DNS_CONSECUTIVE_TIMEOUTS) + && bundle.getInt(KEY_DNS_CONSECUTIVE_TIMEOUTS) == timeoutCount); } - private ArgumentMatcher<PersistableBundle> getDataStallTcpBundleMatcher() { - return bundle -> + private PersistableBundle bundleForTcpDataStall() { + return argThat(bundle -> bundle.containsKey(KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS) - && bundle.containsKey(KEY_TCP_PACKET_FAIL_RATE); + && bundle.containsKey(KEY_TCP_PACKET_FAIL_RATE)); } } |