Release keepalive slot after stopped

Currntly, keepalive slot is released when stop() is called. Next
starting keepalive can use the same slot number while previous
keepalive is still stopping. When the previous keepalive is
stopped, the incoming as will be processed by the new keepalive.

This change release keepalive slot after the result of stopping
has returned. Thus, newly created keepalive cannot allocate the
same slot number while lower layer is still processing stop event.

This change also disable flaky assertions that are caused by
test port has been occupied by other process.

Bug: 129512753
Test: 1. atest com.android.server.ConnectivityServiceTest \
         #testNattSocketKeepalives --generate-new-metrics 100
      2. atest FrameworksNetTests --generate-new-metrics 10
      3. simulate the fail case manually.

Change-Id: I790f6bbc5efc3f088034ac45ec379da5f781d0ca
Merged-In: I1991627545519ee5cb408a3df3a006f710f4af7b
(cherry picked from commit 3523a3d02a1f88a3990ab9cc4948c705ecc713c8)
This commit is contained in:
Junyu Lai
2019-04-15 23:00:02 -07:00
committed by junyulai
parent c602b40237
commit 5e135ee2b9
2 changed files with 48 additions and 19 deletions

View File

@@ -209,6 +209,7 @@ public class KeepaliveTracker {
case NOT_STARTED : return "NOT_STARTED";
case STARTING : return "STARTING";
case STARTED : return "STARTED";
case STOPPING : return "STOPPING";
}
throw new IllegalArgumentException("Unknown state");
}
@@ -362,18 +363,27 @@ public class KeepaliveTracker {
Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
}
}
if (NOT_STARTED != mStartedState) {
mStartedState = STOPPING;
Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name());
if (mType == TYPE_NATT) {
mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
} else if (mType == TYPE_TCP) {
mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, mSlot);
mTcpController.stopSocketMonitor(mSlot);
} else {
Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType);
}
Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name() + ": " + reason);
switch (mStartedState) {
case NOT_STARTED:
// Remove the reference of the keepalive that meet error before starting,
// e.g. invalid parameter.
cleanupStoppedKeepalive(mNai, mSlot);
break;
case STOPPING:
// Keepalive is already in stopping state, ignore.
return;
default:
mStartedState = STOPPING;
if (mType == TYPE_NATT) {
mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
} else if (mType == TYPE_TCP) {
mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, mSlot);
mTcpController.stopSocketMonitor(mSlot);
} else {
Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType);
}
}
// Close the duplicated fd that maintains the lifecycle of socket whenever
@@ -453,9 +463,9 @@ public class KeepaliveTracker {
for (KeepaliveInfo ki : networkKeepalives.values()) {
ki.stop(reason);
}
networkKeepalives.clear();
mKeepalives.remove(nai);
}
// Clean up keepalives will be done as a result of calling ki.stop() after the slots are
// freed.
}
public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
@@ -471,8 +481,24 @@ public class KeepaliveTracker {
return;
}
ki.stop(reason);
// Clean up keepalives will be done as a result of calling ki.stop() after the slots are
// freed.
}
private void cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot) {
String networkName = (nai == null) ? "(null)" : nai.name();
HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
if (networkKeepalives == null) {
Log.e(TAG, "Attempt to remove keepalive on nonexistent network " + networkName);
return;
}
KeepaliveInfo ki = networkKeepalives.get(slot);
if (ki == null) {
Log.e(TAG, "Attempt to remove nonexistent keepalive " + slot + " on " + networkName);
return;
}
networkKeepalives.remove(slot);
Log.d(TAG, "Stopped keepalive " + slot + " on " + networkName + ", "
Log.d(TAG, "Remove keepalive " + slot + " on " + networkName + ", "
+ networkKeepalives.size() + " remains.");
if (networkKeepalives.isEmpty()) {
mKeepalives.remove(nai);
@@ -543,10 +569,11 @@ public class KeepaliveTracker {
handleStopKeepalive(nai, slot, reason);
}
} else if (KeepaliveInfo.STOPPING == ki.mStartedState) {
// The message indicated result of stopping : don't call handleStopKeepalive.
// The message indicated result of stopping : clean up keepalive slots.
Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.name()
+ " stopped: " + reason);
ki.mStartedState = KeepaliveInfo.NOT_STARTED;
cleanupStoppedKeepalive(nai, slot);
} else {
Log.wtf(TAG, "Event " + message.what + "," + slot + "," + reason
+ " for keepalive in wrong state: " + ki.toString());

View File

@@ -4342,8 +4342,9 @@ public class ConnectivityServiceTest {
}
// Check that there is no port leaked after all keepalives and sockets are closed.
assertFalse(isUdpPortInUse(srcPort));
assertFalse(isUdpPortInUse(srcPort2));
// TODO: enable this check after ensuring a valid free port. See b/129512753#comment7.
// assertFalse(isUdpPortInUse(srcPort));
// assertFalse(isUdpPortInUse(srcPort2));
mWiFiNetworkAgent.disconnect();
waitFor(mWiFiNetworkAgent.getDisconnectedCV());
@@ -4471,7 +4472,8 @@ public class ConnectivityServiceTest {
assertEquals(anyIPv4, sa.getAddress());
testPfd.close();
assertFalse(isUdpPortInUse(srcPort));
// TODO: enable this check after ensuring a valid free port. See b/129512753#comment7.
// assertFalse(isUdpPortInUse(srcPort));
mWiFiNetworkAgent.disconnect();
waitFor(mWiFiNetworkAgent.getDisconnectedCV());