Merge "Handle non IPv6 packet in getRaPios" into main
diff --git a/staticlibs/device/com/android/net/module/util/TimerFileDescriptor.java b/staticlibs/device/com/android/net/module/util/TimerFileDescriptor.java
index 5a16de6..dbbccc5 100644
--- a/staticlibs/device/com/android/net/module/util/TimerFileDescriptor.java
+++ b/staticlibs/device/com/android/net/module/util/TimerFileDescriptor.java
@@ -21,6 +21,7 @@
 
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Message;
 import android.os.MessageQueue;
 import android.os.ParcelFileDescriptor;
 import android.util.CloseGuard;
@@ -69,7 +70,61 @@
     private final ParcelFileDescriptor mParcelFileDescriptor;
     private final int mFdInt;
     @Nullable
-    private Runnable mTask;
+    private ITask mTask;
+
+    /**
+     * An interface for defining tasks that can be executed using a {@link Handler}.
+     */
+    public interface ITask {
+        /**
+         * Executes the task using the provided {@link Handler}.
+         *
+         * @param handler The {@link Handler} to use for executing the task.
+         */
+        void post(Handler handler);
+    }
+
+    /**
+     * A task that sends a {@link Message} using a {@link Handler}.
+     */
+    public static class MessageTask implements ITask {
+        private final Message mMessage;
+
+        public MessageTask(Message message) {
+            mMessage = message;
+        }
+
+        /**
+         * Sends the {@link Message} using the provided {@link Handler}.
+         *
+         * @param handler The {@link Handler} to use for sending the message.
+         */
+        @Override
+        public void post(Handler handler) {
+            handler.sendMessage(mMessage);
+        }
+    }
+
+    /**
+     * A task that posts a {@link Runnable} to a {@link Handler}.
+     */
+    public static class RunnableTask implements ITask {
+        private final Runnable mRunnable;
+
+        public RunnableTask(Runnable runnable) {
+            mRunnable = runnable;
+        }
+
+        /**
+         * Posts the {@link Runnable} to the provided {@link Handler}.
+         *
+         * @param handler The {@link Handler} to use for posting the runnable.
+         */
+        @Override
+        public void post(Handler handler) {
+            handler.post(mRunnable);
+        }
+    }
 
     /**
      * TimerFileDescriptor constructor
@@ -98,13 +153,13 @@
      * @throws IllegalArgumentException if try to replace the current scheduled task
      * @throws IllegalArgumentException if the delay time is less than 0
      */
-    public void setDelayedTask(@NonNull Runnable task, long delayMs) {
+    public void setDelayedTask(@NonNull ITask task, long delayMs) {
         ensureRunningOnCorrectThread();
         if (mTask != null) {
             throw new IllegalArgumentException("task is already scheduled");
         }
         if (delayMs <= 0L) {
-            mHandler.post(task);
+            task.post(mHandler);
             return;
         }
 
@@ -163,7 +218,7 @@
     private void handleExpiration() {
         // Execute the task
         if (mTask != null) {
-            mTask.run();
+            mTask.post(mHandler);
             mTask = null;
         }
     }
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/TimerFileDescriptorTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/TimerFileDescriptorTest.kt
index 2018902..f5e47c9 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/TimerFileDescriptorTest.kt
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/TimerFileDescriptorTest.kt
@@ -20,7 +20,12 @@
 import android.os.ConditionVariable
 import android.os.Handler
 import android.os.HandlerThread
+import android.os.Looper
+import android.os.Message
 import androidx.test.filters.SmallTest
+import com.android.net.module.util.TimerFileDescriptor.ITask
+import com.android.net.module.util.TimerFileDescriptor.MessageTask
+import com.android.net.module.util.TimerFileDescriptor.RunnableTask
 import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.DevSdkIgnoreRunner
 import com.android.testutils.tryTest
@@ -33,13 +38,21 @@
 import kotlin.test.assertFalse
 import kotlin.test.assertTrue
 
+private const val MSG_TEST = 1
+
 @DevSdkIgnoreRunner.MonitorThreadLeak
 @RunWith(DevSdkIgnoreRunner::class)
 @SmallTest
 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
 class TimerFileDescriptorTest {
+    private class TestHandler(looper: Looper) : Handler(looper) {
+        override fun handleMessage(msg: Message) {
+            val cv = msg.obj as ConditionVariable
+            cv.open()
+        }
+    }
     private val thread = HandlerThread(TimerFileDescriptorTest::class.simpleName).apply { start() }
-    private val handler by lazy { Handler(thread.looper) }
+    private val handler by lazy { TestHandler(thread.looper) }
 
     @After
     fun tearDown() {
@@ -47,18 +60,33 @@
         thread.join()
     }
 
+    private fun assertDelayedTaskPost(
+            timerFd: TimerFileDescriptor,
+            task: ITask,
+            cv: ConditionVariable
+    ) {
+        val delayTime = 10L
+        val startTime1 = Instant.now()
+        handler.post { timerFd.setDelayedTask(task, delayTime) }
+        assertTrue(cv.block(100L /* timeoutMs*/))
+        assertTrue(Duration.between(startTime1, Instant.now()).toMillis() >= delayTime)
+    }
+
     @Test
     fun testSetDelayedTask() {
-        val delayTime = 10L
         val timerFd = TimerFileDescriptor(handler)
-        val cv = ConditionVariable()
-        val startTime = Instant.now()
         tryTest {
-            handler.post { timerFd.setDelayedTask({ cv.open() }, delayTime) }
-            assertTrue(cv.block(100L /* timeoutMs*/))
-            // Verify that the delay time has actually passed.
-            val duration = Duration.between(startTime, Instant.now())
-            assertTrue(duration.toMillis() >= delayTime)
+            // Verify the delayed task is executed with the self-implemented ITask
+            val cv1 = ConditionVariable()
+            assertDelayedTaskPost(timerFd, { cv1.open() }, cv1)
+
+            // Verify the delayed task is executed with the RunnableTask
+            val cv2 = ConditionVariable()
+            assertDelayedTaskPost(timerFd, RunnableTask{ cv2.open() }, cv2)
+
+            // Verify the delayed task is executed with the MessageTask
+            val cv3 = ConditionVariable()
+            assertDelayedTaskPost(timerFd, MessageTask(handler.obtainMessage(MSG_TEST, cv3)), cv3)
         } cleanup {
             visibleOnHandlerThread(handler) { timerFd.close() }
         }
diff --git a/staticlibs/testutils/host/python/tether_utils.py b/staticlibs/testutils/host/python/tether_utils.py
index 702b596..c63de1f 100644
--- a/staticlibs/testutils/host/python/tether_utils.py
+++ b/staticlibs/testutils/host/python/tether_utils.py
@@ -108,3 +108,6 @@
     server.unregisterAll()
   # Teardown the hotspot.
   server.stopAllTethering()
+  # Some test cases would disable wifi, e.g. cellular upstream tests.
+  # Reconnect to it if feasible.
+  server.reconnectWifiIfSupported()
diff --git a/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt b/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
index 49688cc..6da7e9a 100644
--- a/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
+++ b/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
@@ -89,6 +89,11 @@
         ctsNetUtils.expectNetworkIsSystemDefault(network)
     }
 
+    @Rpc(description = "Reconnect to wifi if supported.")
+    fun reconnectWifiIfSupported() {
+        ctsNetUtils.reconnectWifiIfSupported()
+    }
+
     @Rpc(description = "Unregister all connections.")
     fun unregisterAll() {
         cbHelper.unregisterAll()
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index 30d5a02..c55096b 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -351,14 +351,14 @@
         }
 
         otDaemon.initialize(
-                mTunIfController.getTunFd(),
                 shouldEnableThread(),
                 newOtDaemonConfig(mPersistentSettings.getConfiguration()),
+                mTunIfController.getTunFd(),
                 mNsdPublisher,
                 getMeshcopTxtAttributes(mResources.get()),
-                mOtDaemonCallbackProxy,
                 mCountryCodeSupplier.get(),
-                FeatureFlags.isTrelEnabled());
+                FeatureFlags.isTrelEnabled(),
+                mOtDaemonCallbackProxy);
         otDaemon.asBinder().linkToDeath(() -> mHandler.post(this::onOtDaemonDied), 0);
         mOtDaemon = otDaemon;
         mHandler.post(mNat64CidrController::maybeUpdateNat64Cidr);
diff --git a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
index aeeed65..875a4ad 100644
--- a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
+++ b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
@@ -16,7 +16,6 @@
 
 package android.net.thread;
 
-import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
 import static android.net.InetAddresses.parseNumericAddress;
 import static android.net.thread.utils.IntegrationTestUtils.DEFAULT_DATASET;
 import static android.net.thread.utils.IntegrationTestUtils.buildIcmpv4EchoReply;
@@ -38,7 +37,6 @@
 
 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REPLY_TYPE;
 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE;
-import static com.android.testutils.TestPermissionUtil.runAsShell;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -56,7 +54,6 @@
 import android.net.RouteInfo;
 import android.net.thread.utils.FullThreadDevice;
 import android.net.thread.utils.InfraNetworkDevice;
-import android.net.thread.utils.IntegrationTestUtils;
 import android.net.thread.utils.OtDaemonController;
 import android.net.thread.utils.TestTunNetworkUtils;
 import android.net.thread.utils.ThreadFeatureCheckerRule;