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

Commit 98c78624 authored by chiachangwang's avatar chiachangwang Committed by Automerger Merge Worker
Browse files

Perform VPN data stall recovery on unvalidated VPN network am: b4f300d7 am: 837c8f67

parents 2d54ca65 837c8f67
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.
     */
@@ -392,7 +398,6 @@ public class Vpn {
    private final UserManager mUserManager;

    private final VpnProfileStore mVpnProfileStore;
    protected boolean mDataStallSuspected = false;

    @VisibleForTesting
    VpnProfileStore getVpnProfileStore() {
@@ -685,14 +690,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];
            }
        }

@@ -3044,7 +3049,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;
@@ -3055,7 +3059,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.
@@ -3136,15 +3140,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) {
@@ -3875,39 +3870,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 */);
@@ -3918,8 +3886,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);
@@ -3928,7 +3909,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);
            }
        }

@@ -4231,8 +4214,6 @@ public class Vpn {
            mCarrierConfigManager.unregisterCarrierConfigChangeListener(
                    mCarrierConfigChangeListener);
            mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
            mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
                    mDiagnosticsCallback);
            clearVpnNetworkPreference(mSessionKey);

            mExecutor.shutdown();
@@ -5216,7 +5197,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");
                }