Merge changes I3250bdb9,I20f6e5e1,I70c96a09,I88f77bd4,If6cefa51, ...

* changes:
  ethernet: rename methods in EthernetNetworkFactory
  ethernet: move registerNetworkOffer into its own function
  ethernet: provider to keep track of requestId
  ethernet: return result when removing interface
  ethernet: enable testCallbacks_forServerModeInterfaces on CF
  ethernet: expand server mode callbacks test
  ethernet: add test for removing interface while in server mode
This commit is contained in:
Patrick Rohr
2022-07-01 23:03:24 +00:00
committed by Gerrit Code Review
2 changed files with 85 additions and 41 deletions

View File

@@ -60,7 +60,9 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
* {@link NetworkProvider} that manages NetworkOffers for Ethernet networks. * Class that manages NetworkOffers for Ethernet networks.
*
* TODO: this class should be merged into EthernetTracker.
*/ */
public class EthernetNetworkFactory { public class EthernetNetworkFactory {
private final static String TAG = EthernetNetworkFactory.class.getSimpleName(); private final static String TAG = EthernetNetworkFactory.class.getSimpleName();
@@ -221,11 +223,17 @@ public class EthernetNetworkFactory {
} }
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected void removeInterface(String interfaceName) { protected boolean removeInterface(String interfaceName) {
NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName); NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName);
if (iface != null) { if (iface != null) {
iface.destroy(); iface.unregisterNetworkOfferAndStop();
return true;
} }
// TODO(b/236892130): if an interface is currently in server mode, it may not be properly
// removed.
// TODO: when false is returned, do not send a STATE_ABSENT callback.
Log.w(TAG, interfaceName + " is not tracked and cannot be removed");
return false;
} }
/** Returns true if state has been modified */ /** Returns true if state has been modified */
@@ -292,7 +300,7 @@ public class EthernetNetworkFactory {
private boolean mLinkUp; private boolean mLinkUp;
private int mLegacyType; private int mLegacyType;
private LinkProperties mLinkProperties = new LinkProperties(); private LinkProperties mLinkProperties = new LinkProperties();
private Set<NetworkRequest> mRequests = new ArraySet<>(); private final Set<Integer> mRequestIds = new ArraySet<>();
private volatile @Nullable IpClientManager mIpClient; private volatile @Nullable IpClientManager mIpClient;
private @NonNull NetworkCapabilities mCapabilities; private @NonNull NetworkCapabilities mCapabilities;
@@ -401,7 +409,7 @@ public class EthernetNetworkFactory {
// existing requests. // existing requests.
// ConnectivityService filters requests for us based on the NetworkCapabilities // ConnectivityService filters requests for us based on the NetworkCapabilities
// passed in the registerNetworkOffer() call. // passed in the registerNetworkOffer() call.
mRequests.add(request); mRequestIds.add(request.requestId);
// if the network is already started, this is a no-op. // if the network is already started, this is a no-op.
start(); start();
} }
@@ -412,8 +420,12 @@ public class EthernetNetworkFactory {
Log.d(TAG, Log.d(TAG,
String.format("%s: onNetworkUnneeded for request: %s", name, request)); String.format("%s: onNetworkUnneeded for request: %s", name, request));
} }
mRequests.remove(request); if (!mRequestIds.remove(request.requestId)) {
if (mRequests.isEmpty()) { // This can only happen if onNetworkNeeded was not called for a request or if
// the requestId changed. Both should *never* happen.
Log.wtf(TAG, "onNetworkUnneeded called for unknown request");
}
if (mRequestIds.isEmpty()) {
// not currently serving any requests, stop the network. // not currently serving any requests, stop the network.
stop(); stop();
} }
@@ -454,7 +466,7 @@ public class EthernetNetworkFactory {
+ "transport type."); + "transport type.");
} }
private static NetworkScore getBestNetworkScore() { private static NetworkScore getNetworkScore() {
return new NetworkScore.Builder().build(); return new NetworkScore.Builder().build();
} }
@@ -465,9 +477,7 @@ public class EthernetNetworkFactory {
if (mLinkUp) { if (mLinkUp) {
// registering a new network offer will update the existing one, not install a // registering a new network offer will update the existing one, not install a
// new one. // new one.
mNetworkProvider.registerNetworkOffer(getBestNetworkScore(), registerNetworkOffer();
new NetworkCapabilities(capabilities), cmd -> mHandler.post(cmd),
mNetworkOfferCallback);
} }
} }
@@ -629,14 +639,12 @@ public class EthernetNetworkFactory {
if (!up) { // was up, goes down if (!up) { // was up, goes down
// retract network offer and stop IpClient. // retract network offer and stop IpClient.
destroy(); unregisterNetworkOfferAndStop();
// If only setting the interface down, send a callback to signal completion. // If only setting the interface down, send a callback to signal completion.
EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null); EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null);
} else { // was down, goes up } else { // was down, goes up
// register network offer // register network offer
mNetworkProvider.registerNetworkOffer(getBestNetworkScore(), registerNetworkOffer();
new NetworkCapabilities(mCapabilities), (cmd) -> mHandler.post(cmd),
mNetworkOfferCallback);
} }
return true; return true;
@@ -660,10 +668,16 @@ public class EthernetNetworkFactory {
mLinkProperties.clear(); mLinkProperties.clear();
} }
public void destroy() { private void registerNetworkOffer() {
mNetworkProvider.registerNetworkOffer(getNetworkScore(),
new NetworkCapabilities(mCapabilities), cmd -> mHandler.post(cmd),
mNetworkOfferCallback);
}
public void unregisterNetworkOfferAndStop() {
mNetworkProvider.unregisterNetworkOffer(mNetworkOfferCallback); mNetworkProvider.unregisterNetworkOffer(mNetworkOfferCallback);
stop(); stop();
mRequests.clear(); mRequestIds.clear();
} }
private static void provisionIpClient(@NonNull final IpClientManager ipClient, private static void provisionIpClient(@NonNull final IpClientManager ipClient,

View File

@@ -49,7 +49,6 @@ import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.os.OutcomeReceiver import android.os.OutcomeReceiver
import android.os.SystemProperties
import android.platform.test.annotations.AppModeFull import android.platform.test.annotations.AppModeFull
import android.util.ArraySet import android.util.ArraySet
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
@@ -69,13 +68,13 @@ import com.android.testutils.runAsShell
import com.android.testutils.waitForIdle import com.android.testutils.waitForIdle
import org.junit.After import org.junit.After
import org.junit.Assume.assumeTrue import org.junit.Assume.assumeTrue
import org.junit.Assume.assumeFalse
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import java.net.Inet6Address import java.net.Inet6Address
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeoutException
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
@@ -206,12 +205,8 @@ class EthernetManagerTest {
fun eventuallyExpect(expected: CallbackEntry) = events.poll(TIMEOUT_MS) { it == expected } fun eventuallyExpect(expected: CallbackEntry) = events.poll(TIMEOUT_MS) { it == expected }
fun eventuallyExpect(interfaceName: String, state: Int, role: Int) {
assertNotNull(eventuallyExpect(createChangeEvent(interfaceName, state, role)))
}
fun eventuallyExpect(iface: EthernetTestInterface, state: Int, role: Int) { fun eventuallyExpect(iface: EthernetTestInterface, state: Int, role: Int) {
eventuallyExpect(iface.name, state, role) assertNotNull(eventuallyExpect(createChangeEvent(iface.name, state, role)))
} }
fun assertNoCallback() { fun assertNoCallback() {
@@ -458,32 +453,45 @@ class EthernetManagerTest {
} }
} }
// TODO: this function is now used in two places (EthernetManagerTest and private fun assumeNoInterfaceForTetheringAvailable() {
// EthernetTetheringTest), so it should be moved to testutils. // Interfaces that have configured NetworkCapabilities will never be used for tethering,
private fun isAdbOverNetwork(): Boolean { // see aosp/2123900.
// If adb TCP port opened, this test may running by adb over network. try {
return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1 || // assumeException does not exist.
SystemProperties.getInt("service.adb.tcp.port", -1) > -1) requestTetheredInterface().expectOnAvailable()
// interface used for tethering is available, throw an assumption error.
assumeTrue(false)
} catch (e: TimeoutException) {
// do nothing -- the TimeoutException indicates that no interface is available for
// tethering.
releaseTetheredInterface()
}
} }
@Test @Test
fun testCallbacks_forServerModeInterfaces() { fun testCallbacks_forServerModeInterfaces() {
// do not run this test when adb might be connected over ethernet. // do not run this test if an interface that can be used for tethering already exists.
assumeFalse(isAdbOverNetwork()) assumeNoInterfaceForTetheringAvailable()
val iface = createInterface()
requestTetheredInterface().expectOnAvailable()
val listener = EthernetStateListener() val listener = EthernetStateListener()
addInterfaceStateListener(listener) addInterfaceStateListener(listener)
// TODO(b/236895792): THIS IS A BUG! Existing server mode interfaces are not reported when
// it is possible that a physical interface is present, so it is not guaranteed that iface // an InterfaceStateListener is registered.
// will be put into server mode. This should not matter for the test though. Calling // Note: using eventuallyExpect as there may be other interfaces present.
// createInterface() makes sure we have at least one interface available. // listener.eventuallyExpect(iface, STATE_LINK_UP, ROLE_SERVER)
val iface = createInterface()
val cb = requestTetheredInterface()
val ifaceName = cb.expectOnAvailable()
listener.eventuallyExpect(ifaceName, STATE_LINK_UP, ROLE_SERVER)
releaseTetheredInterface() releaseTetheredInterface()
listener.eventuallyExpect(ifaceName, STATE_LINK_UP, ROLE_CLIENT) listener.eventuallyExpect(iface, STATE_LINK_UP, ROLE_CLIENT)
requestTetheredInterface().expectOnAvailable()
// This should be changed to expectCallback, once b/236895792 is fixed.
listener.eventuallyExpect(iface, STATE_LINK_UP, ROLE_SERVER)
releaseTetheredInterface()
listener.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
} }
/** /**
@@ -651,4 +659,26 @@ class EthernetManagerTest {
iface.setCarrierEnabled(false) iface.setCarrierEnabled(false)
cb.eventuallyExpectLost() cb.eventuallyExpectLost()
} }
@Test
fun testRemoveInterface_whileInServerMode() {
assumeNoInterfaceForTetheringAvailable()
val listener = EthernetStateListener()
addInterfaceStateListener(listener)
val iface = createInterface()
val ifaceName = requestTetheredInterface().expectOnAvailable()
assertEquals(iface.name, ifaceName)
listener.eventuallyExpect(iface, STATE_LINK_UP, ROLE_SERVER)
removeInterface(iface)
// Note: removeInterface already verifies that a STATE_ABSENT, ROLE_NONE callback is
// received, but it can't hurt to explicitly check for it.
listener.expectCallback(iface, STATE_ABSENT, ROLE_NONE)
releaseTetheredInterface()
listener.assertNoCallback()
}
} }