summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Chiachang Wang <chiachangwang@google.com> 2022-10-27 06:15:13 +0000
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2022-10-27 06:15:13 +0000
commit284af510d0e636b92b52cb2b67423de5bea45025 (patch)
treed003d76528b2bc599a1d11353ed73e01171e58f0
parent7938926336bd438d993f4a5a8edd4b5f4abe3aeb (diff)
parent80b23ae4a915927c872f2cab258c760c579d766f (diff)
Merge "Reset Ike session if stall is not recovered by MOBIKE" am: 246fc31ef5 am: 80b23ae4a9
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2159077 Change-Id: I839943359bbad7136d1a56672a305e8cc23ea9bd Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java103
1 files changed, 95 insertions, 8 deletions
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6795b6b4158d..45b0f0a6d04a 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -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;
@@ -226,6 +227,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.
*/
private static final int STARTING_TOKEN = -1;
@@ -271,6 +282,7 @@ public class Vpn {
private final UserManager mUserManager;
private final VpnProfileStore mVpnProfileStore;
+ protected boolean mDataStallSuspected = false;
@VisibleForTesting
VpnProfileStore getVpnProfileStore() {
@@ -522,12 +534,30 @@ 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,
@UserIdInt int userId, VpnProfileStore vpnProfileStore) {
this(looper, context, new Dependencies(), netService, netd, userId, vpnProfileStore,
@@ -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;
@@ -2750,6 +2785,14 @@ public class Vpn {
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.
*
* <p>This variable controls the retry delay, and is reset when a new IKE session is
@@ -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);
+ }
+ }
}
/**