Allow network providers to set the linger duration.

Test: atest CtsNetTestCases:NetworkAgentTest#testSetLingerDuration
CTS-Coverage-Bug: 184796264
Bug: 184227264
Merged-In: I3c2563d4ae4e3715d0c6270344ba8f7ef067872f
Merged-In: I7f420faa40863385114705d6971cf00887d03318
Change-Id: I7f420faa40863385114705d6971cf00887d03318
  (cherry-picked from ag/14100410)
This commit is contained in:
Chalard Jean
2021-03-05 23:07:53 +09:00
committed by Junyu Lai
parent 36c02987c6
commit 550b5214d3
7 changed files with 103 additions and 11 deletions

View File

@@ -239,6 +239,7 @@ package android.net {
method public final void sendQosSessionLost(int, int, int); method public final void sendQosSessionLost(int, int, int);
method public final void sendSocketKeepaliveEvent(int, int); method public final void sendSocketKeepaliveEvent(int, int);
method @Deprecated public void setLegacySubtype(int, @NonNull String); method @Deprecated public void setLegacySubtype(int, @NonNull String);
method public void setLingerDuration(@NonNull java.time.Duration);
method public void setTeardownDelayMillis(@IntRange(from=0, to=0x1388) int); method public void setTeardownDelayMillis(@IntRange(from=0, to=0x1388) int);
method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>); method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
method public void unregister(); method public void unregister();

View File

@@ -42,4 +42,5 @@ oneway interface INetworkAgentRegistry {
void sendQosSessionLost(int qosCallbackId, in QosSession session); void sendQosSessionLost(int qosCallbackId, in QosSession session);
void sendQosCallbackError(int qosCallbackId, int exceptionType); void sendQosCallbackError(int qosCallbackId, int exceptionType);
void sendTeardownDelayMs(int teardownDelayMs); void sendTeardownDelayMs(int teardownDelayMs);
void sendLingerDuration(int durationMs);
} }

View File

@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.annotation.SystemApi; import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage; import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
@@ -106,6 +107,9 @@ public abstract class NetworkAgent {
private final String LOG_TAG; private final String LOG_TAG;
private static final boolean DBG = true; private static final boolean DBG = true;
private static final boolean VDBG = false; private static final boolean VDBG = false;
/** @hide */
@TestApi
public static final int MIN_LINGER_TIMER_MS = 2000;
private final ArrayList<RegistryAction> mPreConnectedQueue = new ArrayList<>(); private final ArrayList<RegistryAction> mPreConnectedQueue = new ArrayList<>();
private volatile long mLastBwRefreshTime = 0; private volatile long mLastBwRefreshTime = 0;
private static final long BW_REFRESH_MIN_WIN_MS = 500; private static final long BW_REFRESH_MIN_WIN_MS = 500;
@@ -391,6 +395,15 @@ public abstract class NetworkAgent {
*/ */
public static final int CMD_NETWORK_DESTROYED = BASE + 23; public static final int CMD_NETWORK_DESTROYED = BASE + 23;
/**
* Sent by the NetworkAgent to ConnectivityService to set the linger duration for this network
* agent.
* arg1 = the linger duration, represents by {@link Duration}.
*
* @hide
*/
public static final int EVENT_LINGER_DURATION_CHANGED = BASE + 24;
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) { private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType, final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
config.legacyTypeName, config.legacySubTypeName); config.legacyTypeName, config.legacySubTypeName);
@@ -1287,6 +1300,22 @@ public abstract class NetworkAgent {
queueOrSendMessage(ra -> ra.sendQosCallbackError(qosCallbackId, exceptionType)); queueOrSendMessage(ra -> ra.sendQosCallbackError(qosCallbackId, exceptionType));
} }
/**
* Set the linger duration for this network agent.
* @param duration the delay between the moment the network becomes unneeded and the
* moment the network is disconnected or moved into the background.
* Note that If this duration has greater than millisecond precision, then
* the internal implementation will drop any excess precision.
*/
public void setLingerDuration(@NonNull final Duration duration) {
Objects.requireNonNull(duration);
final long durationMs = duration.toMillis();
if (durationMs < MIN_LINGER_TIMER_MS || durationMs > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Duration must be within ["
+ MIN_LINGER_TIMER_MS + "," + Integer.MAX_VALUE + "]ms");
}
queueOrSendMessage(ra -> ra.sendLingerDuration((int) durationMs));
}
/** @hide */ /** @hide */
protected void log(final String s) { protected void log(final String s) {

View File

@@ -1400,8 +1400,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
new NetworkInfo(TYPE_NONE, 0, "", ""), new NetworkInfo(TYPE_NONE, 0, "", ""),
new LinkProperties(), new NetworkCapabilities(), new LinkProperties(), new NetworkCapabilities(),
new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null, new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null,
new NetworkAgentConfig(), this, null, null, 0, INVALID_UID, mQosCallbackTracker, new NetworkAgentConfig(), this, null, null, 0, INVALID_UID,
mDeps); mLingerDelayMs, mQosCallbackTracker, mDeps);
} }
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) { private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -3234,6 +3234,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else { } else {
logwtf(nai.toShortString() + " set invalid teardown delay " + msg.arg1); logwtf(nai.toShortString() + " set invalid teardown delay " + msg.arg1);
} }
break;
}
case NetworkAgent.EVENT_LINGER_DURATION_CHANGED: {
nai.setLingerDuration((int) arg.second);
break;
} }
} }
} }
@@ -6597,7 +6602,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkAgentInfo nai = new NetworkAgentInfo(na, final NetworkAgentInfo nai = new NetworkAgentInfo(na,
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc, new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig), currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
this, mNetd, mDnsResolver, providerId, uid, mQosCallbackTracker, mDeps); this, mNetd, mDnsResolver, providerId, uid, mLingerDelayMs,
mQosCallbackTracker, mDeps);
// Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says. // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
processCapabilitiesFromAgent(nai, nc); processCapabilitiesFromAgent(nai, nc);
@@ -7841,7 +7847,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
log(" accepting network in place of " + previousSatisfier.toShortString()); log(" accepting network in place of " + previousSatisfier.toShortString());
} }
previousSatisfier.removeRequest(previousRequest.requestId); previousSatisfier.removeRequest(previousRequest.requestId);
previousSatisfier.lingerRequest(previousRequest.requestId, now, mLingerDelayMs); previousSatisfier.lingerRequest(previousRequest.requestId, now);
} else { } else {
if (VDBG || DDBG) log(" accepting network in place of null"); if (VDBG || DDBG) log(" accepting network in place of null");
} }

View File

@@ -59,6 +59,7 @@ import com.android.internal.util.WakeupMessage;
import com.android.server.ConnectivityService; import com.android.server.ConnectivityService;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
@@ -281,6 +282,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
*/ */
public static final int ARG_AGENT_SUCCESS = 1; public static final int ARG_AGENT_SUCCESS = 1;
// How long this network should linger for.
private int mLingerDurationMs;
// All inactivity timers for this network, sorted by expiry time. A timer is added whenever // All inactivity timers for this network, sorted by expiry time. A timer is added whenever
// a request is moved to a network with a better score, regardless of whether the network is or // a request is moved to a network with a better score, regardless of whether the network is or
// was lingering or not. An inactivity timer is also added when a network connects // was lingering or not. An inactivity timer is also added when a network connects
@@ -349,7 +353,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
@NonNull NetworkScore score, Context context, @NonNull NetworkScore score, Context context,
Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd, Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid, IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid,
QosCallbackTracker qosCallbackTracker, ConnectivityService.Dependencies deps) { int lingerDurationMs, QosCallbackTracker qosCallbackTracker,
ConnectivityService.Dependencies deps) {
Objects.requireNonNull(net); Objects.requireNonNull(net);
Objects.requireNonNull(info); Objects.requireNonNull(info);
Objects.requireNonNull(lp); Objects.requireNonNull(lp);
@@ -370,6 +375,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
mHandler = handler; mHandler = handler;
this.factorySerialNumber = factorySerialNumber; this.factorySerialNumber = factorySerialNumber;
this.creatorUid = creatorUid; this.creatorUid = creatorUid;
mLingerDurationMs = lingerDurationMs;
mQosCallbackTracker = qosCallbackTracker; mQosCallbackTracker = qosCallbackTracker;
} }
@@ -685,6 +691,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
mHandler.obtainMessage(NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED, mHandler.obtainMessage(NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED,
teardownDelayMs, 0, new Pair<>(NetworkAgentInfo.this, null)).sendToTarget(); teardownDelayMs, 0, new Pair<>(NetworkAgentInfo.this, null)).sendToTarget();
} }
@Override
public void sendLingerDuration(final int durationMs) {
mHandler.obtainMessage(NetworkAgent.EVENT_LINGER_DURATION_CHANGED,
new Pair<>(NetworkAgentInfo.this, durationMs)).sendToTarget();
}
} }
/** /**
@@ -974,13 +986,14 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
/** /**
* Sets the specified requestId to linger on this network for the specified time. Called by * Sets the specified requestId to linger on this network for the specified time. Called by
* ConnectivityService when the request is moved to another network with a higher score, or * ConnectivityService when any request is moved to another network with a higher score, or
* when a network is newly created. * when a network is newly created.
* *
* @param requestId The requestId of the request that no longer need to be served by this * @param requestId The requestId of the request that no longer need to be served by this
* network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the * network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the
* {@code LingerTimer} for a newly created network. * {@code InactivityTimer} for a newly created network.
*/ */
// TODO: Consider creating a dedicated function for nascent network, e.g. start/stopNascent.
public void lingerRequest(int requestId, long now, long duration) { public void lingerRequest(int requestId, long now, long duration) {
if (mInactivityTimerForRequest.get(requestId) != null) { if (mInactivityTimerForRequest.get(requestId) != null) {
// Cannot happen. Once a request is lingering on a particular network, we cannot // Cannot happen. Once a request is lingering on a particular network, we cannot
@@ -995,6 +1008,19 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
mInactivityTimerForRequest.put(requestId, timer); mInactivityTimerForRequest.put(requestId, timer);
} }
/**
* Sets the specified requestId to linger on this network for the timeout set when
* initializing or modified by {@link #setLingerDuration(int)}. Called by
* ConnectivityService when any request is moved to another network with a higher score.
*
* @param requestId The requestId of the request that no longer need to be served by this
* network.
* @param now current system timestamp obtained by {@code SystemClock.elapsedRealtime}.
*/
public void lingerRequest(int requestId, long now) {
lingerRequest(requestId, now, mLingerDurationMs);
}
/** /**
* Cancel lingering. Called by ConnectivityService when a request is added to this network. * Cancel lingering. Called by ConnectivityService when a request is added to this network.
* Returns true if the given requestId was lingering on this network, false otherwise. * Returns true if the given requestId was lingering on this network, false otherwise.
@@ -1032,6 +1058,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
} }
if (newExpiry > 0) { if (newExpiry > 0) {
// If the newExpiry timestamp is in the past, the wakeup message will fire immediately.
mInactivityMessage = new WakeupMessage( mInactivityMessage = new WakeupMessage(
mContext, mHandler, mContext, mHandler,
"NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */, "NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */,
@@ -1061,8 +1088,33 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
} }
/** /**
* Return whether the network is just connected and about to be torn down because of not * Set the linger duration for this NAI.
* satisfying any request. * @param durationMs The new linger duration, in milliseconds.
*/
public void setLingerDuration(final int durationMs) {
final long diff = durationMs - mLingerDurationMs;
final ArrayList<InactivityTimer> newTimers = new ArrayList<>();
for (final InactivityTimer timer : mInactivityTimers) {
if (timer.requestId == NetworkRequest.REQUEST_ID_NONE) {
// Don't touch nascent timer, re-add as is.
newTimers.add(timer);
} else {
newTimers.add(new InactivityTimer(timer.requestId, timer.expiryMs + diff));
}
}
mInactivityTimers.clear();
mInactivityTimers.addAll(newTimers);
updateInactivityTimer();
mLingerDurationMs = durationMs;
}
/**
* Return whether the network satisfies no request, but is still being kept up
* because it has just connected less than
* {@code ConnectivityService#DEFAULT_NASCENT_DELAY_MS}ms ago and is thus still considered
* nascent. Note that nascent mechanism uses inactivity timer which isn't
* associated with a request. Thus, use {@link NetworkRequest#REQUEST_ID_NONE} to identify it.
*
*/ */
public boolean isNascent() { public boolean isNascent() {
return mInactive && mInactivityTimers.size() == 1 return mInactive && mInactivityTimers.size() == 1

View File

@@ -9832,7 +9832,8 @@ public class ConnectivityServiceTest {
return new NetworkAgentInfo(null, new Network(NET_ID), networkInfo, new LinkProperties(), return new NetworkAgentInfo(null, new Network(NET_ID), networkInfo, new LinkProperties(),
nc, new NetworkScore.Builder().setLegacyInt(0).build(), nc, new NetworkScore.Builder().setLegacyInt(0).build(),
mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
INVALID_UID, mQosCallbackTracker, new ConnectivityService.Dependencies()); INVALID_UID, TEST_LINGER_DELAY_MS, mQosCallbackTracker,
new ConnectivityService.Dependencies());
} }
@Test @Test

View File

@@ -71,6 +71,8 @@ public class LingerMonitorTest {
static final int LOW_DAILY_LIMIT = 2; static final int LOW_DAILY_LIMIT = 2;
static final int HIGH_DAILY_LIMIT = 1000; static final int HIGH_DAILY_LIMIT = 1000;
private static final int TEST_LINGER_DELAY_MS = 400;
LingerMonitor mMonitor; LingerMonitor mMonitor;
@Mock ConnectivityService mConnService; @Mock ConnectivityService mConnService;
@@ -366,7 +368,7 @@ public class LingerMonitorTest {
NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info, NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(), new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(),
mCtx, null, new NetworkAgentConfig.Builder().build(), mConnService, mNetd, mCtx, null, new NetworkAgentConfig.Builder().build(), mConnService, mNetd,
mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), TEST_LINGER_DELAY_MS,
mQosCallbackTracker, new ConnectivityService.Dependencies()); mQosCallbackTracker, new ConnectivityService.Dependencies());
nai.everValidated = true; nai.everValidated = true;
return nai; return nai;