Skip stop if keepalive is already in stopping state
In current design, crash has been generated when stop function
has been re-entered to catch unexpected behavior. However,
it is possible to re-enter stop function if the network
disconnection occurs after stopping.
Thus, skip stop if keepalive is already in stopping state.
Test: atest ConnectivityServiceTest#testNattSocketKeepalives \
--rerun-until-failure 60000
Bug: 167332570
Change-Id: Ic7068ad3dc990e957c37b8d87d48ebb6469b101f
This commit is contained in:
@@ -367,6 +367,13 @@ public class KeepaliveTracker {
|
|||||||
Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
|
Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Ignore the case when the network disconnects immediately after stop() has been
|
||||||
|
// called and the keepalive code is waiting for the response from the modem. This
|
||||||
|
// might happen when the caller listens for a lower-layer network disconnect
|
||||||
|
// callback and stop the keepalive at that time. But the stop() races with the
|
||||||
|
// stop() generated in ConnectivityService network disconnection code path.
|
||||||
|
if (mStartedState == STOPPING && reason == ERROR_INVALID_NETWORK) return;
|
||||||
|
|
||||||
// Store the reason of stopping, and report it after the keepalive is fully stopped.
|
// Store the reason of stopping, and report it after the keepalive is fully stopped.
|
||||||
if (mStopReason != ERROR_STOP_REASON_UNINITIALIZED) {
|
if (mStopReason != ERROR_STOP_REASON_UNINITIALIZED) {
|
||||||
throw new IllegalStateException("Unexpected stop reason: " + mStopReason);
|
throw new IllegalStateException("Unexpected stop reason: " + mStopReason);
|
||||||
@@ -380,9 +387,6 @@ public class KeepaliveTracker {
|
|||||||
// e.g. invalid parameter.
|
// e.g. invalid parameter.
|
||||||
cleanupStoppedKeepalive(mNai, mSlot);
|
cleanupStoppedKeepalive(mNai, mSlot);
|
||||||
break;
|
break;
|
||||||
case STOPPING:
|
|
||||||
// Keepalive is already in stopping state, ignore.
|
|
||||||
return;
|
|
||||||
default:
|
default:
|
||||||
mStartedState = STOPPING;
|
mStartedState = STOPPING;
|
||||||
switch (mType) {
|
switch (mType) {
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
|
|||||||
private NetworkAgent mNetworkAgent;
|
private NetworkAgent mNetworkAgent;
|
||||||
private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
|
private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
|
||||||
private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
|
private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
|
||||||
|
// Controls how test network agent is going to wait before responding to keepalive
|
||||||
|
// start/stop. Useful when simulate KeepaliveTracker is waiting for response from modem.
|
||||||
|
private long mKeepaliveResponseDelay = 0L;
|
||||||
private Integer mExpectedKeepaliveSlot = null;
|
private Integer mExpectedKeepaliveSlot = null;
|
||||||
|
|
||||||
public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context)
|
public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context)
|
||||||
@@ -134,12 +137,17 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
|
|||||||
if (mWrapper.mExpectedKeepaliveSlot != null) {
|
if (mWrapper.mExpectedKeepaliveSlot != null) {
|
||||||
assertEquals((int) mWrapper.mExpectedKeepaliveSlot, slot);
|
assertEquals((int) mWrapper.mExpectedKeepaliveSlot, slot);
|
||||||
}
|
}
|
||||||
onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError);
|
mWrapper.mHandlerThread.getThreadHandler().postDelayed(
|
||||||
|
() -> onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError),
|
||||||
|
mWrapper.mKeepaliveResponseDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopSocketKeepalive(Message msg) {
|
public void stopSocketKeepalive(Message msg) {
|
||||||
onSocketKeepaliveEvent(msg.arg1, mWrapper.mStopKeepaliveError);
|
final int slot = msg.arg1;
|
||||||
|
mWrapper.mHandlerThread.getThreadHandler().postDelayed(
|
||||||
|
() -> onSocketKeepaliveEvent(slot, mWrapper.mStopKeepaliveError),
|
||||||
|
mWrapper.mKeepaliveResponseDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -248,6 +256,10 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
|
|||||||
mStopKeepaliveError = reason;
|
mStopKeepaliveError = reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setKeepaliveResponseDelay(long delay) {
|
||||||
|
mKeepaliveResponseDelay = delay;
|
||||||
|
}
|
||||||
|
|
||||||
public void setExpectedKeepaliveSlot(Integer slot) {
|
public void setExpectedKeepaliveSlot(Integer slot) {
|
||||||
mExpectedKeepaliveSlot = slot;
|
mExpectedKeepaliveSlot = slot;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4292,6 +4292,32 @@ public class ConnectivityServiceTest {
|
|||||||
myNet = connectKeepaliveNetwork(lp);
|
myNet = connectKeepaliveNetwork(lp);
|
||||||
mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
|
mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
|
||||||
|
|
||||||
|
// Check that a stop followed by network disconnects does not result in crash.
|
||||||
|
try (SocketKeepalive ka = mCm.createSocketKeepalive(
|
||||||
|
myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
|
||||||
|
ka.start(validKaInterval);
|
||||||
|
callback.expectStarted();
|
||||||
|
// Delay the response of keepalive events in networkAgent long enough to make sure
|
||||||
|
// the follow-up network disconnection will be processed first.
|
||||||
|
mWiFiNetworkAgent.setKeepaliveResponseDelay(3 * TIMEOUT_MS);
|
||||||
|
ka.stop();
|
||||||
|
|
||||||
|
// Make sure the stop has been processed. Wait for executor idle is needed to prevent
|
||||||
|
// flaky since the actual stop call to the service is delegated to executor thread.
|
||||||
|
waitForIdleSerialExecutor(executor, TIMEOUT_MS);
|
||||||
|
waitForIdle();
|
||||||
|
|
||||||
|
mWiFiNetworkAgent.disconnect();
|
||||||
|
mWiFiNetworkAgent.expectDisconnected();
|
||||||
|
callback.expectStopped();
|
||||||
|
callback.assertNoCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconnect.
|
||||||
|
waitForIdle();
|
||||||
|
myNet = connectKeepaliveNetwork(lp);
|
||||||
|
mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
|
||||||
|
|
||||||
// Check that keepalive slots start from 1 and increment. The first one gets slot 1.
|
// Check that keepalive slots start from 1 and increment. The first one gets slot 1.
|
||||||
mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
|
mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
|
||||||
int srcPort2 = 0;
|
int srcPort2 = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user