Merge "Perform VPN data stall recovery on unvalidated VPN network" into udc-d1-dev am: 65f865f847

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

Change-Id: I7bac902b1d853850f084d86e5d53eb7546bfa92e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Jean Chalard
2023-07-06 05:27:34 +00:00
committed by Automerger Merge Worker

View File

@@ -22,7 +22,6 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
@@ -280,15 +279,22 @@ 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
* The recovery timer for data stall. If a session has not successfully revalidated after
* the delay, the session will perform MOBIKE or be restarted in an attempt to recover. Delay
* counter is reset on successful validation only.
*
* <p>The first {@code MOBIKE_RECOVERY_ATTEMPT} timers are used for performing MOBIKE.
* System will perform session reset for the remaining timers.
* <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};
// TODO: use ms instead to speed up the test.
private static final long[] DATA_STALL_RECOVERY_DELAYS_SEC =
{1L, 5L, 30L, 60L, 120L, 240L, 480L, 960L};
/**
* Maximum attempts to perform MOBIKE when the network is bad.
*/
private static final int MAX_MOBIKE_RECOVERY_ATTEMPT = 2;
/**
* The initial token value of IKE session.
*/
@@ -393,7 +399,6 @@ public class Vpn {
private final UserManager mUserManager;
private final VpnProfileStore mVpnProfileStore;
protected boolean mDataStallSuspected = false;
@VisibleForTesting
VpnProfileStore getVpnProfileStore() {
@@ -686,14 +691,14 @@ public class Vpn {
}
/**
* Get the length of time to wait before resetting the ike session when a data stall is
* suspected.
* Get the length of time to wait before perform data stall recovery when the validation
* result is bad.
*/
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];
public long getValidationFailRecoverySeconds(int count) {
if (count >= DATA_STALL_RECOVERY_DELAYS_SEC.length) {
return DATA_STALL_RECOVERY_DELAYS_SEC[DATA_STALL_RECOVERY_DELAYS_SEC.length - 1];
} else {
return DATA_STALL_RESET_DELAYS_SEC[count];
return DATA_STALL_RECOVERY_DELAYS_SEC[count];
}
}
@@ -3052,7 +3057,6 @@ public class Vpn {
@Nullable private IkeSessionWrapper mSession;
@Nullable private IkeSessionConnectionInfo mIkeConnectionInfo;
@Nullable private VpnConnectivityDiagnosticsCallback mDiagnosticsCallback;
// mMobikeEnabled can only be updated after IKE AUTH is finished.
private boolean mMobikeEnabled = false;
@@ -3063,7 +3067,7 @@ public class Vpn {
* <p>This variable controls the retry delay, and is reset when the VPN pass network
* validation.
*/
private int mDataStallRetryCount = 0;
private int mValidationFailRetryCount = 0;
/**
* The number of attempts since the last successful connection.
@@ -3145,15 +3149,6 @@ public class Vpn {
mConnectivityManager.registerSystemDefaultNetworkCallback(mNetworkCallback,
new Handler(mLooper));
}
// DiagnosticsCallback may return more than one alive VPNs, but VPN will filter based on
// Network object.
final NetworkRequest diagRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_VPN)
.removeCapability(NET_CAPABILITY_NOT_VPN).build();
mDiagnosticsCallback = new VpnConnectivityDiagnosticsCallback();
mConnectivityDiagnosticsManager.registerConnectivityDiagnosticsCallback(
diagRequest, mExecutor, mDiagnosticsCallback);
}
private boolean isActiveNetwork(@Nullable Network network) {
@@ -3887,39 +3882,12 @@ public class Vpn {
}
}
class VpnConnectivityDiagnosticsCallback
extends ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
// The callback runs in the executor thread.
@Override
public void onDataStallSuspected(
ConnectivityDiagnosticsManager.DataStallReport report) {
synchronized (Vpn.this) {
// Ignore stale runner.
if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
// 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())
&& !mDataStallSuspected) {
Log.d(TAG, "Data stall suspected");
// Trigger MOBIKE.
maybeMigrateIkeSessionAndUpdateVpnTransportInfo(mActiveNetwork);
mDataStallSuspected = true;
}
}
}
}
public void onValidationStatus(int status) {
mEventChanges.log("[Validation] validation status " + status);
if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
// No data stall now. Reset it.
mExecutor.execute(() -> {
mDataStallSuspected = false;
mDataStallRetryCount = 0;
mValidationFailRetryCount = 0;
if (mScheduledHandleDataStallFuture != null) {
Log.d(TAG, "Recovered from stall. Cancel pending reset action.");
mScheduledHandleDataStallFuture.cancel(false /* mayInterruptIfRunning */);
@@ -3930,8 +3898,21 @@ public class Vpn {
// Skip other invalid status if the scheduled recovery exists.
if (mScheduledHandleDataStallFuture != null) return;
if (mValidationFailRetryCount < MAX_MOBIKE_RECOVERY_ATTEMPT) {
Log.d(TAG, "Validation failed");
// Trigger MOBIKE to recover first.
mExecutor.schedule(() -> {
maybeMigrateIkeSessionAndUpdateVpnTransportInfo(mActiveNetwork);
}, mDeps.getValidationFailRecoverySeconds(mValidationFailRetryCount++),
TimeUnit.SECONDS);
return;
}
// Data stall is not recovered by MOBIKE. Try to reset session to recover it.
mScheduledHandleDataStallFuture = mExecutor.schedule(() -> {
if (mDataStallSuspected) {
// Only perform the recovery when the network is still bad.
if (mValidationFailRetryCount > 0) {
Log.d(TAG, "Reset session to recover stalled network");
// This will reset old state if it exists.
startIkeSession(mActiveNetwork);
@@ -3940,7 +3921,9 @@ public class Vpn {
// Reset mScheduledHandleDataStallFuture since it's already run on executor
// thread.
mScheduledHandleDataStallFuture = null;
}, mDeps.getDataStallResetSessionSeconds(mDataStallRetryCount++), TimeUnit.SECONDS);
// TODO: compute the delay based on the last recovery timestamp
}, mDeps.getValidationFailRecoverySeconds(mValidationFailRetryCount++),
TimeUnit.SECONDS);
}
}
@@ -4243,8 +4226,6 @@ public class Vpn {
mCarrierConfigManager.unregisterCarrierConfigChangeListener(
mCarrierConfigChangeListener);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
mDiagnosticsCallback);
clearVpnNetworkPreference(mSessionKey);
mExecutor.shutdown();
@@ -5231,7 +5212,7 @@ public class Vpn {
pw.println("MOBIKE " + (runner.mMobikeEnabled ? "enabled" : "disabled"));
pw.println("Profile: " + runner.mProfile);
pw.println("Token: " + runner.mCurrentToken);
if (mDataStallSuspected) pw.println("Data stall suspected");
pw.println("Validation failed retry count:" + runner.mValidationFailRetryCount);
if (runner.mScheduledHandleDataStallFuture != null) {
pw.println("Reset session scheduled");
}