Merge "Add aggressive query mode" into main
This commit is contained in:
commit
b4fba147cd
5 changed files with 255 additions and 41 deletions
|
|
@ -35,6 +35,7 @@ import static com.android.networkstack.apishim.ConstantsShim.REGISTER_NSD_OFFLOA
|
||||||
import static com.android.server.connectivity.mdns.MdnsAdvertiser.AdvertiserMetrics;
|
import static com.android.server.connectivity.mdns.MdnsAdvertiser.AdvertiserMetrics;
|
||||||
import static com.android.server.connectivity.mdns.MdnsConstants.NO_PACKET;
|
import static com.android.server.connectivity.mdns.MdnsConstants.NO_PACKET;
|
||||||
import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
|
import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
|
||||||
|
import static com.android.server.connectivity.mdns.MdnsSearchOptions.AGGRESSIVE_QUERY_MODE;
|
||||||
import static com.android.server.connectivity.mdns.MdnsSearchOptions.PASSIVE_QUERY_MODE;
|
import static com.android.server.connectivity.mdns.MdnsSearchOptions.PASSIVE_QUERY_MODE;
|
||||||
import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock;
|
import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock;
|
||||||
|
|
||||||
|
|
@ -252,6 +253,8 @@ public class NsdService extends INsdManager.Stub {
|
||||||
|
|
||||||
private final RemoteCallbackList<IOffloadEngine> mOffloadEngines =
|
private final RemoteCallbackList<IOffloadEngine> mOffloadEngines =
|
||||||
new RemoteCallbackList<>();
|
new RemoteCallbackList<>();
|
||||||
|
@NonNull
|
||||||
|
private final MdnsFeatureFlags mMdnsFeatureFlags;
|
||||||
|
|
||||||
private static class OffloadEngineInfo {
|
private static class OffloadEngineInfo {
|
||||||
@NonNull final String mInterfaceName;
|
@NonNull final String mInterfaceName;
|
||||||
|
|
@ -794,7 +797,10 @@ public class NsdService extends INsdManager.Stub {
|
||||||
MdnsSearchOptions.newBuilder()
|
MdnsSearchOptions.newBuilder()
|
||||||
.setNetwork(discoveryRequest.getNetwork())
|
.setNetwork(discoveryRequest.getNetwork())
|
||||||
.setRemoveExpiredService(true)
|
.setRemoveExpiredService(true)
|
||||||
.setQueryMode(PASSIVE_QUERY_MODE);
|
.setQueryMode(
|
||||||
|
mMdnsFeatureFlags.isAggressiveQueryModeEnabled()
|
||||||
|
? AGGRESSIVE_QUERY_MODE
|
||||||
|
: PASSIVE_QUERY_MODE);
|
||||||
if (subtype != null) {
|
if (subtype != null) {
|
||||||
// checkSubtypeLabels() ensures that subtypes start with '_' but
|
// checkSubtypeLabels() ensures that subtypes start with '_' but
|
||||||
// MdnsSearchOptions expects the underscore to not be present.
|
// MdnsSearchOptions expects the underscore to not be present.
|
||||||
|
|
@ -1063,7 +1069,9 @@ public class NsdService extends INsdManager.Stub {
|
||||||
transactionId, resolveServiceType);
|
transactionId, resolveServiceType);
|
||||||
final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
|
final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
|
||||||
.setNetwork(info.getNetwork())
|
.setNetwork(info.getNetwork())
|
||||||
.setQueryMode(PASSIVE_QUERY_MODE)
|
.setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled()
|
||||||
|
? AGGRESSIVE_QUERY_MODE
|
||||||
|
: PASSIVE_QUERY_MODE)
|
||||||
.setResolveInstanceName(info.getServiceName())
|
.setResolveInstanceName(info.getServiceName())
|
||||||
.setRemoveExpiredService(true)
|
.setRemoveExpiredService(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -1161,7 +1169,9 @@ public class NsdService extends INsdManager.Stub {
|
||||||
transactionId, resolveServiceType);
|
transactionId, resolveServiceType);
|
||||||
final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
|
final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
|
||||||
.setNetwork(info.getNetwork())
|
.setNetwork(info.getNetwork())
|
||||||
.setQueryMode(PASSIVE_QUERY_MODE)
|
.setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled()
|
||||||
|
? AGGRESSIVE_QUERY_MODE
|
||||||
|
: PASSIVE_QUERY_MODE)
|
||||||
.setResolveInstanceName(info.getServiceName())
|
.setResolveInstanceName(info.getServiceName())
|
||||||
.setRemoveExpiredService(true)
|
.setRemoveExpiredService(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -1787,7 +1797,7 @@ public class NsdService extends INsdManager.Stub {
|
||||||
am.addOnUidImportanceListener(new UidImportanceListener(handler),
|
am.addOnUidImportanceListener(new UidImportanceListener(handler),
|
||||||
mRunningAppActiveImportanceCutoff);
|
mRunningAppActiveImportanceCutoff);
|
||||||
|
|
||||||
final MdnsFeatureFlags flags = new MdnsFeatureFlags.Builder()
|
mMdnsFeatureFlags = new MdnsFeatureFlags.Builder()
|
||||||
.setIsMdnsOffloadFeatureEnabled(mDeps.isTetheringFeatureNotChickenedOut(
|
.setIsMdnsOffloadFeatureEnabled(mDeps.isTetheringFeatureNotChickenedOut(
|
||||||
mContext, MdnsFeatureFlags.NSD_FORCE_DISABLE_MDNS_OFFLOAD))
|
mContext, MdnsFeatureFlags.NSD_FORCE_DISABLE_MDNS_OFFLOAD))
|
||||||
.setIncludeInetAddressRecordsInProbing(mDeps.isFeatureEnabled(
|
.setIncludeInetAddressRecordsInProbing(mDeps.isFeatureEnabled(
|
||||||
|
|
@ -1800,18 +1810,21 @@ public class NsdService extends INsdManager.Stub {
|
||||||
mContext, MdnsFeatureFlags.NSD_KNOWN_ANSWER_SUPPRESSION))
|
mContext, MdnsFeatureFlags.NSD_KNOWN_ANSWER_SUPPRESSION))
|
||||||
.setIsUnicastReplyEnabled(mDeps.isFeatureEnabled(
|
.setIsUnicastReplyEnabled(mDeps.isFeatureEnabled(
|
||||||
mContext, MdnsFeatureFlags.NSD_UNICAST_REPLY_ENABLED))
|
mContext, MdnsFeatureFlags.NSD_UNICAST_REPLY_ENABLED))
|
||||||
|
.setIsAggressiveQueryModeEnabled(mDeps.isFeatureEnabled(
|
||||||
|
mContext, MdnsFeatureFlags.NSD_AGGRESSIVE_QUERY_MODE))
|
||||||
.setOverrideProvider(flag -> mDeps.isFeatureEnabled(
|
.setOverrideProvider(flag -> mDeps.isFeatureEnabled(
|
||||||
mContext, FORCE_ENABLE_FLAG_FOR_TEST_PREFIX + flag))
|
mContext, FORCE_ENABLE_FLAG_FOR_TEST_PREFIX + flag))
|
||||||
.build();
|
.build();
|
||||||
mMdnsSocketClient =
|
mMdnsSocketClient =
|
||||||
new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider,
|
new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider,
|
||||||
LOGGER.forSubComponent("MdnsMultinetworkSocketClient"), flags);
|
LOGGER.forSubComponent("MdnsMultinetworkSocketClient"), mMdnsFeatureFlags);
|
||||||
mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(),
|
mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(),
|
||||||
mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"), flags);
|
mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"),
|
||||||
|
mMdnsFeatureFlags);
|
||||||
handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
|
handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
|
||||||
mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider,
|
mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider,
|
||||||
new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"), flags,
|
new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"),
|
||||||
mContext);
|
mMdnsFeatureFlags, mContext);
|
||||||
mClock = deps.makeClock();
|
mClock = deps.makeClock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,11 @@ public class MdnsFeatureFlags {
|
||||||
*/
|
*/
|
||||||
public static final String NSD_UNICAST_REPLY_ENABLED = "nsd_unicast_reply_enabled";
|
public static final String NSD_UNICAST_REPLY_ENABLED = "nsd_unicast_reply_enabled";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A feature flag to control whether the aggressive query mode should be enabled.
|
||||||
|
*/
|
||||||
|
public static final String NSD_AGGRESSIVE_QUERY_MODE = "nsd_aggressive_query_mode";
|
||||||
|
|
||||||
// Flag for offload feature
|
// Flag for offload feature
|
||||||
public final boolean mIsMdnsOffloadFeatureEnabled;
|
public final boolean mIsMdnsOffloadFeatureEnabled;
|
||||||
|
|
||||||
|
|
@ -75,6 +80,9 @@ public class MdnsFeatureFlags {
|
||||||
// Flag to enable replying unicast to queries requesting unicast replies
|
// Flag to enable replying unicast to queries requesting unicast replies
|
||||||
public final boolean mIsUnicastReplyEnabled;
|
public final boolean mIsUnicastReplyEnabled;
|
||||||
|
|
||||||
|
// Flag for aggressive query mode
|
||||||
|
public final boolean mIsAggressiveQueryModeEnabled;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final FlagOverrideProvider mOverrideProvider;
|
private final FlagOverrideProvider mOverrideProvider;
|
||||||
|
|
||||||
|
|
@ -102,6 +110,13 @@ public class MdnsFeatureFlags {
|
||||||
return mIsUnicastReplyEnabled || isForceEnabledForTest(NSD_UNICAST_REPLY_ENABLED);
|
return mIsUnicastReplyEnabled || isForceEnabledForTest(NSD_UNICAST_REPLY_ENABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether {@link #NSD_AGGRESSIVE_QUERY_MODE} is enabled, including for testing.
|
||||||
|
*/
|
||||||
|
public boolean isAggressiveQueryModeEnabled() {
|
||||||
|
return mIsAggressiveQueryModeEnabled || isForceEnabledForTest(NSD_AGGRESSIVE_QUERY_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The constructor for {@link MdnsFeatureFlags}.
|
* The constructor for {@link MdnsFeatureFlags}.
|
||||||
*/
|
*/
|
||||||
|
|
@ -111,6 +126,7 @@ public class MdnsFeatureFlags {
|
||||||
boolean isLabelCountLimitEnabled,
|
boolean isLabelCountLimitEnabled,
|
||||||
boolean isKnownAnswerSuppressionEnabled,
|
boolean isKnownAnswerSuppressionEnabled,
|
||||||
boolean isUnicastReplyEnabled,
|
boolean isUnicastReplyEnabled,
|
||||||
|
boolean isAggressiveQueryModeEnabled,
|
||||||
@Nullable FlagOverrideProvider overrideProvider) {
|
@Nullable FlagOverrideProvider overrideProvider) {
|
||||||
mIsMdnsOffloadFeatureEnabled = isOffloadFeatureEnabled;
|
mIsMdnsOffloadFeatureEnabled = isOffloadFeatureEnabled;
|
||||||
mIncludeInetAddressRecordsInProbing = includeInetAddressRecordsInProbing;
|
mIncludeInetAddressRecordsInProbing = includeInetAddressRecordsInProbing;
|
||||||
|
|
@ -118,6 +134,7 @@ public class MdnsFeatureFlags {
|
||||||
mIsLabelCountLimitEnabled = isLabelCountLimitEnabled;
|
mIsLabelCountLimitEnabled = isLabelCountLimitEnabled;
|
||||||
mIsKnownAnswerSuppressionEnabled = isKnownAnswerSuppressionEnabled;
|
mIsKnownAnswerSuppressionEnabled = isKnownAnswerSuppressionEnabled;
|
||||||
mIsUnicastReplyEnabled = isUnicastReplyEnabled;
|
mIsUnicastReplyEnabled = isUnicastReplyEnabled;
|
||||||
|
mIsAggressiveQueryModeEnabled = isAggressiveQueryModeEnabled;
|
||||||
mOverrideProvider = overrideProvider;
|
mOverrideProvider = overrideProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,6 +153,7 @@ public class MdnsFeatureFlags {
|
||||||
private boolean mIsLabelCountLimitEnabled;
|
private boolean mIsLabelCountLimitEnabled;
|
||||||
private boolean mIsKnownAnswerSuppressionEnabled;
|
private boolean mIsKnownAnswerSuppressionEnabled;
|
||||||
private boolean mIsUnicastReplyEnabled;
|
private boolean mIsUnicastReplyEnabled;
|
||||||
|
private boolean mIsAggressiveQueryModeEnabled;
|
||||||
private FlagOverrideProvider mOverrideProvider;
|
private FlagOverrideProvider mOverrideProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -148,6 +166,7 @@ public class MdnsFeatureFlags {
|
||||||
mIsLabelCountLimitEnabled = true; // Default enabled.
|
mIsLabelCountLimitEnabled = true; // Default enabled.
|
||||||
mIsKnownAnswerSuppressionEnabled = false;
|
mIsKnownAnswerSuppressionEnabled = false;
|
||||||
mIsUnicastReplyEnabled = true;
|
mIsUnicastReplyEnabled = true;
|
||||||
|
mIsAggressiveQueryModeEnabled = false;
|
||||||
mOverrideProvider = null;
|
mOverrideProvider = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -223,6 +242,16 @@ public class MdnsFeatureFlags {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether the aggressive query mode is enabled.
|
||||||
|
*
|
||||||
|
* @see #NSD_AGGRESSIVE_QUERY_MODE
|
||||||
|
*/
|
||||||
|
public Builder setIsAggressiveQueryModeEnabled(boolean isAggressiveQueryModeEnabled) {
|
||||||
|
mIsAggressiveQueryModeEnabled = isAggressiveQueryModeEnabled;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a {@link MdnsFeatureFlags} with the arguments supplied to this builder.
|
* Builds a {@link MdnsFeatureFlags} with the arguments supplied to this builder.
|
||||||
*/
|
*/
|
||||||
|
|
@ -233,6 +262,7 @@ public class MdnsFeatureFlags {
|
||||||
mIsLabelCountLimitEnabled,
|
mIsLabelCountLimitEnabled,
|
||||||
mIsKnownAnswerSuppressionEnabled,
|
mIsKnownAnswerSuppressionEnabled,
|
||||||
mIsUnicastReplyEnabled,
|
mIsUnicastReplyEnabled,
|
||||||
|
mIsAggressiveQueryModeEnabled,
|
||||||
mOverrideProvider);
|
mOverrideProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,10 @@ public class MdnsSearchOptions implements Parcelable {
|
||||||
public static final int PASSIVE_QUERY_MODE = 0;
|
public static final int PASSIVE_QUERY_MODE = 0;
|
||||||
// Active query mode scans frequently.
|
// Active query mode scans frequently.
|
||||||
public static final int ACTIVE_QUERY_MODE = 1;
|
public static final int ACTIVE_QUERY_MODE = 1;
|
||||||
|
// Aggressive query mode scans more frequently than the active mode at first, and sends both
|
||||||
|
// unicast and multicast queries simultaneously, but in long sessions it eventually sends as
|
||||||
|
// many queries as the PASSIVE mode.
|
||||||
|
public static final int AGGRESSIVE_QUERY_MODE = 2;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public static final Parcelable.Creator<MdnsSearchOptions> CREATOR =
|
public static final Parcelable.Creator<MdnsSearchOptions> CREATOR =
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package com.android.server.connectivity.mdns;
|
package com.android.server.connectivity.mdns;
|
||||||
|
|
||||||
|
import static com.android.server.connectivity.mdns.MdnsSearchOptions.AGGRESSIVE_QUERY_MODE;
|
||||||
import static com.android.server.connectivity.mdns.MdnsSearchOptions.PASSIVE_QUERY_MODE;
|
import static com.android.server.connectivity.mdns.MdnsSearchOptions.PASSIVE_QUERY_MODE;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
|
@ -35,13 +36,23 @@ public class QueryTaskConfig {
|
||||||
|
|
||||||
private static final int INITIAL_TIME_BETWEEN_BURSTS_MS =
|
private static final int INITIAL_TIME_BETWEEN_BURSTS_MS =
|
||||||
(int) MdnsConfigs.initialTimeBetweenBurstsMs();
|
(int) MdnsConfigs.initialTimeBetweenBurstsMs();
|
||||||
private static final int TIME_BETWEEN_BURSTS_MS = (int) MdnsConfigs.timeBetweenBurstsMs();
|
private static final int MAX_TIME_BETWEEN_ACTIVE_PASSIVE_BURSTS_MS =
|
||||||
|
(int) MdnsConfigs.timeBetweenBurstsMs();
|
||||||
private static final int QUERIES_PER_BURST = (int) MdnsConfigs.queriesPerBurst();
|
private static final int QUERIES_PER_BURST = (int) MdnsConfigs.queriesPerBurst();
|
||||||
private static final int TIME_BETWEEN_QUERIES_IN_BURST_MS =
|
private static final int TIME_BETWEEN_QUERIES_IN_BURST_MS =
|
||||||
(int) MdnsConfigs.timeBetweenQueriesInBurstMs();
|
(int) MdnsConfigs.timeBetweenQueriesInBurstMs();
|
||||||
private static final int QUERIES_PER_BURST_PASSIVE_MODE =
|
private static final int QUERIES_PER_BURST_PASSIVE_MODE =
|
||||||
(int) MdnsConfigs.queriesPerBurstPassive();
|
(int) MdnsConfigs.queriesPerBurstPassive();
|
||||||
private static final int UNSIGNED_SHORT_MAX_VALUE = 65536;
|
private static final int UNSIGNED_SHORT_MAX_VALUE = 65536;
|
||||||
|
@VisibleForTesting
|
||||||
|
// RFC 6762 5.2: The interval between the first two queries MUST be at least one second.
|
||||||
|
static final int INITIAL_AGGRESSIVE_TIME_BETWEEN_BURSTS_MS = 1000;
|
||||||
|
@VisibleForTesting
|
||||||
|
// Basically this tries to send one query per typical DTIM interval 100ms, to maximize the
|
||||||
|
// chances that a query will be received if devices are using a DTIM multiplier (in which case
|
||||||
|
// they only listen once every [multiplier] DTIM intervals).
|
||||||
|
static final int TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS = 100;
|
||||||
|
static final int MAX_TIME_BETWEEN_AGGRESSIVE_BURSTS_MS = 60000;
|
||||||
// The following fields are used by QueryTask so we need to test them.
|
// The following fields are used by QueryTask so we need to test them.
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
final List<String> subtypes;
|
final List<String> subtypes;
|
||||||
|
|
@ -81,6 +92,7 @@ public class QueryTaskConfig {
|
||||||
this.queryCount = queryCount;
|
this.queryCount = queryCount;
|
||||||
this.socketKey = other.socketKey;
|
this.socketKey = other.socketKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryTaskConfig(@NonNull Collection<String> subtypes,
|
QueryTaskConfig(@NonNull Collection<String> subtypes,
|
||||||
int queryMode,
|
int queryMode,
|
||||||
boolean onlyUseIpv6OnIpv6OnlyNetworks,
|
boolean onlyUseIpv6OnIpv6OnlyNetworks,
|
||||||
|
|
@ -96,21 +108,59 @@ public class QueryTaskConfig {
|
||||||
this.expectUnicastResponse = true;
|
this.expectUnicastResponse = true;
|
||||||
this.isFirstBurst = true;
|
this.isFirstBurst = true;
|
||||||
// Config the scan frequency based on the scan mode.
|
// Config the scan frequency based on the scan mode.
|
||||||
if (this.queryMode == PASSIVE_QUERY_MODE) {
|
if (this.queryMode == AGGRESSIVE_QUERY_MODE) {
|
||||||
|
this.timeBetweenBurstsInMs = INITIAL_AGGRESSIVE_TIME_BETWEEN_BURSTS_MS;
|
||||||
|
this.delayUntilNextTaskWithoutBackoffMs =
|
||||||
|
TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS;
|
||||||
|
} else if (this.queryMode == PASSIVE_QUERY_MODE) {
|
||||||
// In passive scan mode, sends a single burst of QUERIES_PER_BURST queries, and then
|
// In passive scan mode, sends a single burst of QUERIES_PER_BURST queries, and then
|
||||||
// in each TIME_BETWEEN_BURSTS interval, sends QUERIES_PER_BURST_PASSIVE_MODE
|
// in each TIME_BETWEEN_BURSTS interval, sends QUERIES_PER_BURST_PASSIVE_MODE
|
||||||
// queries.
|
// queries.
|
||||||
this.timeBetweenBurstsInMs = TIME_BETWEEN_BURSTS_MS;
|
this.timeBetweenBurstsInMs = MAX_TIME_BETWEEN_ACTIVE_PASSIVE_BURSTS_MS;
|
||||||
|
this.delayUntilNextTaskWithoutBackoffMs = TIME_BETWEEN_QUERIES_IN_BURST_MS;
|
||||||
} else {
|
} else {
|
||||||
// In active scan mode, sends a burst of QUERIES_PER_BURST queries,
|
// In active scan mode, sends a burst of QUERIES_PER_BURST queries,
|
||||||
// TIME_BETWEEN_QUERIES_IN_BURST_MS apart, then waits for the scan interval, and
|
// TIME_BETWEEN_QUERIES_IN_BURST_MS apart, then waits for the scan interval, and
|
||||||
// then repeats. The scan interval starts as INITIAL_TIME_BETWEEN_BURSTS_MS and
|
// then repeats. The scan interval starts as INITIAL_TIME_BETWEEN_BURSTS_MS and
|
||||||
// doubles until it maxes out at TIME_BETWEEN_BURSTS_MS.
|
// doubles until it maxes out at TIME_BETWEEN_BURSTS_MS.
|
||||||
this.timeBetweenBurstsInMs = INITIAL_TIME_BETWEEN_BURSTS_MS;
|
this.timeBetweenBurstsInMs = INITIAL_TIME_BETWEEN_BURSTS_MS;
|
||||||
|
this.delayUntilNextTaskWithoutBackoffMs = TIME_BETWEEN_QUERIES_IN_BURST_MS;
|
||||||
}
|
}
|
||||||
this.socketKey = socketKey;
|
this.socketKey = socketKey;
|
||||||
this.queryCount = 0;
|
this.queryCount = 0;
|
||||||
this.delayUntilNextTaskWithoutBackoffMs = TIME_BETWEEN_QUERIES_IN_BURST_MS;
|
}
|
||||||
|
|
||||||
|
long getDelayUntilNextTaskWithoutBackoff(boolean isFirstQueryInBurst,
|
||||||
|
boolean isLastQueryInBurst) {
|
||||||
|
if (isFirstQueryInBurst && queryMode == AGGRESSIVE_QUERY_MODE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (isLastQueryInBurst) {
|
||||||
|
return timeBetweenBurstsInMs;
|
||||||
|
}
|
||||||
|
return queryMode == AGGRESSIVE_QUERY_MODE
|
||||||
|
? TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS
|
||||||
|
: TIME_BETWEEN_QUERIES_IN_BURST_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean getNextExpectUnicastResponse(boolean isLastQueryInBurst) {
|
||||||
|
if (!isLastQueryInBurst) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (queryMode == AGGRESSIVE_QUERY_MODE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return alwaysAskForUnicastResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getNextTimeBetweenBurstsMs(boolean isLastQueryInBurst) {
|
||||||
|
if (!isLastQueryInBurst) {
|
||||||
|
return timeBetweenBurstsInMs;
|
||||||
|
}
|
||||||
|
final int maxTimeBetweenBursts = queryMode == AGGRESSIVE_QUERY_MODE
|
||||||
|
? MAX_TIME_BETWEEN_AGGRESSIVE_BURSTS_MS : MAX_TIME_BETWEEN_ACTIVE_PASSIVE_BURSTS_MS;
|
||||||
|
return timeBetweenBurstsInMs < maxTimeBetweenBursts
|
||||||
|
? Math.min(timeBetweenBurstsInMs * 2, maxTimeBetweenBursts) : timeBetweenBurstsInMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -122,43 +172,28 @@ public class QueryTaskConfig {
|
||||||
if (newTransactionId > UNSIGNED_SHORT_MAX_VALUE) {
|
if (newTransactionId > UNSIGNED_SHORT_MAX_VALUE) {
|
||||||
newTransactionId = 1;
|
newTransactionId = 1;
|
||||||
}
|
}
|
||||||
boolean newExpectUnicastResponse = false;
|
|
||||||
boolean newIsFirstBurst = isFirstBurst;
|
boolean newIsFirstBurst = isFirstBurst;
|
||||||
int newQueriesPerBurst = queriesPerBurst;
|
int newQueriesPerBurst = queriesPerBurst;
|
||||||
int newBurstCounter = burstCounter + 1;
|
int newBurstCounter = burstCounter + 1;
|
||||||
long newDelayUntilNextTaskWithoutBackoffMs = delayUntilNextTaskWithoutBackoffMs;
|
final boolean isFirstQueryInBurst = newBurstCounter == 1;
|
||||||
int newTimeBetweenBurstsInMs = timeBetweenBurstsInMs;
|
final boolean isLastQueryInBurst = newBurstCounter == queriesPerBurst;
|
||||||
// Only the first query expects uni-cast response.
|
if (isLastQueryInBurst) {
|
||||||
if (newBurstCounter == queriesPerBurst) {
|
|
||||||
newBurstCounter = 0;
|
newBurstCounter = 0;
|
||||||
|
|
||||||
if (alwaysAskForUnicastResponse) {
|
|
||||||
newExpectUnicastResponse = true;
|
|
||||||
}
|
|
||||||
// In passive scan mode, sends a single burst of QUERIES_PER_BURST queries, and
|
|
||||||
// then in each TIME_BETWEEN_BURSTS interval, sends QUERIES_PER_BURST_PASSIVE_MODE
|
|
||||||
// queries.
|
|
||||||
if (isFirstBurst) {
|
if (isFirstBurst) {
|
||||||
newIsFirstBurst = false;
|
newIsFirstBurst = false;
|
||||||
|
// In passive scan mode, sends a single burst of QUERIES_PER_BURST queries, and
|
||||||
|
// then in each TIME_BETWEEN_BURSTS interval, sends QUERIES_PER_BURST_PASSIVE_MODE
|
||||||
|
// queries.
|
||||||
if (queryMode == PASSIVE_QUERY_MODE) {
|
if (queryMode == PASSIVE_QUERY_MODE) {
|
||||||
newQueriesPerBurst = QUERIES_PER_BURST_PASSIVE_MODE;
|
newQueriesPerBurst = QUERIES_PER_BURST_PASSIVE_MODE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// In active scan mode, sends a burst of QUERIES_PER_BURST queries,
|
|
||||||
// TIME_BETWEEN_QUERIES_IN_BURST_MS apart, then waits for the scan interval, and
|
|
||||||
// then repeats. The scan interval starts as INITIAL_TIME_BETWEEN_BURSTS_MS and
|
|
||||||
// doubles until it maxes out at TIME_BETWEEN_BURSTS_MS.
|
|
||||||
newDelayUntilNextTaskWithoutBackoffMs = timeBetweenBurstsInMs;
|
|
||||||
if (timeBetweenBurstsInMs < TIME_BETWEEN_BURSTS_MS) {
|
|
||||||
newTimeBetweenBurstsInMs = Math.min(timeBetweenBurstsInMs * 2,
|
|
||||||
TIME_BETWEEN_BURSTS_MS);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newDelayUntilNextTaskWithoutBackoffMs = TIME_BETWEEN_QUERIES_IN_BURST_MS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new QueryTaskConfig(this, newQueryCount, newTransactionId,
|
return new QueryTaskConfig(this, newQueryCount, newTransactionId,
|
||||||
newExpectUnicastResponse, newIsFirstBurst, newBurstCounter, newQueriesPerBurst,
|
getNextExpectUnicastResponse(isLastQueryInBurst), newIsFirstBurst, newBurstCounter,
|
||||||
newTimeBetweenBurstsInMs, newDelayUntilNextTaskWithoutBackoffMs);
|
newQueriesPerBurst, getNextTimeBetweenBurstsMs(isLastQueryInBurst),
|
||||||
|
getDelayUntilNextTaskWithoutBackoff(isFirstQueryInBurst, isLastQueryInBurst));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,12 @@
|
||||||
package com.android.server.connectivity.mdns;
|
package com.android.server.connectivity.mdns;
|
||||||
|
|
||||||
import static com.android.server.connectivity.mdns.MdnsSearchOptions.ACTIVE_QUERY_MODE;
|
import static com.android.server.connectivity.mdns.MdnsSearchOptions.ACTIVE_QUERY_MODE;
|
||||||
|
import static com.android.server.connectivity.mdns.MdnsSearchOptions.AGGRESSIVE_QUERY_MODE;
|
||||||
import static com.android.server.connectivity.mdns.MdnsSearchOptions.PASSIVE_QUERY_MODE;
|
import static com.android.server.connectivity.mdns.MdnsSearchOptions.PASSIVE_QUERY_MODE;
|
||||||
import static com.android.server.connectivity.mdns.MdnsServiceTypeClient.EVENT_START_QUERYTASK;
|
import static com.android.server.connectivity.mdns.MdnsServiceTypeClient.EVENT_START_QUERYTASK;
|
||||||
|
import static com.android.server.connectivity.mdns.QueryTaskConfig.INITIAL_AGGRESSIVE_TIME_BETWEEN_BURSTS_MS;
|
||||||
|
import static com.android.server.connectivity.mdns.QueryTaskConfig.MAX_TIME_BETWEEN_AGGRESSIVE_BURSTS_MS;
|
||||||
|
import static com.android.server.connectivity.mdns.QueryTaskConfig.TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS;
|
||||||
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
|
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
@ -147,8 +151,8 @@ public class MdnsServiceTypeClientTests {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
doReturn(TEST_ELAPSED_REALTIME).when(mockDecoderClock).elapsedRealtime();
|
doReturn(TEST_ELAPSED_REALTIME).when(mockDecoderClock).elapsedRealtime();
|
||||||
|
|
||||||
expectedIPv4Packets = new DatagramPacket[16];
|
expectedIPv4Packets = new DatagramPacket[24];
|
||||||
expectedIPv6Packets = new DatagramPacket[16];
|
expectedIPv6Packets = new DatagramPacket[24];
|
||||||
socketKey = new SocketKey(mockNetwork, INTERFACE_INDEX);
|
socketKey = new SocketKey(mockNetwork, INTERFACE_INDEX);
|
||||||
|
|
||||||
for (int i = 0; i < expectedIPv4Packets.length; ++i) {
|
for (int i = 0; i < expectedIPv4Packets.length; ++i) {
|
||||||
|
|
@ -173,7 +177,15 @@ public class MdnsServiceTypeClientTests {
|
||||||
.thenReturn(expectedIPv4Packets[12])
|
.thenReturn(expectedIPv4Packets[12])
|
||||||
.thenReturn(expectedIPv4Packets[13])
|
.thenReturn(expectedIPv4Packets[13])
|
||||||
.thenReturn(expectedIPv4Packets[14])
|
.thenReturn(expectedIPv4Packets[14])
|
||||||
.thenReturn(expectedIPv4Packets[15]);
|
.thenReturn(expectedIPv4Packets[15])
|
||||||
|
.thenReturn(expectedIPv4Packets[16])
|
||||||
|
.thenReturn(expectedIPv4Packets[17])
|
||||||
|
.thenReturn(expectedIPv4Packets[18])
|
||||||
|
.thenReturn(expectedIPv4Packets[19])
|
||||||
|
.thenReturn(expectedIPv4Packets[20])
|
||||||
|
.thenReturn(expectedIPv4Packets[21])
|
||||||
|
.thenReturn(expectedIPv4Packets[22])
|
||||||
|
.thenReturn(expectedIPv4Packets[23]);
|
||||||
|
|
||||||
when(mockPacketWriter.getPacket(IPV6_ADDRESS))
|
when(mockPacketWriter.getPacket(IPV6_ADDRESS))
|
||||||
.thenReturn(expectedIPv6Packets[0])
|
.thenReturn(expectedIPv6Packets[0])
|
||||||
|
|
@ -191,7 +203,15 @@ public class MdnsServiceTypeClientTests {
|
||||||
.thenReturn(expectedIPv6Packets[12])
|
.thenReturn(expectedIPv6Packets[12])
|
||||||
.thenReturn(expectedIPv6Packets[13])
|
.thenReturn(expectedIPv6Packets[13])
|
||||||
.thenReturn(expectedIPv6Packets[14])
|
.thenReturn(expectedIPv6Packets[14])
|
||||||
.thenReturn(expectedIPv6Packets[15]);
|
.thenReturn(expectedIPv6Packets[15])
|
||||||
|
.thenReturn(expectedIPv6Packets[16])
|
||||||
|
.thenReturn(expectedIPv6Packets[17])
|
||||||
|
.thenReturn(expectedIPv6Packets[18])
|
||||||
|
.thenReturn(expectedIPv6Packets[19])
|
||||||
|
.thenReturn(expectedIPv6Packets[20])
|
||||||
|
.thenReturn(expectedIPv6Packets[21])
|
||||||
|
.thenReturn(expectedIPv6Packets[22])
|
||||||
|
.thenReturn(expectedIPv6Packets[23]);
|
||||||
|
|
||||||
thread = new HandlerThread("MdnsServiceTypeClientTests");
|
thread = new HandlerThread("MdnsServiceTypeClientTests");
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
@ -1672,6 +1692,118 @@ public class MdnsServiceTypeClientTests {
|
||||||
socketKey);
|
socketKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getBetweenBurstTime(int burstCounter, int currentBetweenTime, int maxBetweenTime,
|
||||||
|
int initialBetweenTime) {
|
||||||
|
return currentBetweenTime < maxBetweenTime
|
||||||
|
? Math.min(initialBetweenTime * (int) Math.pow(2, burstCounter), maxBetweenTime)
|
||||||
|
: currentBetweenTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sendQueries_aggressiveScanMode() {
|
||||||
|
final MdnsSearchOptions searchOptions = MdnsSearchOptions.newBuilder()
|
||||||
|
.addSubtype(SUBTYPE).setQueryMode(AGGRESSIVE_QUERY_MODE).build();
|
||||||
|
startSendAndReceive(mockListenerOne, searchOptions);
|
||||||
|
// Always try to remove the task.
|
||||||
|
verify(mockDeps, times(1)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
|
||||||
|
|
||||||
|
int burstCounter = 0;
|
||||||
|
int betweenBurstTime = 0;
|
||||||
|
for (int i = 0; i < expectedIPv4Packets.length; i += 3) {
|
||||||
|
verifyAndSendQuery(i, betweenBurstTime, /* expectsUnicastResponse= */ true);
|
||||||
|
verifyAndSendQuery(i + 1, /* timeInMs= */ 0, /* expectsUnicastResponse= */ false);
|
||||||
|
verifyAndSendQuery(i + 2, TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS,
|
||||||
|
/* expectsUnicastResponse= */ false);
|
||||||
|
betweenBurstTime = getBetweenBurstTime(burstCounter, betweenBurstTime,
|
||||||
|
MAX_TIME_BETWEEN_AGGRESSIVE_BURSTS_MS,
|
||||||
|
INITIAL_AGGRESSIVE_TIME_BETWEEN_BURSTS_MS);
|
||||||
|
burstCounter++;
|
||||||
|
}
|
||||||
|
// Verify that Task is not removed before stopSendAndReceive was called.
|
||||||
|
verify(mockDeps, times(1)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
|
||||||
|
|
||||||
|
// Stop sending packets.
|
||||||
|
stopSendAndReceive(mockListenerOne);
|
||||||
|
verify(mockDeps, times(2)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sendQueries_reentry_aggressiveScanMode() {
|
||||||
|
final MdnsSearchOptions searchOptions = MdnsSearchOptions.newBuilder()
|
||||||
|
.addSubtype(SUBTYPE).setQueryMode(AGGRESSIVE_QUERY_MODE).build();
|
||||||
|
startSendAndReceive(mockListenerOne, searchOptions);
|
||||||
|
// Always try to remove the task.
|
||||||
|
verify(mockDeps, times(1)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
|
||||||
|
|
||||||
|
// First burst, first query is sent.
|
||||||
|
verifyAndSendQuery(0, /* timeInMs= */ 0, /* expectsUnicastResponse= */ true);
|
||||||
|
|
||||||
|
// After the first query is sent, change the subtypes, and restart.
|
||||||
|
final MdnsSearchOptions searchOptions2 = MdnsSearchOptions.newBuilder().addSubtype(SUBTYPE)
|
||||||
|
.addSubtype("_subtype2").setQueryMode(AGGRESSIVE_QUERY_MODE).build();
|
||||||
|
startSendAndReceive(mockListenerOne, searchOptions2);
|
||||||
|
// The previous scheduled task should be canceled.
|
||||||
|
verify(mockDeps, times(2)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
|
||||||
|
|
||||||
|
// Queries should continue to be sent.
|
||||||
|
verifyAndSendQuery(1, /* timeInMs= */ 0, /* expectsUnicastResponse= */ true);
|
||||||
|
verifyAndSendQuery(2, /* timeInMs= */ 0, /* expectsUnicastResponse= */ false);
|
||||||
|
verifyAndSendQuery(3, TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS,
|
||||||
|
/* expectsUnicastResponse= */ false);
|
||||||
|
|
||||||
|
// Stop sending packets.
|
||||||
|
stopSendAndReceive(mockListenerOne);
|
||||||
|
verify(mockDeps, times(3)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sendQueries_blendScanWithQueryBackoff() {
|
||||||
|
final int numOfQueriesBeforeBackoff = 11;
|
||||||
|
final MdnsSearchOptions searchOptions = MdnsSearchOptions.newBuilder()
|
||||||
|
.addSubtype(SUBTYPE)
|
||||||
|
.setQueryMode(AGGRESSIVE_QUERY_MODE)
|
||||||
|
.setNumOfQueriesBeforeBackoff(numOfQueriesBeforeBackoff)
|
||||||
|
.build();
|
||||||
|
startSendAndReceive(mockListenerOne, searchOptions);
|
||||||
|
// Always try to remove the task.
|
||||||
|
verify(mockDeps, times(1)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
|
||||||
|
|
||||||
|
int burstCounter = 0;
|
||||||
|
int betweenBurstTime = 0;
|
||||||
|
for (int i = 0; i < numOfQueriesBeforeBackoff; i += 3) {
|
||||||
|
verifyAndSendQuery(i, betweenBurstTime, /* expectsUnicastResponse= */ true);
|
||||||
|
verifyAndSendQuery(i + 1, /* timeInMs= */ 0, /* expectsUnicastResponse= */ false);
|
||||||
|
verifyAndSendQuery(i + 2, TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS,
|
||||||
|
/* expectsUnicastResponse= */ false);
|
||||||
|
betweenBurstTime = getBetweenBurstTime(burstCounter, betweenBurstTime,
|
||||||
|
MAX_TIME_BETWEEN_AGGRESSIVE_BURSTS_MS,
|
||||||
|
INITIAL_AGGRESSIVE_TIME_BETWEEN_BURSTS_MS);
|
||||||
|
burstCounter++;
|
||||||
|
}
|
||||||
|
// 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(mockDeps).hasMessages(any(), eq(EVENT_START_QUERYTASK));
|
||||||
|
processResponse(createResponse(
|
||||||
|
"service-instance-1", "192.0.2.123", 5353,
|
||||||
|
SERVICE_TYPE_LABELS,
|
||||||
|
Collections.emptyMap(), TEST_TTL), socketKey);
|
||||||
|
verify(mockDeps, times(2)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
|
||||||
|
assertNotNull(delayMessage);
|
||||||
|
verifyAndSendQuery(12 /* index */, (long) (TEST_TTL / 2 * 0.8) /* timeInMs */,
|
||||||
|
true /* expectsUnicastResponse */, true /* multipleSocketDiscovery */,
|
||||||
|
14 /* scheduledCount */);
|
||||||
|
currentTime += (long) (TEST_TTL / 2 * 0.8);
|
||||||
|
doReturn(currentTime).when(mockDecoderClock).elapsedRealtime();
|
||||||
|
verifyAndSendQuery(13 /* index */, 0 /* timeInMs */,
|
||||||
|
false /* expectsUnicastResponse */, true /* multipleSocketDiscovery */,
|
||||||
|
15 /* scheduledCount */);
|
||||||
|
verifyAndSendQuery(14 /* index */, TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS,
|
||||||
|
false /* expectsUnicastResponse */, true /* multipleSocketDiscovery */,
|
||||||
|
16 /* scheduledCount */);
|
||||||
|
}
|
||||||
|
|
||||||
private static MdnsServiceInfo matchServiceName(String name) {
|
private static MdnsServiceInfo matchServiceName(String name) {
|
||||||
return argThat(info -> info.getServiceInstanceName().equals(name));
|
return argThat(info -> info.getServiceInstanceName().equals(name));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue