Revert "Use timerfd to improve the accuracy of query timing"

This reverts commit 3d51a99dc8.

Reason for revert: Does not close the timerfd object properly, which may cause a memory leak.

Change-Id: I25d7da13591696c9dfda60ee0fdd2574dbcdf337
This commit is contained in:
Paul Hu 2024-12-24 00:51:08 -08:00
commit a481dc184f
5 changed files with 38 additions and 206 deletions

View file

@ -95,11 +95,6 @@ public class MdnsFeatureFlags {
"nsd_cached_services_retention_time";
public static final int DEFAULT_CACHED_SERVICES_RETENTION_TIME_MILLISECONDS = 10000;
/**
* A feature flag to control whether the accurate delay callback should be enabled.
*/
public static final String NSD_ACCURATE_DELAY_CALLBACK = "nsd_accurate_delay_callback";
// Flag for offload feature
public final boolean mIsMdnsOffloadFeatureEnabled;
@ -133,9 +128,6 @@ public class MdnsFeatureFlags {
// Retention Time for cached services
public final long mCachedServicesRetentionTime;
// Flag for accurate delay callback
public final boolean mIsAccurateDelayCallbackEnabled;
// Flag to use shorter (16 characters + .local) hostnames
public final boolean mIsShortHostnamesEnabled;
@ -238,14 +230,6 @@ public class MdnsFeatureFlags {
return mIsShortHostnamesEnabled || isForceEnabledForTest(NSD_USE_SHORT_HOSTNAMES);
}
/**
* Indicates whether {@link #NSD_ACCURATE_DELAY_CALLBACK} is enabled, including for testing.
*/
public boolean isAccurateDelayCallbackEnabled() {
return mIsAccurateDelayCallbackEnabled
|| isForceEnabledForTest(NSD_ACCURATE_DELAY_CALLBACK);
}
/**
* The constructor for {@link MdnsFeatureFlags}.
*/
@ -260,7 +244,6 @@ public class MdnsFeatureFlags {
boolean avoidAdvertisingEmptyTxtRecords,
boolean isCachedServicesRemovalEnabled,
long cachedServicesRetentionTime,
boolean isAccurateDelayCallbackEnabled,
boolean isShortHostnamesEnabled,
@Nullable FlagOverrideProvider overrideProvider) {
mIsMdnsOffloadFeatureEnabled = isOffloadFeatureEnabled;
@ -274,7 +257,6 @@ public class MdnsFeatureFlags {
mAvoidAdvertisingEmptyTxtRecords = avoidAdvertisingEmptyTxtRecords;
mIsCachedServicesRemovalEnabled = isCachedServicesRemovalEnabled;
mCachedServicesRetentionTime = cachedServicesRetentionTime;
mIsAccurateDelayCallbackEnabled = isAccurateDelayCallbackEnabled;
mIsShortHostnamesEnabled = isShortHostnamesEnabled;
mOverrideProvider = overrideProvider;
}
@ -299,7 +281,6 @@ public class MdnsFeatureFlags {
private boolean mAvoidAdvertisingEmptyTxtRecords;
private boolean mIsCachedServicesRemovalEnabled;
private long mCachedServicesRetentionTime;
private boolean mIsAccurateDelayCallbackEnabled;
private boolean mIsShortHostnamesEnabled;
private FlagOverrideProvider mOverrideProvider;
@ -318,7 +299,6 @@ public class MdnsFeatureFlags {
mAvoidAdvertisingEmptyTxtRecords = true; // Default enabled.
mIsCachedServicesRemovalEnabled = false;
mCachedServicesRetentionTime = DEFAULT_CACHED_SERVICES_RETENTION_TIME_MILLISECONDS;
mIsAccurateDelayCallbackEnabled = false;
mIsShortHostnamesEnabled = true; // Default enabled.
mOverrideProvider = null;
}
@ -445,16 +425,6 @@ public class MdnsFeatureFlags {
return this;
}
/**
* Set whether the accurate delay callback is enabled.
*
* @see #NSD_ACCURATE_DELAY_CALLBACK
*/
public Builder setIsAccurateDelayCallbackEnabled(boolean isAccurateDelayCallbackEnabled) {
mIsAccurateDelayCallbackEnabled = isAccurateDelayCallbackEnabled;
return this;
}
/**
* Set whether the short hostnames feature is enabled.
*
@ -480,7 +450,6 @@ public class MdnsFeatureFlags {
mAvoidAdvertisingEmptyTxtRecords,
mIsCachedServicesRemovalEnabled,
mCachedServicesRetentionTime,
mIsAccurateDelayCallbackEnabled,
mIsShortHostnamesEnabled,
mOverrideProvider);
}

View file

@ -20,7 +20,6 @@ import static com.android.net.module.util.HandlerUtils.ensureRunningOnHandlerThr
import static com.android.server.connectivity.mdns.MdnsSearchOptions.AGGRESSIVE_QUERY_MODE;
import static com.android.server.connectivity.mdns.MdnsServiceCache.ServiceExpiredCallback;
import static com.android.server.connectivity.mdns.MdnsServiceCache.findMatchedResponse;
import static com.android.server.connectivity.mdns.MdnsQueryScheduler.ScheduledQueryTaskArgs;
import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock;
import static com.android.server.connectivity.mdns.util.MdnsUtils.buildMdnsServiceInfoFromResponse;
@ -38,7 +37,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.DnsUtils;
import com.android.net.module.util.SharedLog;
import com.android.net.module.util.TimerFileDescriptor;
import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
@ -96,9 +94,6 @@ public class MdnsServiceTypeClient {
private final boolean removeServiceAfterTtlExpires =
MdnsConfigs.removeServiceAfterTtlExpires();
private final Clock clock;
// Use TimerFileDescriptor for query scheduling, which allows for more accurate sending of
// queries.
@NonNull private final TimerFileDescriptor timerFd;
@Nullable private MdnsSearchOptions searchOptions;
@ -144,7 +139,8 @@ public class MdnsServiceTypeClient {
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_START_QUERYTASK: {
final ScheduledQueryTaskArgs taskArgs = (ScheduledQueryTaskArgs) msg.obj;
final MdnsQueryScheduler.ScheduledQueryTaskArgs taskArgs =
(MdnsQueryScheduler.ScheduledQueryTaskArgs) msg.obj;
// QueryTask should be run immediately after being created (not be scheduled in
// advance). Because the result of "makeResponsesForResolve" depends on answers
// that were received before it is called, so to take into account all answers
@ -178,7 +174,7 @@ public class MdnsServiceTypeClient {
final long now = clock.elapsedRealtime();
lastSentTime = now;
final long minRemainingTtl = getMinRemainingTtl(now);
final ScheduledQueryTaskArgs args =
MdnsQueryScheduler.ScheduledQueryTaskArgs args =
mdnsQueryScheduler.scheduleNextRun(
sentResult.taskArgs.config,
minRemainingTtl,
@ -193,14 +189,10 @@ public class MdnsServiceTypeClient {
sharedLog.log(String.format("Query sent with transactionId: %d. "
+ "Next run: sessionId: %d, in %d ms",
sentResult.transactionId, args.sessionId, timeToNextTaskMs));
if (featureFlags.isAccurateDelayCallbackEnabled()) {
setDelayedTask(args, timeToNextTaskMs);
} else {
dependencies.sendMessageDelayed(
handler,
handler.obtainMessage(EVENT_START_QUERYTASK, args),
timeToNextTaskMs);
}
dependencies.sendMessageDelayed(
handler,
handler.obtainMessage(EVENT_START_QUERYTASK, args),
timeToNextTaskMs);
break;
}
default:
@ -262,14 +254,6 @@ public class MdnsServiceTypeClient {
return List.of(new DatagramPacket(queryBuffer, 0, queryBuffer.length, address));
}
}
/**
* @see TimerFileDescriptor
*/
@Nullable
public TimerFileDescriptor createTimerFd(@NonNull Handler handler) {
return new TimerFileDescriptor(handler);
}
}
/**
@ -317,7 +301,6 @@ public class MdnsServiceTypeClient {
this.mdnsQueryScheduler = new MdnsQueryScheduler();
this.cacheKey = new MdnsServiceCache.CacheKey(serviceType, socketKey);
this.featureFlags = featureFlags;
this.timerFd = dependencies.createTimerFd(handler);
}
/**
@ -334,13 +317,6 @@ public class MdnsServiceTypeClient {
? serviceCache.getCachedServices(cacheKey) : Collections.emptyList();
}
private void setDelayedTask(ScheduledQueryTaskArgs args, long timeToNextTaskMs) {
timerFd.cancelTask();
timerFd.setDelayedTask(new TimerFileDescriptor.MessageTask(
handler.obtainMessage(EVENT_START_QUERYTASK, args)),
timeToNextTaskMs);
}
/**
* Registers {@code listener} for receiving discovery event of mDNS service instances, and
* starts
@ -387,7 +363,7 @@ public class MdnsServiceTypeClient {
}
final long minRemainingTtl = getMinRemainingTtl(now);
if (hadReply) {
final ScheduledQueryTaskArgs args =
MdnsQueryScheduler.ScheduledQueryTaskArgs args =
mdnsQueryScheduler.scheduleNextRun(
taskConfig,
minRemainingTtl,
@ -401,14 +377,10 @@ public class MdnsServiceTypeClient {
final long timeToNextTaskMs = calculateTimeToNextTask(args, now);
sharedLog.log(String.format("Schedule a query. Next run: sessionId: %d, in %d ms",
args.sessionId, timeToNextTaskMs));
if (featureFlags.isAccurateDelayCallbackEnabled()) {
setDelayedTask(args, timeToNextTaskMs);
} else {
dependencies.sendMessageDelayed(
handler,
handler.obtainMessage(EVENT_START_QUERYTASK, args),
timeToNextTaskMs);
}
dependencies.sendMessageDelayed(
handler,
handler.obtainMessage(EVENT_START_QUERYTASK, args),
timeToNextTaskMs);
} else {
final List<MdnsResponse> servicesToResolve = makeResponsesForResolve(socketKey);
final QueryTask queryTask = new QueryTask(
@ -448,11 +420,7 @@ public class MdnsServiceTypeClient {
}
private void removeScheduledTask() {
if (featureFlags.isAccurateDelayCallbackEnabled()) {
timerFd.cancelTask();
} else {
dependencies.removeMessages(handler, EVENT_START_QUERYTASK);
}
dependencies.removeMessages(handler, EVENT_START_QUERYTASK);
sharedLog.log("Remove EVENT_START_QUERYTASK"
+ ", current session: " + currentSessionId);
++currentSessionId;
@ -538,13 +506,10 @@ public class MdnsServiceTypeClient {
}
}
}
final boolean hasScheduledTask = featureFlags.isAccurateDelayCallbackEnabled()
? timerFd.hasDelayedTask()
: dependencies.hasMessages(handler, EVENT_START_QUERYTASK);
if (hasScheduledTask) {
if (dependencies.hasMessages(handler, EVENT_START_QUERYTASK)) {
final long now = clock.elapsedRealtime();
final long minRemainingTtl = getMinRemainingTtl(now);
final ScheduledQueryTaskArgs args =
MdnsQueryScheduler.ScheduledQueryTaskArgs args =
mdnsQueryScheduler.maybeRescheduleCurrentRun(now, minRemainingTtl,
lastSentTime, currentSessionId + 1,
searchOptions.numOfQueriesBeforeBackoff());
@ -553,14 +518,10 @@ public class MdnsServiceTypeClient {
final long timeToNextTaskMs = calculateTimeToNextTask(args, now);
sharedLog.log(String.format("Reschedule a query. Next run: sessionId: %d, in %d ms",
args.sessionId, timeToNextTaskMs));
if (featureFlags.isAccurateDelayCallbackEnabled()) {
setDelayedTask(args, timeToNextTaskMs);
} else {
dependencies.sendMessageDelayed(
handler,
handler.obtainMessage(EVENT_START_QUERYTASK, args),
timeToNextTaskMs);
}
dependencies.sendMessageDelayed(
handler,
handler.obtainMessage(EVENT_START_QUERYTASK, args),
timeToNextTaskMs);
}
}
}
@ -725,10 +686,10 @@ public class MdnsServiceTypeClient {
private static class QuerySentArguments {
private final int transactionId;
private final List<String> subTypes = new ArrayList<>();
private final ScheduledQueryTaskArgs taskArgs;
private final MdnsQueryScheduler.ScheduledQueryTaskArgs taskArgs;
QuerySentArguments(int transactionId, @NonNull List<String> subTypes,
@NonNull ScheduledQueryTaskArgs taskArgs) {
@NonNull MdnsQueryScheduler.ScheduledQueryTaskArgs taskArgs) {
this.transactionId = transactionId;
this.subTypes.addAll(subTypes);
this.taskArgs = taskArgs;
@ -737,14 +698,14 @@ public class MdnsServiceTypeClient {
// A FutureTask that enqueues a single query, and schedule a new FutureTask for the next task.
private class QueryTask implements Runnable {
private final ScheduledQueryTaskArgs taskArgs;
private final MdnsQueryScheduler.ScheduledQueryTaskArgs taskArgs;
private final List<MdnsResponse> servicesToResolve = new ArrayList<>();
private final List<String> subtypes = new ArrayList<>();
private final boolean sendDiscoveryQueries;
private final List<MdnsResponse> existingServices = new ArrayList<>();
private final boolean onlyUseIpv6OnIpv6OnlyNetworks;
private final SocketKey socketKey;
QueryTask(@NonNull ScheduledQueryTaskArgs taskArgs,
QueryTask(@NonNull MdnsQueryScheduler.ScheduledQueryTaskArgs taskArgs,
@NonNull Collection<MdnsResponse> servicesToResolve,
@NonNull Collection<String> subtypes, boolean sendDiscoveryQueries,
@NonNull Collection<MdnsResponse> existingServices,
@ -810,7 +771,7 @@ public class MdnsServiceTypeClient {
return minRemainingTtl == Long.MAX_VALUE ? 0 : minRemainingTtl;
}
private static long calculateTimeToNextTask(ScheduledQueryTaskArgs args,
private static long calculateTimeToNextTask(MdnsQueryScheduler.ScheduledQueryTaskArgs args,
long now) {
return Math.max(args.timeToRun - now, 0);
}

View file

@ -438,10 +438,7 @@ java_library {
srcs: [
"device/com/android/net/module/util/FdEventsReader.java",
"device/com/android/net/module/util/HandlerUtils.java",
"device/com/android/net/module/util/JniUtil.java",
"device/com/android/net/module/util/SharedLog.java",
"device/com/android/net/module/util/TimerFdUtils.java",
"device/com/android/net/module/util/TimerFileDescriptor.java",
"framework/com/android/net/module/util/ByteUtils.java",
"framework/com/android/net/module/util/CollectionUtils.java",
"framework/com/android/net/module/util/DnsUtils.java",

View file

@ -103,13 +103,6 @@ public class TimerFileDescriptor {
public void post(Handler handler) {
handler.sendMessage(mMessage);
}
/**
* Get scheduled message
*/
public Message getMessage() {
return mMessage;
}
}
/**

View file

@ -60,7 +60,6 @@ import android.text.TextUtils;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.SharedLog;
import com.android.net.module.util.TimerFileDescriptor;
import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
import com.android.server.connectivity.mdns.util.MdnsUtils;
import com.android.testutils.DevSdkIgnoreRule;
@ -128,8 +127,6 @@ public class MdnsServiceTypeClientTests {
private SharedLog mockSharedLog;
@Mock
private MdnsServiceTypeClient.Dependencies mockDeps;
@Mock
private TimerFileDescriptor mockTimerFd;
@Captor
private ArgumentCaptor<MdnsServiceInfo> serviceInfoCaptor;
@ -148,7 +145,6 @@ public class MdnsServiceTypeClientTests {
private Message delayMessage = null;
private Handler realHandler = null;
private MdnsFeatureFlags featureFlags = MdnsFeatureFlags.newBuilder().build();
private TimerFileDescriptor.MessageTask task = null;
@Before
@SuppressWarnings("DoNotMock")
@ -248,21 +244,10 @@ public class MdnsServiceTypeClientTests {
return true;
}).when(mockDeps).sendMessage(any(Handler.class), any(Message.class));
doAnswer(inv -> {
realHandler = (Handler) inv.getArguments()[0];
return mockTimerFd;
}).when(mockDeps).createTimerFd(any(Handler.class));
doAnswer(inv -> {
task = (TimerFileDescriptor.MessageTask) inv.getArguments()[0];
latestDelayMs = (long) inv.getArguments()[1];
return null;
}).when(mockTimerFd).setDelayedTask(any(), anyLong());
client = makeMdnsServiceTypeClient(featureFlags);
client = makeMdnsServiceTypeClient();
}
private MdnsServiceTypeClient makeMdnsServiceTypeClient(MdnsFeatureFlags featureFlags) {
private MdnsServiceTypeClient makeMdnsServiceTypeClient() {
return new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
serviceCache, featureFlags);
@ -1941,7 +1926,9 @@ public class MdnsServiceTypeClientTests {
@Test
public void testSendQueryWithKnownAnswers() throws Exception {
client = makeMdnsServiceTypeClient(
client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
serviceCache,
MdnsFeatureFlags.newBuilder().setIsQueryWithKnownAnswerEnabled(true).build());
doCallRealMethod().when(mockDeps).getDatagramPacketsFromMdnsPacket(
@ -2003,7 +1990,9 @@ public class MdnsServiceTypeClientTests {
@Test
public void testSendQueryWithSubTypeWithKnownAnswers() throws Exception {
client = makeMdnsServiceTypeClient(
client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
serviceCache,
MdnsFeatureFlags.newBuilder().setIsQueryWithKnownAnswerEnabled(true).build());
doCallRealMethod().when(mockDeps).getDatagramPacketsFromMdnsPacket(
@ -2125,66 +2114,6 @@ public class MdnsServiceTypeClientTests {
assertEquals(9680L, latestDelayMs);
}
@Test
public void sendQueries_AccurateDelayCallback() {
client = makeMdnsServiceTypeClient(
MdnsFeatureFlags.newBuilder().setIsAccurateDelayCallbackEnabled(true).build());
final int numOfQueriesBeforeBackoff = 2;
final MdnsSearchOptions searchOptions = MdnsSearchOptions.newBuilder()
.addSubtype(SUBTYPE)
.setQueryMode(AGGRESSIVE_QUERY_MODE)
.setNumOfQueriesBeforeBackoff(numOfQueriesBeforeBackoff)
.build();
startSendAndReceive(mockListenerOne, searchOptions);
verify(mockTimerFd, times(1)).cancelTask();
// Verify that the first query has been sent.
verifyAndSendQuery(0 /* index */, 0 /* timeInMs */, true /* expectsUnicastResponse */,
true /* multipleSocketDiscovery */, 1 /* scheduledCount */,
1 /* sendMessageCount */, true /* useAccurateDelayCallback */);
// Verify that the task cancellation occurred before scheduling another query.
verify(mockTimerFd, times(2)).cancelTask();
// Verify that the second query has been sent
verifyAndSendQuery(1 /* index */, 0 /* timeInMs */, false /* expectsUnicastResponse */,
true /* multipleSocketDiscovery */, 2 /* scheduledCount */,
2 /* sendMessageCount */, true /* useAccurateDelayCallback */);
// Verify that the task cancellation occurred before scheduling another query.
verify(mockTimerFd, times(3)).cancelTask();
// Verify that the third query has been sent
verifyAndSendQuery(2 /* index */, TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS,
false /* expectsUnicastResponse */, true /* multipleSocketDiscovery */,
3 /* scheduledCount */, 3 /* sendMessageCount */,
true /* useAccurateDelayCallback */);
// Verify that the task cancellation occurred before scheduling another query.
verify(mockTimerFd, times(4)).cancelTask();
// In backoff mode, the current scheduled task will be canceled and reschedule if the
// 0.8 * smallestRemainingTtl is larger than time to next run.
long currentTime = TEST_TTL / 2 + TEST_ELAPSED_REALTIME;
doReturn(currentTime).when(mockDecoderClock).elapsedRealtime();
doReturn(true).when(mockTimerFd).hasDelayedTask();
processResponse(createResponse(
"service-instance-1", "192.0.2.123", 5353,
SERVICE_TYPE_LABELS,
Collections.emptyMap(), TEST_TTL), socketKey);
// Verify that the task cancellation occurred twice.
verify(mockTimerFd, times(6)).cancelTask();
assertNotNull(task);
verifyAndSendQuery(3 /* index */, (long) (TEST_TTL / 2 * 0.8) /* timeInMs */,
true /* expectsUnicastResponse */, true /* multipleSocketDiscovery */,
5 /* scheduledCount */, 4 /* sendMessageCount */,
true /* useAccurateDelayCallback */);
// Verify that the task cancellation occurred before scheduling another query.
verify(mockTimerFd, times(7)).cancelTask();
// Stop sending packets.
stopSendAndReceive(mockListenerOne);
verify(mockTimerFd, times(8)).cancelTask();
}
private static MdnsServiceInfo matchServiceName(String name) {
return argThat(info -> info.getServiceInstanceName().equals(name));
}
@ -2198,22 +2127,9 @@ public class MdnsServiceTypeClientTests {
private void verifyAndSendQuery(int index, long timeInMs, boolean expectsUnicastResponse,
boolean multipleSocketDiscovery, int scheduledCount) {
verifyAndSendQuery(index, timeInMs, expectsUnicastResponse,
multipleSocketDiscovery, scheduledCount, index + 1 /* sendMessageCount */,
false /* useAccurateDelayCallback */);
}
private void verifyAndSendQuery(int index, long timeInMs, boolean expectsUnicastResponse,
boolean multipleSocketDiscovery, int scheduledCount, int sendMessageCount,
boolean useAccurateDelayCallback) {
if (useAccurateDelayCallback && task != null && realHandler != null) {
runOnHandler(() -> realHandler.dispatchMessage(task.getMessage()));
task = null;
} else {
// Dispatch the message
if (delayMessage != null && realHandler != null) {
dispatchMessage();
}
// Dispatch the message
if (delayMessage != null && realHandler != null) {
dispatchMessage();
}
assertEquals(timeInMs, latestDelayMs);
currentThreadExecutor.getAndClearLastScheduledRunnable().run();
@ -2236,15 +2152,11 @@ public class MdnsServiceTypeClientTests {
eq(socketKey), eq(false));
}
}
verify(mockDeps, times(sendMessageCount))
verify(mockDeps, times(index + 1))
.sendMessage(any(Handler.class), any(Message.class));
// Verify the task has been scheduled.
if (useAccurateDelayCallback) {
verify(mockTimerFd, times(scheduledCount)).setDelayedTask(any(), anyLong());
} else {
verify(mockDeps, times(scheduledCount))
.sendMessageDelayed(any(Handler.class), any(Message.class), anyLong());
}
verify(mockDeps, times(scheduledCount))
.sendMessageDelayed(any(Handler.class), any(Message.class), anyLong());
}
private static String[] getTestServiceName(String instanceName) {