Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 65f865f8 authored by Jean Chalard's avatar Jean Chalard Committed by Android (Google) Code Review
Browse files

Merge "Perform VPN data stall recovery on unvalidated VPN network" into udc-d1-dev

parents eeaf8557 371c0a82
Loading
Loading
Loading
Loading
+37 −56
Original line number Diff line number Diff line
@@ -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");
                }