diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/PacketReflector.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/PacketReflector.java index a4a2956d3a..124c2c3862 100644 --- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/PacketReflector.java +++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/PacketReflector.java @@ -16,6 +16,11 @@ package com.android.cts.net.hostside; +import static android.system.OsConstants.ICMP6_ECHO_REPLY; +import static android.system.OsConstants.ICMP6_ECHO_REQUEST; +import static android.system.OsConstants.ICMP_ECHO; +import static android.system.OsConstants.ICMP_ECHOREPLY; + import android.system.ErrnoException; import android.system.Os; import android.util.Log; @@ -47,8 +52,6 @@ public class PacketReflector extends Thread { private static final byte ICMP_ECHO = 8; private static final byte ICMP_ECHOREPLY = 0; - private static final byte ICMPV6_ECHO_REQUEST = (byte) 128; - private static final byte ICMPV6_ECHO_REPLY = (byte) 129; private static String TAG = "PacketReflector"; @@ -125,7 +128,7 @@ public class PacketReflector extends Thread { byte type = buf[hdrLen]; if (!(version == 4 && type == ICMP_ECHO) && - !(version == 6 && type == ICMPV6_ECHO_REQUEST)) { + !(version == 6 && type == (byte) ICMP6_ECHO_REQUEST)) { return; } @@ -145,10 +148,18 @@ public class PacketReflector extends Thread { return; } + byte replyType = buf[hdrLen]; + if ((type == ICMP_ECHO && replyType != ICMP_ECHOREPLY) + || (type == (byte) ICMP6_ECHO_REQUEST && replyType != (byte) ICMP6_ECHO_REPLY)) { + Log.i(TAG, "Received unexpected ICMP reply: original " + type + + ", reply " + replyType); + return; + } + // Compare the response we got with the original packet. // The only thing that should have changed are addresses, type and checksum. // Overwrite them with the received bytes and see if the packet is otherwise identical. - request[hdrLen] = buf[hdrLen]; // Type. + request[hdrLen] = buf[hdrLen]; // Type request[hdrLen + 2] = buf[hdrLen + 2]; // Checksum byte 1. request[hdrLen + 3] = buf[hdrLen + 3]; // Checksum byte 2. diff --git a/tests/cts/net/jni/NativeDnsJni.c b/tests/cts/net/jni/NativeDnsJni.c index 352c0c52cc..6d3d1c3250 100644 --- a/tests/cts/net/jni/NativeDnsJni.c +++ b/tests/cts/net/jni/NativeDnsJni.c @@ -120,8 +120,8 @@ JNIEXPORT jboolean Java_android_net_cts_DnsTest_testNativeDns(JNIEnv* env, jclas gai_strerror(res)); return JNI_FALSE; } - if (strstr(buf, "google.com") == NULL) { - ALOGD("getnameinfo(%s (GoogleDNS) ) didn't return google.com: %s", + if (strstr(buf, "google.com") == NULL && strstr(buf, "dns.google") == NULL) { + ALOGD("getnameinfo(%s (GoogleDNS) ) didn't return google.com or dns.google: %s", GoogleDNSIpV4Address, buf); return JNI_FALSE; } @@ -133,8 +133,9 @@ JNIEXPORT jboolean Java_android_net_cts_DnsTest_testNativeDns(JNIEnv* env, jclas res, gai_strerror(res)); return JNI_FALSE; } - if (strstr(buf, "google.com") == NULL) { - ALOGD("getnameinfo(%s) didn't return google.com: %s", GoogleDNSIpV6Address2, buf); + if (strstr(buf, "google.com") == NULL && strstr(buf, "dns.google") == NULL) { + ALOGD("getnameinfo(%s (GoogleDNS) ) didn't return google.com or dns.google: %s", + GoogleDNSIpV6Address2, buf); return JNI_FALSE; } diff --git a/tests/cts/net/jni/NativeMultinetworkJni.cpp b/tests/cts/net/jni/NativeMultinetworkJni.cpp index d1a92a47a1..a6b5e90b1d 100644 --- a/tests/cts/net/jni/NativeMultinetworkJni.cpp +++ b/tests/cts/net/jni/NativeMultinetworkJni.cpp @@ -245,13 +245,12 @@ JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runResNcancelCheck( net_handle_t handle = (net_handle_t) nethandle; int fd = android_res_nquery(handle, kGoogleName, ns_c_in, ns_t_a, 0); - int rcode = -1; - uint8_t buf[MAXPACKET] = {}; + errno = 0; android_res_cancel(fd); - EXPECT_EQ(env, -EBADF, android_res_nresult(fd, &rcode, buf, MAXPACKET), "res_cancel"); - - android_res_cancel(fd); - EXPECT_EQ(env, -EBADF, android_res_nresult(fd, &rcode, buf, MAXPACKET), "res_cancel"); + int err = errno; + EXPECT_EQ(env, 0, err, "res_cancel"); + // DO NOT call cancel or result with the same fd more than once, + // otherwise it will hit fdsan double-close fd. return 0; } @@ -288,10 +287,10 @@ JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runResNapiMalformedCheck fd = android_res_nsend(handle, largeBuf, sizeof(largeBuf), 0); EXPECT_EQ(env, -EMSGSIZE, fd, "res_nsend buffer larger than 8KB"); - // 1000 bytes filled with 0. This returns EMSGSIZE because FrameworkListener limits the size of - // commands to 1024 bytes. TODO: b/126307309 - fd = android_res_nsend(handle, largeBuf, 1000, 0); - EXPECT_EQ(env, -EMSGSIZE, fd, "res_nsend 1000 bytes filled with 0"); + // 5000 bytes filled with 0. This returns EMSGSIZE because FrameworkListener limits the size of + // commands to 4096 bytes. + fd = android_res_nsend(handle, largeBuf, 5000, 0); + EXPECT_EQ(env, -EMSGSIZE, fd, "res_nsend 5000 bytes filled with 0"); // 500 bytes filled with 0 fd = android_res_nsend(handle, largeBuf, 500, 0); @@ -299,16 +298,16 @@ JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runResNapiMalformedCheck EXPECT_EQ(env, 0, expectAnswersNotValid(env, fd, -EINVAL), "res_nsend 500 bytes filled with 0 check answers"); - // 1000 bytes filled with 0xFF - uint8_t ffBuf[1001] = {}; + // 5000 bytes filled with 0xFF + uint8_t ffBuf[5001] = {}; memset(ffBuf, 0xFF, sizeof(ffBuf)); - ffBuf[1000] = '\0'; + ffBuf[5000] = '\0'; fd = android_res_nsend(handle, ffBuf, sizeof(ffBuf), 0); - EXPECT_EQ(env, -EMSGSIZE, fd, "res_nsend 1000 bytes filled with 0xFF"); + EXPECT_EQ(env, -EMSGSIZE, fd, "res_nsend 5000 bytes filled with 0xFF"); // 500 bytes filled with 0xFF - ffBuf[501] = '\0'; - fd = android_res_nsend(handle, ffBuf, 500, 0); + ffBuf[500] = '\0'; + fd = android_res_nsend(handle, ffBuf, 501, 0); EXPECT_GE(env, fd, 0, "res_nsend 500 bytes filled with 0xFF"); EXPECT_EQ(env, 0, expectAnswersNotValid(env, fd, -EINVAL), "res_nsend 500 bytes filled with 0xFF check answers"); diff --git a/tests/cts/net/native/dns/NativeDnsAsyncTest.cpp b/tests/cts/net/native/dns/NativeDnsAsyncTest.cpp index 2fc9ff8fd8..e501475996 100644 --- a/tests/cts/net/native/dns/NativeDnsAsyncTest.cpp +++ b/tests/cts/net/native/dns/NativeDnsAsyncTest.cpp @@ -194,13 +194,12 @@ TEST (NativeDnsAsyncTest, Async_NXDOMAIN) { TEST (NativeDnsAsyncTest, Async_Cancel) { int fd = android_res_nquery( NETWORK_UNSPECIFIED, "www.google.com", ns_c_in, ns_t_a, 0); - int rcode = -1; - uint8_t buf[MAXPACKET] = {}; + errno = 0; android_res_cancel(fd); - android_res_cancel(fd); - - int res = android_res_nresult(fd, &rcode, buf, MAXPACKET); - EXPECT_EQ(-EBADF, res); + int err = errno; + EXPECT_EQ(err, 0); + // DO NOT call cancel or result with the same fd more than once, + // otherwise it will hit fdsan double-close fd. } TEST (NativeDnsAsyncTest, Async_Query_MALFORMED) { @@ -235,9 +234,9 @@ TEST (NativeDnsAsyncTest, Async_Send_MALFORMED) { NETWORK_UNSPECIFIED, largeBuf.data(), largeBuf.size(), 0); EXPECT_EQ(-EMSGSIZE, fd); - // 1000 bytes filled with 0. This returns EMSGSIZE because FrameworkListener limits the size of - // commands to 1024 bytes. TODO: fix this. - fd = android_res_nsend(NETWORK_UNSPECIFIED, largeBuf.data(), 1000, 0); + // 5000 bytes filled with 0. This returns EMSGSIZE because FrameworkListener limits the size of + // commands to 4096 bytes. + fd = android_res_nsend(NETWORK_UNSPECIFIED, largeBuf.data(), 5000, 0); EXPECT_EQ(-EMSGSIZE, fd); // 500 bytes filled with 0 @@ -245,8 +244,8 @@ TEST (NativeDnsAsyncTest, Async_Send_MALFORMED) { EXPECT_GE(fd, 0); expectAnswersNotValid(fd, -EINVAL); - // 1000 bytes filled with 0xFF - std::vector ffBuf(1000, 0xFF); + // 5000 bytes filled with 0xFF + std::vector ffBuf(5000, 0xFF); fd = android_res_nsend( NETWORK_UNSPECIFIED, ffBuf.data(), ffBuf.size(), 0); EXPECT_EQ(-EMSGSIZE, fd); diff --git a/tests/cts/net/src/android/net/cts/DnsResolverTest.java b/tests/cts/net/src/android/net/cts/DnsResolverTest.java index f6cc76852b..0ff6cd8bac 100644 --- a/tests/cts/net/src/android/net/cts/DnsResolverTest.java +++ b/tests/cts/net/src/android/net/cts/DnsResolverTest.java @@ -370,7 +370,6 @@ public class DnsResolverTest extends AndroidTestCase { @Override public void onQueryException(@NonNull ErrnoException e) { - if (mCancelSignal.isCanceled() && e.errno == EBADF) return; fail(mMsg + e.getMessage()); } } @@ -380,7 +379,7 @@ public class DnsResolverTest extends AndroidTestCase { final String msg = "Test cancel query " + dname; // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect // that the query is cancelled before it succeeds. If it is not cancelled before it - // succeeds, retry the until it is. + // succeeds, retry the test until it is. for (Network network : getTestableNetworks()) { boolean retry = false; int round = 0; @@ -426,7 +425,7 @@ public class DnsResolverTest extends AndroidTestCase { final String msg = "Test cancel raw Query " + byteArrayToHexString(blob); // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect // that the query is cancelled before it succeeds. If it is not cancelled before it - // succeeds, retry the until it is. + // succeeds, retry the test until it is. for (Network network : getTestableNetworks()) { boolean retry = false; int round = 0; @@ -470,4 +469,108 @@ public class DnsResolverTest extends AndroidTestCase { } } } + + /** + * A query callback for InetAddress that ensures that the query is + * cancelled and that onAnswer is never called. If the query succeeds + * before it is cancelled, needRetry will return true so the + * test can retry. + */ + class VerifyCancelInetAddressCallback extends DnsResolver.InetAddressAnswerCallback { + private static final int CANCEL_TIMEOUT = 3_000; + + private final CountDownLatch mLatch = new CountDownLatch(1); + private final String mMsg; + private final List mAnswers; + private final CancellationSignal mCancelSignal; + + VerifyCancelInetAddressCallback(@NonNull String msg, @Nullable CancellationSignal cancel) { + this.mMsg = msg; + this.mCancelSignal = cancel; + mAnswers = new ArrayList<>(); + } + + public boolean waitForAnswer() throws InterruptedException { + return mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + public boolean needRetry() throws InterruptedException { + return mLatch.await(CANCEL_TIMEOUT, TimeUnit.MILLISECONDS); + } + + public boolean isAnswerEmpty() { + return mAnswers.isEmpty(); + } + + @Override + public void onAnswer(@NonNull List answerList) { + if (mCancelSignal != null && mCancelSignal.isCanceled()) { + fail(mMsg + " should not have returned any answers"); + } + mAnswers.clear(); + mAnswers.addAll(answerList); + mLatch.countDown(); + } + + @Override + public void onParseException(@NonNull ParseException e) { + fail(mMsg + e.getMessage()); + } + + @Override + public void onQueryException(@NonNull ErrnoException e) { + fail(mMsg + e.getMessage()); + } + } + + public void testQueryForInetAddress() { + final String dname = "www.google.com"; + final String msg = "Test query for InetAddress " + dname; + for (Network network : getTestableNetworks()) { + final VerifyCancelInetAddressCallback callback = + new VerifyCancelInetAddressCallback(msg, null); + mDns.query(network, dname, FLAG_NO_CACHE_LOOKUP, + mExecutor, null, callback); + try { + assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", + callback.waitForAnswer()); + assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty()); + } catch (InterruptedException e) { + fail(msg + " Waiting for DNS lookup was interrupted"); + } + } + } + + public void testQueryCancelForInetAddress() throws ErrnoException { + final String dname = "www.google.com"; + final String msg = "Test cancel query for InetAddress " + dname; + // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect + // that the query is cancelled before it succeeds. If it is not cancelled before it + // succeeds, retry the test until it is. + for (Network network : getTestableNetworks()) { + boolean retry = false; + int round = 0; + do { + if (++round > CANCEL_RETRY_TIMES) { + fail(msg + " cancel failed " + CANCEL_RETRY_TIMES + " times"); + } + final CountDownLatch latch = new CountDownLatch(1); + final CancellationSignal cancelSignal = new CancellationSignal(); + final VerifyCancelInetAddressCallback callback = + new VerifyCancelInetAddressCallback(msg, cancelSignal); + mDns.query(network, dname, FLAG_EMPTY, mExecutor, cancelSignal, callback); + mExecutor.execute(() -> { + cancelSignal.cancel(); + latch.countDown(); + }); + try { + retry = callback.needRetry(); + assertTrue(msg + " query was not cancelled", + latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } catch (InterruptedException e) { + fail(msg + "Waiting for DNS lookup was interrupted"); + } + } while (retry); + } + } } diff --git a/tests/cts/net/src/android/net/cts/TrafficStatsTest.java b/tests/cts/net/src/android/net/cts/TrafficStatsTest.java index af096da089..503ba51727 100755 --- a/tests/cts/net/src/android/net/cts/TrafficStatsTest.java +++ b/tests/cts/net/src/android/net/cts/TrafficStatsTest.java @@ -16,15 +16,14 @@ package android.net.cts; +import android.content.pm.PackageManager; import android.net.NetworkStats; import android.net.TrafficStats; import android.os.Process; +import android.os.SystemProperties; import android.test.AndroidTestCase; import android.util.Log; -import java.io.BufferedReader; -import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -237,19 +236,37 @@ public class TrafficStatsTest extends AndroidTestCase { uidRxDeltaBytes <= tcpPacketToIpBytes(packetCount, byteCount) + tcpPacketToIpBytes(packetCount + maxExpectedExtraPackets + deltaRxOtherPackets, 0)); // Localhost traffic *does* count against total stats. - // Fudge by 132 packets of 1500 bytes not related to the test. + // Check the total stats increased after test data transfer over localhost has been made. assertTrue("ttxp: " + totalTxPacketsBefore + " -> " + totalTxPacketsAfter, - totalTxPacketsAfter >= totalTxPacketsBefore + uidTxDeltaPackets && - totalTxPacketsAfter <= totalTxPacketsBefore + uidTxDeltaPackets + 132); + totalTxPacketsAfter >= totalTxPacketsBefore + uidTxDeltaPackets); assertTrue("trxp: " + totalRxPacketsBefore + " -> " + totalRxPacketsAfter, - totalRxPacketsAfter >= totalRxPacketsBefore + uidRxDeltaPackets && - totalRxPacketsAfter <= totalRxPacketsBefore + uidRxDeltaPackets + 132); + totalRxPacketsAfter >= totalRxPacketsBefore + uidRxDeltaPackets); assertTrue("ttxb: " + totalTxBytesBefore + " -> " + totalTxBytesAfter, - totalTxBytesAfter >= totalTxBytesBefore + uidTxDeltaBytes && - totalTxBytesAfter <= totalTxBytesBefore + uidTxDeltaBytes + 132 * 1500); + totalTxBytesAfter >= totalTxBytesBefore + uidTxDeltaBytes); assertTrue("trxb: " + totalRxBytesBefore + " -> " + totalRxBytesAfter, - totalRxBytesAfter >= totalRxBytesBefore + uidRxDeltaBytes && - totalRxBytesAfter <= totalRxBytesBefore + uidRxDeltaBytes + 132 * 1500); + totalRxBytesAfter >= totalRxBytesBefore + uidRxDeltaBytes); + + // If the adb TCP port is opened, this test may be run by adb over network. + // Huge amount of data traffic might go through the network and accounted into total packets + // stats. The upper bound check would be meaningless. + // TODO: Consider precisely calculate the traffic accounted due to adb over network and + // subtract it when checking upper bound instead of skip checking. + final PackageManager pm = mContext.getPackageManager(); + if (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1 + || SystemProperties.getInt("service.adb.tcp.port", -1) > -1 + || !pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY)) { + Log.i(LOG_TAG, "adb is running over the network, skip the upper bound check"); + } else { + // Fudge by 132 packets of 1500 bytes not related to the test. + assertTrue("ttxp: " + totalTxPacketsBefore + " -> " + totalTxPacketsAfter, + totalTxPacketsAfter <= totalTxPacketsBefore + uidTxDeltaPackets + 132); + assertTrue("trxp: " + totalRxPacketsBefore + " -> " + totalRxPacketsAfter, + totalRxPacketsAfter <= totalRxPacketsBefore + uidRxDeltaPackets + 132); + assertTrue("ttxb: " + totalTxBytesBefore + " -> " + totalTxBytesAfter, + totalTxBytesAfter <= totalTxBytesBefore + uidTxDeltaBytes + 132 * 1500); + assertTrue("trxb: " + totalRxBytesBefore + " -> " + totalRxBytesAfter, + totalRxBytesAfter <= totalRxBytesBefore + uidRxDeltaBytes + 132 * 1500); + } // Localhost traffic should *not* count against mobile stats, // There might be some other traffic, but nowhere near 1MB. @@ -265,6 +282,5 @@ public class TrafficStatsTest extends AndroidTestCase { assertTrue("mrxb: " + mobileRxBytesBefore + " -> " + mobileRxBytesAfter, mobileRxBytesAfter >= mobileRxBytesBefore && mobileRxBytesAfter <= mobileRxBytesBefore + 200000); - } } diff --git a/tests/cts/net/src/android/net/ipv6/cts/PingTest.java b/tests/cts/net/src/android/net/ipv6/cts/PingTest.java index c23ad30f05..146fd83978 100644 --- a/tests/cts/net/src/android/net/ipv6/cts/PingTest.java +++ b/tests/cts/net/src/android/net/ipv6/cts/PingTest.java @@ -61,7 +61,7 @@ public class PingTest extends AndroidTestCase { /** The beginning of an ICMPv6 echo request: type, code, and uninitialized checksum. */ private static final byte[] PING_HEADER = new byte[] { - (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00 + (byte) ICMP6_ECHO_REQUEST, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; /** @@ -135,7 +135,7 @@ public class PingTest extends AndroidTestCase { byte[] response = new byte[bytesRead]; responseBuffer.flip(); responseBuffer.get(response, 0, bytesRead); - assertEquals((byte) 0x81, response[0]); + assertEquals((byte) ICMP6_ECHO_REPLY, response[0]); // Find out what ICMP ID was used in the packet that was sent. int id = ((InetSocketAddress) Os.getsockname(s)).getPort(); diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java index 93795b2bf0..1d666827fa 100644 --- a/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java +++ b/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java @@ -881,6 +881,30 @@ public class WifiManagerTest extends AndroidTestCase { } } + /** + * Verify that the {@link android.Manifest.permission#NETWORK_CARRIER_PROVISIONING} permission + * is held by at most one application. + */ + public void testNetworkCarrierProvisioningPermission() { + final PackageManager pm = getContext().getPackageManager(); + + final List holding = pm.getPackagesHoldingPermissions(new String[] { + android.Manifest.permission.NETWORK_CARRIER_PROVISIONING + }, PackageManager.MATCH_UNINSTALLED_PACKAGES); + + List uniquePackageNames = holding + .stream() + .map(pi -> pi.packageName) + .distinct() + .collect(Collectors.toList()); + + if (uniquePackageNames.size() > 1) { + fail("The NETWORK_CARRIER_PROVISIONING permission must not be held by more than one " + + "application, but is held by " + uniquePackageNames.size() + " applications: " + + String.join(", ", uniquePackageNames)); + } + } + /** * Verify that the {@link android.Manifest.permission#WIFI_UPDATE_USABILITY_STATS_SCORE} * permission is held by at most one application.