Implement RealtimeScheduler in DiscoveryExecutor
This provides more accurate delay callbacks, thereby enhancing the executor's overall functionality. Bug: 366373064 Test: atest FrameworksNetTests NsdManagerTest Change-Id: I903851464a92427b6e256573d693bb89a96d013f
This commit is contained in:
parent
520093b251
commit
0f4d1ab4d9
3 changed files with 83 additions and 13 deletions
|
|
@ -27,6 +27,7 @@ import android.util.Pair;
|
|||
import androidx.annotation.GuardedBy;
|
||||
|
||||
import com.android.net.module.util.HandlerUtils;
|
||||
import com.android.net.module.util.RealtimeScheduler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
|
|
@ -49,7 +50,13 @@ public class DiscoveryExecutor implements Executor {
|
|||
@NonNull
|
||||
private final ArrayList<Pair<Runnable, Long>> mPendingTasks = new ArrayList<>();
|
||||
|
||||
DiscoveryExecutor(@Nullable Looper defaultLooper) {
|
||||
@GuardedBy("mPendingTasks")
|
||||
@Nullable
|
||||
RealtimeScheduler mRealtimeScheduler;
|
||||
@NonNull private final MdnsFeatureFlags mMdnsFeatureFlags;
|
||||
|
||||
DiscoveryExecutor(@Nullable Looper defaultLooper, @NonNull MdnsFeatureFlags mdnsFeatureFlags) {
|
||||
mMdnsFeatureFlags = mdnsFeatureFlags;
|
||||
if (defaultLooper != null) {
|
||||
this.mHandlerThread = null;
|
||||
synchronized (mPendingTasks) {
|
||||
|
|
@ -62,7 +69,7 @@ public class DiscoveryExecutor implements Executor {
|
|||
synchronized (mPendingTasks) {
|
||||
mHandler = new Handler(getLooper());
|
||||
for (Pair<Runnable, Long> pendingTask : mPendingTasks) {
|
||||
mHandler.postDelayed(pendingTask.first, pendingTask.second);
|
||||
executeDelayed(pendingTask.first, pendingTask.second);
|
||||
}
|
||||
mPendingTasks.clear();
|
||||
}
|
||||
|
|
@ -95,15 +102,33 @@ public class DiscoveryExecutor implements Executor {
|
|||
/** Execute the given function after the specified amount of time elapses. */
|
||||
public void executeDelayed(Runnable function, long delayMillis) {
|
||||
final Handler handler;
|
||||
final RealtimeScheduler realtimeScheduler;
|
||||
synchronized (mPendingTasks) {
|
||||
if (this.mHandler == null) {
|
||||
mPendingTasks.add(Pair.create(function, delayMillis));
|
||||
return;
|
||||
} else {
|
||||
handler = this.mHandler;
|
||||
if (mMdnsFeatureFlags.mIsAccurateDelayCallbackEnabled
|
||||
&& this.mRealtimeScheduler == null) {
|
||||
this.mRealtimeScheduler = new RealtimeScheduler(mHandler);
|
||||
}
|
||||
realtimeScheduler = this.mRealtimeScheduler;
|
||||
}
|
||||
}
|
||||
handler.postDelayed(function, delayMillis);
|
||||
if (realtimeScheduler != null) {
|
||||
if (delayMillis == 0L) {
|
||||
handler.post(function);
|
||||
return;
|
||||
}
|
||||
if (HandlerUtils.isRunningOnHandlerThread(handler)) {
|
||||
realtimeScheduler.postDelayed(function, delayMillis);
|
||||
} else {
|
||||
handler.post(() -> realtimeScheduler.postDelayed(function, delayMillis));
|
||||
}
|
||||
} else {
|
||||
handler.postDelayed(function, delayMillis);
|
||||
}
|
||||
}
|
||||
|
||||
/** Shutdown the thread if necessary. */
|
||||
|
|
@ -111,6 +136,11 @@ public class DiscoveryExecutor implements Executor {
|
|||
if (this.mHandlerThread != null) {
|
||||
this.mHandlerThread.quitSafely();
|
||||
}
|
||||
synchronized (mPendingTasks) {
|
||||
if (mRealtimeScheduler != null) {
|
||||
mRealtimeScheduler.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback {
|
|||
this.sharedLog = sharedLog;
|
||||
this.perSocketServiceTypeClients = new PerSocketServiceTypeClients();
|
||||
this.mdnsFeatureFlags = mdnsFeatureFlags;
|
||||
this.discoveryExecutor = new DiscoveryExecutor(socketClient.getLooper());
|
||||
this.discoveryExecutor = new DiscoveryExecutor(socketClient.getLooper(), mdnsFeatureFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package com.android.server.connectivity.mdns
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.testing.TestableLooper
|
||||
import com.android.testutils.DevSdkIgnoreRule
|
||||
|
|
@ -36,6 +37,8 @@ private const val DEFAULT_TIMEOUT = 2000L
|
|||
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
|
||||
class DiscoveryExecutorTest {
|
||||
private val thread = HandlerThread(DiscoveryExecutorTest::class.simpleName).apply { start() }
|
||||
private val handler by lazy { Handler(thread.looper) }
|
||||
private val testableLooper by lazy { TestableLooper(thread.looper) }
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
|
|
@ -45,8 +48,10 @@ class DiscoveryExecutorTest {
|
|||
|
||||
@Test
|
||||
fun testCheckAndRunOnHandlerThread() {
|
||||
val testableLooper = TestableLooper(thread.looper)
|
||||
val executor = DiscoveryExecutor(testableLooper.looper)
|
||||
val executor = DiscoveryExecutor(
|
||||
testableLooper.looper,
|
||||
MdnsFeatureFlags.newBuilder().build()
|
||||
)
|
||||
try {
|
||||
val future = CompletableFuture<Boolean>()
|
||||
executor.checkAndRunOnHandlerThread { future.complete(true) }
|
||||
|
|
@ -58,17 +63,17 @@ class DiscoveryExecutorTest {
|
|||
|
||||
// Create a DiscoveryExecutor with the null defaultLooper and verify the task can execute
|
||||
// normally.
|
||||
val executor2 = DiscoveryExecutor(null /* defaultLooper */)
|
||||
val executor2 = DiscoveryExecutor(
|
||||
null /* defaultLooper */,
|
||||
MdnsFeatureFlags.newBuilder().build()
|
||||
)
|
||||
val future2 = CompletableFuture<Boolean>()
|
||||
executor2.checkAndRunOnHandlerThread { future2.complete(true) }
|
||||
assertTrue(future2.get(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS))
|
||||
executor2.shutDown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExecute() {
|
||||
val testableLooper = TestableLooper(thread.looper)
|
||||
val executor = DiscoveryExecutor(testableLooper.looper)
|
||||
private fun verifyExecute(executor: DiscoveryExecutor) {
|
||||
try {
|
||||
val future = CompletableFuture<Boolean>()
|
||||
executor.execute { future.complete(true) }
|
||||
|
|
@ -80,10 +85,28 @@ class DiscoveryExecutorTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExecute() {
|
||||
verifyExecute(DiscoveryExecutor(
|
||||
testableLooper.looper,
|
||||
MdnsFeatureFlags.newBuilder().build()
|
||||
))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExecute_RealtimeScheduler() {
|
||||
verifyExecute(DiscoveryExecutor(
|
||||
testableLooper.looper,
|
||||
MdnsFeatureFlags.newBuilder().setIsAccurateDelayCallbackEnabled(true).build()
|
||||
))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExecuteDelayed() {
|
||||
val testableLooper = TestableLooper(thread.looper)
|
||||
val executor = DiscoveryExecutor(testableLooper.looper)
|
||||
val executor = DiscoveryExecutor(
|
||||
testableLooper.looper,
|
||||
MdnsFeatureFlags.newBuilder().build()
|
||||
)
|
||||
try {
|
||||
// Verify the executeDelayed method
|
||||
val future = CompletableFuture<Boolean>()
|
||||
|
|
@ -107,4 +130,21 @@ class DiscoveryExecutorTest {
|
|||
testableLooper.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExecuteDelayed_RealtimeScheduler() {
|
||||
val executor = DiscoveryExecutor(
|
||||
thread.looper,
|
||||
MdnsFeatureFlags.newBuilder().setIsAccurateDelayCallbackEnabled(true).build()
|
||||
)
|
||||
try {
|
||||
// Verify the executeDelayed method
|
||||
val future = CompletableFuture<Boolean>()
|
||||
// Schedule a task with 50ms delay
|
||||
executor.executeDelayed({ future.complete(true) }, 50L)
|
||||
assertTrue(future.get(500L, TimeUnit.MILLISECONDS))
|
||||
} finally {
|
||||
testableLooper.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue