Merge "Reset Ike session if stall is not recovered by MOBIKE" am: 246fc31ef5
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2159077 Change-Id: Ife1ebd8e4bbfcbf37dbff8b4cf0de1b7dbfb6583 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -79,6 +79,7 @@ import android.net.NetworkScore;
|
|||||||
import android.net.RouteInfo;
|
import android.net.RouteInfo;
|
||||||
import android.net.UidRangeParcel;
|
import android.net.UidRangeParcel;
|
||||||
import android.net.UnderlyingNetworkInfo;
|
import android.net.UnderlyingNetworkInfo;
|
||||||
|
import android.net.Uri;
|
||||||
import android.net.VpnManager;
|
import android.net.VpnManager;
|
||||||
import android.net.VpnProfileState;
|
import android.net.VpnProfileState;
|
||||||
import android.net.VpnService;
|
import android.net.VpnService;
|
||||||
@@ -225,6 +226,16 @@ public class Vpn {
|
|||||||
*/
|
*/
|
||||||
private static final int VPN_DEFAULT_SCORE = 101;
|
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
|
||||||
|
* counter is reset on successful validation only.
|
||||||
|
*
|
||||||
|
* <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};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The initial token value of IKE session.
|
* The initial token value of IKE session.
|
||||||
*/
|
*/
|
||||||
@@ -271,6 +282,7 @@ public class Vpn {
|
|||||||
private final UserManager mUserManager;
|
private final UserManager mUserManager;
|
||||||
|
|
||||||
private final VpnProfileStore mVpnProfileStore;
|
private final VpnProfileStore mVpnProfileStore;
|
||||||
|
protected boolean mDataStallSuspected = false;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
VpnProfileStore getVpnProfileStore() {
|
VpnProfileStore getVpnProfileStore() {
|
||||||
@@ -522,10 +534,28 @@ public class Vpn {
|
|||||||
@NonNull LinkProperties lp,
|
@NonNull LinkProperties lp,
|
||||||
@NonNull NetworkScore score,
|
@NonNull NetworkScore score,
|
||||||
@NonNull NetworkAgentConfig config,
|
@NonNull NetworkAgentConfig config,
|
||||||
@Nullable NetworkProvider provider) {
|
@Nullable NetworkProvider provider,
|
||||||
|
@Nullable ValidationStatusCallback callback) {
|
||||||
return new VpnNetworkAgentWrapper(
|
return new VpnNetworkAgentWrapper(
|
||||||
context, looper, logTag, nc, lp, score, config, provider);
|
context, looper, logTag, nc, lp, score, config, provider, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the length of time to wait before resetting the ike session when a data stall is
|
||||||
|
* suspected.
|
||||||
|
*/
|
||||||
|
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];
|
||||||
|
} else {
|
||||||
|
return DATA_STALL_RESET_DELAYS_SEC[count];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
interface ValidationStatusCallback {
|
||||||
|
void onValidationStatus(int status);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
|
public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
|
||||||
@@ -1460,6 +1490,11 @@ public class Vpn {
|
|||||||
|
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
private void agentConnect() {
|
private void agentConnect() {
|
||||||
|
agentConnect(null /* validationCallback */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
|
private void agentConnect(@Nullable ValidationStatusCallback validationCallback) {
|
||||||
LinkProperties lp = makeLinkProperties();
|
LinkProperties lp = makeLinkProperties();
|
||||||
|
|
||||||
// VPN either provide a default route (IPv4 or IPv6 or both), or they are a split tunnel
|
// VPN either provide a default route (IPv4 or IPv6 or both), or they are a split tunnel
|
||||||
@@ -1507,7 +1542,7 @@ public class Vpn {
|
|||||||
mNetworkAgent = mDeps.newNetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
|
mNetworkAgent = mDeps.newNetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
|
||||||
mNetworkCapabilities, lp,
|
mNetworkCapabilities, lp,
|
||||||
new NetworkScore.Builder().setLegacyInt(VPN_DEFAULT_SCORE).build(),
|
new NetworkScore.Builder().setLegacyInt(VPN_DEFAULT_SCORE).build(),
|
||||||
networkAgentConfig, mNetworkProvider);
|
networkAgentConfig, mNetworkProvider, validationCallback);
|
||||||
final long token = Binder.clearCallingIdentity();
|
final long token = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
mNetworkAgent.register();
|
mNetworkAgent.register();
|
||||||
@@ -2723,7 +2758,7 @@ public class Vpn {
|
|||||||
|
|
||||||
@Nullable private ScheduledFuture<?> mScheduledHandleNetworkLostFuture;
|
@Nullable private ScheduledFuture<?> mScheduledHandleNetworkLostFuture;
|
||||||
@Nullable private ScheduledFuture<?> mScheduledHandleRetryIkeSessionFuture;
|
@Nullable private ScheduledFuture<?> mScheduledHandleRetryIkeSessionFuture;
|
||||||
|
@Nullable private ScheduledFuture<?> mScheduledHandleDataStallFuture;
|
||||||
/** Signal to ensure shutdown is honored even if a new Network is connected. */
|
/** Signal to ensure shutdown is honored even if a new Network is connected. */
|
||||||
private boolean mIsRunning = true;
|
private boolean mIsRunning = true;
|
||||||
|
|
||||||
@@ -2749,6 +2784,14 @@ public class Vpn {
|
|||||||
// mMobikeEnabled can only be updated after IKE AUTH is finished.
|
// mMobikeEnabled can only be updated after IKE AUTH is finished.
|
||||||
private boolean mMobikeEnabled = false;
|
private boolean mMobikeEnabled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of attempts to reset the IKE session since the last successful connection.
|
||||||
|
*
|
||||||
|
* <p>This variable controls the retry delay, and is reset when the VPN pass network
|
||||||
|
* validation.
|
||||||
|
*/
|
||||||
|
private int mDataStallRetryCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of attempts since the last successful connection.
|
* The number of attempts since the last successful connection.
|
||||||
*
|
*
|
||||||
@@ -2931,7 +2974,7 @@ public class Vpn {
|
|||||||
if (isSettingsVpnLocked()) {
|
if (isSettingsVpnLocked()) {
|
||||||
prepareStatusIntent();
|
prepareStatusIntent();
|
||||||
}
|
}
|
||||||
agentConnect();
|
agentConnect(this::onValidationStatus);
|
||||||
return; // Link properties are already sent.
|
return; // Link properties are already sent.
|
||||||
} else {
|
} else {
|
||||||
// Underlying networks also set in agentConnect()
|
// Underlying networks also set in agentConnect()
|
||||||
@@ -3200,18 +3243,52 @@ public class Vpn {
|
|||||||
// Ignore stale runner.
|
// Ignore stale runner.
|
||||||
if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
|
if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
|
||||||
|
|
||||||
// Handle the report only for current VPN network.
|
// 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
|
if (mNetworkAgent != null
|
||||||
&& mNetworkAgent.getNetwork().equals(report.getNetwork())) {
|
&& mNetworkAgent.getNetwork().equals(report.getNetwork())
|
||||||
|
&& !mDataStallSuspected) {
|
||||||
Log.d(TAG, "Data stall suspected");
|
Log.d(TAG, "Data stall suspected");
|
||||||
|
|
||||||
// Trigger MOBIKE.
|
// Trigger MOBIKE.
|
||||||
maybeMigrateIkeSession(mActiveNetwork);
|
maybeMigrateIkeSession(mActiveNetwork);
|
||||||
|
mDataStallSuspected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onValidationStatus(int status) {
|
||||||
|
if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
|
||||||
|
// No data stall now. Reset it.
|
||||||
|
mExecutor.execute(() -> {
|
||||||
|
mDataStallSuspected = false;
|
||||||
|
mDataStallRetryCount = 0;
|
||||||
|
if (mScheduledHandleDataStallFuture != null) {
|
||||||
|
Log.d(TAG, "Recovered from stall. Cancel pending reset action.");
|
||||||
|
mScheduledHandleDataStallFuture.cancel(false /* mayInterruptIfRunning */);
|
||||||
|
mScheduledHandleDataStallFuture = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Skip other invalid status if the scheduled recovery exists.
|
||||||
|
if (mScheduledHandleDataStallFuture != null) return;
|
||||||
|
|
||||||
|
mScheduledHandleDataStallFuture = mExecutor.schedule(() -> {
|
||||||
|
if (mDataStallSuspected) {
|
||||||
|
Log.d(TAG, "Reset session to recover stalled network");
|
||||||
|
// This will reset old state if it exists.
|
||||||
|
startIkeSession(mActiveNetwork);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset mScheduledHandleDataStallFuture since it's already run on executor
|
||||||
|
// thread.
|
||||||
|
mScheduledHandleDataStallFuture = null;
|
||||||
|
}, mDeps.getDataStallResetSessionSeconds(mDataStallRetryCount++), TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles loss of the default underlying network
|
* Handles loss of the default underlying network
|
||||||
*
|
*
|
||||||
@@ -4339,6 +4416,7 @@ public class Vpn {
|
|||||||
// un-finalized.
|
// un-finalized.
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static class VpnNetworkAgentWrapper extends NetworkAgent {
|
public static class VpnNetworkAgentWrapper extends NetworkAgent {
|
||||||
|
private final ValidationStatusCallback mCallback;
|
||||||
/** Create an VpnNetworkAgentWrapper */
|
/** Create an VpnNetworkAgentWrapper */
|
||||||
public VpnNetworkAgentWrapper(
|
public VpnNetworkAgentWrapper(
|
||||||
@NonNull Context context,
|
@NonNull Context context,
|
||||||
@@ -4348,8 +4426,10 @@ public class Vpn {
|
|||||||
@NonNull LinkProperties lp,
|
@NonNull LinkProperties lp,
|
||||||
@NonNull NetworkScore score,
|
@NonNull NetworkScore score,
|
||||||
@NonNull NetworkAgentConfig config,
|
@NonNull NetworkAgentConfig config,
|
||||||
@Nullable NetworkProvider provider) {
|
@Nullable NetworkProvider provider,
|
||||||
|
@Nullable ValidationStatusCallback callback) {
|
||||||
super(context, looper, logTag, nc, lp, score, config, provider);
|
super(context, looper, logTag, nc, lp, score, config, provider);
|
||||||
|
mCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update the LinkProperties */
|
/** Update the LinkProperties */
|
||||||
@@ -4371,6 +4451,13 @@ public class Vpn {
|
|||||||
public void onNetworkUnwanted() {
|
public void onNetworkUnwanted() {
|
||||||
// We are user controlled, not driven by NetworkRequest.
|
// We are user controlled, not driven by NetworkRequest.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onValidationStatus(int status, Uri redirectUri) {
|
||||||
|
if (mCallback != null) {
|
||||||
|
mCallback.onValidationStatus(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user