Merge changes from topic "statsaccess"
* changes:
Check MAINLINE_NETWORK_STACK as well to make GTS can access proper stats
Move checkAnyPermissionOf to PermissionUtils
diff --git a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
index e4949d3..422f4d5 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
@@ -34,13 +34,11 @@
import android.net.http.HttpException;
import android.net.http.InlineExecutionProhibitedException;
import android.net.http.UploadDataProvider;
-import android.net.http.UploadDataSink;
import android.net.http.UrlRequest;
import android.net.http.UrlRequest.Status;
import android.net.http.UrlResponseInfo;
import android.net.http.cts.util.HttpCtsTestServer;
import android.net.http.cts.util.TestStatusListener;
-import android.net.http.cts.util.TestUploadDataProvider;
import android.net.http.cts.util.TestUrlRequestCallback;
import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep;
import android.net.http.cts.util.UploadDataProviders;
@@ -140,14 +138,11 @@
}
@Test
- public void testUrlRequestPost_EchoRequestBody() throws Exception {
+ public void testUrlRequestPost_EchoRequestBody() {
String testData = "test";
UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getEchoBodyUrl());
- TestUploadDataProvider dataProvider =
- new TestUploadDataProvider(
- TestUploadDataProvider.SuccessCallbackMode.SYNC, mCallback.getExecutor());
- dataProvider.addRead(testData.getBytes());
+ UploadDataProvider dataProvider = UploadDataProviders.create(testData);
builder.setUploadDataProvider(dataProvider, mCallback.getExecutor());
builder.addHeader("Content-Type", "text/html");
builder.build().start();
@@ -155,7 +150,6 @@
assertOKStatusCode(mCallback.mResponseInfo);
assertEquals(testData, mCallback.mResponseAsString);
- dataProvider.assertClosed();
}
@Test
@@ -170,7 +164,7 @@
callback.setAllowDirectExecutor(true);
UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
mTestServer.getEchoBodyUrl(), DIRECT_EXECUTOR, callback);
- UploadDataProvider dataProvider = InMemoryUploadDataProvider.fromUtf8String("test");
+ UploadDataProvider dataProvider = UploadDataProviders.create("test");
builder.setUploadDataProvider(dataProvider, DIRECT_EXECUTOR);
builder.addHeader("Content-Type", "text/plain;charset=UTF-8");
builder.setDirectExecutorAllowed(true);
@@ -193,7 +187,7 @@
UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
mTestServer.getEchoBodyUrl(), Executors.newSingleThreadExecutor(), callback);
- UploadDataProvider dataProvider = InMemoryUploadDataProvider.fromUtf8String("test");
+ UploadDataProvider dataProvider = UploadDataProviders.create("test");
builder.setUploadDataProvider(dataProvider, DIRECT_EXECUTOR)
.addHeader("Content-Type", "text/plain;charset=UTF-8")
@@ -213,7 +207,7 @@
UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
mTestServer.getEchoBodyUrl(), DIRECT_EXECUTOR, callback);
- UploadDataProvider dataProvider = InMemoryUploadDataProvider.fromUtf8String("test");
+ UploadDataProvider dataProvider = UploadDataProviders.create("test");
builder.setUploadDataProvider(dataProvider, Executors.newSingleThreadExecutor())
.addHeader("Content-Type", "text/plain;charset=UTF-8")
@@ -415,39 +409,4 @@
throw new UnsupportedOperationException();
}
}
-
- private static class InMemoryUploadDataProvider extends UploadDataProvider {
- private final byte[] mBody;
- private int mNextChunkStartIndex = 0;
-
- private InMemoryUploadDataProvider(byte[] body) {
- this.mBody = body;
- }
-
- static InMemoryUploadDataProvider fromUtf8String(String body) {
- return new InMemoryUploadDataProvider(body.getBytes(StandardCharsets.UTF_8));
- }
-
- @Override
- public long getLength() {
- return mBody.length;
- }
-
- @Override
- public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) {
- if (mNextChunkStartIndex >= getLength()) {
- throw new IllegalStateException("Body of known length is exhausted");
- }
- int nextChunkSize =
- Math.min(mBody.length - mNextChunkStartIndex, byteBuffer.remaining());
- byteBuffer.put(mBody, mNextChunkStartIndex, nextChunkSize);
- mNextChunkStartIndex += nextChunkSize;
- uploadDataSink.onReadSucceeded(false);
- }
-
- @Override
- public void rewind(UploadDataSink uploadDataSink) {
- mNextChunkStartIndex = 0;
- }
- }
}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/TestUploadDataProvider.java b/Cronet/tests/cts/src/android/net/http/cts/util/TestUploadDataProvider.java
deleted file mode 100644
index d047828..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/util/TestUploadDataProvider.java
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright (C) 2022 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.http.cts.util;
-
-import android.net.http.UploadDataProvider;
-import android.net.http.UploadDataSink;
-import android.os.ConditionVariable;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ClosedChannelException;
-import java.util.ArrayList;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/** An UploadDataProvider implementation used in tests. */
-public class TestUploadDataProvider extends UploadDataProvider {
- // Indicates whether all success callbacks are synchronous or asynchronous.
- // Doesn't apply to errors.
- public enum SuccessCallbackMode {
- SYNC,
- ASYNC
- }
-
- // Indicates whether failures should throw exceptions, invoke callbacks
- // synchronously, or invoke callback asynchronously.
- public enum FailMode {
- NONE,
- THROWN,
- CALLBACK_SYNC,
- CALLBACK_ASYNC
- }
-
- private final ArrayList<byte[]> mReads = new ArrayList<byte[]>();
- private final SuccessCallbackMode mSuccessCallbackMode;
- private final Executor mExecutor;
-
- private boolean mChunked;
-
- // Index of read to fail on.
- private int mReadFailIndex = -1;
- // Indicates how to fail on a read.
- private FailMode mReadFailMode = FailMode.NONE;
-
- private FailMode mRewindFailMode = FailMode.NONE;
-
- private FailMode mLengthFailMode = FailMode.NONE;
-
- private int mNumReadCalls;
- private int mNumRewindCalls;
-
- private int mNextRead;
- private boolean mStarted;
- private boolean mReadPending;
- private boolean mRewindPending;
- // Used to ensure there are no read/rewind requests after a failure.
- private boolean mFailed;
-
- private final AtomicBoolean mClosed = new AtomicBoolean(false);
- private final ConditionVariable mAwaitingClose = new ConditionVariable(false);
-
- public TestUploadDataProvider(
- SuccessCallbackMode successCallbackMode, final Executor executor) {
- mSuccessCallbackMode = successCallbackMode;
- mExecutor = executor;
- }
-
- // Adds the result to be returned by a successful read request. The
- // returned bytes must all fit within the read buffer provided by Cronet.
- // After a rewind, if there is one, all reads will be repeated.
- public void addRead(byte[] read) {
- if (mStarted) {
- throw new IllegalStateException("Adding bytes after read");
- }
- mReads.add(read);
- }
-
- public void setReadFailure(int readFailIndex, FailMode readFailMode) {
- mReadFailIndex = readFailIndex;
- mReadFailMode = readFailMode;
- }
-
- public void setLengthFailure() {
- mLengthFailMode = FailMode.THROWN;
- }
-
- public void setRewindFailure(FailMode rewindFailMode) {
- mRewindFailMode = rewindFailMode;
- }
-
- public void setChunked(boolean chunked) {
- mChunked = chunked;
- }
-
- public int getNumReadCalls() {
- return mNumReadCalls;
- }
-
- public int getNumRewindCalls() {
- return mNumRewindCalls;
- }
-
- /** Returns the cumulative length of all data added by calls to addRead. */
- @Override
- public long getLength() throws IOException {
- if (mClosed.get()) {
- throw new ClosedChannelException();
- }
- if (mLengthFailMode == FailMode.THROWN) {
- throw new IllegalStateException("Sync length failure");
- }
- return getUploadedLength();
- }
-
- public long getUploadedLength() {
- if (mChunked) {
- return -1;
- }
- long length = 0;
- for (byte[] read : mReads) {
- length += read.length;
- }
- return length;
- }
-
- @Override
- public void read(final UploadDataSink uploadDataSink, final ByteBuffer byteBuffer)
- throws IOException {
- int currentReadCall = mNumReadCalls;
- ++mNumReadCalls;
- if (mClosed.get()) {
- throw new ClosedChannelException();
- }
- assertIdle();
-
- if (maybeFailRead(currentReadCall, uploadDataSink)) {
- mFailed = true;
- return;
- }
-
- mReadPending = true;
- mStarted = true;
-
- final boolean finalChunk = (mChunked && mNextRead == mReads.size() - 1);
- if (mNextRead < mReads.size()) {
- if ((byteBuffer.limit() - byteBuffer.position()) < mReads.get(mNextRead).length) {
- throw new IllegalStateException("Read buffer smaller than expected.");
- }
- byteBuffer.put(mReads.get(mNextRead));
- ++mNextRead;
- } else {
- throw new IllegalStateException("Too many reads: " + mNextRead);
- }
-
- Runnable completeRunnable =
- new Runnable() {
- @Override
- public void run() {
- mReadPending = false;
- uploadDataSink.onReadSucceeded(finalChunk);
- }
- };
- if (mSuccessCallbackMode == SuccessCallbackMode.SYNC) {
- completeRunnable.run();
- } else {
- mExecutor.execute(completeRunnable);
- }
- }
-
- @Override
- public void rewind(final UploadDataSink uploadDataSink) throws IOException {
- ++mNumRewindCalls;
- if (mClosed.get()) {
- throw new ClosedChannelException();
- }
- assertIdle();
-
- if (maybeFailRewind(uploadDataSink)) {
- mFailed = true;
- return;
- }
-
- if (mNextRead == 0) {
- // Should never try and rewind when rewinding does nothing.
- throw new IllegalStateException("Unexpected rewind when already at beginning");
- }
-
- mRewindPending = true;
- mNextRead = 0;
-
- Runnable completeRunnable =
- new Runnable() {
- @Override
- public void run() {
- mRewindPending = false;
- uploadDataSink.onRewindSucceeded();
- }
- };
- if (mSuccessCallbackMode == SuccessCallbackMode.SYNC) {
- completeRunnable.run();
- } else {
- mExecutor.execute(completeRunnable);
- }
- }
-
- private void assertIdle() {
- if (mReadPending) {
- throw new IllegalStateException("Unexpected operation during read");
- }
- if (mRewindPending) {
- throw new IllegalStateException("Unexpected operation during rewind");
- }
- if (mFailed) {
- throw new IllegalStateException("Unexpected operation after failure");
- }
- }
-
- private boolean maybeFailRead(int readIndex, final UploadDataSink uploadDataSink) {
- if (readIndex != mReadFailIndex) return false;
-
- switch (mReadFailMode) {
- case THROWN:
- throw new IllegalStateException("Thrown read failure");
- case CALLBACK_SYNC:
- uploadDataSink.onReadError(new IllegalStateException("Sync read failure"));
- return true;
- case CALLBACK_ASYNC:
- Runnable errorRunnable =
- new Runnable() {
- @Override
- public void run() {
- uploadDataSink.onReadError(
- new IllegalStateException("Async read failure"));
- }
- };
- mExecutor.execute(errorRunnable);
- return true;
- default:
- return false;
- }
- }
-
- private boolean maybeFailRewind(final UploadDataSink uploadDataSink) {
- switch (mRewindFailMode) {
- case THROWN:
- throw new IllegalStateException("Thrown rewind failure");
- case CALLBACK_SYNC:
- uploadDataSink.onRewindError(new IllegalStateException("Sync rewind failure"));
- return true;
- case CALLBACK_ASYNC:
- Runnable errorRunnable =
- new Runnable() {
- @Override
- public void run() {
- uploadDataSink.onRewindError(
- new IllegalStateException("Async rewind failure"));
- }
- };
- mExecutor.execute(errorRunnable);
- return true;
- default:
- return false;
- }
- }
-
- @Override
- public void close() throws IOException {
- if (!mClosed.compareAndSet(false, true)) {
- throw new AssertionError("Closed twice");
- }
- mAwaitingClose.open();
- }
-
- public void assertClosed() {
- mAwaitingClose.block(5000);
- if (!mClosed.get()) {
- throw new AssertionError("Was not closed");
- }
- }
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java b/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java
index 889f8f2..3b90fa0 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java
@@ -25,6 +25,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
+import java.nio.charset.StandardCharsets;
/**
* Provides implementations of {@link UploadDataProvider} for common use cases. Corresponds to
@@ -91,6 +92,16 @@
return create(data, 0, data.length);
}
+ /**
+ * Uploads the UTF-8 representation of {@code data}
+ *
+ * @param data String containing data to upload
+ * @return A new UploadDataProvider for the given data
+ */
+ public static UploadDataProvider create(String data) {
+ return create(data.getBytes(StandardCharsets.UTF_8));
+ }
+
private interface FileChannelProvider {
FileChannel getChannel() throws IOException;
}
diff --git a/Cronet/tests/mts/Android.bp b/Cronet/tests/mts/Android.bp
index adbc384..8fec6f3 100644
--- a/Cronet/tests/mts/Android.bp
+++ b/Cronet/tests/mts/Android.bp
@@ -48,8 +48,12 @@
libs: [
"android.test.base",
// Needed for direct access to tethering's hidden apis and to avoid `symbol not found`
- // errors on some builds.
+ // errors on some builds.
"framework-tethering.impl",
+ // android.net.Network apis
+ "framework-connectivity",
+ // android.net.TrafficStats apis
+ "framework-connectivity-t",
],
lint: { test: true }
}
diff --git a/Cronet/tests/mts/jarjar_excludes.txt b/Cronet/tests/mts/jarjar_excludes.txt
index d1d062a..cf3a017 100644
--- a/Cronet/tests/mts/jarjar_excludes.txt
+++ b/Cronet/tests/mts/jarjar_excludes.txt
@@ -8,4 +8,6 @@
# Do not jarjar the tests and its utils as they also do JNI with cronet_tests.so
org\.chromium\.net\..*Test.*(\$.+)?
org\.chromium\.net\.NativeTestServer(\$.+)?
-org\.chromium\.net\.MockUrlRequestJobFactory(\$.+)?
\ No newline at end of file
+org\.chromium\.net\.MockUrlRequestJobFactory(\$.+)?
+org\.chromium\.net\.QuicTestServer(\$.+)?
+org\.chromium\.net\.MockCertVerifier(\$.+)?
\ No newline at end of file
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 70c5f85..d2f6d6a 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -75,9 +75,6 @@
"name": "connectivity_native_test"
},
{
- "name": "CtsNetHttpTestCases"
- },
- {
"name": "libclat_test"
},
{
@@ -97,6 +94,16 @@
},
{
"name": "FrameworksNetIntegrationTests"
+ },
+ // Runs both NetHttpTests and CtsNetHttpTestCases
+ {
+ "name": "NetHttpCoverageTests",
+ "options": [
+ {
+ // These sometimes take longer than 1 min which is the presubmit timeout
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
}
],
"postsubmit": [
@@ -113,11 +120,6 @@
},
{
"name": "FrameworksNetDeflakeTest"
- },
- // Run in postsubmit to confirm test passes continously since this is a new test setup
- // TODO: move tests to presubmit
- {
- "name": "NetHttpTests"
}
],
"mainline-presubmit": [
@@ -215,7 +217,13 @@
"name": "libnetworkstats_test[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
},
{
- "name": "NetHttpCoverageTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
+ "name": "NetHttpCoverageTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+ "options": [
+ {
+ // These sometimes take longer than 1 min which is the presubmit timeout
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
}
],
"mainline-postsubmit": [
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 8810a8c..83ca2b7 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -250,6 +250,9 @@
// e.g. *classpath_fragments.
"com.android.tethering",
],
+ native_shared_libs: [
+ "libnetd_updatable",
+ ],
}
java_library_static {
diff --git a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
index 69eb58f..007bf23 100644
--- a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
+++ b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
@@ -215,6 +215,13 @@
}
}
+ protected void stopEthernetTethering(final MyTetheringEventCallback callback) {
+ runAsShell(TETHER_PRIVILEGED, () -> {
+ mTm.stopTethering(TETHERING_ETHERNET);
+ maybeUnregisterTetheringEventCallback(callback);
+ });
+ }
+
protected void cleanUp() throws Exception {
setPreferTestNetworks(false);
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 12ac454..55854e2 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -364,7 +364,7 @@
// Enable Ethernet tethering and check that it starts.
tetheringEventCallback = enableEthernetTethering(iface, null /* any upstream */);
} finally {
- maybeUnregisterTetheringEventCallback(tetheringEventCallback);
+ stopEthernetTethering(tetheringEventCallback);
}
// There is nothing more we can do on a physical interface without connecting an actual
// client, which is not possible in this test.
diff --git a/Tethering/tests/mts/src/android/tethering/mts/MtsEthernetTetheringTest.java b/Tethering/tests/mts/src/android/tethering/mts/MtsEthernetTetheringTest.java
index cb57d13..c2bc812 100644
--- a/Tethering/tests/mts/src/android/tethering/mts/MtsEthernetTetheringTest.java
+++ b/Tethering/tests/mts/src/android/tethering/mts/MtsEthernetTetheringTest.java
@@ -80,8 +80,8 @@
// Per RX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
private static final int RX_UDP_PACKET_SIZE = 30;
private static final int RX_UDP_PACKET_COUNT = 456;
- // Per TX UDP packet size: ethhdr (14) + iphdr (20) + udphdr (8) + payload (2) = 44 bytes.
- private static final int TX_UDP_PACKET_SIZE = 44;
+ // Per TX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
+ private static final int TX_UDP_PACKET_SIZE = 30;
private static final int TX_UDP_PACKET_COUNT = 123;
private static final String DUMPSYS_TETHERING_RAWMAP_ARG = "bpfRawMap";
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index a8612df..56ace19 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -232,13 +232,13 @@
// This would require a much newer kernel with newer ebpf accessors.
// (This is also blindly assuming 12 bytes of tcp timestamp option in tcp header)
uint64_t packets = 1;
- uint64_t bytes = skb->len;
- if (bytes > v->pmtu) {
- const int tcp_overhead = sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + 12;
- const int mss = v->pmtu - tcp_overhead;
- const uint64_t payload = bytes - tcp_overhead;
+ uint64_t L3_bytes = skb->len - l2_header_size;
+ if (L3_bytes > v->pmtu) {
+ const int tcp6_overhead = sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + 12;
+ const int mss = v->pmtu - tcp6_overhead;
+ const uint64_t payload = L3_bytes - tcp6_overhead;
packets = (payload + mss - 1) / mss;
- bytes = tcp_overhead * packets + payload;
+ L3_bytes = tcp6_overhead * packets + payload;
}
// Are we past the limit? If so, then abort...
@@ -247,7 +247,7 @@
// a packet we let the core stack deal with things.
// (The core stack needs to handle limits correctly anyway,
// since we don't offload all traffic in both directions)
- if (stat_v->rxBytes + stat_v->txBytes + bytes > *limit_v) TC_PUNT(LIMIT_REACHED);
+ if (stat_v->rxBytes + stat_v->txBytes + L3_bytes > *limit_v) TC_PUNT(LIMIT_REACHED);
if (!is_ethernet) {
// Try to inject an ethernet header, and simply return if we fail.
@@ -287,7 +287,7 @@
bpf_csum_update(skb, 0xFFFF - ntohs(old_hl) + ntohs(new_hl));
__sync_fetch_and_add(downstream ? &stat_v->rxPackets : &stat_v->txPackets, packets);
- __sync_fetch_and_add(downstream ? &stat_v->rxBytes : &stat_v->txBytes, bytes);
+ __sync_fetch_and_add(downstream ? &stat_v->rxBytes : &stat_v->txBytes, L3_bytes);
// Overwrite any mac header with the new one
// For a rawip tx interface it will simply be a bunch of zeroes and later stripped.
@@ -449,13 +449,13 @@
// This would require a much newer kernel with newer ebpf accessors.
// (This is also blindly assuming 12 bytes of tcp timestamp option in tcp header)
uint64_t packets = 1;
- uint64_t bytes = skb->len;
- if (bytes > v->pmtu) {
- const int tcp_overhead = sizeof(struct iphdr) + sizeof(struct tcphdr) + 12;
- const int mss = v->pmtu - tcp_overhead;
- const uint64_t payload = bytes - tcp_overhead;
+ uint64_t L3_bytes = skb->len - l2_header_size;
+ if (L3_bytes > v->pmtu) {
+ const int tcp4_overhead = sizeof(struct iphdr) + sizeof(struct tcphdr) + 12;
+ const int mss = v->pmtu - tcp4_overhead;
+ const uint64_t payload = L3_bytes - tcp4_overhead;
packets = (payload + mss - 1) / mss;
- bytes = tcp_overhead * packets + payload;
+ L3_bytes = tcp4_overhead * packets + payload;
}
// Are we past the limit? If so, then abort...
@@ -464,7 +464,7 @@
// a packet we let the core stack deal with things.
// (The core stack needs to handle limits correctly anyway,
// since we don't offload all traffic in both directions)
- if (stat_v->rxBytes + stat_v->txBytes + bytes > *limit_v) TC_PUNT(LIMIT_REACHED);
+ if (stat_v->rxBytes + stat_v->txBytes + L3_bytes > *limit_v) TC_PUNT(LIMIT_REACHED);
if (!is_ethernet) {
// Try to inject an ethernet header, and simply return if we fail.
@@ -540,7 +540,7 @@
if (updatetime) v->last_used = bpf_ktime_get_boot_ns();
__sync_fetch_and_add(downstream ? &stat_v->rxPackets : &stat_v->txPackets, packets);
- __sync_fetch_and_add(downstream ? &stat_v->rxBytes : &stat_v->txBytes, bytes);
+ __sync_fetch_and_add(downstream ? &stat_v->rxBytes : &stat_v->txBytes, L3_bytes);
// Redirect to forwarded interface.
//
diff --git a/framework-t/Android.bp b/framework-t/Android.bp
index f8d7e4c..ffa2857 100644
--- a/framework-t/Android.bp
+++ b/framework-t/Android.bp
@@ -140,7 +140,7 @@
"//packages/modules/Connectivity/apex",
"//packages/modules/Connectivity/service", // For R8 only
"//packages/modules/Connectivity/service-t",
- "//packages/modules/Connectivity/nearby/service",
+ "//packages/modules/Connectivity/nearby:__subpackages__",
"//frameworks/base",
// Tests using hidden APIs
@@ -157,7 +157,6 @@
"//frameworks/opt/telephony/tests/telephonytests",
"//packages/modules/CaptivePortalLogin/tests",
"//packages/modules/Connectivity/Tethering/tests:__subpackages__",
- "//packages/modules/Connectivity/nearby:__subpackages__",
"//packages/modules/Connectivity/tests:__subpackages__",
"//packages/modules/IPsec/tests/iketests",
"//packages/modules/NetworkStack/tests:__subpackages__",
diff --git a/framework-t/src/android/net/NetworkTemplate.java b/framework-t/src/android/net/NetworkTemplate.java
index b3c70cf..33bd884 100644
--- a/framework-t/src/android/net/NetworkTemplate.java
+++ b/framework-t/src/android/net/NetworkTemplate.java
@@ -61,6 +61,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -183,6 +184,23 @@
}
}
+ private static Set<String> setOf(@Nullable final String item) {
+ if (item == null) {
+ // Set.of will throw if item is null
+ final Set<String> set = new HashSet<>();
+ set.add(null);
+ return Collections.unmodifiableSet(set);
+ } else {
+ return Set.of(item);
+ }
+ }
+
+ private static void throwAtLeastU() {
+ if (SdkLevel.isAtLeastU()) {
+ throw new UnsupportedOperationException("Method not supported on Android U or above");
+ }
+ }
+
/**
* Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
* the given IMSI.
@@ -195,7 +213,7 @@
publicAlternatives = "Use {@code Builder} instead.")
public static NetworkTemplate buildTemplateMobileAll(@NonNull String subscriberId) {
return new NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES)
- .setSubscriberIds(Set.of(subscriberId)).build();
+ .setSubscriberIds(setOf(subscriberId)).build();
}
/**
@@ -259,10 +277,9 @@
// TODO(b/270089918): Remove this method. This can only be done after there are no more callers,
// including in OEM code which can access this by linking against the framework.
public static NetworkTemplate buildTemplateBluetooth() {
- if (SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException(
- "buildTemplateBluetooth is not supported on Android U devices or above");
- }
+ // TODO : this is part of hidden-o txt, does that mean it should be annotated with
+ // @UnsupportedAppUsage(maxTargetSdk = O) ? If yes, can't throwAtLeastU() lest apps
+ // targeting O- crash on those devices.
return new NetworkTemplate.Builder(MATCH_BLUETOOTH).build();
}
@@ -275,10 +292,9 @@
// TODO(b/270089918): Remove this method. This can only be done after there are no more callers,
// including in OEM code which can access this by linking against the framework.
public static NetworkTemplate buildTemplateProxy() {
- if (SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException(
- "buildTemplateProxy is not supported on Android U devices or above");
- }
+ // TODO : this is part of hidden-o txt, does that mean it should be annotated with
+ // @UnsupportedAppUsage(maxTargetSdk = O) ? If yes, can't throwAtLeastU() lest apps
+ // targeting O- crash on those devices.
return new NetworkTemplate(MATCH_PROXY, null, null);
}
@@ -290,12 +306,10 @@
// TODO(b/273963543): Remove this method. This can only be done after there are no more callers,
// including in OEM code which can access this by linking against the framework.
public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) {
- if (SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException(
- "buildTemplateCarrierMetered is not supported on Android U devices or above");
- }
+ throwAtLeastU();
return new NetworkTemplate.Builder(MATCH_CARRIER)
- // Set.of will throw if subscriberId is null
+ // Set.of will throw if subscriberId is null, which is the historical
+ // behavior and should be preserved.
.setSubscriberIds(Set.of(subscriberId))
.setMeteredness(METERED_YES)
.build();
@@ -312,10 +326,7 @@
// including in OEM code which can access this by linking against the framework.
public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
int ratType, int metered) {
- if (SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException("buildTemplateMobileWithRatType is not "
- + "supported on Android U devices or above");
- }
+ throwAtLeastU();
return new NetworkTemplate.Builder(MATCH_MOBILE)
.setSubscriberIds(TextUtils.isEmpty(subscriberId)
? Collections.emptySet()
@@ -325,7 +336,6 @@
.build();
}
-
/**
* Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
* given key of the wifi network.
@@ -337,12 +347,12 @@
// TODO(b/273963543): Remove this method. This can only be done after there are no more callers,
// including in OEM code which can access this by linking against the framework.
public static NetworkTemplate buildTemplateWifi(@NonNull String wifiNetworkKey) {
- if (SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException("buildTemplateWifi is not "
- + "supported on Android U devices or above");
- }
+ // TODO : this is part of hidden-o txt, does that mean it should be annotated with
+ // @UnsupportedAppUsage(maxTargetSdk = O) ? If yes, can't throwAtLeastU() lest apps
+ // targeting O- crash on those devices.
return new NetworkTemplate.Builder(MATCH_WIFI)
- // Set.of will throw if wifiNetworkKey is null
+ // Set.of will throw if wifiNetworkKey is null, which is the historical
+ // behavior and should be preserved.
.setWifiNetworkKeys(Set.of(wifiNetworkKey))
.build();
}
@@ -364,14 +374,9 @@
// including in OEM code which can access this by linking against the framework.
public static NetworkTemplate buildTemplateWifi(@Nullable String wifiNetworkKey,
@Nullable String subscriberId) {
- if (SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException("buildTemplateWifi is not "
- + "supported on Android U devices or above");
- }
+ throwAtLeastU();
return new NetworkTemplate.Builder(MATCH_WIFI)
- .setSubscriberIds(subscriberId == null
- ? Collections.emptySet()
- : Set.of(subscriberId))
+ .setSubscriberIds(setOf(subscriberId))
.setWifiNetworkKeys(wifiNetworkKey == null
? Collections.emptySet()
: Set.of(wifiNetworkKey))
@@ -410,12 +415,20 @@
// subscriber ID.
case MATCH_CARRIER:
if (matchSubscriberIds.length == 0) {
- throw new IllegalArgumentException("checkValidMatchSubscriberIds with empty"
- + " list of ids for rule" + getMatchRuleName(matchRule));
+ throw new IllegalArgumentException("matchSubscriberIds may not contain"
+ + " null for rule " + getMatchRuleName(matchRule));
}
- // fall through
- case MATCH_MOBILE:
if (CollectionUtils.contains(matchSubscriberIds, null)) {
+ throw new IllegalArgumentException("matchSubscriberIds may not contain"
+ + " null for rule " + getMatchRuleName(matchRule));
+ }
+ break;
+ case MATCH_MOBILE:
+ // Prevent from crash for b/273963543, where the OEMs still call into unsupported
+ // buildTemplateMobileAll with null subscriberId and get crashed.
+ final int firstSdk = Build.VERSION.DEVICE_INITIAL_SDK_INT;
+ if (firstSdk > Build.VERSION_CODES.TIRAMISU
+ && CollectionUtils.contains(matchSubscriberIds, null)) {
throw new IllegalArgumentException("checkValidMatchSubscriberIds list of ids"
+ " may not contain null for rule " + getMatchRuleName(matchRule));
}
@@ -448,10 +461,6 @@
if (matchRule == 6 || matchRule == 7) {
Log.e(TAG, "Use MATCH_MOBILE with empty subscriberIds or MATCH_WIFI with empty "
+ "wifiNetworkKeys instead of template with matchRule=" + matchRule);
- if (SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException(
- "Wildcard templates are not supported on Android U devices or above");
- }
}
}
@@ -485,10 +494,9 @@
getMeterednessForBackwardsCompatibility(matchRule),
ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
OEM_MANAGED_ALL);
- if (SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException(
- "This constructor is not supported on Android U devices or above");
- }
+ // TODO : this is part of hidden-o txt, does that mean it should be annotated with
+ // @UnsupportedAppUsage(maxTargetSdk = O) ? If yes, can't throwAtLeastU() lest apps
+ // targeting O- crash on those devices.
}
/** @hide */
@@ -503,10 +511,7 @@
this(getBackwardsCompatibleMatchRule(matchRule),
matchSubscriberIds == null ? new String[]{} : matchSubscriberIds,
matchWifiNetworkKeys, metered, roaming, defaultNetwork, ratType, oemManaged);
- if (SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException(
- "This constructor is not supported on Android U devices or above");
- }
+ throwAtLeastU();
}
/** @hide */
@@ -613,10 +618,9 @@
// including in OEM code which can access this by linking against the framework.
/** @hide */
public boolean isMatchRuleMobile() {
- if (SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException(
- "isMatchRuleMobile is not supported on Android U devices or above");
- }
+ // TODO : this is part of hidden-o txt, does that mean it should be annotated with
+ // @UnsupportedAppUsage(maxTargetSdk = O) ? If yes, can't throwAtLeastU() lest apps
+ // targeting O- crash on those devices.
switch (mMatchRule) {
case MATCH_MOBILE:
// Old MATCH_MOBILE_WILDCARD
@@ -958,10 +962,7 @@
// TODO(b/273963543): Remove this method. This can only be done after there are no more callers,
// including in OEM code which can access this by linking against the framework.
public static NetworkTemplate normalize(NetworkTemplate template, List<String[]> mergedList) {
- if (SdkLevel.isAtLeastU()) {
- throw new UnsupportedOperationException(
- "normalize is not supported on Android U devices or above");
- }
+ throwAtLeastU();
return normalizeImpl(template, mergedList);
}
diff --git a/framework-t/src/android/net/nsd/NsdManager.java b/framework-t/src/android/net/nsd/NsdManager.java
index 96f2f80..d119db6 100644
--- a/framework-t/src/android/net/nsd/NsdManager.java
+++ b/framework-t/src/android/net/nsd/NsdManager.java
@@ -281,6 +281,9 @@
EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK, "UNREGISTER_SERVICE_CALLBACK");
EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK_SUCCEEDED,
"UNREGISTER_SERVICE_CALLBACK_SUCCEEDED");
+ EVENT_NAMES.put(MDNS_DISCOVERY_MANAGER_EVENT, "MDNS_DISCOVERY_MANAGER_EVENT");
+ EVENT_NAMES.put(REGISTER_CLIENT, "REGISTER_CLIENT");
+ EVENT_NAMES.put(UNREGISTER_CLIENT, "UNREGISTER_CLIENT");
}
/** @hide */
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index cd2daf0..3cc9c65 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -1134,14 +1134,16 @@
mTransportTypes =
(originalTransportTypes & UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS)
| (1 << TRANSPORT_TEST);
-
- // SubIds are only allowed for Test Networks that only declare TRANSPORT_TEST.
- setSubscriptionIds(originalSubIds);
} else {
// If the test network is restricted, then it may declare any transport.
mTransportTypes = (originalTransportTypes | (1 << TRANSPORT_TEST));
}
+ if (hasSingleTransport(TRANSPORT_TEST)) {
+ // SubIds are only allowed for Test Networks that only declare TRANSPORT_TEST.
+ setSubscriptionIds(originalSubIds);
+ }
+
mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
if (!hasTransport(TRANSPORT_CELLULAR)) {
mNetworkCapabilities |=
diff --git a/service-t/Android.bp b/service-t/Android.bp
index 5bf2973..7de749c 100644
--- a/service-t/Android.bp
+++ b/service-t/Android.bp
@@ -75,3 +75,47 @@
"//packages/modules/IPsec/tests/iketests",
],
}
+
+// Test building mDNS as a standalone, so that it can be imported into other repositories as-is.
+// The mDNS code is platform code so it should use framework-annotations-lib, contrary to apps that
+// should use sdk_version: "system_current" and only androidx.annotation_annotation. But this build
+// rule verifies that the mDNS code can be built into apps, if code transformations are applied to
+// the annotations.
+// When using "system_current", framework annotations are not available; they would appear as
+// package-private as they are marked as such in the system_current stubs. So build against
+// core_platform and add the stubs manually in "libs". See http://b/147773144#comment7.
+java_library {
+ name: "service-connectivity-mdns-standalone-build-test",
+ sdk_version: "core_platform",
+ srcs: [
+ ":service-mdns-droidstubs",
+ "src/com/android/server/connectivity/mdns/**/*.java",
+ ],
+ exclude_srcs: [
+ "src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java",
+ "src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java"
+ ],
+ static_libs: [
+ "net-utils-device-common-mdns-standalone-build-test",
+ ],
+ libs: [
+ "framework-annotations-lib",
+ "android_system_stubs_current",
+ "androidx.annotation_annotation",
+ ],
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+droidstubs {
+ name: "service-mdns-droidstubs",
+ srcs: ["src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java"],
+ libs: [
+ "net-utils-device-common-mdns-standalone-build-test",
+ "service-connectivity-tiramisu-pre-jarjar"
+ ],
+ visibility: [
+ "//visibility:private",
+ ],
+}
\ No newline at end of file
diff --git a/service-t/jni/com_android_server_net_NetworkStatsFactory.cpp b/service-t/jni/com_android_server_net_NetworkStatsFactory.cpp
index 2dbe771..a16757b 100644
--- a/service-t/jni/com_android_server_net_NetworkStatsFactory.cpp
+++ b/service-t/jni/com_android_server_net_NetworkStatsFactory.cpp
@@ -170,23 +170,10 @@
return 0;
}
-static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jint limitUid,
- jobjectArray limitIfacesObj, jint limitTag) {
-
- std::vector<std::string> limitIfaces;
- if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
- int num = env->GetArrayLength(limitIfacesObj);
- for (int i = 0; i < num; i++) {
- jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i);
- ScopedUtfChars string8(env, string);
- if (string8.c_str() != NULL) {
- limitIfaces.push_back(std::string(string8.c_str()));
- }
- }
- }
+static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats) {
std::vector<stats_line> lines;
- if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0)
+ if (parseBpfNetworkStatsDetail(&lines) < 0)
return -1;
return statsLinesToNetworkStats(env, clazz, stats, lines);
@@ -202,8 +189,7 @@
}
static const JNINativeMethod gMethods[] = {
- { "nativeReadNetworkStatsDetail",
- "(Landroid/net/NetworkStats;I[Ljava/lang/String;I)I",
+ { "nativeReadNetworkStatsDetail", "(Landroid/net/NetworkStats;)I",
(void*) readNetworkStatsDetail },
{ "nativeReadNetworkStatsDev", "(Landroid/net/NetworkStats;)I",
(void*) readNetworkStatsDev },
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
index 4fbc5f4..cdcb0f8 100644
--- a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
+++ b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
@@ -110,12 +110,11 @@
}
int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>* lines,
- const std::vector<std::string>& limitIfaces, int limitTag,
- int limitUid, const BpfMap<StatsKey, StatsValue>& statsMap,
+ const BpfMap<StatsKey, StatsValue>& statsMap,
const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
int64_t unknownIfaceBytesTotal = 0;
const auto processDetailUidStats =
- [lines, &limitIfaces, &limitTag, &limitUid, &unknownIfaceBytesTotal, &ifaceMap](
+ [lines, &unknownIfaceBytesTotal, &ifaceMap](
const StatsKey& key,
const BpfMap<StatsKey, StatsValue>& statsMap) -> Result<void> {
char ifname[IFNAMSIZ];
@@ -123,18 +122,6 @@
&unknownIfaceBytesTotal)) {
return Result<void>();
}
- std::string ifnameStr(ifname);
- if (limitIfaces.size() > 0 &&
- std::find(limitIfaces.begin(), limitIfaces.end(), ifnameStr) == limitIfaces.end()) {
- // Nothing matched; skip this line.
- return Result<void>();
- }
- if (limitTag != TAG_ALL && uint32_t(limitTag) != key.tag) {
- return Result<void>();
- }
- if (limitUid != UID_ALL && uint32_t(limitUid) != key.uid) {
- return Result<void>();
- }
Result<StatsValue> statsEntry = statsMap.readValue(key);
if (!statsEntry.ok()) {
return base::ResultError(statsEntry.error().message(), statsEntry.error().code());
@@ -162,9 +149,7 @@
return 0;
}
-int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines,
- const std::vector<std::string>& limitIfaces, int limitTag,
- int limitUid) {
+int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines) {
static BpfMapRO<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
static BpfMapRO<uint32_t, uint32_t> configurationMap(CONFIGURATION_MAP_PATH);
static BpfMap<StatsKey, StatsValue> statsMapA(STATS_MAP_A_PATH);
@@ -195,8 +180,7 @@
// TODO: the above comment feels like it may be obsolete / out of date,
// since we no longer swap the map via netd binder rpc - though we do
// still swap it.
- int ret = parseBpfNetworkStatsDetailInternal(lines, limitIfaces, limitTag, limitUid,
- *inactiveStatsMap, ifaceIndexNameMap);
+ int ret = parseBpfNetworkStatsDetailInternal(lines, *inactiveStatsMap, ifaceIndexNameMap);
if (ret) {
ALOGE("parse detail network stats failed: %s", strerror(errno));
return ret;
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
index ff62c0b..bf42b62 100644
--- a/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
+++ b/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
@@ -225,18 +225,11 @@
ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID2, &result2, mFakeAppUidStatsMap));
expectStatsEqual(value2, result2);
std::vector<stats_line> lines;
- std::vector<std::string> ifaces;
populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
populateFakeStats(TEST_UID1, 0, IFACE_INDEX2, TEST_COUNTERSET1, value1, mFakeStatsMap);
populateFakeStats(TEST_UID2, 0, IFACE_INDEX3, TEST_COUNTERSET1, value1, mFakeStatsMap);
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, TEST_UID1,
- mFakeStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((unsigned long)2, lines.size());
- lines.clear();
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, TEST_UID2,
- mFakeStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((unsigned long)1, lines.size());
- expectStatsLineEqual(value1, IFACE_NAME3, TEST_UID2, TEST_COUNTERSET1, 0, lines.front());
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
+ ASSERT_EQ((unsigned long)3, lines.size());
}
TEST_F(BpfNetworkStatsHelperTest, TestGetIfaceStatsInternal) {
@@ -297,24 +290,8 @@
mFakeStatsMap);
populateFakeStats(TEST_UID2, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
std::vector<stats_line> lines;
- std::vector<std::string> ifaces;
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, UID_ALL, mFakeStatsMap,
- mFakeIfaceIndexNameMap));
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((unsigned long)4, lines.size());
- lines.clear();
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, TEST_UID1,
- mFakeStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((unsigned long)3, lines.size());
- lines.clear();
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TEST_TAG, TEST_UID1,
- mFakeStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((unsigned long)2, lines.size());
- lines.clear();
- ifaces.push_back(std::string(IFACE_NAME1));
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TEST_TAG, TEST_UID1,
- mFakeStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((unsigned long)1, lines.size());
- expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, TEST_TAG, lines.front());
}
TEST_F(BpfNetworkStatsHelperTest, TestGetStatsWithSkippedIface) {
@@ -333,24 +310,8 @@
populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET1, value1, mFakeStatsMap);
populateFakeStats(TEST_UID2, 0, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
std::vector<stats_line> lines;
- std::vector<std::string> ifaces;
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, UID_ALL, mFakeStatsMap,
- mFakeIfaceIndexNameMap));
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((unsigned long)4, lines.size());
- lines.clear();
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, TEST_UID1,
- mFakeStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((unsigned long)3, lines.size());
- lines.clear();
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, TEST_UID2,
- mFakeStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((unsigned long)1, lines.size());
- expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID2, TEST_COUNTERSET0, 0, lines.front());
- lines.clear();
- ifaces.push_back(std::string(IFACE_NAME1));
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, TEST_UID1,
- mFakeStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((unsigned long)2, lines.size());
}
TEST_F(BpfNetworkStatsHelperTest, TestUnknownIfaceError) {
@@ -387,10 +348,8 @@
ifname, curKey, &unknownIfaceBytesTotal));
ASSERT_EQ(-1, unknownIfaceBytesTotal);
std::vector<stats_line> lines;
- std::vector<std::string> ifaces;
// TODO: find a way to test the total of unknown Iface Bytes go above limit.
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, UID_ALL, mFakeStatsMap,
- mFakeIfaceIndexNameMap));
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((unsigned long)1, lines.size());
expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, 0, lines.front());
}
@@ -458,18 +417,15 @@
};
std::vector<stats_line> lines;
- std::vector<std::string> ifaces;
// Test empty stats.
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, UID_ALL, mFakeStatsMap,
- mFakeIfaceIndexNameMap));
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((size_t) 0, lines.size());
lines.clear();
// Test 1 line stats.
populateFakeStats(TEST_UID1, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, UID_ALL, mFakeStatsMap,
- mFakeIfaceIndexNameMap));
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((size_t) 1, lines.size());
expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, TEST_TAG, lines[0]);
lines.clear();
@@ -480,8 +436,7 @@
populateFakeStats(TEST_UID1, TEST_TAG + 1, IFACE_INDEX1, TEST_COUNTERSET0, value2,
mFakeStatsMap);
populateFakeStats(TEST_UID2, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, UID_ALL, mFakeStatsMap,
- mFakeIfaceIndexNameMap));
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((size_t) 5, lines.size());
lines.clear();
@@ -489,8 +444,7 @@
populateFakeStats(TEST_UID1, TEST_TAG, IFACE_INDEX3, TEST_COUNTERSET0, value1, mFakeStatsMap);
populateFakeStats(TEST_UID2, TEST_TAG, IFACE_INDEX3, TEST_COUNTERSET0, value1, mFakeStatsMap);
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, UID_ALL, mFakeStatsMap,
- mFakeIfaceIndexNameMap));
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((size_t) 5, lines.size());
// Verify Sorted & Grouped.
@@ -547,9 +501,7 @@
// TODO: Mutate counterSet and enlarge TEST_MAP_SIZE if overflow on counterSet is possible.
std::vector<stats_line> lines;
- std::vector<std::string> ifaces;
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, UID_ALL, mFakeStatsMap,
- mFakeIfaceIndexNameMap));
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((size_t) 8, lines.size());
// Uid 0 first
diff --git a/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
index 696a29a..6aa0fb4 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
@@ -101,6 +101,11 @@
void NetworkTraceHandler::InitPerfettoTracing() {
perfetto::TracingInitArgs args = {};
args.backends |= perfetto::kSystemBackend;
+ // The following line disables the Perfetto system consumer. Perfetto inlines
+ // the call to `Initialize` which allows the compiler to see that the branch
+ // with the SystemConsumerTracingBackend is not used. With LTO enabled, this
+ // strips the Perfetto consumer code and reduces the size of this binary by
+ // around 270KB total. Be careful when changing this value.
args.enable_system_consumer = false;
perfetto::Tracing::Initialize(args);
NetworkTraceHandler::RegisterDataSource();
@@ -136,10 +141,12 @@
}
void NetworkTraceHandler::OnStart(const StartArgs&) {
+ if (mIsTest) return; // Don't touch non-hermetic bpf in test.
mStarted = sPoller.Start(mPollMs);
}
void NetworkTraceHandler::OnStop(const StopArgs&) {
+ if (mIsTest) return; // Don't touch non-hermetic bpf in test.
if (mStarted) sPoller.Stop();
mStarted = false;
}
@@ -179,24 +186,25 @@
bundle.bytes += pkt.length;
}
- // If state was cleared, emit a separate packet to indicate it. This uses the
- // overall minTs so it is sorted before any packets that follow.
NetworkTraceState* incr_state = ctx.GetIncrementalState();
- if (!bundles.empty() && mInternLimit && incr_state->cleared) {
- auto clear = ctx.NewTracePacket();
- clear->set_sequence_flags(TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
- clear->set_timestamp(minTs);
- incr_state->cleared = false;
- }
-
for (const auto& kv : bundles) {
const BundleKey& key = kv.first;
const BundleDetails& details = kv.second;
auto dst = ctx.NewTracePacket();
- dst->set_sequence_flags(TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
dst->set_timestamp(details.minTs);
+ // Incremental state is only used when interning. Set the flag based on
+ // whether state was cleared. Leave the flag empty in non-intern configs.
+ if (mInternLimit > 0) {
+ if (incr_state->cleared) {
+ dst->set_sequence_flags(TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
+ incr_state->cleared = false;
+ } else {
+ dst->set_sequence_flags(TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
+ }
+ }
+
auto* event = FillWithInterning(incr_state, key, dst.get());
int count = details.time_and_len.size();
diff --git a/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp b/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp
index c9eb183..f2c1a86 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp
@@ -33,14 +33,6 @@
using ::perfetto::protos::TracePacket;
using ::perfetto::protos::TrafficDirection;
-// This handler makes OnStart and OnStop a no-op so that tracing is not really
-// started on the device.
-class HandlerForTest : public NetworkTraceHandler {
- public:
- void OnStart(const StartArgs&) override {}
- void OnStop(const StopArgs&) override {}
-};
-
class NetworkTraceHandlerTest : public testing::Test {
protected:
// Starts a tracing session with the handler under test.
@@ -52,7 +44,7 @@
perfetto::DataSourceDescriptor dsd;
dsd.set_name("test.network_packets");
- HandlerForTest::Register(dsd);
+ NetworkTraceHandler::Register(dsd, /*isTest=*/true);
perfetto::TraceConfig cfg;
cfg.add_buffers()->set_size_kb(1024);
@@ -94,7 +86,7 @@
std::vector<TracePacket>* output,
NetworkPacketTraceConfig config = {}) {
auto session = StartTracing(config);
- HandlerForTest::Trace([&](HandlerForTest::TraceContext ctx) {
+ NetworkTraceHandler::Trace([&](NetworkTraceHandler::TraceContext ctx) {
ctx.GetDataSourceLocked()->Write(input, ctx);
ctx.Flush();
});
@@ -136,6 +128,7 @@
EXPECT_THAT(events[0].network_packet().ip_proto(), 6);
EXPECT_THAT(events[0].network_packet().tcp_flags(), 1);
EXPECT_THAT(events[0].network_packet().length(), 100);
+ EXPECT_THAT(events[0].has_sequence_flags(), false);
}
TEST_F(NetworkTraceHandlerTest, WriteDirectionAndPorts) {
@@ -356,7 +349,7 @@
auto session = StartTracing(config);
- HandlerForTest::Trace([&](HandlerForTest::TraceContext ctx) {
+ NetworkTraceHandler::Trace([&](NetworkTraceHandler::TraceContext ctx) {
ctx.GetDataSourceLocked()->Write(inputs[0], ctx);
ctx.GetDataSourceLocked()->Write(inputs[1], ctx);
ctx.GetDataSourceLocked()->Write(inputs[2], ctx);
@@ -374,20 +367,28 @@
ASSERT_EQ(events[0].interned_data().packet_context().size(), 1);
EXPECT_EQ(events[0].interned_data().packet_context(0).iid(), 1);
EXPECT_EQ(events[0].interned_data().packet_context(0).ctx().uid(), 123);
+ EXPECT_EQ(events[0].sequence_flags(),
+ TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
// First time seen, emit new interned data, bundle uses iid instead of ctx.
EXPECT_EQ(events[1].network_packet_bundle().iid(), 2);
ASSERT_EQ(events[1].interned_data().packet_context().size(), 1);
EXPECT_EQ(events[1].interned_data().packet_context(0).iid(), 2);
EXPECT_EQ(events[1].interned_data().packet_context(0).ctx().uid(), 456);
+ EXPECT_EQ(events[1].sequence_flags(),
+ TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
// Not enough room in intern table (limit 2), inline the context.
EXPECT_EQ(events[2].network_packet_bundle().ctx().uid(), 789);
EXPECT_EQ(events[2].interned_data().packet_context().size(), 0);
+ EXPECT_EQ(events[2].sequence_flags(),
+ TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
// Second time seen, no need to re-emit interned data, only record iid.
EXPECT_EQ(events[3].network_packet_bundle().iid(), 1);
EXPECT_EQ(events[3].interned_data().packet_context().size(), 0);
+ EXPECT_EQ(events[3].sequence_flags(),
+ TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
}
} // namespace bpf
diff --git a/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp b/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
index 3abb49a..3de9897 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
@@ -99,6 +99,15 @@
ALOGW("Failed to disable tracing: %s", res.error().message().c_str());
}
+ // Make sure everything in the system has actually seen the 'false' we just
+ // wrote, things should now be well and truly disabled.
+ synchronizeKernelRCU();
+
+ // Drain remaining events from the ring buffer now that tracing is disabled.
+ // This prevents the next trace from seeing stale events and allows writing
+ // the last batch of events to Perfetto.
+ ConsumeAllLocked();
+
mTaskRunner.reset();
mRingBuffer.reset();
diff --git a/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h b/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
index 03a1a44..1ffa927 100644
--- a/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
+++ b/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
@@ -26,7 +26,7 @@
// TODO: set this to a proper value based on the map size;
constexpr int TAG_STATS_MAP_SOFT_LIMIT = 3;
constexpr int UID_ALL = -1;
-constexpr int TAG_ALL = -1;
+//constexpr int TAG_ALL = -1;
constexpr int TAG_NONE = 0;
constexpr int SET_ALL = -1;
constexpr int SET_DEFAULT = 0;
@@ -64,8 +64,7 @@
const BpfMap<uint32_t, IfaceValue>& ifaceNameMap);
// For test only
int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>* lines,
- const std::vector<std::string>& limitIfaces, int limitTag,
- int limitUid, const BpfMap<StatsKey, StatsValue>& statsMap,
+ const BpfMap<StatsKey, StatsValue>& statsMap,
const BpfMap<uint32_t, IfaceValue>& ifaceMap);
// For test only
int cleanStatsMapInternal(const base::unique_fd& cookieTagMap, const base::unique_fd& tagStatsMap);
@@ -113,9 +112,7 @@
int bpfGetUidStats(uid_t uid, Stats* stats);
int bpfGetIfaceStats(const char* iface, Stats* stats);
-int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines,
- const std::vector<std::string>& limitIfaces, int limitTag,
- int limitUid);
+int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines);
int parseBpfNetworkStatsDev(std::vector<stats_line>* lines);
void groupNetworkStats(std::vector<stats_line>* lines);
diff --git a/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h b/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h
index 80871c6..bc10e68 100644
--- a/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h
+++ b/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h
@@ -49,7 +49,7 @@
// loss). When state is cleared, the state object is replaced with a new default
// constructed instance.
struct NetworkTraceState {
- bool cleared;
+ bool cleared = true;
std::unordered_map<BundleKey, uint64_t, BundleHash, BundleEq> iids;
};
@@ -70,6 +70,9 @@
// Connects to the system Perfetto daemon and registers the trace handler.
static void InitPerfettoTracing();
+ // When isTest is true, skip non-hermetic code.
+ NetworkTraceHandler(bool isTest = false) : mIsTest(isTest) {}
+
// perfetto::DataSource overrides:
void OnSetup(const SetupArgs& args) override;
void OnStart(const StartArgs&) override;
@@ -92,6 +95,7 @@
static internal::NetworkTracePoller sPoller;
bool mStarted;
+ bool mIsTest;
// Values from config, see proto for details.
uint32_t mPollMs;
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index cbe6691..a658791 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -72,6 +72,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
@@ -1105,9 +1106,12 @@
final String serviceName = serviceInfo.getServiceInstanceName();
final NsdServiceInfo servInfo = new NsdServiceInfo(serviceName, serviceType);
final Network network = serviceInfo.getNetwork();
+ // In MdnsDiscoveryManagerEvent, the Network can be null which means it is a
+ // network for Tethering interface. In other words, the network == null means the
+ // network has netId = INetd.LOCAL_NET_ID.
setServiceNetworkForCallback(
servInfo,
- network == null ? NETID_UNSET : network.netId,
+ network == null ? INetd.LOCAL_NET_ID : network.netId,
serviceInfo.getInterfaceIndex());
return servInfo;
}
@@ -1155,22 +1159,7 @@
Log.e(TAG, "Invalid attribute", e);
}
}
- final List<InetAddress> addresses = new ArrayList<>();
- for (String ipv4Address : serviceInfo.getIpv4Addresses()) {
- try {
- addresses.add(InetAddresses.parseNumericAddress(ipv4Address));
- } catch (IllegalArgumentException e) {
- Log.wtf(TAG, "Invalid ipv4 address", e);
- }
- }
- for (String ipv6Address : serviceInfo.getIpv6Addresses()) {
- try {
- addresses.add(InetAddresses.parseNumericAddress(ipv6Address));
- } catch (IllegalArgumentException e) {
- Log.wtf(TAG, "Invalid ipv6 address", e);
- }
- }
-
+ final List<InetAddress> addresses = getInetAddresses(serviceInfo);
if (addresses.size() != 0) {
info.setHostAddresses(addresses);
clientInfo.onResolveServiceSucceeded(clientId, info);
@@ -1202,21 +1191,7 @@
}
}
- final List<InetAddress> addresses = new ArrayList<>();
- for (String ipv4Address : serviceInfo.getIpv4Addresses()) {
- try {
- addresses.add(InetAddresses.parseNumericAddress(ipv4Address));
- } catch (IllegalArgumentException e) {
- Log.wtf(TAG, "Invalid ipv4 address", e);
- }
- }
- for (String ipv6Address : serviceInfo.getIpv6Addresses()) {
- try {
- addresses.add(InetAddresses.parseNumericAddress(ipv6Address));
- } catch (IllegalArgumentException e) {
- Log.wtf(TAG, "Invalid ipv6 address", e);
- }
- }
+ final List<InetAddress> addresses = getInetAddresses(serviceInfo);
info.setHostAddresses(addresses);
clientInfo.onServiceUpdated(clientId, info);
break;
@@ -1232,6 +1207,36 @@
}
}
+ @NonNull
+ private static List<InetAddress> getInetAddresses(@NonNull MdnsServiceInfo serviceInfo) {
+ final List<String> v4Addrs = serviceInfo.getIpv4Addresses();
+ final List<String> v6Addrs = serviceInfo.getIpv6Addresses();
+ final List<InetAddress> addresses = new ArrayList<>(v4Addrs.size() + v6Addrs.size());
+ for (String ipv4Address : v4Addrs) {
+ try {
+ addresses.add(InetAddresses.parseNumericAddress(ipv4Address));
+ } catch (IllegalArgumentException e) {
+ Log.wtf(TAG, "Invalid ipv4 address", e);
+ }
+ }
+ for (String ipv6Address : v6Addrs) {
+ try {
+ final InetAddress addr = InetAddresses.parseNumericAddress(ipv6Address);
+ if (addr.isLinkLocalAddress()) {
+ final Inet6Address v6Addr = Inet6Address.getByAddress(
+ null /* host */, addr.getAddress(),
+ serviceInfo.getInterfaceIndex());
+ addresses.add(v6Addr);
+ } else {
+ addresses.add(addr);
+ }
+ } catch (IllegalArgumentException | UnknownHostException e) {
+ Log.wtf(TAG, "Invalid ipv6 address", e);
+ }
+ }
+ return addresses;
+ }
+
private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) {
switch (netId) {
case NETID_UNSET:
diff --git a/service-t/src/com/android/server/connectivity/mdns/ISocketNetLinkMonitor.java b/service-t/src/com/android/server/connectivity/mdns/ISocketNetLinkMonitor.java
new file mode 100644
index 0000000..ef3928c
--- /dev/null
+++ b/service-t/src/com/android/server/connectivity/mdns/ISocketNetLinkMonitor.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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.connectivity.mdns;
+
+/**
+ * The interface for netlink monitor.
+ */
+public interface ISocketNetLinkMonitor {
+
+ /**
+ * Returns if the netlink monitor is supported or not. By default, it is not supported.
+ */
+ default boolean isSupported() {
+ return false;
+ }
+
+ /**
+ * Starts the monitor.
+ */
+ default void startMonitoring() {
+ }
+
+ /**
+ * Stops the monitor.
+ */
+ default void stopMonitoring() {
+ }
+}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
index 119c7a8..31627f8 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
@@ -78,7 +78,7 @@
}
mPacketReader = new MulticastPacketReader(networkInterface.getName(), mFileDescriptor,
- new Handler(looper), packetReadBuffer);
+ new Handler(looper), packetReadBuffer, port);
mPacketReader.start();
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
index 5254342..7af2231 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
@@ -70,7 +70,7 @@
}
@Override
- public void onSocketCreated(@NonNull Network network,
+ public void onSocketCreated(@Nullable Network network,
@NonNull MdnsInterfaceSocket socket, @NonNull List<LinkAddress> addresses) {
// The socket may be already created by other request before, try to get the stored
// ReadPacketHandler.
@@ -86,7 +86,7 @@
}
@Override
- public void onInterfaceDestroyed(@NonNull Network network,
+ public void onInterfaceDestroyed(@Nullable Network network,
@NonNull MdnsInterfaceSocket socket) {
mSocketPacketHandlers.remove(socket);
mActiveNetworkSockets.remove(socket);
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
index 1d9a24c..0952e88 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
@@ -16,30 +16,30 @@
package com.android.server.connectivity.mdns;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
-import android.net.INetd;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TetheringManager;
import android.net.TetheringManager.TetheringEventCallback;
import android.os.Handler;
import android.os.Looper;
-import android.system.OsConstants;
import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
-import com.android.net.module.util.ip.NetlinkMonitor;
-import com.android.net.module.util.netlink.NetlinkConstants;
-import com.android.net.module.util.netlink.NetlinkMessage;
import com.android.server.connectivity.mdns.util.MdnsLogger;
import java.io.IOException;
@@ -71,11 +71,12 @@
@NonNull private final Dependencies mDependencies;
@NonNull private final NetworkCallback mNetworkCallback;
@NonNull private final TetheringEventCallback mTetheringEventCallback;
- @NonNull private final NetlinkMonitor mNetlinkMonitor;
+ @NonNull private final ISocketNetLinkMonitor mSocketNetlinkMonitor;
private final ArrayMap<Network, SocketInfo> mNetworkSockets = new ArrayMap<>();
private final ArrayMap<String, SocketInfo> mTetherInterfaceSockets = new ArrayMap<>();
private final ArrayMap<Network, LinkProperties> mActiveNetworksLinkProperties =
new ArrayMap<>();
+ private final ArrayMap<Network, int[]> mActiveNetworksTransports = new ArrayMap<>();
private final ArrayMap<SocketCallback, Network> mCallbacksToRequestedNetworks =
new ArrayMap<>();
private final List<String> mLocalOnlyInterfaces = new ArrayList<>();
@@ -98,7 +99,14 @@
@Override
public void onLost(Network network) {
mActiveNetworksLinkProperties.remove(network);
- removeSocket(network, null /* interfaceName */);
+ mActiveNetworksTransports.remove(network);
+ removeNetworkSocket(network);
+ }
+
+ @Override
+ public void onCapabilitiesChanged(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities) {
+ mActiveNetworksTransports.put(network, networkCapabilities.getTransportTypes());
}
@Override
@@ -118,7 +126,8 @@
}
};
- mNetlinkMonitor = new SocketNetlinkMonitor(mHandler);
+ mSocketNetlinkMonitor = SocketNetLinkMonitorFactory.createNetLinkMonitor(mHandler,
+ LOGGER.mLog);
}
/**
@@ -133,11 +142,6 @@
return ni == null ? null : new NetworkInterfaceWrapper(ni);
}
- /*** Check whether given network interface can support mdns */
- public boolean canScanOnInterface(@NonNull NetworkInterfaceWrapper networkInterface) {
- return MulticastNetworkInterfaceProvider.canScanOnInterface(networkInterface);
- }
-
/*** Create a MdnsInterfaceSocket */
public MdnsInterfaceSocket createMdnsInterfaceSocket(
@NonNull NetworkInterface networkInterface, int port, @NonNull Looper looper,
@@ -157,18 +161,6 @@
}
}
- private static class SocketNetlinkMonitor extends NetlinkMonitor {
- SocketNetlinkMonitor(Handler handler) {
- super(handler, LOGGER.mLog, TAG, OsConstants.NETLINK_ROUTE,
- NetlinkConstants.RTMGRP_IPV4_IFADDR | NetlinkConstants.RTMGRP_IPV6_IFADDR);
- }
-
- @Override
- public void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) {
- // TODO: Handle netlink message.
- }
- }
-
/*** Ensure that current running thread is same as given handler thread */
public static void ensureRunningOnHandlerThread(Handler handler) {
if (handler.getLooper().getThread() != Thread.currentThread()) {
@@ -193,7 +185,9 @@
final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
tetheringManager.registerTetheringEventCallback(mHandler::post, mTetheringEventCallback);
- mHandler.post(mNetlinkMonitor::start);
+ if (mSocketNetlinkMonitor.isSupported()) {
+ mHandler.post(mSocketNetlinkMonitor::startMonitoring);
+ }
mMonitoringSockets = true;
}
@@ -210,7 +204,9 @@
TetheringManager.class);
tetheringManager.unregisterTetheringEventCallback(mTetheringEventCallback);
- mHandler.post(mNetlinkMonitor::stop);
+ if (mSocketNetlinkMonitor.isSupported()) {
+ mHandler.post(mSocketNetlinkMonitor::stopMonitoring);
+ }
// Clear all saved status.
mActiveNetworksLinkProperties.clear();
mNetworkSockets.clear();
@@ -235,7 +231,7 @@
/*** Check whether the target network is matched current network */
public static boolean isNetworkMatched(@Nullable Network targetNetwork,
- @NonNull Network currentNetwork) {
+ @Nullable Network currentNetwork) {
return targetNetwork == null || targetNetwork.equals(currentNetwork);
}
@@ -258,9 +254,10 @@
return;
}
+ final NetworkAsKey networkKey = new NetworkAsKey(network);
final SocketInfo socketInfo = mNetworkSockets.get(network);
if (socketInfo == null) {
- createSocket(network, lp);
+ createSocket(networkKey, lp);
} else {
// Update the addresses of this socket.
final List<LinkAddress> addresses = lp.getLinkAddresses();
@@ -295,16 +292,16 @@
final CompareResult<String> interfaceDiff = new CompareResult<>(
current, updated);
for (String name : interfaceDiff.added) {
- createSocket(new Network(INetd.LOCAL_NET_ID), createLPForTetheredInterface(name));
+ createSocket(LOCAL_NET, createLPForTetheredInterface(name));
}
for (String name : interfaceDiff.removed) {
- removeSocket(new Network(INetd.LOCAL_NET_ID), name);
+ removeTetherInterfaceSocket(name);
}
current.clear();
current.addAll(updated);
}
- private void createSocket(Network network, LinkProperties lp) {
+ private void createSocket(NetworkKey networkKey, LinkProperties lp) {
final String interfaceName = lp.getInterfaceName();
if (interfaceName == null) {
Log.e(TAG, "Can not create socket with null interface name.");
@@ -314,46 +311,97 @@
try {
final NetworkInterfaceWrapper networkInterface =
mDependencies.getNetworkInterfaceByName(interfaceName);
- if (networkInterface == null || !mDependencies.canScanOnInterface(networkInterface)) {
+ // There are no transports for tethered interfaces. Other interfaces should always
+ // have transports since LinkProperties updates are always sent after
+ // NetworkCapabilities updates.
+ final int[] transports;
+ if (networkKey == LOCAL_NET) {
+ transports = new int[0];
+ } else {
+ transports = mActiveNetworksTransports.getOrDefault(
+ ((NetworkAsKey) networkKey).mNetwork, new int[0]);
+ }
+ if (networkInterface == null || !isMdnsCapableInterface(networkInterface, transports)) {
return;
}
if (DBG) {
- Log.d(TAG, "Create a socket on network:" + network
+ Log.d(TAG, "Create a socket on network:" + networkKey
+ " with interfaceName:" + interfaceName);
}
final MdnsInterfaceSocket socket = mDependencies.createMdnsInterfaceSocket(
networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT, mLooper,
mPacketReadBuffer);
final List<LinkAddress> addresses;
- if (network.netId == INetd.LOCAL_NET_ID) {
+ if (networkKey == LOCAL_NET) {
addresses = CollectionUtils.map(
- networkInterface.getInterfaceAddresses(), LinkAddress::new);
+ networkInterface.getInterfaceAddresses(),
+ i -> new LinkAddress(i.getAddress(), i.getNetworkPrefixLength()));
mTetherInterfaceSockets.put(interfaceName, new SocketInfo(socket, addresses));
} else {
addresses = lp.getLinkAddresses();
- mNetworkSockets.put(network, new SocketInfo(socket, addresses));
+ mNetworkSockets.put(((NetworkAsKey) networkKey).mNetwork,
+ new SocketInfo(socket, addresses));
}
// Try to join IPv4/IPv6 group.
socket.joinGroup(addresses);
// Notify the listeners which need this socket.
- notifySocketCreated(network, socket, addresses);
+ if (networkKey == LOCAL_NET) {
+ notifySocketCreated(null /* network */, socket, addresses);
+ } else {
+ notifySocketCreated(((NetworkAsKey) networkKey).mNetwork, socket, addresses);
+ }
} catch (IOException e) {
Log.e(TAG, "Create a socket failed with interface=" + interfaceName, e);
}
}
- private void removeSocket(Network network, String interfaceName) {
- final SocketInfo socketInfo = network.netId == INetd.LOCAL_NET_ID
- ? mTetherInterfaceSockets.remove(interfaceName)
- : mNetworkSockets.remove(network);
+ private boolean isMdnsCapableInterface(
+ @NonNull NetworkInterfaceWrapper iface, @NonNull int[] transports) {
+ try {
+ // Never try mDNS on cellular, or on interfaces with incompatible flags
+ if (CollectionUtils.contains(transports, TRANSPORT_CELLULAR)
+ || iface.isLoopback()
+ || iface.isPointToPoint()
+ || iface.isVirtual()
+ || !iface.isUp()) {
+ return false;
+ }
+
+ // Otherwise, always try mDNS on non-VPN Wifi.
+ if (!CollectionUtils.contains(transports, TRANSPORT_VPN)
+ && CollectionUtils.contains(transports, TRANSPORT_WIFI)) {
+ return true;
+ }
+
+ // For other transports, or no transports (tethering downstreams), do mDNS based on the
+ // interface flags. This is not always reliable (for example some Wifi interfaces may
+ // not have the MULTICAST flag even though they can do mDNS, and some cellular
+ // interfaces may have the BROADCAST or MULTICAST flags), so checks are done based on
+ // transports above in priority.
+ return iface.supportsMulticast();
+ } catch (SocketException e) {
+ Log.e(TAG, "Error checking interface flags", e);
+ return false;
+ }
+ }
+
+ private void removeNetworkSocket(Network network) {
+ final SocketInfo socketInfo = mNetworkSockets.remove(network);
if (socketInfo == null) return;
socketInfo.mSocket.destroy();
notifyInterfaceDestroyed(network, socketInfo.mSocket);
}
+ private void removeTetherInterfaceSocket(String interfaceName) {
+ final SocketInfo socketInfo = mTetherInterfaceSockets.remove(interfaceName);
+ if (socketInfo == null) return;
+ socketInfo.mSocket.destroy();
+ notifyInterfaceDestroyed(null /* network */, socketInfo.mSocket);
+ }
+
private void notifySocketCreated(Network network, MdnsInterfaceSocket socket,
List<LinkAddress> addresses) {
for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
@@ -393,7 +441,7 @@
if (DBG) Log.d(TAG, "There is no LinkProperties for this network:" + network);
return;
}
- createSocket(network, lp);
+ createSocket(new NetworkAsKey(network), lp);
} else {
// Notify the socket for requested network.
cb.onSocketCreated(network, socketInfo.mSocket, socketInfo.mAddresses);
@@ -403,12 +451,11 @@
private void retrieveAndNotifySocketFromInterface(String interfaceName, SocketCallback cb) {
final SocketInfo socketInfo = mTetherInterfaceSockets.get(interfaceName);
if (socketInfo == null) {
- createSocket(
- new Network(INetd.LOCAL_NET_ID), createLPForTetheredInterface(interfaceName));
+ createSocket(LOCAL_NET, createLPForTetheredInterface(interfaceName));
} else {
// Notify the socket for requested network.
cb.onSocketCreated(
- new Network(INetd.LOCAL_NET_ID), socketInfo.mSocket, socketInfo.mAddresses);
+ null /* network */, socketInfo.mSocket, socketInfo.mAddresses);
}
}
@@ -467,7 +514,7 @@
final SocketInfo info = mTetherInterfaceSockets.valueAt(i);
info.mSocket.destroy();
// Still notify to unrequester for socket destroy.
- cb.onInterfaceDestroyed(new Network(INetd.LOCAL_NET_ID), info.mSocket);
+ cb.onInterfaceDestroyed(null /* network */, info.mSocket);
}
mTetherInterfaceSockets.clear();
@@ -478,13 +525,49 @@
/*** Callbacks for listening socket changes */
public interface SocketCallback {
/*** Notify the socket is created */
- default void onSocketCreated(@NonNull Network network, @NonNull MdnsInterfaceSocket socket,
+ default void onSocketCreated(@Nullable Network network, @NonNull MdnsInterfaceSocket socket,
@NonNull List<LinkAddress> addresses) {}
/*** Notify the interface is destroyed */
- default void onInterfaceDestroyed(@NonNull Network network,
+ default void onInterfaceDestroyed(@Nullable Network network,
@NonNull MdnsInterfaceSocket socket) {}
/*** Notify the addresses is changed on the network */
- default void onAddressesChanged(@NonNull Network network,
+ default void onAddressesChanged(@Nullable Network network,
@NonNull MdnsInterfaceSocket socket, @NonNull List<LinkAddress> addresses) {}
}
+
+ private interface NetworkKey {
+ }
+
+ private static final NetworkKey LOCAL_NET = new NetworkKey() {
+ @Override
+ public String toString() {
+ return "NetworkKey:LOCAL_NET";
+ }
+ };
+
+ private static class NetworkAsKey implements NetworkKey {
+ private final Network mNetwork;
+
+ NetworkAsKey(Network network) {
+ this.mNetwork = network;
+ }
+
+ @Override
+ public int hashCode() {
+ return mNetwork.hashCode();
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof NetworkAsKey)) {
+ return false;
+ }
+ return mNetwork.equals(((NetworkAsKey) other).mNetwork);
+ }
+
+ @Override
+ public String toString() {
+ return "NetworkAsKey{ network=" + mNetwork + " }";
+ }
+ }
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProvider.java b/service-t/src/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProvider.java
index ade7b95..f248c98 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProvider.java
@@ -148,7 +148,7 @@
}
/*** Check whether given network interface can support mdns */
- public static boolean canScanOnInterface(@Nullable NetworkInterfaceWrapper networkInterface) {
+ private static boolean canScanOnInterface(@Nullable NetworkInterfaceWrapper networkInterface) {
try {
if ((networkInterface == null)
|| networkInterface.isLoopback()
diff --git a/service-t/src/com/android/server/connectivity/mdns/MulticastPacketReader.java b/service-t/src/com/android/server/connectivity/mdns/MulticastPacketReader.java
index b597f0a..078c4dd 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MulticastPacketReader.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MulticastPacketReader.java
@@ -59,11 +59,12 @@
* Create a new {@link MulticastPacketReader}.
* @param socket Socket to read from. This will *not* be closed when the reader terminates.
* @param buffer Buffer to read packets into. Will only be used from the handler thread.
+ * @param port the port number for the socket
*/
protected MulticastPacketReader(@NonNull String interfaceTag,
@NonNull ParcelFileDescriptor socket, @NonNull Handler handler,
- @NonNull byte[] buffer) {
- super(handler, new RecvBuffer(buffer, new InetSocketAddress()));
+ @NonNull byte[] buffer, int port) {
+ super(handler, new RecvBuffer(buffer, new InetSocketAddress(port)));
mLogTag = MulticastPacketReader.class.getSimpleName() + "/" + interfaceTag;
mSocket = socket;
mHandler = handler;
diff --git a/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java b/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
new file mode 100644
index 0000000..8f6aecc
--- /dev/null
+++ b/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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.connectivity.mdns;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+
+import com.android.net.module.util.SharedLog;
+import com.android.server.connectivity.mdns.internal.SocketNetlinkMonitor;
+
+/**
+ * The factory class for creating the netlink monitor.
+ */
+public class SocketNetLinkMonitorFactory {
+
+ /**
+ * Creates a new netlink monitor.
+ */
+ public static ISocketNetLinkMonitor createNetLinkMonitor(@NonNull final Handler handler,
+ @NonNull SharedLog log) {
+ return new SocketNetlinkMonitor(handler, log);
+ }
+
+ private SocketNetLinkMonitorFactory() {
+ }
+
+}
diff --git a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
new file mode 100644
index 0000000..e053413
--- /dev/null
+++ b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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.connectivity.mdns.internal;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.system.OsConstants;
+
+import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.ip.NetlinkMonitor;
+import com.android.net.module.util.netlink.NetlinkConstants;
+import com.android.net.module.util.netlink.NetlinkMessage;
+import com.android.server.connectivity.mdns.ISocketNetLinkMonitor;
+
+/**
+ * The netlink monitor for MdnsSocketProvider.
+ */
+public class SocketNetlinkMonitor extends NetlinkMonitor implements ISocketNetLinkMonitor {
+
+ public SocketNetlinkMonitor(@NonNull final Handler handler, @NonNull SharedLog log) {
+ super(handler, log, SocketNetlinkMonitor.class.getSimpleName(), OsConstants.NETLINK_ROUTE,
+ NetlinkConstants.RTMGRP_IPV4_IFADDR | NetlinkConstants.RTMGRP_IPV6_IFADDR);
+ }
+
+ @Override
+ public void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) {
+
+ }
+
+ @Override
+ public boolean isSupported() {
+ return true;
+ }
+
+ @Override
+ public void startMonitoring() {
+ this.start();
+ }
+
+ @Override
+ public void stopMonitoring() {
+ this.stop();
+ }
+}
diff --git a/service-t/src/com/android/server/net/NetworkStatsFactory.java b/service-t/src/com/android/server/net/NetworkStatsFactory.java
index 5952eae..5f66f47 100644
--- a/service-t/src/com/android/server/net/NetworkStatsFactory.java
+++ b/service-t/src/com/android/server/net/NetworkStatsFactory.java
@@ -84,12 +84,9 @@
* are expected to monotonically increase since device boot.
*/
@NonNull
- public NetworkStats getNetworkStatsDetail(int limitUid, @Nullable String[] limitIfaces,
- int limitTag) throws IOException {
+ public NetworkStats getNetworkStatsDetail() throws IOException {
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- // TODO: remove both path and useBpfStats arguments.
- // The path is never used if useBpfStats is true.
- final int ret = nativeReadNetworkStatsDetail(stats, limitUid, limitIfaces, limitTag);
+ final int ret = nativeReadNetworkStatsDetail(stats);
if (ret != 0) {
throw new IOException("Failed to parse network stats");
}
@@ -213,8 +210,7 @@
requestSwapActiveStatsMapLocked();
// Stats are always read from the inactive map, so they must be read after the
// swap
- final NetworkStats stats = mDeps.getNetworkStatsDetail(
- UID_ALL, INTERFACES_ALL, TAG_ALL);
+ final NetworkStats stats = mDeps.getNetworkStatsDetail();
// BPF stats are incremental; fold into mPersistSnapshot.
mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
mPersistSnapshot.combineAllValues(stats);
@@ -301,8 +297,7 @@
* are expected to monotonically increase since device boot.
*/
@VisibleForTesting
- public static native int nativeReadNetworkStatsDetail(NetworkStats stats, int limitUid,
- String[] limitIfaces, int limitTag);
+ public static native int nativeReadNetworkStatsDetail(NetworkStats stats);
@VisibleForTesting
public static native int nativeReadNetworkStatsDev(NetworkStats stats);
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index ad4596d..19f2fb8 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -26,9 +26,14 @@
#include <nativehelper/JNIHelp.h>
#include <net/if.h>
#include <spawn.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <sys/wait.h>
+#include <sys/xattr.h>
#include <string>
+#include <unistd.h>
+#include <android-modules-utils/sdk_level.h>
#include <bpf/BpfMap.h>
#include <bpf/BpfUtils.h>
#include <netjniutils/netjniutils.h>
@@ -45,7 +50,97 @@
#define DEVICEPREFIX "v4-"
namespace android {
-static const char* kClatdPath = "/apex/com.android.tethering/bin/for-system/clatd";
+
+#define ALOGF(s ...) do { ALOGE(s); abort(); } while(0)
+
+enum verify { VERIFY_DIR, VERIFY_BIN, VERIFY_PROG, VERIFY_MAP_RO, VERIFY_MAP_RW };
+
+static void verifyPerms(const char * const path,
+ const mode_t mode, const uid_t uid, const gid_t gid,
+ const char * const ctxt,
+ const verify vtype) {
+ struct stat s = {};
+
+ if (lstat(path, &s)) ALOGF("lstat '%s' errno=%d", path, errno);
+ if (s.st_mode != mode) ALOGF("'%s' mode is 0%o != 0%o", path, s.st_mode, mode);
+ if (s.st_uid != uid) ALOGF("'%s' uid is %d != %d", path, s.st_uid, uid);
+ if (s.st_gid != gid) ALOGF("'%s' gid is %d != %d", path, s.st_gid, gid);
+
+ char b[255] = {};
+ int v = lgetxattr(path, "security.selinux", &b, sizeof(b));
+ if (v < 0) ALOGF("lgetxattr '%s' errno=%d", path, errno);
+ if (strncmp(ctxt, b, sizeof(b))) ALOGF("context of '%s' is '%s' != '%s'", path, b, ctxt);
+
+ int fd = -1;
+
+ switch (vtype) {
+ case VERIFY_DIR: return;
+ case VERIFY_BIN: return;
+ case VERIFY_PROG: fd = bpf::retrieveProgram(path); break;
+ case VERIFY_MAP_RO: fd = bpf::mapRetrieveRO(path); break;
+ case VERIFY_MAP_RW: fd = bpf::mapRetrieveRW(path); break;
+ }
+
+ if (fd < 0) ALOGF("bpf_obj_get '%s' failed, errno=%d", path, errno);
+
+ if (fd >= 0) close(fd);
+}
+
+#undef ALOGF
+
+bool isGsiImage() {
+ // this implementation matches 2 other places in the codebase (same function name too)
+ return !access("/system/system_ext/etc/init/init.gsi.rc", F_OK);
+}
+
+static const char* kClatdDir = "/apex/com.android.tethering/bin/for-system";
+static const char* kClatdBin = "/apex/com.android.tethering/bin/for-system/clatd";
+
+#define V(path, md, uid, gid, ctx, vtype) \
+ verifyPerms((path), (md), AID_ ## uid, AID_ ## gid, "u:object_r:" ctx ":s0", VERIFY_ ## vtype)
+
+static void verifyClatPerms() {
+ // We might run as part of tests instead of as part of system server
+ if (getuid() != AID_SYSTEM) return;
+
+ // First verify the clatd directory and binary,
+ // since this is built into the apex file system image,
+ // failures here are 99% likely to be build problems.
+ V(kClatdDir, S_IFDIR|0750, ROOT, SYSTEM, "system_file", DIR);
+ V(kClatdBin, S_IFREG|S_ISUID|S_ISGID|0755, CLAT, CLAT, "clatd_exec", BIN);
+
+ // Move on to verifying that the bpf programs and maps are as expected.
+ // This relies on the kernel and bpfloader.
+
+ // Clat BPF was only mainlined during T.
+ if (!modules::sdklevel::IsAtLeastT()) return;
+
+ // HACK: some old vendor kernels lack ~5.10 backport of 'bpffs selinux genfscon' support.
+ // This is *NOT* supported, but let's allow, at least for now, U+ GSI to boot on them.
+ // (without this hack pixel5 R vendor + U gsi breaks)
+ if (isGsiImage() && !bpf::isAtLeastKernelVersion(5, 10, 0)) return;
+
+ V("/sys/fs/bpf", S_IFDIR|S_ISVTX|0777, ROOT, ROOT, "fs_bpf", DIR);
+ V("/sys/fs/bpf/net_shared", S_IFDIR|S_ISVTX|0777, ROOT, ROOT, "fs_bpf_net_shared", DIR);
+
+ // pre-U we do not have selinux privs to getattr on bpf maps/progs
+ // so while the below *should* be as listed, we have no way to actually verify
+ if (!modules::sdklevel::IsAtLeastU()) return;
+
+#define V2(path, md, vtype) \
+ V("/sys/fs/bpf/net_shared/" path, (md), ROOT, SYSTEM, "fs_bpf_net_shared", vtype)
+
+ V2("prog_clatd_schedcls_egress4_clat_rawip", S_IFREG|0440, PROG);
+ V2("prog_clatd_schedcls_ingress6_clat_rawip", S_IFREG|0440, PROG);
+ V2("prog_clatd_schedcls_ingress6_clat_ether", S_IFREG|0440, PROG);
+ V2("map_clatd_clat_egress4_map", S_IFREG|0660, MAP_RW);
+ V2("map_clatd_clat_ingress6_map", S_IFREG|0660, MAP_RW);
+
+#undef V2
+
+}
+
+#undef V
static void throwIOException(JNIEnv* env, const char* msg, int error) {
jniThrowExceptionFmt(env, "java/io/IOException", "%s: %s", msg, strerror(error));
@@ -365,7 +460,7 @@
// 5. actually perform vfork/dup2/execve
pid_t pid;
- if (int ret = posix_spawn(&pid, kClatdPath, &fa, &attr, (char* const*)args, nullptr)) {
+ if (int ret = posix_spawn(&pid, kClatdBin, &fa, &attr, (char* const*)args, nullptr)) {
posix_spawnattr_destroy(&attr);
posix_spawn_file_actions_destroy(&fa);
throwIOException(env, "posix_spawn failed", ret);
@@ -405,7 +500,9 @@
if (ret == 0) {
ALOGE("Failed to SIGTERM clatd pid=%d, try SIGKILL", pid);
// TODO: fix that kill failed or waitpid doesn't return.
- kill(pid, SIGKILL);
+ if (kill(pid, SIGKILL)) {
+ ALOGE("Failed to SIGKILL clatd pid=%d: %s", pid, strerror(errno));
+ }
ret = waitpid(pid, &status, 0);
}
if (ret == -1) {
@@ -484,6 +581,7 @@
};
int register_com_android_server_connectivity_ClatCoordinator(JNIEnv* env) {
+ verifyClatPerms();
return jniRegisterNativeMethods(env,
"android/net/connectivity/com/android/server/connectivity/ClatCoordinator",
gMethods, NELEM(gMethods));
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index 7e288c6..acce95d 100644
--- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -52,6 +52,7 @@
import android.system.Os;
import android.system.OsConstants;
import android.system.StructTimeval;
+import android.util.LocalLog;
import android.util.Log;
import android.util.SparseArray;
@@ -152,6 +153,9 @@
// TODO: Remove this when TCP polling design is replaced with callback.
private long mTestLowTcpPollingTimerUntilMs = 0;
+ private static final int MAX_EVENTS_LOGS = 40;
+ private final LocalLog mEventLog = new LocalLog(MAX_EVENTS_LOGS);
+
/**
* Information about a managed keepalive.
*
@@ -230,6 +234,7 @@
@Override
public void binderDied() {
+ mEventLog.log("Binder died : " + mCallback);
mConnectivityServiceHandler.post(() -> cleanupAutoOnOffKeepalive(this));
}
@@ -332,6 +337,7 @@
* @param autoKi the keepalive to resume
*/
public void handleMaybeResumeKeepalive(@NonNull AutomaticOnOffKeepalive autoKi) {
+ mEventLog.log("Resume keepalive " + autoKi.mCallback + " on " + autoKi.getNetwork());
// Might happen if the automatic keepalive was removed by the app just as the alarm fires.
if (!mAutomaticOnOffKeepalives.contains(autoKi)) return;
if (STATE_ALWAYS_ON == autoKi.mAutomaticOnOffState) {
@@ -377,6 +383,7 @@
* Handle stop all keepalives on the specific network.
*/
public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) {
+ mEventLog.log("Stop all keepalives on " + nai.network + " because " + reason);
mKeepaliveTracker.handleStopAllKeepalives(nai, reason);
final List<AutomaticOnOffKeepalive> matches =
CollectionUtils.filter(mAutomaticOnOffKeepalives, it -> it.mKi.getNai() == nai);
@@ -392,6 +399,7 @@
*/
public void handleStartKeepalive(Message message) {
final AutomaticOnOffKeepalive autoKi = (AutomaticOnOffKeepalive) message.obj;
+ mEventLog.log("Start keepalive " + autoKi.mCallback + " on " + autoKi.getNetwork());
mKeepaliveTracker.handleStartKeepalive(autoKi.mKi);
// Add automatic on/off request into list to track its life cycle.
@@ -410,9 +418,11 @@
private void handleResumeKeepalive(@NonNull final KeepaliveTracker.KeepaliveInfo ki) {
mKeepaliveTracker.handleStartKeepalive(ki);
+ mEventLog.log("Resumed successfully keepalive " + ki.mCallback + " on " + ki.mNai);
}
private void handlePauseKeepalive(@NonNull final KeepaliveTracker.KeepaliveInfo ki) {
+ mEventLog.log("Suspend keepalive " + ki.mCallback + " on " + ki.mNai);
// TODO : mKT.handleStopKeepalive should take a KeepaliveInfo instead
mKeepaliveTracker.handleStopKeepalive(ki.getNai(), ki.getSlot(), SUCCESS_PAUSED);
}
@@ -421,6 +431,7 @@
* Handle stop keepalives on the specific network with given slot.
*/
public void handleStopKeepalive(@NonNull final AutomaticOnOffKeepalive autoKi, int reason) {
+ mEventLog.log("Stop keepalive " + autoKi.mCallback + " because " + reason);
// Stop the keepalive unless it was suspended. This includes the case where it's managed
// but enabled, and the case where it's always on.
if (autoKi.mAutomaticOnOffState != STATE_SUSPENDED) {
@@ -466,6 +477,11 @@
try {
final AutomaticOnOffKeepalive autoKi = new AutomaticOnOffKeepalive(ki,
automaticOnOffKeepalives, underpinnedNetwork);
+ mEventLog.log("Start natt keepalive " + cb + " on " + nai.network
+ + " " + srcAddrString + ":" + srcPort
+ + " → " + dstAddrString + ":" + dstPort
+ + " auto=" + autoKi
+ + " underpinned=" + underpinnedNetwork);
mConnectivityServiceHandler.obtainMessage(NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
// TODO : move ConnectivityService#encodeBool to a static lib.
automaticOnOffKeepalives ? 1 : 0, 0, autoKi).sendToTarget();
@@ -496,6 +512,11 @@
try {
final AutomaticOnOffKeepalive autoKi = new AutomaticOnOffKeepalive(ki,
automaticOnOffKeepalives, underpinnedNetwork);
+ mEventLog.log("Start natt keepalive " + cb + " on " + nai.network
+ + " " + srcAddrString
+ + " → " + dstAddrString + ":" + dstPort
+ + " auto=" + autoKi
+ + " underpinned=" + underpinnedNetwork);
mConnectivityServiceHandler.obtainMessage(NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
// TODO : move ConnectivityService#encodeBool to a static lib.
automaticOnOffKeepalives ? 1 : 0, 0, autoKi).sendToTarget();
@@ -550,6 +571,11 @@
pw.println(autoKi.toString());
}
pw.decreaseIndent();
+
+ pw.println("Events (most recent first):");
+ pw.increaseIndent();
+ mEventLog.reverseDump(pw);
+ pw.decreaseIndent();
}
/**
diff --git a/service/src/com/android/server/connectivity/NetworkNotificationManager.java b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
index 45da0ea..cdc0aa9 100644
--- a/service/src/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
@@ -22,6 +22,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import android.annotation.NonNull;
+import android.app.ActivityOptions;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -33,6 +34,8 @@
import android.net.NetworkSpecifier;
import android.net.TelephonyNetworkSpecifier;
import android.net.wifi.WifiInfo;
+import android.os.Build;
+import android.os.Bundle;
import android.os.UserHandle;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -45,6 +48,7 @@
import com.android.connectivity.resources.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.modules.utils.build.SdkLevel;
public class NetworkNotificationManager {
@@ -328,7 +332,26 @@
}
try {
- intent.send();
+ Bundle options = null;
+
+ if (SdkLevel.isAtLeastU() && intent.isActivity()) {
+ // Also check SDK_INT >= T separately, as the linter in some T-based branches does
+ // not recognize "isAtLeastU && something" as an SDK check for T+ APIs.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ // Android U requires pending intent background start mode to be specified:
+ // See #background-activity-restrictions in
+ // https://developer.android.com/about/versions/14/behavior-changes-14
+ // But setPendingIntentBackgroundActivityStartMode is U+, and replaces
+ // setPendingIntentBackgroundActivityLaunchAllowed which is T+ but deprecated.
+ // Use setPendingIntentBackgroundActivityLaunchAllowed as the U+ version is not
+ // yet available in all branches.
+ final ActivityOptions activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setPendingIntentBackgroundActivityLaunchAllowed(true);
+ options = activityOptions.toBundle();
+ }
+ }
+
+ intent.send(null, 0, null, null, null, null, options);
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Error sending dialog PendingIntent", e);
}
diff --git a/tests/common/java/android/net/NetworkCapabilitiesTest.java b/tests/common/java/android/net/NetworkCapabilitiesTest.java
index 06af3c0..aae3425 100644
--- a/tests/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -1387,6 +1387,9 @@
// If the test network is restricted, then the network may declare any transport, and
// appended with TRANSPORT_TEST.
expectedNcBuilder.addTransportType(TRANSPORT_CELLULAR);
+ } else {
+ // If the test network only has TRANSPORT_TEST, then it can keep the subscription IDs.
+ expectedNcBuilder.setSubscriptionIds(Set.of(TEST_SUBID1));
}
expectedNcBuilder.addTransportType(TRANSPORT_TEST);
diff --git a/tests/common/java/android/net/netstats/NetworkTemplateTest.kt b/tests/common/java/android/net/netstats/NetworkTemplateTest.kt
index fb6759e..fd7bd74 100644
--- a/tests/common/java/android/net/netstats/NetworkTemplateTest.kt
+++ b/tests/common/java/android/net/netstats/NetworkTemplateTest.kt
@@ -30,16 +30,17 @@
import android.net.NetworkTemplate.MATCH_WIFI
import android.net.NetworkTemplate.NETWORK_TYPE_ALL
import android.net.NetworkTemplate.OEM_MANAGED_ALL
+import android.os.Build
import android.telephony.TelephonyManager
import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.SC_V2
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
private const val TEST_IMSI1 = "imsi"
private const val TEST_WIFI_KEY1 = "wifiKey1"
@@ -96,9 +97,27 @@
}
// Verify carrier and mobile template cannot contain one of subscriber Id is null.
- listOf(MATCH_MOBILE, MATCH_CARRIER).forEach {
+ assertFailsWith<IllegalArgumentException> {
+ NetworkTemplate.Builder(MATCH_CARRIER).setSubscriberIds(setOf(null)).build()
+ }
+ val firstSdk = Build.VERSION.DEVICE_INITIAL_SDK_INT
+ if (firstSdk > Build.VERSION_CODES.TIRAMISU) {
assertFailsWith<IllegalArgumentException> {
- NetworkTemplate.Builder(it).setSubscriberIds(setOf(null)).build()
+ NetworkTemplate.Builder(MATCH_MOBILE).setSubscriberIds(setOf(null)).build()
+ }
+ } else {
+ NetworkTemplate.Builder(MATCH_MOBILE).setSubscriberIds(setOf(null)).build().let {
+ val expectedTemplate = NetworkTemplate(
+ MATCH_MOBILE,
+ arrayOfNulls<String>(1) /*subscriberIds*/,
+ emptyArray<String>() /*wifiNetworkKey*/,
+ METERED_ALL,
+ ROAMING_ALL,
+ DEFAULT_NETWORK_ALL,
+ NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL
+ )
+ assertEquals(expectedTemplate, it)
}
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index b6902b5..c28ee64 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -1274,6 +1274,31 @@
}
@Test
+ public void testSocketClosed() throws Exception {
+ assumeTrue(supportedHardware());
+
+ final FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS);
+ final List<FileDescriptor> remoteFds = new ArrayList<>();
+
+ for (int i = 0; i < 30; i++) {
+ remoteFds.add(openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS));
+ }
+
+ final String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+ new String[] {"192.0.2.0/24", "2001:db8::/32"},
+ allowedApps, "", null, null /* underlyingNetworks */, false /* isAlwaysMetered */);
+
+ // Socket owned by VPN uid is not closed
+ assertSocketStillOpen(localFd, TEST_HOST);
+
+ // Sockets not owned by VPN uid are closed
+ for (final FileDescriptor remoteFd: remoteFds) {
+ assertSocketClosed(remoteFd, TEST_HOST);
+ }
+ }
+
+ @Test
public void testExcludedRoutes() throws Exception {
assumeTrue(supportedHardware());
assumeTrue(SdkLevel.isAtLeastT());
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
index 603779d..3ca4775 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
@@ -51,6 +51,10 @@
runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testAppDisallowed");
}
+ public void testSocketClosed() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testSocketClosed");
+ }
+
public void testGetConnectionOwnerUidSecurity() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testGetConnectionOwnerUidSecurity");
}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 774176f..d6d384b 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -2409,6 +2409,7 @@
}
}
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
public void testBlockedStatusCallback() throws Exception {
// Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
@@ -2555,15 +2556,14 @@
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
- final TestableNetworkCallback wifiCb = new TestableNetworkCallback();
- mCtsNetUtils.ensureWifiConnected();
- registerCallbackAndWaitForAvailable(makeWifiNetworkRequest(), wifiCb);
+ tetherUtils.startWifiTethering(tetherEventCallback);
// Update setting to verify the behavior.
setAirplaneMode(true);
- // Verify wifi lost to make sure airplane mode takes effect. This could
+ // Verify softap lost to make sure airplane mode takes effect. This could
// prevent the race condition between airplane mode enabled and the followed
// up wifi tethering enabled.
- waitForLost(wifiCb);
+ tetherEventCallback.expectNoTetheringActive();
+
// start wifi tethering
tetherUtils.startWifiTethering(tetherEventCallback);
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 4c63cb0..db7f38c 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -96,6 +96,8 @@
import com.android.testutils.waitForIdle
import java.io.File
import java.io.IOException
+import java.net.Inet6Address
+import java.net.InetAddress
import java.net.NetworkInterface
import java.net.ServerSocket
import java.nio.charset.StandardCharsets
@@ -132,6 +134,7 @@
@AppModeFull(reason = "Socket cannot bind in instant app mode")
@RunWith(AndroidJUnit4::class)
+@ConnectivityModuleTest
class NsdManagerTest {
// Rule used to filter CtsNetTestCasesMaxTargetSdkXX
@get:Rule
@@ -400,6 +403,13 @@
// Wait until the link-local address can be used. Address flags are not available without
// elevated permissions, so check that bindSocket works.
PollingCheck.check("No usable v6 address on interface after $TIMEOUT_MS ms", TIMEOUT_MS) {
+ // To avoid race condition between socket connection succeeding and interface returning
+ // a non-empty address list. Verify that interface returns a non-empty list, before
+ // trying the socket connection.
+ if (NetworkInterface.getByName(ifaceName).interfaceAddresses.isEmpty()) {
+ return@check false
+ }
+
val sock = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
tryTest {
network.bindSocket(sock)
@@ -697,6 +707,20 @@
}
}
+ private fun checkAddressScopeId(iface: TestNetworkInterface, address: List<InetAddress>) {
+ val targetSdkVersion = context.packageManager
+ .getTargetSdkVersion(context.applicationInfo.packageName)
+ if (targetSdkVersion <= Build.VERSION_CODES.TIRAMISU) {
+ return
+ }
+ val ifaceIdx = NetworkInterface.getByName(iface.interfaceName).index
+ address.forEach {
+ if (it is Inet6Address && it.isLinkLocalAddress) {
+ assertEquals(ifaceIdx, it.scopeId)
+ }
+ }
+ }
+
@Test
fun testNsdManager_ResolveOnNetwork() {
// This test requires shims supporting T+ APIs (NsdServiceInfo.network)
@@ -732,6 +756,7 @@
assertEquals(registeredInfo.serviceName, it.serviceName)
assertEquals(si.port, it.port)
assertEquals(testNetwork1.network, nsdShim.getNetwork(it))
+ checkAddressScopeId(testNetwork1.iface, it.hostAddresses)
}
// TODO: check that MDNS packets are sent only on testNetwork1.
} cleanupStep {
@@ -971,6 +996,7 @@
for (hostAddress in hostAddresses) {
assertTrue(addresses.contains(hostAddress))
}
+ checkAddressScopeId(testNetwork1.iface, serviceInfoCb.serviceInfo.hostAddresses)
} cleanupStep {
nsdManager.unregisterService(registrationRecord)
registrationRecord.expectCallback<ServiceUnregistered>()
diff --git a/tests/cts/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java b/tests/cts/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
index cbe54f8..1a780a7 100644
--- a/tests/cts/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
+++ b/tests/cts/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
@@ -66,7 +66,6 @@
InetAddress[] addresses;
try {
addresses = InetAddress.getAllByName(TEST_HOST);
- mTestHostAddress = addresses[0];
} catch (UnknownHostException uhe) {
throw new AssertionError(
"Unable to test SSLCertificateSocketFactory: cannot resolve " + TEST_HOST, uhe);
@@ -76,10 +75,11 @@
.map(addr -> new InetSocketAddress(addr, HTTPS_PORT))
.collect(Collectors.toList());
- // Find the local IP address which will be used to connect to TEST_HOST.
+ // Find the local and remote IP addresses which will be used to connect to TEST_HOST.
try {
Socket testSocket = new Socket(TEST_HOST, HTTPS_PORT);
mLocalAddress = testSocket.getLocalAddress();
+ mTestHostAddress = testSocket.getInetAddress();
testSocket.close();
} catch (IOException ioe) {
throw new AssertionError(""
diff --git a/tests/unit/java/android/net/NetworkTemplateTest.kt b/tests/unit/java/android/net/NetworkTemplateTest.kt
index fc25fd8..2f6c76b 100644
--- a/tests/unit/java/android/net/NetworkTemplateTest.kt
+++ b/tests/unit/java/android/net/NetworkTemplateTest.kt
@@ -50,16 +50,17 @@
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.assertParcelSane
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertNotEquals
+import kotlin.test.assertTrue
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import kotlin.test.assertEquals
-import kotlin.test.assertFalse
-import kotlin.test.assertNotEquals
-import kotlin.test.assertTrue
private const val TEST_IMSI1 = "imsi1"
private const val TEST_IMSI2 = "imsi2"
@@ -70,6 +71,8 @@
@RunWith(DevSdkIgnoreRunner::class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
class NetworkTemplateTest {
+ @get:Rule
+ val ignoreRule = DevSdkIgnoreRule()
private val mockContext = mock(Context::class.java)
private val mockWifiInfo = mock(WifiInfo::class.java)
@@ -215,6 +218,18 @@
templateNullWifiKey.assertDoesNotMatch(identWifiNullKey)
}
+ @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.TIRAMISU)
+ @Test
+ fun testBuildTemplateMobileAll_nullSubscriberId() {
+ val templateMobileAllWithNullImsi = buildTemplateMobileAll(null)
+ val setWithNull = HashSet<String?>().apply {
+ add(null)
+ }
+ val templateFromBuilder = NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES)
+ .setSubscriberIds(setWithNull).build()
+ assertEquals(templateFromBuilder, templateMobileAllWithNullImsi)
+ }
+
@Test
fun testMobileMatches() {
val templateMobileImsi1 = buildTemplateMobileAll(TEST_IMSI1)
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
index 6e1debe..d9420b8 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
@@ -16,6 +16,11 @@
package com.android.server.connectivity.mdns;
+import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
import static com.android.testutils.ContextUtils.mockService;
import static org.junit.Assert.assertEquals;
@@ -35,10 +40,10 @@
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
-import android.net.INetd;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.TetheringManager;
import android.net.TetheringManager.TetheringEventCallback;
import android.os.Build;
@@ -74,8 +79,6 @@
private static final LinkAddress LINKADDRV6 =
new LinkAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64");
private static final Network TEST_NETWORK = new Network(123);
- private static final Network LOCAL_NETWORK = new Network(INetd.LOCAL_NET_ID);
-
@Mock private Context mContext;
@Mock private Dependencies mDeps;
@Mock private ConnectivityManager mCm;
@@ -101,8 +104,13 @@
// Test is using mockito-extended
doCallRealMethod().when(mContext).getSystemService(TetheringManager.class);
}
- doReturn(true).when(mDeps).canScanOnInterface(any());
doReturn(mTestNetworkIfaceWrapper).when(mDeps).getNetworkInterfaceByName(anyString());
+ doReturn(true).when(mTestNetworkIfaceWrapper).isUp();
+ doReturn(true).when(mLocalOnlyIfaceWrapper).isUp();
+ doReturn(true).when(mTetheredIfaceWrapper).isUp();
+ doReturn(true).when(mTestNetworkIfaceWrapper).supportsMulticast();
+ doReturn(true).when(mLocalOnlyIfaceWrapper).supportsMulticast();
+ doReturn(true).when(mTetheredIfaceWrapper).supportsMulticast();
doReturn(mLocalOnlyIfaceWrapper).when(mDeps)
.getNetworkInterfaceByName(LOCAL_ONLY_IFACE_NAME);
doReturn(mTetheredIfaceWrapper).when(mDeps).getNetworkInterfaceByName(TETHERED_IFACE_NAME);
@@ -208,6 +216,24 @@
}
}
+ private static NetworkCapabilities makeCapabilities(int... transports) {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ for (int transport : transports) {
+ nc.addTransportType(transport);
+ }
+ return nc;
+ }
+
+ private void postNetworkAvailable(int... transports) {
+ final LinkProperties testLp = new LinkProperties();
+ testLp.setInterfaceName(TEST_IFACE_NAME);
+ testLp.setLinkAddresses(List.of(LINKADDRV4));
+ final NetworkCapabilities testNc = makeCapabilities(transports);
+ mHandler.post(() -> mNetworkCallback.onCapabilitiesChanged(TEST_NETWORK, testNc));
+ mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ }
+
@Test
public void testSocketRequestAndUnrequestSocket() {
startMonitoringSockets();
@@ -217,12 +243,7 @@
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
testCallback1.expectedNoCallback();
- final LinkProperties testLp = new LinkProperties();
- testLp.setInterfaceName(TEST_IFACE_NAME);
- testLp.setLinkAddresses(List.of(LINKADDRV4));
- mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
- verify(mTestNetworkIfaceWrapper).getNetworkInterface();
+ postNetworkAvailable(TRANSPORT_WIFI);
testCallback1.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
final TestSocketCallback testCallback2 = new TestSocketCallback();
@@ -244,7 +265,7 @@
verify(mLocalOnlyIfaceWrapper).getNetworkInterface();
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
- testCallback3.expectedSocketCreatedForNetwork(LOCAL_NETWORK, List.of());
+ testCallback3.expectedSocketCreatedForNetwork(null /* network */, List.of());
mHandler.post(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
List.of(TETHERED_IFACE_NAME)));
@@ -252,7 +273,7 @@
verify(mTetheredIfaceWrapper).getNetworkInterface();
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
- testCallback3.expectedSocketCreatedForNetwork(LOCAL_NETWORK, List.of());
+ testCallback3.expectedSocketCreatedForNetwork(null /* network */, List.of());
mHandler.post(() -> mSocketProvider.unrequestSocket(testCallback1));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
@@ -270,14 +291,14 @@
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
- testCallback3.expectedInterfaceDestroyedForNetwork(LOCAL_NETWORK);
+ testCallback3.expectedInterfaceDestroyedForNetwork(null /* network */);
mHandler.post(() -> mSocketProvider.unrequestSocket(testCallback3));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
// Expect the socket destroy for tethered interface.
- testCallback3.expectedInterfaceDestroyedForNetwork(LOCAL_NETWORK);
+ testCallback3.expectedInterfaceDestroyedForNetwork(null /* network */);
}
@Test
@@ -289,12 +310,7 @@
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
testCallback.expectedNoCallback();
- final LinkProperties testLp = new LinkProperties();
- testLp.setInterfaceName(TEST_IFACE_NAME);
- testLp.setLinkAddresses(List.of(LINKADDRV4));
- mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
- verify(mTestNetworkIfaceWrapper, times(1)).getNetworkInterface();
+ postNetworkAvailable(TRANSPORT_WIFI);
testCallback.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
final LinkProperties newTestLp = new LinkProperties();
@@ -302,7 +318,6 @@
newTestLp.setLinkAddresses(List.of(LINKADDRV4, LINKADDRV6));
mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, newTestLp));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
- verify(mTestNetworkIfaceWrapper, times(1)).getNetworkInterface();
testCallback.expectedAddressesChangedForNetwork(
TEST_NETWORK, List.of(LINKADDRV4, LINKADDRV6));
}
@@ -406,4 +421,77 @@
verify(mTestNetworkIfaceWrapper, times(2)).getNetworkInterface();
testCallback.expectedSocketCreatedForNetwork(otherNetwork, List.of(otherAddress));
}
+
+ @Test
+ public void testNoSocketCreatedForCellular() {
+ startMonitoringSockets();
+
+ final TestSocketCallback testCallback = new TestSocketCallback();
+ mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+
+ postNetworkAvailable(TRANSPORT_CELLULAR);
+ testCallback.expectedNoCallback();
+ }
+
+ @Test
+ public void testNoSocketCreatedForNonMulticastInterface() throws Exception {
+ doReturn(false).when(mTestNetworkIfaceWrapper).supportsMulticast();
+ startMonitoringSockets();
+
+ final TestSocketCallback testCallback = new TestSocketCallback();
+ mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+
+ postNetworkAvailable(TRANSPORT_BLUETOOTH);
+ testCallback.expectedNoCallback();
+ }
+
+ @Test
+ public void testSocketCreatedForMulticastInterface() throws Exception {
+ doReturn(true).when(mTestNetworkIfaceWrapper).supportsMulticast();
+ startMonitoringSockets();
+
+ final TestSocketCallback testCallback = new TestSocketCallback();
+ mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+
+ postNetworkAvailable(TRANSPORT_BLUETOOTH);
+ testCallback.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
+ }
+
+ @Test
+ public void testNoSocketCreatedForPTPInterface() throws Exception {
+ doReturn(true).when(mTestNetworkIfaceWrapper).isPointToPoint();
+ startMonitoringSockets();
+
+ final TestSocketCallback testCallback = new TestSocketCallback();
+ mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+
+ postNetworkAvailable(TRANSPORT_BLUETOOTH);
+ testCallback.expectedNoCallback();
+ }
+
+ @Test
+ public void testNoSocketCreatedForVPNInterface() throws Exception {
+ // VPN interfaces generally also have IFF_POINTOPOINT, but even if they don't, they should
+ // not be included even with TRANSPORT_WIFI.
+ doReturn(false).when(mTestNetworkIfaceWrapper).supportsMulticast();
+ startMonitoringSockets();
+
+ final TestSocketCallback testCallback = new TestSocketCallback();
+ mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+
+ postNetworkAvailable(TRANSPORT_VPN, TRANSPORT_WIFI);
+ testCallback.expectedNoCallback();
+ }
+
+ @Test
+ public void testSocketCreatedForWifiWithoutMulticastFlag() throws Exception {
+ doReturn(false).when(mTestNetworkIfaceWrapper).supportsMulticast();
+ startMonitoringSockets();
+
+ final TestSocketCallback testCallback = new TestSocketCallback();
+ mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+
+ postNetworkAvailable(TRANSPORT_WIFI);
+ testCallback.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
+ }
}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
index 04db6d3..63daebc 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -472,8 +472,7 @@
256L, 16L, 512L, 32L, 0L)
.insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 64L, 3L, 1024L, 8L, 0L);
- doReturn(stats).when(mDeps).getNetworkStatsDetail(anyInt(), any(),
- anyInt());
+ doReturn(stats).when(mDeps).getNetworkStatsDetail();
final String[] ifaces = new String[]{TEST_IFACE};
final NetworkStats res = mFactory.readNetworkStatsDetail(UID_ALL, ifaces, TAG_ALL);
@@ -488,8 +487,7 @@
mFactory.removeUidsLocked(removedUids);
// Return empty stats for reading the result of removing uids stats later.
- doReturn(buildEmptyStats()).when(mDeps).getNetworkStatsDetail(anyInt(), any(),
- anyInt());
+ doReturn(buildEmptyStats()).when(mDeps).getNetworkStatsDetail();
final NetworkStats removedUidsStats =
mFactory.readNetworkStatsDetail(UID_ALL, ifaces, TAG_ALL);
@@ -574,8 +572,7 @@
final NetworkStats statsFromResource = parseNetworkStatsFromGoldenSample(resourceId,
24 /* initialSize */, true /* consumeHeader */, false /* checkActive */,
true /* isUidData */);
- doReturn(statsFromResource).when(mDeps).getNetworkStatsDetail(anyInt(), any(),
- anyInt());
+ doReturn(statsFromResource).when(mDeps).getNetworkStatsDetail();
return mFactory.readNetworkStatsDetail();
}