Loading services/core/java/com/android/server/connectivity/Vpn.java +37 −56 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -393,7 +399,6 @@ public class Vpn { private final UserManager mUserManager; private final VpnProfileStore mVpnProfileStore; protected boolean mDataStallSuspected = false; @VisibleForTesting VpnProfileStore getVpnProfileStore() { Loading Loading @@ -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]; } } Loading Loading @@ -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; Loading @@ -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. Loading Loading @@ -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) { Loading Loading @@ -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 */); Loading @@ -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); Loading @@ -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); } } Loading Loading @@ -4243,8 +4226,6 @@ public class Vpn { mCarrierConfigManager.unregisterCarrierConfigChangeListener( mCarrierConfigChangeListener); mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback( mDiagnosticsCallback); clearVpnNetworkPreference(mSessionKey); mExecutor.shutdown(); Loading Loading @@ -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"); } Loading Loading
services/core/java/com/android/server/connectivity/Vpn.java +37 −56 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -393,7 +399,6 @@ public class Vpn { private final UserManager mUserManager; private final VpnProfileStore mVpnProfileStore; protected boolean mDataStallSuspected = false; @VisibleForTesting VpnProfileStore getVpnProfileStore() { Loading Loading @@ -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]; } } Loading Loading @@ -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; Loading @@ -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. Loading Loading @@ -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) { Loading Loading @@ -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 */); Loading @@ -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); Loading @@ -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); } } Loading Loading @@ -4243,8 +4226,6 @@ public class Vpn { mCarrierConfigManager.unregisterCarrierConfigChangeListener( mCarrierConfigChangeListener); mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback( mDiagnosticsCallback); clearVpnNetworkPreference(mSessionKey); mExecutor.shutdown(); Loading Loading @@ -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"); } Loading