Merge "Reset Ike session if stall is not recovered by MOBIKE" am: 246fc31ef5

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2159077

Change-Id: Ife1ebd8e4bbfcbf37dbff8b4cf0de1b7dbfb6583
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Chiachang Wang
2022-10-27 05:44:10 +00:00
committed by Automerger Merge Worker

View File

@@ -79,6 +79,7 @@ import android.net.NetworkScore;
import android.net.RouteInfo; import android.net.RouteInfo;
import android.net.UidRangeParcel; import android.net.UidRangeParcel;
import android.net.UnderlyingNetworkInfo; import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
import android.net.VpnManager; import android.net.VpnManager;
import android.net.VpnProfileState; import android.net.VpnProfileState;
import android.net.VpnService; import android.net.VpnService;
@@ -225,6 +226,16 @@ public class Vpn {
*/ */
private static final int VPN_DEFAULT_SCORE = 101; 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. * The initial token value of IKE session.
*/ */
@@ -271,6 +282,7 @@ public class Vpn {
private final UserManager mUserManager; private final UserManager mUserManager;
private final VpnProfileStore mVpnProfileStore; private final VpnProfileStore mVpnProfileStore;
protected boolean mDataStallSuspected = false;
@VisibleForTesting @VisibleForTesting
VpnProfileStore getVpnProfileStore() { VpnProfileStore getVpnProfileStore() {
@@ -522,10 +534,28 @@ public class Vpn {
@NonNull LinkProperties lp, @NonNull LinkProperties lp,
@NonNull NetworkScore score, @NonNull NetworkScore score,
@NonNull NetworkAgentConfig config, @NonNull NetworkAgentConfig config,
@Nullable NetworkProvider provider) { @Nullable NetworkProvider provider,
@Nullable ValidationStatusCallback callback) {
return new VpnNetworkAgentWrapper( 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, public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
@@ -1460,6 +1490,11 @@ public class Vpn {
@GuardedBy("this") @GuardedBy("this")
private void agentConnect() { private void agentConnect() {
agentConnect(null /* validationCallback */);
}
@GuardedBy("this")
private void agentConnect(@Nullable ValidationStatusCallback validationCallback) {
LinkProperties lp = makeLinkProperties(); LinkProperties lp = makeLinkProperties();
// VPN either provide a default route (IPv4 or IPv6 or both), or they are a split tunnel // 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 */, mNetworkAgent = mDeps.newNetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
mNetworkCapabilities, lp, mNetworkCapabilities, lp,
new NetworkScore.Builder().setLegacyInt(VPN_DEFAULT_SCORE).build(), new NetworkScore.Builder().setLegacyInt(VPN_DEFAULT_SCORE).build(),
networkAgentConfig, mNetworkProvider); networkAgentConfig, mNetworkProvider, validationCallback);
final long token = Binder.clearCallingIdentity(); final long token = Binder.clearCallingIdentity();
try { try {
mNetworkAgent.register(); mNetworkAgent.register();
@@ -2723,7 +2758,7 @@ public class Vpn {
@Nullable private ScheduledFuture<?> mScheduledHandleNetworkLostFuture; @Nullable private ScheduledFuture<?> mScheduledHandleNetworkLostFuture;
@Nullable private ScheduledFuture<?> mScheduledHandleRetryIkeSessionFuture; @Nullable private ScheduledFuture<?> mScheduledHandleRetryIkeSessionFuture;
@Nullable private ScheduledFuture<?> mScheduledHandleDataStallFuture;
/** Signal to ensure shutdown is honored even if a new Network is connected. */ /** Signal to ensure shutdown is honored even if a new Network is connected. */
private boolean mIsRunning = true; private boolean mIsRunning = true;
@@ -2749,6 +2784,14 @@ public class Vpn {
// mMobikeEnabled can only be updated after IKE AUTH is finished. // mMobikeEnabled can only be updated after IKE AUTH is finished.
private boolean mMobikeEnabled = false; 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. * The number of attempts since the last successful connection.
* *
@@ -2931,7 +2974,7 @@ public class Vpn {
if (isSettingsVpnLocked()) { if (isSettingsVpnLocked()) {
prepareStatusIntent(); prepareStatusIntent();
} }
agentConnect(); agentConnect(this::onValidationStatus);
return; // Link properties are already sent. return; // Link properties are already sent.
} else { } else {
// Underlying networks also set in agentConnect() // Underlying networks also set in agentConnect()
@@ -3200,18 +3243,52 @@ public class Vpn {
// Ignore stale runner. // Ignore stale runner.
if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return; 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 if (mNetworkAgent != null
&& mNetworkAgent.getNetwork().equals(report.getNetwork())) { && mNetworkAgent.getNetwork().equals(report.getNetwork())
&& !mDataStallSuspected) {
Log.d(TAG, "Data stall suspected"); Log.d(TAG, "Data stall suspected");
// Trigger MOBIKE. // Trigger MOBIKE.
maybeMigrateIkeSession(mActiveNetwork); 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 * Handles loss of the default underlying network
* *
@@ -4339,6 +4416,7 @@ public class Vpn {
// un-finalized. // un-finalized.
@VisibleForTesting @VisibleForTesting
public static class VpnNetworkAgentWrapper extends NetworkAgent { public static class VpnNetworkAgentWrapper extends NetworkAgent {
private final ValidationStatusCallback mCallback;
/** Create an VpnNetworkAgentWrapper */ /** Create an VpnNetworkAgentWrapper */
public VpnNetworkAgentWrapper( public VpnNetworkAgentWrapper(
@NonNull Context context, @NonNull Context context,
@@ -4348,8 +4426,10 @@ public class Vpn {
@NonNull LinkProperties lp, @NonNull LinkProperties lp,
@NonNull NetworkScore score, @NonNull NetworkScore score,
@NonNull NetworkAgentConfig config, @NonNull NetworkAgentConfig config,
@Nullable NetworkProvider provider) { @Nullable NetworkProvider provider,
@Nullable ValidationStatusCallback callback) {
super(context, looper, logTag, nc, lp, score, config, provider); super(context, looper, logTag, nc, lp, score, config, provider);
mCallback = callback;
} }
/** Update the LinkProperties */ /** Update the LinkProperties */
@@ -4371,6 +4451,13 @@ public class Vpn {
public void onNetworkUnwanted() { public void onNetworkUnwanted() {
// We are user controlled, not driven by NetworkRequest. // We are user controlled, not driven by NetworkRequest.
} }
@Override
public void onValidationStatus(int status, Uri redirectUri) {
if (mCallback != null) {
mCallback.onValidationStatus(status);
}
}
} }
/** /**