Merge "Reset Ike session if stall is not recovered by MOBIKE"
This commit is contained in:
@@ -79,6 +79,7 @@ import android.net.NetworkScore;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.UidRangeParcel;
|
||||
import android.net.UnderlyingNetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.net.VpnManager;
|
||||
import android.net.VpnProfileState;
|
||||
import android.net.VpnService;
|
||||
@@ -225,6 +226,16 @@ public class Vpn {
|
||||
*/
|
||||
private static final int VPN_DEFAULT_SCORE = 101;
|
||||
|
||||
/**
|
||||
* The reset session timer for data stall. If a session has not successfully revalidated after
|
||||
* the delay, the session will be torn down and restarted in an attempt to recover. Delay
|
||||
* counter is reset on successful validation only.
|
||||
*
|
||||
* <p>If retries have exceeded the length of this array, the last entry in the array will be
|
||||
* used as a repeating interval.
|
||||
*/
|
||||
private static final long[] DATA_STALL_RESET_DELAYS_SEC = {30L, 60L, 120L, 240L, 480L, 960L};
|
||||
|
||||
/**
|
||||
* The initial token value of IKE session.
|
||||
*/
|
||||
@@ -271,6 +282,7 @@ public class Vpn {
|
||||
private final UserManager mUserManager;
|
||||
|
||||
private final VpnProfileStore mVpnProfileStore;
|
||||
protected boolean mDataStallSuspected = false;
|
||||
|
||||
@VisibleForTesting
|
||||
VpnProfileStore getVpnProfileStore() {
|
||||
@@ -522,10 +534,28 @@ public class Vpn {
|
||||
@NonNull LinkProperties lp,
|
||||
@NonNull NetworkScore score,
|
||||
@NonNull NetworkAgentConfig config,
|
||||
@Nullable NetworkProvider provider) {
|
||||
@Nullable NetworkProvider provider,
|
||||
@Nullable ValidationStatusCallback callback) {
|
||||
return new VpnNetworkAgentWrapper(
|
||||
context, looper, logTag, nc, lp, score, config, provider);
|
||||
context, looper, logTag, nc, lp, score, config, provider, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of time to wait before resetting the ike session when a data stall is
|
||||
* suspected.
|
||||
*/
|
||||
public long getDataStallResetSessionSeconds(int count) {
|
||||
if (count >= DATA_STALL_RESET_DELAYS_SEC.length) {
|
||||
return DATA_STALL_RESET_DELAYS_SEC[DATA_STALL_RESET_DELAYS_SEC.length - 1];
|
||||
} else {
|
||||
return DATA_STALL_RESET_DELAYS_SEC[count];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
interface ValidationStatusCallback {
|
||||
void onValidationStatus(int status);
|
||||
}
|
||||
|
||||
public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
|
||||
@@ -1460,6 +1490,11 @@ public class Vpn {
|
||||
|
||||
@GuardedBy("this")
|
||||
private void agentConnect() {
|
||||
agentConnect(null /* validationCallback */);
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private void agentConnect(@Nullable ValidationStatusCallback validationCallback) {
|
||||
LinkProperties lp = makeLinkProperties();
|
||||
|
||||
// VPN either provide a default route (IPv4 or IPv6 or both), or they are a split tunnel
|
||||
@@ -1507,7 +1542,7 @@ public class Vpn {
|
||||
mNetworkAgent = mDeps.newNetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
|
||||
mNetworkCapabilities, lp,
|
||||
new NetworkScore.Builder().setLegacyInt(VPN_DEFAULT_SCORE).build(),
|
||||
networkAgentConfig, mNetworkProvider);
|
||||
networkAgentConfig, mNetworkProvider, validationCallback);
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mNetworkAgent.register();
|
||||
@@ -2723,7 +2758,7 @@ public class Vpn {
|
||||
|
||||
@Nullable private ScheduledFuture<?> mScheduledHandleNetworkLostFuture;
|
||||
@Nullable private ScheduledFuture<?> mScheduledHandleRetryIkeSessionFuture;
|
||||
|
||||
@Nullable private ScheduledFuture<?> mScheduledHandleDataStallFuture;
|
||||
/** Signal to ensure shutdown is honored even if a new Network is connected. */
|
||||
private boolean mIsRunning = true;
|
||||
|
||||
@@ -2749,6 +2784,14 @@ public class Vpn {
|
||||
// mMobikeEnabled can only be updated after IKE AUTH is finished.
|
||||
private boolean mMobikeEnabled = false;
|
||||
|
||||
/**
|
||||
* The number of attempts to reset the IKE session since the last successful connection.
|
||||
*
|
||||
* <p>This variable controls the retry delay, and is reset when the VPN pass network
|
||||
* validation.
|
||||
*/
|
||||
private int mDataStallRetryCount = 0;
|
||||
|
||||
/**
|
||||
* The number of attempts since the last successful connection.
|
||||
*
|
||||
@@ -2931,7 +2974,7 @@ public class Vpn {
|
||||
if (isSettingsVpnLocked()) {
|
||||
prepareStatusIntent();
|
||||
}
|
||||
agentConnect();
|
||||
agentConnect(this::onValidationStatus);
|
||||
return; // Link properties are already sent.
|
||||
} else {
|
||||
// Underlying networks also set in agentConnect()
|
||||
@@ -3200,18 +3243,52 @@ public class Vpn {
|
||||
// Ignore stale runner.
|
||||
if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
|
||||
|
||||
// Handle the report only for current VPN network.
|
||||
// Handle the report only for current VPN network. If data stall is already
|
||||
// reported, ignoring the other reports. It means that the stall is not
|
||||
// recovered by MOBIKE and should be on the way to reset the ike session.
|
||||
if (mNetworkAgent != null
|
||||
&& mNetworkAgent.getNetwork().equals(report.getNetwork())) {
|
||||
&& mNetworkAgent.getNetwork().equals(report.getNetwork())
|
||||
&& !mDataStallSuspected) {
|
||||
Log.d(TAG, "Data stall suspected");
|
||||
|
||||
// Trigger MOBIKE.
|
||||
maybeMigrateIkeSession(mActiveNetwork);
|
||||
mDataStallSuspected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onValidationStatus(int status) {
|
||||
if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
|
||||
// No data stall now. Reset it.
|
||||
mExecutor.execute(() -> {
|
||||
mDataStallSuspected = false;
|
||||
mDataStallRetryCount = 0;
|
||||
if (mScheduledHandleDataStallFuture != null) {
|
||||
Log.d(TAG, "Recovered from stall. Cancel pending reset action.");
|
||||
mScheduledHandleDataStallFuture.cancel(false /* mayInterruptIfRunning */);
|
||||
mScheduledHandleDataStallFuture = null;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Skip other invalid status if the scheduled recovery exists.
|
||||
if (mScheduledHandleDataStallFuture != null) return;
|
||||
|
||||
mScheduledHandleDataStallFuture = mExecutor.schedule(() -> {
|
||||
if (mDataStallSuspected) {
|
||||
Log.d(TAG, "Reset session to recover stalled network");
|
||||
// This will reset old state if it exists.
|
||||
startIkeSession(mActiveNetwork);
|
||||
}
|
||||
|
||||
// Reset mScheduledHandleDataStallFuture since it's already run on executor
|
||||
// thread.
|
||||
mScheduledHandleDataStallFuture = null;
|
||||
}, mDeps.getDataStallResetSessionSeconds(mDataStallRetryCount++), TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles loss of the default underlying network
|
||||
*
|
||||
@@ -4339,6 +4416,7 @@ public class Vpn {
|
||||
// un-finalized.
|
||||
@VisibleForTesting
|
||||
public static class VpnNetworkAgentWrapper extends NetworkAgent {
|
||||
private final ValidationStatusCallback mCallback;
|
||||
/** Create an VpnNetworkAgentWrapper */
|
||||
public VpnNetworkAgentWrapper(
|
||||
@NonNull Context context,
|
||||
@@ -4348,8 +4426,10 @@ public class Vpn {
|
||||
@NonNull LinkProperties lp,
|
||||
@NonNull NetworkScore score,
|
||||
@NonNull NetworkAgentConfig config,
|
||||
@Nullable NetworkProvider provider) {
|
||||
@Nullable NetworkProvider provider,
|
||||
@Nullable ValidationStatusCallback callback) {
|
||||
super(context, looper, logTag, nc, lp, score, config, provider);
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
/** Update the LinkProperties */
|
||||
@@ -4371,6 +4451,13 @@ public class Vpn {
|
||||
public void onNetworkUnwanted() {
|
||||
// We are user controlled, not driven by NetworkRequest.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onValidationStatus(int status, Uri redirectUri) {
|
||||
if (mCallback != null) {
|
||||
mCallback.onValidationStatus(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user