Update TimerFileDescriptor to support different task types

Introduced ITask and its concrete implementations (e.g.
MessageTask, RunnableTask) for TimerFileDescriptor to enable more
flexible task scheduling.

Bug: 366373064
Test: atest NetworkStaticLibTests
Change-Id: I3a5177b66916f50f3f943d8b63b0c760bece60e4
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() }
         }