Merge "Move query scheduling time to MdnsQueryScheduler" into main
This commit is contained in:
commit
d7f6e059b9
3 changed files with 145 additions and 143 deletions
|
|
@ -16,9 +16,14 @@
|
|||
|
||||
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 android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* The query scheduler class for calculating next query tasks parameters.
|
||||
* <p>
|
||||
|
|
@ -26,6 +31,25 @@ import android.annotation.Nullable;
|
|||
*/
|
||||
public class MdnsQueryScheduler {
|
||||
|
||||
@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;
|
||||
private static final int INITIAL_TIME_BETWEEN_BURSTS_MS =
|
||||
(int) MdnsConfigs.initialTimeBetweenBurstsMs();
|
||||
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 TIME_BETWEEN_QUERIES_IN_BURST_MS =
|
||||
(int) MdnsConfigs.timeBetweenQueriesInBurstMs();
|
||||
private static final int QUERIES_PER_BURST_PASSIVE_MODE =
|
||||
(int) MdnsConfigs.queriesPerBurstPassive();
|
||||
@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 argument for tracking the query tasks status.
|
||||
*/
|
||||
|
|
@ -72,19 +96,21 @@ public class MdnsQueryScheduler {
|
|||
if (mLastScheduledQueryTaskArgs == null) {
|
||||
return null;
|
||||
}
|
||||
if (!mLastScheduledQueryTaskArgs.config.shouldUseQueryBackoff(numOfQueriesBeforeBackoff)) {
|
||||
final QueryTaskConfig lastConfig = mLastScheduledQueryTaskArgs.config;
|
||||
if (!shouldUseQueryBackoff(lastConfig.queryIndex, lastConfig.queryMode,
|
||||
numOfQueriesBeforeBackoff)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final long timeToRun = calculateTimeToRun(mLastScheduledQueryTaskArgs,
|
||||
mLastScheduledQueryTaskArgs.config, now, minRemainingTtl, lastSentTime,
|
||||
lastConfig.queryIndex, lastConfig.queryMode, now, minRemainingTtl, lastSentTime,
|
||||
numOfQueriesBeforeBackoff, false /* forceEnableBackoff */);
|
||||
|
||||
if (timeToRun <= mLastScheduledQueryTaskArgs.timeToRun) {
|
||||
return null;
|
||||
}
|
||||
|
||||
mLastScheduledQueryTaskArgs = new ScheduledQueryTaskArgs(mLastScheduledQueryTaskArgs.config,
|
||||
mLastScheduledQueryTaskArgs = new ScheduledQueryTaskArgs(lastConfig,
|
||||
timeToRun,
|
||||
minRemainingTtl + now,
|
||||
sessionId);
|
||||
|
|
@ -104,17 +130,19 @@ public class MdnsQueryScheduler {
|
|||
int queryMode,
|
||||
int numOfQueriesBeforeBackoff,
|
||||
boolean forceEnableBackoff) {
|
||||
final QueryTaskConfig nextRunConfig = currentConfig.getConfigForNextRun(queryMode);
|
||||
final int newQueryIndex = currentConfig.getConfigForNextRun(queryMode).queryIndex;
|
||||
long timeToRun;
|
||||
if (mLastScheduledQueryTaskArgs == null && !forceEnableBackoff) {
|
||||
timeToRun = now + nextRunConfig.getDelayBeforeTaskWithoutBackoff();
|
||||
timeToRun = now + getDelayBeforeTaskWithoutBackoff(
|
||||
newQueryIndex, queryMode);
|
||||
} else {
|
||||
timeToRun = calculateTimeToRun(mLastScheduledQueryTaskArgs,
|
||||
nextRunConfig, now, minRemainingTtl, lastSentTime, numOfQueriesBeforeBackoff,
|
||||
forceEnableBackoff);
|
||||
timeToRun = calculateTimeToRun(mLastScheduledQueryTaskArgs, newQueryIndex,
|
||||
queryMode, now, minRemainingTtl, lastSentTime,
|
||||
numOfQueriesBeforeBackoff, forceEnableBackoff);
|
||||
}
|
||||
mLastScheduledQueryTaskArgs = new ScheduledQueryTaskArgs(nextRunConfig, timeToRun,
|
||||
minRemainingTtl + now,
|
||||
mLastScheduledQueryTaskArgs = new ScheduledQueryTaskArgs(
|
||||
currentConfig.getConfigForNextRun(queryMode),
|
||||
timeToRun, minRemainingTtl + now,
|
||||
sessionId);
|
||||
return mLastScheduledQueryTaskArgs;
|
||||
}
|
||||
|
|
@ -131,11 +159,11 @@ public class MdnsQueryScheduler {
|
|||
}
|
||||
|
||||
private static long calculateTimeToRun(@Nullable ScheduledQueryTaskArgs taskArgs,
|
||||
QueryTaskConfig queryTaskConfig, long now, long minRemainingTtl, long lastSentTime,
|
||||
int queryIndex, int queryMode, long now, long minRemainingTtl, long lastSentTime,
|
||||
int numOfQueriesBeforeBackoff, boolean forceEnableBackoff) {
|
||||
final long baseDelayInMs = queryTaskConfig.getDelayBeforeTaskWithoutBackoff();
|
||||
final long baseDelayInMs = getDelayBeforeTaskWithoutBackoff(queryIndex, queryMode);
|
||||
if (!(forceEnableBackoff
|
||||
|| queryTaskConfig.shouldUseQueryBackoff(numOfQueriesBeforeBackoff))) {
|
||||
|| shouldUseQueryBackoff(queryIndex, queryMode, numOfQueriesBeforeBackoff))) {
|
||||
return lastSentTime + baseDelayInMs;
|
||||
}
|
||||
if (minRemainingTtl <= 0) {
|
||||
|
|
@ -152,4 +180,93 @@ public class MdnsQueryScheduler {
|
|||
}
|
||||
return Math.max(now + (long) (0.8 * minRemainingTtl), lastSentTime + baseDelayInMs);
|
||||
}
|
||||
|
||||
private static int getBurstIndex(int queryIndex, int queryMode) {
|
||||
if (queryMode == PASSIVE_QUERY_MODE && queryIndex >= QUERIES_PER_BURST) {
|
||||
// In passive mode, after the first burst of QUERIES_PER_BURST queries, subsequent
|
||||
// bursts have QUERIES_PER_BURST_PASSIVE_MODE queries.
|
||||
final int queryIndexAfterFirstBurst = queryIndex - QUERIES_PER_BURST;
|
||||
return 1 + (queryIndexAfterFirstBurst / QUERIES_PER_BURST_PASSIVE_MODE);
|
||||
} else {
|
||||
return queryIndex / QUERIES_PER_BURST;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getQueryIndexInBurst(int queryIndex, int queryMode) {
|
||||
if (queryMode == PASSIVE_QUERY_MODE && queryIndex >= QUERIES_PER_BURST) {
|
||||
final int queryIndexAfterFirstBurst = queryIndex - QUERIES_PER_BURST;
|
||||
return queryIndexAfterFirstBurst % QUERIES_PER_BURST_PASSIVE_MODE;
|
||||
} else {
|
||||
return queryIndex % QUERIES_PER_BURST;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isFirstBurst(int queryIndex, int queryMode) {
|
||||
return getBurstIndex(queryIndex, queryMode) == 0;
|
||||
}
|
||||
|
||||
static boolean isFirstQueryInBurst(int queryIndex, int queryMode) {
|
||||
return getQueryIndexInBurst(queryIndex, queryMode) == 0;
|
||||
}
|
||||
|
||||
private static long getDelayBeforeTaskWithoutBackoff(int queryIndex, int queryMode) {
|
||||
final int burstIndex = getBurstIndex(queryIndex, queryMode);
|
||||
final int queryIndexInBurst = getQueryIndexInBurst(queryIndex, queryMode);
|
||||
if (queryIndexInBurst == 0) {
|
||||
return getTimeToBurstMs(burstIndex, queryMode);
|
||||
} else if (queryIndexInBurst == 1 && queryMode == AGGRESSIVE_QUERY_MODE) {
|
||||
// In aggressive mode, the first 2 queries are sent without delay.
|
||||
return 0;
|
||||
}
|
||||
return queryMode == AGGRESSIVE_QUERY_MODE
|
||||
? TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS
|
||||
: TIME_BETWEEN_QUERIES_IN_BURST_MS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts a value left by the specified number of bits, coercing to at most maxValue.
|
||||
*
|
||||
* <p>This allows calculating min(value*2^shift, maxValue) without overflow.
|
||||
*/
|
||||
private static int boundedLeftShift(int value, int shift, int maxValue) {
|
||||
// There must be at least one leading zero for positive values, so the maximum left shift
|
||||
// without overflow is the number of leading zeros minus one.
|
||||
final int maxShift = Integer.numberOfLeadingZeros(value) - 1;
|
||||
if (shift > maxShift) {
|
||||
// The shift would overflow positive integers, so is greater than maxValue.
|
||||
return maxValue;
|
||||
}
|
||||
return Math.min(value << shift, maxValue);
|
||||
}
|
||||
|
||||
private static int getTimeToBurstMs(int burstIndex, int queryMode) {
|
||||
if (burstIndex == 0) {
|
||||
// No delay before the first burst
|
||||
return 0;
|
||||
}
|
||||
switch (queryMode) {
|
||||
case PASSIVE_QUERY_MODE:
|
||||
return MAX_TIME_BETWEEN_ACTIVE_PASSIVE_BURSTS_MS;
|
||||
case AGGRESSIVE_QUERY_MODE:
|
||||
return boundedLeftShift(INITIAL_AGGRESSIVE_TIME_BETWEEN_BURSTS_MS,
|
||||
burstIndex - 1,
|
||||
MAX_TIME_BETWEEN_AGGRESSIVE_BURSTS_MS);
|
||||
default: // ACTIVE_QUERY_MODE
|
||||
return boundedLeftShift(INITIAL_TIME_BETWEEN_BURSTS_MS,
|
||||
burstIndex - 1,
|
||||
MAX_TIME_BETWEEN_ACTIVE_PASSIVE_BURSTS_MS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the query backoff should be used.
|
||||
*/
|
||||
public static boolean shouldUseQueryBackoff(int queryIndex, int queryMode,
|
||||
int numOfQueriesBeforeBackoff) {
|
||||
// Don't enable backoff mode during the burst or in the first burst
|
||||
if (!isFirstQueryInBurst(queryIndex, queryMode) || isFirstBurst(queryIndex, queryMode)) {
|
||||
return false;
|
||||
}
|
||||
return queryIndex > numOfQueriesBeforeBackoff;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
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 com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
|
|
@ -26,136 +25,25 @@ import com.android.internal.annotations.VisibleForTesting;
|
|||
* Call to getConfigForNextRun returns a config that can be used to build the next query task.
|
||||
*/
|
||||
public class QueryTaskConfig {
|
||||
|
||||
private static final int INITIAL_TIME_BETWEEN_BURSTS_MS =
|
||||
(int) MdnsConfigs.initialTimeBetweenBurstsMs();
|
||||
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 TIME_BETWEEN_QUERIES_IN_BURST_MS =
|
||||
(int) MdnsConfigs.timeBetweenQueriesInBurstMs();
|
||||
private static final int QUERIES_PER_BURST_PASSIVE_MODE =
|
||||
(int) MdnsConfigs.queriesPerBurstPassive();
|
||||
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;
|
||||
private final boolean alwaysAskForUnicastResponse =
|
||||
MdnsConfigs.alwaysAskForUnicastResponseInEachBurst();
|
||||
@VisibleForTesting
|
||||
final int transactionId;
|
||||
@VisibleForTesting
|
||||
final boolean expectUnicastResponse;
|
||||
private final int queryIndex;
|
||||
private final int queryMode;
|
||||
final int queryIndex;
|
||||
final int queryMode;
|
||||
|
||||
QueryTaskConfig(int queryMode, int queryIndex, int transactionId,
|
||||
boolean expectUnicastResponse) {
|
||||
QueryTaskConfig(int queryMode, int queryIndex, int transactionId) {
|
||||
this.queryMode = queryMode;
|
||||
this.transactionId = transactionId;
|
||||
this.queryIndex = queryIndex;
|
||||
this.expectUnicastResponse = expectUnicastResponse;
|
||||
this.expectUnicastResponse = getExpectUnicastResponse();
|
||||
}
|
||||
|
||||
QueryTaskConfig(int queryMode) {
|
||||
this(queryMode, 0, 1, true);
|
||||
}
|
||||
|
||||
private static int getBurstIndex(int queryIndex, int queryMode) {
|
||||
if (queryMode == PASSIVE_QUERY_MODE && queryIndex >= QUERIES_PER_BURST) {
|
||||
// In passive mode, after the first burst of QUERIES_PER_BURST queries, subsequent
|
||||
// bursts have QUERIES_PER_BURST_PASSIVE_MODE queries.
|
||||
final int queryIndexAfterFirstBurst = queryIndex - QUERIES_PER_BURST;
|
||||
return 1 + (queryIndexAfterFirstBurst / QUERIES_PER_BURST_PASSIVE_MODE);
|
||||
} else {
|
||||
return queryIndex / QUERIES_PER_BURST;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getQueryIndexInBurst(int queryIndex, int queryMode) {
|
||||
if (queryMode == PASSIVE_QUERY_MODE && queryIndex >= QUERIES_PER_BURST) {
|
||||
final int queryIndexAfterFirstBurst = queryIndex - QUERIES_PER_BURST;
|
||||
return queryIndexAfterFirstBurst % QUERIES_PER_BURST_PASSIVE_MODE;
|
||||
} else {
|
||||
return queryIndex % QUERIES_PER_BURST;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isFirstBurst(int queryIndex, int queryMode) {
|
||||
return getBurstIndex(queryIndex, queryMode) == 0;
|
||||
}
|
||||
|
||||
private static boolean isFirstQueryInBurst(int queryIndex, int queryMode) {
|
||||
return getQueryIndexInBurst(queryIndex, queryMode) == 0;
|
||||
}
|
||||
|
||||
// TODO: move delay calculations to MdnsQueryScheduler
|
||||
long getDelayBeforeTaskWithoutBackoff() {
|
||||
return getDelayBeforeTaskWithoutBackoff(queryIndex, queryMode);
|
||||
}
|
||||
|
||||
private static long getDelayBeforeTaskWithoutBackoff(int queryIndex, int queryMode) {
|
||||
final int burstIndex = getBurstIndex(queryIndex, queryMode);
|
||||
final int queryIndexInBurst = getQueryIndexInBurst(queryIndex, queryMode);
|
||||
if (queryIndexInBurst == 0) {
|
||||
return getTimeToBurstMs(burstIndex, queryMode);
|
||||
} else if (queryIndexInBurst == 1 && queryMode == AGGRESSIVE_QUERY_MODE) {
|
||||
// In aggressive mode, the first 2 queries are sent without delay.
|
||||
return 0;
|
||||
}
|
||||
return queryMode == AGGRESSIVE_QUERY_MODE
|
||||
? TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS
|
||||
: TIME_BETWEEN_QUERIES_IN_BURST_MS;
|
||||
}
|
||||
|
||||
private boolean getExpectUnicastResponse(int queryIndex, int queryMode) {
|
||||
if (queryMode == AGGRESSIVE_QUERY_MODE) {
|
||||
if (isFirstQueryInBurst(queryIndex, queryMode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return alwaysAskForUnicastResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts a value left by the specified number of bits, coercing to at most maxValue.
|
||||
*
|
||||
* <p>This allows calculating min(value*2^shift, maxValue) without overflow.
|
||||
*/
|
||||
private static int boundedLeftShift(int value, int shift, int maxValue) {
|
||||
// There must be at least one leading zero for positive values, so the maximum left shift
|
||||
// without overflow is the number of leading zeros minus one.
|
||||
final int maxShift = Integer.numberOfLeadingZeros(value) - 1;
|
||||
if (shift > maxShift) {
|
||||
// The shift would overflow positive integers, so is greater than maxValue.
|
||||
return maxValue;
|
||||
}
|
||||
return Math.min(value << shift, maxValue);
|
||||
}
|
||||
|
||||
private static int getTimeToBurstMs(int burstIndex, int queryMode) {
|
||||
if (burstIndex == 0) {
|
||||
// No delay before the first burst
|
||||
return 0;
|
||||
}
|
||||
switch (queryMode) {
|
||||
case PASSIVE_QUERY_MODE:
|
||||
return MAX_TIME_BETWEEN_ACTIVE_PASSIVE_BURSTS_MS;
|
||||
case AGGRESSIVE_QUERY_MODE:
|
||||
return boundedLeftShift(INITIAL_AGGRESSIVE_TIME_BETWEEN_BURSTS_MS,
|
||||
burstIndex - 1,
|
||||
MAX_TIME_BETWEEN_AGGRESSIVE_BURSTS_MS);
|
||||
default: // ACTIVE_QUERY_MODE
|
||||
return boundedLeftShift(INITIAL_TIME_BETWEEN_BURSTS_MS,
|
||||
burstIndex - 1,
|
||||
MAX_TIME_BETWEEN_ACTIVE_PASSIVE_BURSTS_MS);
|
||||
}
|
||||
this(queryMode, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -168,18 +56,15 @@ public class QueryTaskConfig {
|
|||
newTransactionId = 1;
|
||||
}
|
||||
|
||||
return new QueryTaskConfig(queryMode, newQueryIndex, newTransactionId,
|
||||
getExpectUnicastResponse(newQueryIndex, queryMode));
|
||||
return new QueryTaskConfig(queryMode, newQueryIndex, newTransactionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the query backoff should be used.
|
||||
*/
|
||||
public boolean shouldUseQueryBackoff(int numOfQueriesBeforeBackoff) {
|
||||
// Don't enable backoff mode during the burst or in the first burst
|
||||
if (!isFirstQueryInBurst(queryIndex, queryMode) || isFirstBurst(queryIndex, queryMode)) {
|
||||
return false;
|
||||
private boolean getExpectUnicastResponse() {
|
||||
if (queryMode == AGGRESSIVE_QUERY_MODE) {
|
||||
if (MdnsQueryScheduler.isFirstQueryInBurst(queryIndex, queryMode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return queryIndex > numOfQueriesBeforeBackoff;
|
||||
return queryIndex == 0 || alwaysAskForUnicastResponse;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@
|
|||
|
||||
package com.android.server.connectivity.mdns;
|
||||
|
||||
import static com.android.server.connectivity.mdns.MdnsQueryScheduler.INITIAL_AGGRESSIVE_TIME_BETWEEN_BURSTS_MS;
|
||||
import static com.android.server.connectivity.mdns.MdnsQueryScheduler.MAX_TIME_BETWEEN_AGGRESSIVE_BURSTS_MS;
|
||||
import static com.android.server.connectivity.mdns.MdnsQueryScheduler.TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS;
|
||||
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.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 org.junit.Assert.assertArrayEquals;
|
||||
|
|
|
|||
Loading…
Reference in a new issue