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

Commit 3072de4c authored by Lucas Lin's avatar Lucas Lin Committed by Automerger Merge Worker
Browse files

Merge "Add a retry mechanism when error is recoverable" am: b02eee5c am:...

Merge "Add a retry mechanism when error is recoverable" am: b02eee5c am: 6059e6c3 am: 361074d9 am: 2f66da25

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



Change-Id: I31322596364c76a134e09aca7020a9b14b2e95f9
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 7d97a9a7 2f66da25
Loading
Loading
Loading
Loading
+123 −25
Original line number Diff line number Diff line
@@ -193,11 +193,19 @@ public class Vpn {
    private static final long VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS = 60 * 1000;

    // Length of time (in milliseconds) that an app registered for VpnManager events is placed on
    // the device idle allowlist each time the a VpnManager event is fired.
    // the device idle allowlist each time the VpnManager event is fired.
    private static final long VPN_MANAGER_EVENT_ALLOWLIST_DURATION_MS = 30 * 1000;

    private static final String LOCKDOWN_ALLOWLIST_SETTING_NAME =
            Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST;

    /**
     * The retries for consecutive failures.
     *
     * <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[] IKEV2_VPN_RETRY_DELAYS_SEC = {1L, 2L, 5L, 30L, 60L, 300L, 900L};
    /**
     * Largest profile size allowable for Platform VPNs.
     *
@@ -476,6 +484,20 @@ public class Vpn {
                        "Cannot set tunnel's fd as blocking=" + blocking, e);
            }
        }

        /**
         * Retrieves the next retry delay
         *
         * <p>If retries have exceeded the IKEV2_VPN_RETRY_DELAYS_SEC, the last entry in
         * the array will be used as a repeating interval.
         */
        public long getNextRetryDelaySeconds(int retryCount) {
            if (retryCount >= IKEV2_VPN_RETRY_DELAYS_SEC.length) {
                return IKEV2_VPN_RETRY_DELAYS_SEC[IKEV2_VPN_RETRY_DELAYS_SEC.length - 1];
            } else {
                return IKEV2_VPN_RETRY_DELAYS_SEC[retryCount];
            }
        }
    }

    public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
@@ -2674,6 +2696,7 @@ public class Vpn {
        private final ScheduledThreadPoolExecutor mExecutor = new ScheduledThreadPoolExecutor(1);

        @Nullable private ScheduledFuture<?> mScheduledHandleNetworkLostTimeout;
        @Nullable private ScheduledFuture<?> mScheduledHandleRetryIkeSessionTimeout;

        /** Signal to ensure shutdown is honored even if a new Network is connected. */
        private boolean mIsRunning = true;
@@ -2697,6 +2720,14 @@ public class Vpn {
        // mMobikeEnabled can only be updated after IKE AUTH is finished.
        private boolean mMobikeEnabled = false;

        /**
         * 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
         * opened or when there is a new default network.
         */
        private int mRetryCount = 0;

        IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) {
            super(TAG);
            mProfile = profile;
@@ -2773,6 +2804,7 @@ public class Vpn {
                    ikeConfiguration.isIkeExtensionEnabled(
                            IkeSessionConfiguration.EXTENSION_TYPE_MOBIKE);
            onIkeConnectionInfoChanged(token, ikeConfiguration.getIkeSessionConnectionInfo());
            mRetryCount = 0;
        }

        /**
@@ -2969,16 +3001,42 @@ public class Vpn {
        public void onDefaultNetworkChanged(@NonNull Network network) {
            Log.d(TAG, "onDefaultNetworkChanged: " + network);

            // If there is a new default network brought up, cancel the retry task to prevent
            // establishing an unnecessary IKE session.
            cancelRetryNewIkeSessionFuture();

            // If there is a new default network brought up, cancel the obsolete reset and retry
            // task.
            cancelHandleNetworkLostTimeout();

            try {
            if (!mIsRunning) {
                Log.d(TAG, "onDefaultNetworkChanged after exit");
                return; // VPN has been shut down.
            }

            mActiveNetwork = network;
            mRetryCount = 0;

            startOrMigrateIkeSession(network);
        }

        /**
         * Start a new IKE session.
         *
         * <p>This method MUST always be called on the mExecutor thread in order to ensure
         * consistency of the Ikev2VpnRunner fields.
         *
         * @param underlyingNetwork if the value is {@code null}, which means there is no active
         *              network can be used, do nothing and return immediately. Otherwise, use the
         *              given network to start a new IKE session.
         */
        private void startOrMigrateIkeSession(@Nullable Network underlyingNetwork) {
            if (underlyingNetwork == null) {
                Log.d(TAG, "There is no active network for starting an IKE session");
                return;
            }

            try {
                if (mSession != null && mMobikeEnabled) {
                    // IKE session can schedule a migration event only when IKE AUTH is finished
                    // and mMobikeEnabled is true.
@@ -2987,11 +3045,13 @@ public class Vpn {
                            "Migrate IKE Session with token "
                                    + mCurrentToken
                                    + " to network "
                                    + network);
                    mSession.setNetwork(network);
                                    + underlyingNetwork);
                    mSession.setNetwork(underlyingNetwork);
                    return;
                }

                Log.d(TAG, "Start new IKE session on network " + underlyingNetwork);

                // Clear mInterface to prevent Ikev2VpnRunner being cleared when
                // interfaceRemoved() is called.
                mInterface = null;
@@ -3007,12 +3067,12 @@ public class Vpn {
                final ChildSessionParams childSessionParams;
                if (ikeTunConnParams != null) {
                    final IkeSessionParams.Builder builder = new IkeSessionParams.Builder(
                            ikeTunConnParams.getIkeSessionParams()).setNetwork(network);
                            ikeTunConnParams.getIkeSessionParams()).setNetwork(underlyingNetwork);
                    ikeSessionParams = builder.build();
                    childSessionParams = ikeTunConnParams.getTunnelModeChildSessionParams();
                } else {
                    ikeSessionParams = VpnIkev2Utils.buildIkeSessionParams(
                            mContext, mProfile, network);
                            mContext, mProfile, underlyingNetwork);
                    childSessionParams = VpnIkev2Utils.buildChildSessionParams(
                            mProfile.getAllowedAlgorithms());
                }
@@ -3027,7 +3087,7 @@ public class Vpn {
                // called. Thus it is safe to build a mTunnelIface before IKE setup.
                mTunnelIface =
                        mIpSecManager.createIpSecTunnelInterface(
                                address /* unused */, address /* unused */, network);
                                address /* unused */, address /* unused */, underlyingNetwork);
                NetdUtils.setInterfaceUp(mNetd, mTunnelIface.getInterfaceName());

                final int token = ++mCurrentToken;
@@ -3048,6 +3108,22 @@ public class Vpn {
            }
        }

        private void scheduleRetryNewIkeSession() {
            final long retryDelay = mDeps.getNextRetryDelaySeconds(mRetryCount++);
            Log.d(TAG, "Retry new IKE session after " + retryDelay + " seconds.");
            // If the default network is lost during the retry delay, the mActiveNetwork will be
            // null, and the new IKE session won't be established until there is a new default
            // network bringing up.
            mScheduledHandleRetryIkeSessionTimeout =
                    mExecutor.schedule(() -> {
                        startOrMigrateIkeSession(mActiveNetwork);

                        // Reset mScheduledHandleRetryIkeSessionTimeout since it's already run on
                        // executor thread.
                        mScheduledHandleRetryIkeSessionTimeout = null;
                    }, retryDelay, TimeUnit.SECONDS);
        }

        /** Called when the NetworkCapabilities of underlying network is changed */
        public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) {
            mUnderlyingNetworkCapabilities = nc;
@@ -3069,6 +3145,11 @@ public class Vpn {
         * consistency of the Ikev2VpnRunner fields.
         */
        public void onDefaultNetworkLost(@NonNull Network network) {
            // If the default network is torn down, there is no need to call
            // startOrMigrateIkeSession() since it will always check if there is an active network
            // can be used or not.
            cancelRetryNewIkeSessionFuture();

            if (!isActiveNetwork(network)) {
                Log.d(TAG, "onDefaultNetworkLost called for obsolete network " + network);

@@ -3078,6 +3159,8 @@ public class Vpn {
                // or an error was encountered somewhere else). In both cases, all resources and
                // sessions are torn down via resetIkeState().
                return;
            } else {
                mActiveNetwork = null;
            }

            if (mScheduledHandleNetworkLostTimeout != null
@@ -3090,7 +3173,7 @@ public class Vpn {
                        TAG,
                        "Unexpected error in onDefaultNetworkLost. Tear down session",
                        exception);
                handleSessionLost(exception);
                handleSessionLost(exception, network);
                return;
            }

@@ -3108,13 +3191,13 @@ public class Vpn {
                mScheduledHandleNetworkLostTimeout =
                        mExecutor.schedule(
                                () -> {
                                    handleSessionLost(null);
                                    handleSessionLost(null, network);
                                },
                                NETWORK_LOST_TIMEOUT_MS,
                                TimeUnit.MILLISECONDS);
            } else {
                Log.d(TAG, "Call handleSessionLost for losing network " + network);
                handleSessionLost(null);
                handleSessionLost(null, network);
            }
        }

@@ -3127,6 +3210,20 @@ public class Vpn {
                // mExecutor who has only one thread.
                Log.d(TAG, "Cancel the task for handling network lost timeout");
                mScheduledHandleNetworkLostTimeout.cancel(false /* mayInterruptIfRunning */);
                mScheduledHandleNetworkLostTimeout = null;
            }
        }

        private void cancelRetryNewIkeSessionFuture() {
            if (mScheduledHandleRetryIkeSessionTimeout != null
                    && !mScheduledHandleRetryIkeSessionTimeout.isDone()) {
                // It does not matter what to put in #cancel(boolean), because it is impossible
                // that the task tracked by mScheduledHandleRetryIkeSessionTimeout is
                // in-progress since both that task and onDefaultNetworkChanged are submitted to
                // mExecutor who has only one thread.
                Log.d(TAG, "Cancel the task for handling new ike session timeout");
                mScheduledHandleRetryIkeSessionTimeout.cancel(false /* mayInterruptIfRunning */);
                mScheduledHandleRetryIkeSessionTimeout = null;
            }
        }

@@ -3162,10 +3259,10 @@ public class Vpn {
                return;
            }

            handleSessionLost(exception);
            handleSessionLost(exception, mActiveNetwork);
        }

        private void handleSessionLost(@Nullable Exception exception) {
        private void handleSessionLost(@Nullable Exception exception, @Nullable Network network) {
            // Cancel mScheduledHandleNetworkLostTimeout if the session it is going to terminate is
            // already terminated due to other failures.
            cancelHandleNetworkLostTimeout();
@@ -3189,7 +3286,7 @@ public class Vpn {
                                        VpnManager.ERROR_CLASS_NOT_RECOVERABLE,
                                        ikeException.getErrorType(),
                                        getPackage(), mSessionKey, makeVpnProfileStateLocked(),
                                        mActiveNetwork,
                                        network,
                                        getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
                                                mUnderlyingNetworkCapabilities),
                                        getRedactedLinkPropertiesOfUnderlyingNetwork(
@@ -3207,7 +3304,7 @@ public class Vpn {
                                        VpnManager.ERROR_CLASS_RECOVERABLE,
                                        ikeException.getErrorType(),
                                        getPackage(), mSessionKey, makeVpnProfileStateLocked(),
                                        mActiveNetwork,
                                        network,
                                        getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
                                                mUnderlyingNetworkCapabilities),
                                        getRedactedLinkPropertiesOfUnderlyingNetwork(
@@ -3226,7 +3323,7 @@ public class Vpn {
                                VpnManager.ERROR_CLASS_RECOVERABLE,
                                VpnManager.ERROR_CODE_NETWORK_LOST,
                                getPackage(), mSessionKey, makeVpnProfileStateLocked(),
                                mActiveNetwork,
                                network,
                                getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
                                        mUnderlyingNetworkCapabilities),
                                getRedactedLinkPropertiesOfUnderlyingNetwork(
@@ -3241,7 +3338,7 @@ public class Vpn {
                                    VpnManager.ERROR_CLASS_RECOVERABLE,
                                    VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST,
                                    getPackage(), mSessionKey, makeVpnProfileStateLocked(),
                                    mActiveNetwork,
                                    network,
                                    getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
                                            mUnderlyingNetworkCapabilities),
                                    getRedactedLinkPropertiesOfUnderlyingNetwork(
@@ -3255,7 +3352,7 @@ public class Vpn {
                                    VpnManager.ERROR_CLASS_RECOVERABLE,
                                    VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT,
                                    getPackage(), mSessionKey, makeVpnProfileStateLocked(),
                                    mActiveNetwork,
                                    network,
                                    getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
                                            mUnderlyingNetworkCapabilities),
                                    getRedactedLinkPropertiesOfUnderlyingNetwork(
@@ -3269,7 +3366,7 @@ public class Vpn {
                                    VpnManager.ERROR_CLASS_RECOVERABLE,
                                    VpnManager.ERROR_CODE_NETWORK_IO,
                                    getPackage(), mSessionKey, makeVpnProfileStateLocked(),
                                    mActiveNetwork,
                                    network,
                                    getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
                                            mUnderlyingNetworkCapabilities),
                                    getRedactedLinkPropertiesOfUnderlyingNetwork(
@@ -3279,9 +3376,10 @@ public class Vpn {
                } else if (exception != null) {
                    Log.wtf(TAG, "onSessionLost: exception = " + exception);
                }

                scheduleRetryNewIkeSession();
            }

            mActiveNetwork = null;
            mUnderlyingNetworkCapabilities = null;
            mUnderlyingLinkProperties = null;