Merge "Baseline NewApi issues" into main
diff --git a/Cronet/tests/common/AndroidTest.xml b/Cronet/tests/common/AndroidTest.xml
index 2ac418f..33c3184 100644
--- a/Cronet/tests/common/AndroidTest.xml
+++ b/Cronet/tests/common/AndroidTest.xml
@@ -28,6 +28,24 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.net.http.tests.coverage" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <!-- b/298380508 -->
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testSetLibraryLoaderIsEnforcedByDefaultEmbeddedProvider" />
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testSetLibraryLoaderIsIgnoredInNativeCronetEngineBuilderImpl" />
+ <!-- b/316571753 -->
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testBaseFeatureFlagsOverridesEnabled" />
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAppliedIfAppIdMatches" />
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAreLoaded" />
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testSetLibraryLoaderIsEnforcedByDefaultEmbeddedProvider" />
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAppliedIfAtMinVersion" />
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAppliedIfAboveMinVersion" />
+ <!-- b/316567693 -->
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestTest#testSSLCertificateError" />
+ <!-- b/316559294 -->
+ <option name="exclude-filter" value="org.chromium.net.NQETest#testQuicDisabled" />
+ <!-- b/316554711-->
+ <option name="exclude-filter" value="org.chromium.net.NetworkChangesTest" />
+ <!-- b/316550794 -->
+ <option name="exclude-filter" value="org.chromium.net.impl.CronetLoggerTest#testEngineCreation" />
<option name="hidden-api-checks" value="false"/>
<option
name="device-listeners"
diff --git a/Cronet/tests/mts/AndroidTest.xml b/Cronet/tests/mts/AndroidTest.xml
index 0d780a1..3470531 100644
--- a/Cronet/tests/mts/AndroidTest.xml
+++ b/Cronet/tests/mts/AndroidTest.xml
@@ -28,6 +28,24 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.net.http.mts" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <!-- b/298380508 -->
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testSetLibraryLoaderIsEnforcedByDefaultEmbeddedProvider" />
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testSetLibraryLoaderIsIgnoredInNativeCronetEngineBuilderImpl" />
+ <!-- b/316571753 -->
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testBaseFeatureFlagsOverridesEnabled" />
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAppliedIfAppIdMatches" />
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAreLoaded" />
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testSetLibraryLoaderIsEnforcedByDefaultEmbeddedProvider" />
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAppliedIfAtMinVersion" />
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAppliedIfAboveMinVersion" />
+ <!-- b/316567693 -->
+ <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestTest#testSSLCertificateError" />
+ <!-- b/316559294 -->
+ <option name="exclude-filter" value="org.chromium.net.NQETest#testQuicDisabled" />
+ <!-- b/316554711-->
+ <option name="exclude-filter" value="org.chromium.net.NetworkChangesTest" />
+ <!-- b/316550794 -->
+ <option name="exclude-filter" value="org.chromium.net.impl.CronetLoggerTest#testEngineCreation" />
<option name="hidden-api-checks" value="false"/>
</test>
diff --git a/Cronet/tests/mts/jarjar_excludes.txt b/Cronet/tests/mts/jarjar_excludes.txt
index a0ce5c2..fd0a0f6 100644
--- a/Cronet/tests/mts/jarjar_excludes.txt
+++ b/Cronet/tests/mts/jarjar_excludes.txt
@@ -17,4 +17,5 @@
org\.chromium\.net\.NativeTestServer(\$.+)?
org\.chromium\.net\.MockUrlRequestJobFactory(\$.+)?
org\.chromium\.net\.QuicTestServer(\$.+)?
-org\.chromium\.net\.MockCertVerifier(\$.+)?
\ No newline at end of file
+org\.chromium\.net\.MockCertVerifier(\$.+)?
+org\.chromium\.net\.LogcatCapture(\$.+)?
\ No newline at end of file
diff --git a/Cronet/tests/mts/res/values/cronet-test-rule-configuration.xml b/Cronet/tests/mts/res/values/cronet-test-rule-configuration.xml
new file mode 100644
index 0000000..48ce420
--- /dev/null
+++ b/Cronet/tests/mts/res/values/cronet-test-rule-configuration.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<resources>
+ <bool name="is_running_in_aosp">true</bool>
+</resources>
\ No newline at end of file
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 5022b40..552b105 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -136,6 +136,7 @@
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.HandlerUtils;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.SdkUtil.LateSdk;
import com.android.net.module.util.SharedLog;
@@ -161,11 +162,8 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
/**
*
@@ -2694,31 +2692,10 @@
return;
}
- final CountDownLatch latch = new CountDownLatch(1);
-
- // Don't crash the system if something in doDump throws an exception, but try to propagate
- // the exception to the caller.
- AtomicReference<RuntimeException> exceptionRef = new AtomicReference<>();
- mHandler.post(() -> {
- try {
- doDump(fd, writer, args);
- } catch (RuntimeException e) {
- exceptionRef.set(e);
- }
- latch.countDown();
- });
-
- try {
- if (!latch.await(DUMP_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- writer.println("Dump timeout after " + DUMP_TIMEOUT_MS + "ms");
- return;
- }
- } catch (InterruptedException e) {
- exceptionRef.compareAndSet(null, new IllegalStateException("Dump interrupted", e));
+ if (!HandlerUtils.runWithScissorsForDump(mHandler, () -> doDump(fd, writer, args),
+ DUMP_TIMEOUT_MS)) {
+ writer.println("Dump timeout after " + DUMP_TIMEOUT_MS + "ms");
}
-
- final RuntimeException e = exceptionRef.get();
- if (e != null) throw e;
}
private void maybeDhcpLeasesChanged() {
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 82b8845..750bfce 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -2810,12 +2810,10 @@
final FileDescriptor mockFd = mock(FileDescriptor.class);
final PrintWriter mockPw = mock(PrintWriter.class);
runUsbTethering(null);
- mLooper.startAutoDispatch();
mTethering.dump(mockFd, mockPw, new String[0]);
verify(mConfig).dump(any());
verify(mEntitleMgr).dump(any());
verify(mOffloadCtrl).dump(any());
- mLooper.stopAutoDispatch();
}
@Test
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 76481c8..6c25d76 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -271,15 +271,11 @@
protected final int mClientRequestId;
protected final int mTransactionId;
@NonNull
- protected final NsdServiceInfo mReqServiceInfo;
- @NonNull
protected final String mListenedServiceType;
- MdnsListener(int clientRequestId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo,
- @NonNull String listenedServiceType) {
+ MdnsListener(int clientRequestId, int transactionId, @NonNull String listenedServiceType) {
mClientRequestId = clientRequestId;
mTransactionId = transactionId;
- mReqServiceInfo = reqServiceInfo;
mListenedServiceType = listenedServiceType;
}
@@ -322,8 +318,8 @@
private class DiscoveryListener extends MdnsListener {
DiscoveryListener(int clientRequestId, int transactionId,
- @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType) {
- super(clientRequestId, transactionId, reqServiceInfo, listenServiceType);
+ @NonNull String listenServiceType) {
+ super(clientRequestId, transactionId, listenServiceType);
}
@Override
@@ -352,8 +348,8 @@
private class ResolutionListener extends MdnsListener {
ResolutionListener(int clientRequestId, int transactionId,
- @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType) {
- super(clientRequestId, transactionId, reqServiceInfo, listenServiceType);
+ @NonNull String listenServiceType) {
+ super(clientRequestId, transactionId, listenServiceType);
}
@Override
@@ -374,8 +370,8 @@
private class ServiceInfoListener extends MdnsListener {
ServiceInfoListener(int clientRequestId, int transactionId,
- @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType) {
- super(clientRequestId, transactionId, reqServiceInfo, listenServiceType);
+ @NonNull String listenServiceType) {
+ super(clientRequestId, transactionId, listenServiceType);
}
@Override
@@ -542,13 +538,13 @@
}
private void maybeStartDaemon() {
- if (mMDnsManager == null) {
- Log.wtf(TAG, "maybeStartDaemon: mMDnsManager is null");
+ if (mIsDaemonStarted) {
+ if (DBG) Log.d(TAG, "Daemon is already started.");
return;
}
- if (mIsDaemonStarted) {
- if (DBG) Log.d(TAG, "Daemon is already started.");
+ if (mMDnsManager == null) {
+ Log.wtf(TAG, "maybeStartDaemon: mMDnsManager is null");
return;
}
mMDnsManager.registerEventListener(mMDnsEventCallback);
@@ -559,13 +555,13 @@
}
private void maybeStopDaemon() {
- if (mMDnsManager == null) {
- Log.wtf(TAG, "maybeStopDaemon: mMDnsManager is null");
+ if (!mIsDaemonStarted) {
+ if (DBG) Log.d(TAG, "Daemon has not been started.");
return;
}
- if (!mIsDaemonStarted) {
- if (DBG) Log.d(TAG, "Daemon has not been started.");
+ if (mMDnsManager == null) {
+ Log.wtf(TAG, "maybeStopDaemon: mMDnsManager is null");
return;
}
mMDnsManager.unregisterEventListener(mMDnsEventCallback);
@@ -780,7 +776,7 @@
final String listenServiceType = serviceType + ".local";
maybeStartMonitoringSockets();
final MdnsListener listener = new DiscoveryListener(clientRequestId,
- transactionId, info, listenServiceType);
+ transactionId, listenServiceType);
final MdnsSearchOptions.Builder optionsBuilder =
MdnsSearchOptions.newBuilder()
.setNetwork(info.getNetwork())
@@ -1032,7 +1028,7 @@
maybeStartMonitoringSockets();
final MdnsListener listener = new ResolutionListener(clientRequestId,
- transactionId, info, resolveServiceType);
+ transactionId, resolveServiceType);
final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
.setNetwork(info.getNetwork())
.setIsPassiveMode(true)
@@ -1130,7 +1126,7 @@
maybeStartMonitoringSockets();
final MdnsListener listener = new ServiceInfoListener(clientRequestId,
- transactionId, info, resolveServiceType);
+ transactionId, resolveServiceType);
final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
.setNetwork(info.getNetwork())
.setIsPassiveMode(true)
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 6b47654..3b31ed2 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -291,6 +291,7 @@
import com.android.net.module.util.BpfUtils;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.DeviceConfigUtils;
+import com.android.net.module.util.HandlerUtils;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
@@ -315,7 +316,6 @@
import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
import com.android.server.connectivity.DscpPolicyTracker;
import com.android.server.connectivity.FullScore;
-import com.android.server.connectivity.HandlerUtils;
import com.android.server.connectivity.InvalidTagException;
import com.android.server.connectivity.KeepaliveResourceUtil;
import com.android.server.connectivity.KeepaliveTracker;
@@ -1280,7 +1280,7 @@
LocalPriorityDump() {}
private void dumpHigh(FileDescriptor fd, PrintWriter pw) {
- if (!HandlerUtils.runWithScissors(mHandler, () -> {
+ if (!HandlerUtils.runWithScissorsForDump(mHandler, () -> {
doDump(fd, pw, new String[]{DIAG_ARG});
doDump(fd, pw, new String[]{SHORT_ARG});
}, DUMPSYS_DEFAULT_TIMEOUT_MS)) {
@@ -1289,7 +1289,7 @@
}
private void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!HandlerUtils.runWithScissors(mHandler, () -> doDump(fd, pw, args),
+ if (!HandlerUtils.runWithScissorsForDump(mHandler, () -> doDump(fd, pw, args),
DUMPSYS_DEFAULT_TIMEOUT_MS)) {
pw.println("dumpNormal timeout");
}
diff --git a/service/src/com/android/server/connectivity/HandlerUtils.java b/service/src/com/android/server/connectivity/HandlerUtils.java
deleted file mode 100644
index 997ecbf..0000000
--- a/service/src/com/android/server/connectivity/HandlerUtils.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2023 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;
-
-import android.annotation.NonNull;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-
-/**
- * Helper class for Handler related utilities.
- *
- * @hide
- */
-public class HandlerUtils {
- // Note: @hide methods copied from android.os.Handler
- /**
- * Runs the specified task synchronously.
- * <p>
- * If the current thread is the same as the handler thread, then the runnable
- * runs immediately without being enqueued. Otherwise, posts the runnable
- * to the handler and waits for it to complete before returning.
- * </p><p>
- * This method is dangerous! Improper use can result in deadlocks.
- * Never call this method while any locks are held or use it in a
- * possibly re-entrant manner.
- * </p><p>
- * This method is occasionally useful in situations where a background thread
- * must synchronously await completion of a task that must run on the
- * handler's thread. However, this problem is often a symptom of bad design.
- * Consider improving the design (if possible) before resorting to this method.
- * </p><p>
- * One example of where you might want to use this method is when you just
- * set up a Handler thread and need to perform some initialization steps on
- * it before continuing execution.
- * </p><p>
- * If timeout occurs then this method returns <code>false</code> but the runnable
- * will remain posted on the handler and may already be in progress or
- * complete at a later time.
- * </p><p>
- * When using this method, be sure to use {@link Looper#quitSafely} when
- * quitting the looper. Otherwise {@link #runWithScissors} may hang indefinitely.
- * (TODO: We should fix this by making MessageQueue aware of blocking runnables.)
- * </p>
- *
- * @param h The target handler.
- * @param r The Runnable that will be executed synchronously.
- * @param timeout The timeout in milliseconds, or 0 to wait indefinitely.
- *
- * @return Returns true if the Runnable was successfully executed.
- * Returns false on failure, usually because the
- * looper processing the message queue is exiting.
- *
- * @hide This method is prone to abuse and should probably not be in the API.
- * If we ever do make it part of the API, we might want to rename it to something
- * less funny like runUnsafe().
- */
- public static boolean runWithScissors(@NonNull Handler h, @NonNull Runnable r, long timeout) {
- if (r == null) {
- throw new IllegalArgumentException("runnable must not be null");
- }
- if (timeout < 0) {
- throw new IllegalArgumentException("timeout must be non-negative");
- }
-
- if (Looper.myLooper() == h.getLooper()) {
- r.run();
- return true;
- }
-
- BlockingRunnable br = new BlockingRunnable(r);
- return br.postAndWait(h, timeout);
- }
-
- private static final class BlockingRunnable implements Runnable {
- private final Runnable mTask;
- private boolean mDone;
-
- BlockingRunnable(Runnable task) {
- mTask = task;
- }
-
- @Override
- public void run() {
- try {
- mTask.run();
- } finally {
- synchronized (this) {
- mDone = true;
- notifyAll();
- }
- }
- }
-
- public boolean postAndWait(Handler handler, long timeout) {
- if (!handler.post(this)) {
- return false;
- }
-
- synchronized (this) {
- if (timeout > 0) {
- final long expirationTime = SystemClock.uptimeMillis() + timeout;
- while (!mDone) {
- long delay = expirationTime - SystemClock.uptimeMillis();
- if (delay <= 0) {
- return false; // timeout
- }
- try {
- wait(delay);
- } catch (InterruptedException ex) {
- }
- }
- } else {
- while (!mDone) {
- try {
- wait();
- } catch (InterruptedException ex) {
- }
- }
- }
- }
- return true;
- }
- }
-}
diff --git a/staticlibs/Android.bp b/staticlibs/Android.bp
index 6f7ea4c..8f018c0 100644
--- a/staticlibs/Android.bp
+++ b/staticlibs/Android.bp
@@ -43,6 +43,7 @@
"device/com/android/net/module/util/SharedLog.java",
"device/com/android/net/module/util/SocketUtils.java",
"device/com/android/net/module/util/FeatureVersions.java",
+ "device/com/android/net/module/util/HandlerUtils.java",
// This library is used by system modules, for which the system health impact of Kotlin
// has not yet been evaluated. Annotations may need jarjar'ing.
// "src_devicecommon/**/*.kt",
diff --git a/staticlibs/device/com/android/net/module/util/HandlerUtils.java b/staticlibs/device/com/android/net/module/util/HandlerUtils.java
new file mode 100644
index 0000000..c620368
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/HandlerUtils.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2023 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.net.module.util;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Helper class for Handler related utilities.
+ *
+ * @hide
+ */
+public class HandlerUtils {
+ /**
+ * Runs the specified task synchronously for dump method.
+ * <p>
+ * If the current thread is the same as the handler thread, then the runnable
+ * runs immediately without being enqueued. Otherwise, posts the runnable
+ * to the handler and waits for it to complete before returning.
+ * </p><p>
+ * This method is dangerous! Improper use can result in deadlocks.
+ * Never call this method while any locks are held or use it in a
+ * possibly re-entrant manner.
+ * </p><p>
+ * This method is made to let dump method access members on the handler thread to
+ * avoid concurrent access problems or races.
+ * </p><p>
+ * If timeout occurs then this method returns <code>false</code> but the runnable
+ * will remain posted on the handler and may already be in progress or
+ * complete at a later time.
+ * </p><p>
+ * When using this method, be sure to use {@link Looper#quitSafely} when
+ * quitting the looper. Otherwise {@link #runWithScissorsForDump} may hang indefinitely.
+ * (TODO: We should fix this by making MessageQueue aware of blocking runnables.)
+ * </p>
+ *
+ * @param h The target handler.
+ * @param r The Runnable that will be executed synchronously.
+ * @param timeout The timeout in milliseconds, or 0 to not wait at all.
+ *
+ * @return Returns true if the Runnable was successfully executed.
+ * Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ *
+ * @hide
+ */
+ public static boolean runWithScissorsForDump(@NonNull Handler h, @NonNull Runnable r,
+ long timeout) {
+ if (r == null) {
+ throw new IllegalArgumentException("runnable must not be null");
+ }
+ if (timeout < 0) {
+ throw new IllegalArgumentException("timeout must be non-negative");
+ }
+ if (Looper.myLooper() == h.getLooper()) {
+ r.run();
+ return true;
+ }
+
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ // Don't crash in the handler if something in the runnable throws an exception,
+ // but try to propagate the exception to the caller.
+ AtomicReference<RuntimeException> exceptionRef = new AtomicReference<>();
+ h.post(() -> {
+ try {
+ r.run();
+ } catch (RuntimeException e) {
+ exceptionRef.set(e);
+ }
+ latch.countDown();
+ });
+
+ try {
+ if (!latch.await(timeout, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ } catch (InterruptedException e) {
+ exceptionRef.compareAndSet(null, new IllegalStateException("Thread interrupted", e));
+ }
+
+ final RuntimeException e = exceptionRef.get();
+ if (e != null) throw e;
+ return true;
+ }
+}
diff --git a/tests/unit/java/com/android/server/HandlerUtilsTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/HandlerUtilsTest.kt
similarity index 90%
rename from tests/unit/java/com/android/server/HandlerUtilsTest.kt
rename to staticlibs/tests/unit/src/com/android/net/module/util/HandlerUtilsTest.kt
index 62bb651..f2c902f 100644
--- a/tests/unit/java/com/android/server/HandlerUtilsTest.kt
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/HandlerUtilsTest.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.server
+package com.android.net.module.util
import android.os.HandlerThread
-import com.android.server.connectivity.HandlerUtils
import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.DevSdkIgnoreRunner.MonitorThreadLeak
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import org.junit.After
@@ -27,6 +27,8 @@
const val THREAD_BLOCK_TIMEOUT_MS = 1000L
const val TEST_REPEAT_COUNT = 100
+
+@MonitorThreadLeak
@RunWith(DevSdkIgnoreRunner::class)
class HandlerUtilsTest {
val handlerThread = HandlerThread("HandlerUtilsTestHandlerThread").also {
@@ -39,7 +41,7 @@
// Repeat the test a fair amount of times to ensure that it does not pass by chance.
repeat(TEST_REPEAT_COUNT) {
var result = false
- HandlerUtils.runWithScissors(handler, {
+ HandlerUtils.runWithScissorsForDump(handler, {
assertEquals(Thread.currentThread(), handlerThread)
result = true
}, THREAD_BLOCK_TIMEOUT_MS)
diff --git a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 496d163..76d30e6 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -56,9 +56,11 @@
import com.android.server.connectivity.MockableSystemProperties
import com.android.server.connectivity.MultinetworkPolicyTracker
import com.android.server.connectivity.ProxyTracker
+import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.DeviceInfoUtils
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.TestableNetworkCallback
+import com.android.testutils.tryTest
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@@ -254,13 +256,18 @@
na.addCapability(NET_CAPABILITY_INTERNET)
na.connect()
- testCallback.expectAvailableThenValidatedCallbacks(na.network, TEST_TIMEOUT_MS)
- val requestedSize = nsInstrumentation.getRequestUrls().size
- if (requestedSize == 2 || (requestedSize == 1 &&
- nsInstrumentation.getRequestUrls()[0] == httpsProbeUrl)) {
- return
+ tryTest {
+ testCallback.expectAvailableThenValidatedCallbacks(na.network, TEST_TIMEOUT_MS)
+ val requestedSize = nsInstrumentation.getRequestUrls().size
+ if (requestedSize == 2 || (requestedSize == 1 &&
+ nsInstrumentation.getRequestUrls()[0] == httpsProbeUrl)
+ ) {
+ return@tryTest
+ }
+ fail("Unexpected request urls: ${nsInstrumentation.getRequestUrls()}")
+ } cleanup {
+ na.destroy()
}
- fail("Unexpected request urls: ${nsInstrumentation.getRequestUrls()}")
}
@Test
@@ -292,24 +299,32 @@
val lp = LinkProperties()
lp.captivePortalApiUrl = Uri.parse(apiUrl)
val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, null /* ncTemplate */, context)
- networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
- na.addCapability(NET_CAPABILITY_INTERNET)
- na.connect()
+ tryTest {
+ networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
- testCb.expectAvailableCallbacks(na.network, validated = false, tmt = TEST_TIMEOUT_MS)
+ na.addCapability(NET_CAPABILITY_INTERNET)
+ na.connect()
- val capportData = testCb.expect<LinkPropertiesChanged>(na, TEST_TIMEOUT_MS) {
- it.lp.captivePortalData != null
- }.lp.captivePortalData
- assertNotNull(capportData)
- assertTrue(capportData.isCaptive)
- assertEquals(Uri.parse("https://login.capport.android.com"), capportData.userPortalUrl)
- assertEquals(Uri.parse("https://venueinfo.capport.android.com"), capportData.venueInfoUrl)
+ testCb.expectAvailableCallbacks(na.network, validated = false, tmt = TEST_TIMEOUT_MS)
- testCb.expectCaps(na, TEST_TIMEOUT_MS) {
- it.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) &&
- !it.hasCapability(NET_CAPABILITY_VALIDATED)
+ val capportData = testCb.expect<LinkPropertiesChanged>(na, TEST_TIMEOUT_MS) {
+ it.lp.captivePortalData != null
+ }.lp.captivePortalData
+ assertNotNull(capportData)
+ assertTrue(capportData.isCaptive)
+ assertEquals(Uri.parse("https://login.capport.android.com"), capportData.userPortalUrl)
+ assertEquals(
+ Uri.parse("https://venueinfo.capport.android.com"),
+ capportData.venueInfoUrl
+ )
+
+ testCb.expectCaps(na, TEST_TIMEOUT_MS) {
+ it.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) &&
+ !it.hasCapability(NET_CAPABILITY_VALIDATED)
+ }
+ } cleanup {
+ na.destroy()
}
}
diff --git a/tests/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
index ec09f9e..960c6ca 100644
--- a/tests/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -36,6 +36,7 @@
import static org.junit.Assert.fail;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
@@ -51,6 +52,7 @@
import android.os.ConditionVariable;
import android.os.HandlerThread;
import android.os.Message;
+import android.util.CloseGuard;
import android.util.Log;
import android.util.Range;
@@ -65,11 +67,14 @@
import java.util.function.Consumer;
public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
+ private static final long DESTROY_TIMEOUT_MS = 10_000L;
+
// Note : Please do not add any new instrumentation here. If you need new instrumentation,
// please add it in CSAgentWrapper and use subclasses of CSTest instead of adding more
// tools in ConnectivityServiceTest.
private final NetworkCapabilities mNetworkCapabilities;
private final HandlerThread mHandlerThread;
+ private final CloseGuard mCloseGuard;
private final Context mContext;
private final String mLogTag;
private final NetworkAgentConfig mNetworkAgentConfig;
@@ -157,6 +162,8 @@
mLogTag = "Mock-" + typeName;
mHandlerThread = new HandlerThread(mLogTag);
mHandlerThread.start();
+ mCloseGuard = new CloseGuard();
+ mCloseGuard.open("destroy");
// extraInfo is set to "" by default in NetworkAgentConfig.
final String extraInfo = (transport == TRANSPORT_CELLULAR) ? "internet.apn" : "";
@@ -359,6 +366,35 @@
mNetworkAgent.unregister();
}
+ /**
+ * Destroy the network agent and stop its looper.
+ *
+ * <p>This must always be called.
+ */
+ public void destroy() {
+ mHandlerThread.quitSafely();
+ try {
+ mHandlerThread.join(DESTROY_TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ Log.e(mLogTag, "Interrupted when waiting for handler thread on destroy", e);
+ }
+ mCloseGuard.close();
+ }
+
+ @SuppressLint("Finalize") // Follows the recommended pattern for CloseGuard
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ // Note that mCloseGuard could be null if the constructor threw.
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ destroy();
+ } finally {
+ super.finalize();
+ }
+ }
+
@Override
public Network getNetwork() {
return mNetworkAgent.getNetwork();
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 8f5fd7c..c681356 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -589,6 +589,7 @@
private TestNetworkAgentWrapper mWiFiAgent;
private TestNetworkAgentWrapper mCellAgent;
private TestNetworkAgentWrapper mEthernetAgent;
+ private final List<TestNetworkAgentWrapper> mCreatedAgents = new ArrayList<>();
private MockVpn mMockVpn;
private Context mContext;
private NetworkPolicyCallback mPolicyCallback;
@@ -1092,6 +1093,7 @@
NetworkCapabilities ncTemplate, NetworkProvider provider,
NetworkAgentWrapper.Callbacks callbacks) throws Exception {
super(transport, linkProperties, ncTemplate, provider, callbacks, mServiceContext);
+ mCreatedAgents.add(this);
// Waits for the NetworkAgent to be registered, which includes the creation of the
// NetworkMonitor.
@@ -2404,6 +2406,11 @@
FakeSettingsProvider.clearSettingsProvider();
ConnectivityResources.setResourcesContextForTest(null);
+ for (TestNetworkAgentWrapper agent : mCreatedAgents) {
+ agent.destroy();
+ }
+ mCreatedAgents.clear();
+
mCsHandlerThread.quitSafely();
mCsHandlerThread.join();
mAlarmManagerThread.quitSafely();