Merge "Test tethered callback with TetheringInterface" am: c4d26414f8

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/1722733

Change-Id: If6863c282c259c3fbc6fd3a3f8fc7e4b2510ee23
This commit is contained in:
Treehugger Robot
2021-06-03 09:10:47 +00:00
committed by Automerger Merge Worker
4 changed files with 149 additions and 61 deletions

View File

@@ -80,6 +80,7 @@ import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -349,7 +350,7 @@ public class EthernetTetheringTest {
private final CountDownLatch mLocalOnlyStartedLatch = new CountDownLatch(1);
private final CountDownLatch mLocalOnlyStoppedLatch = new CountDownLatch(1);
private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1);
private final String mIface;
private final TetheringInterface mIface;
private volatile boolean mInterfaceWasTethered = false;
private volatile boolean mInterfaceWasLocalOnly = false;
@@ -358,20 +359,24 @@ public class EthernetTetheringTest {
MyTetheringEventCallback(TetheringManager tm, String iface) {
mTm = tm;
mIface = iface;
mIface = new TetheringInterface(TETHERING_ETHERNET, iface);
}
public void unregister() {
mTm.unregisterTetheringEventCallback(this);
mUnregistered = true;
}
@Override
public void onTetheredInterfacesChanged(List<String> interfaces) {
fail("Should only call callback that takes a Set<TetheringInterface>");
}
@Override
public void onTetheredInterfacesChanged(Set<TetheringInterface> interfaces) {
// Ignore stale callbacks registered by previous test cases.
if (mUnregistered) return;
if (!mInterfaceWasTethered && (mIface == null || interfaces.contains(mIface))) {
if (!mInterfaceWasTethered && interfaces.contains(mIface)) {
// This interface is being tethered for the first time.
Log.d(TAG, "Tethering started: " + interfaces);
mInterfaceWasTethered = true;
@@ -384,10 +389,15 @@ public class EthernetTetheringTest {
@Override
public void onLocalOnlyInterfacesChanged(List<String> interfaces) {
fail("Should only call callback that takes a Set<TetheringInterface>");
}
@Override
public void onLocalOnlyInterfacesChanged(Set<TetheringInterface> interfaces) {
// Ignore stale callbacks registered by previous test cases.
if (mUnregistered) return;
if (!mInterfaceWasLocalOnly && (mIface == null || interfaces.contains(mIface))) {
if (!mInterfaceWasLocalOnly && interfaces.contains(mIface)) {
// This interface is being put into local-only mode for the first time.
Log.d(TAG, "Local-only started: " + interfaces);
mInterfaceWasLocalOnly = true;

View File

@@ -20,12 +20,12 @@ import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.READ_DEVICE_CONFIG;
import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.Manifest.permission.WRITE_SETTINGS;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
@@ -35,6 +35,7 @@ import android.app.UiAutomation;
import android.content.Context;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.TetheringInterface;
import android.net.TetheringManager;
import android.net.cts.util.CtsTetheringUtils;
import android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
@@ -102,12 +103,13 @@ public class TetheringModuleTest {
try {
tetherEventCallback.assumeTetheringSupported();
assumeTrue(isWifiTetheringSupported(tetherEventCallback));
tetherEventCallback.expectNoTetheringActive();
final TetheringInterface tetheredIface =
mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
final List<String> tetheredIfaces = tetherEventCallback.getTetheredInterfaces();
assertEquals(1, tetheredIfaces.size());
final String wifiTetheringIface = tetheredIfaces.get(0);
assertNotNull(tetheredIface);
final String wifiTetheringIface = tetheredIface.getInterface();
NetworkInterface nif = NetworkInterface.getByName(wifiTetheringIface);
// Tethering downstream only have one ipv4 address.
@@ -120,11 +122,11 @@ public class TetheringModuleTest {
tnt = setUpTestNetwork(
new LinkAddress(testPrefix.getAddress(), testPrefix.getPrefixLength()));
tetherEventCallback.expectTetheredInterfacesChanged(null);
tetherEventCallback.expectNoTetheringActive();
final List<String> wifiRegexs =
tetherEventCallback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs);
tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
nif = NetworkInterface.getByName(wifiTetheringIface);
final LinkAddress newHotspotAddr = getFirstIpv4Address(nif);
assertNotNull(newHotspotAddr);

View File

@@ -22,7 +22,7 @@ import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -35,6 +35,7 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.Network;
import android.net.TetheredClient;
import android.net.TetheringInterface;
import android.net.TetheringManager;
import android.net.TetheringManager.TetheringEventCallback;
import android.net.TetheringManager.TetheringInterfaceRegexps;
@@ -51,6 +52,7 @@ import com.android.net.module.util.ArrayTrackRecord;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public final class CtsTetheringUtils {
private TetheringManager mTm;
@@ -116,25 +118,40 @@ public final class CtsTetheringUtils {
}
}
public static boolean isIfaceMatch(final List<String> ifaceRegexs, final List<String> ifaces) {
return isIfaceMatch(ifaceRegexs.toArray(new String[0]), ifaces);
}
public static boolean isIfaceMatch(final String[] ifaceRegexs, final List<String> ifaces) {
private static boolean isRegexMatch(final String[] ifaceRegexs, String iface) {
if (ifaceRegexs == null) fail("ifaceRegexs should not be null");
for (String regex : ifaceRegexs) {
if (iface.matches(regex)) return true;
}
return false;
}
public static boolean isAnyIfaceMatch(final String[] ifaceRegexs, final List<String> ifaces) {
if (ifaces == null) return false;
for (String s : ifaces) {
for (String regex : ifaceRegexs) {
if (s.matches(regex)) {
return true;
}
}
if (isRegexMatch(ifaceRegexs, s)) return true;
}
return false;
}
private static TetheringInterface getFirstMatchingTetheringInterface(final List<String> regexs,
final int type, final Set<TetheringInterface> ifaces) {
if (ifaces == null || regexs == null) return null;
final String[] regexArray = regexs.toArray(new String[0]);
for (TetheringInterface iface : ifaces) {
if (isRegexMatch(regexArray, iface.getInterface()) && type == iface.getType()) {
return iface;
}
}
return null;
}
// Must poll the callback before looking at the member.
public static class TestTetheringEventCallback implements TetheringEventCallback {
private static final int TIMEOUT_MS = 30_000;
@@ -171,6 +188,8 @@ public final class CtsTetheringUtils {
private TetheringInterfaceRegexps mTetherableRegex;
private List<String> mTetherableIfaces;
private List<String> mTetheredIfaces;
private String mErrorIface;
private int mErrorCode;
@Override
public void onTetheringSupported(boolean supported) {
@@ -191,17 +210,41 @@ public final class CtsTetheringUtils {
@Override
public void onTetherableInterfacesChanged(List<String> interfaces) {
mTetherableIfaces = interfaces;
}
// Call the interface default implementation, which will call
// onTetherableInterfacesChanged(List<String>). This ensures that the default implementation
// of the new callback method calls the old callback method and avoids the need to convert
// Set<TetheringInterface> to List<String> in this code.
@Override
public void onTetherableInterfacesChanged(Set<TetheringInterface> interfaces) {
TetheringEventCallback.super.onTetherableInterfacesChanged(interfaces);
assertHasAllTetheringInterfaces(interfaces, mTetherableIfaces);
mHistory.add(new CallbackValue(CallbackType.ON_TETHERABLE_IFACES, interfaces, 0));
}
@Override
public void onTetheredInterfacesChanged(List<String> interfaces) {
mTetheredIfaces = interfaces;
}
@Override
public void onTetheredInterfacesChanged(Set<TetheringInterface> interfaces) {
TetheringEventCallback.super.onTetheredInterfacesChanged(interfaces);
assertHasAllTetheringInterfaces(interfaces, mTetheredIfaces);
mHistory.add(new CallbackValue(CallbackType.ON_TETHERED_IFACES, interfaces, 0));
}
@Override
public void onError(String ifName, int error) {
mErrorIface = ifName;
mErrorCode = error;
}
@Override
public void onError(TetheringInterface ifName, int error) {
TetheringEventCallback.super.onError(ifName, error);
assertEquals(ifName.getInterface(), mErrorIface);
assertEquals(error, mErrorCode);
mHistory.add(new CallbackValue(CallbackType.ON_ERROR, ifName, error));
}
@@ -215,30 +258,66 @@ public final class CtsTetheringUtils {
mHistory.add(new CallbackValue(CallbackType.ON_OFFLOAD_STATUS, status, 0));
}
public void expectTetherableInterfacesChanged(@NonNull List<String> regexs) {
private void assertHasAllTetheringInterfaces(Set<TetheringInterface> tetheringIfaces,
List<String> ifaces) {
// This does not check that the interfaces are the same. This checks that the
// List<String> has all the interface names contained by the Set<TetheringInterface>.
assertEquals(tetheringIfaces.size(), ifaces.size());
for (TetheringInterface tether : tetheringIfaces) {
assertTrue("iface " + tether.getInterface()
+ " seen by new callback but not old callback",
ifaces.contains(tether.getInterface()));
}
}
public void expectTetherableInterfacesChanged(@NonNull final List<String> regexs,
final int type) {
assertNotNull("No expected tetherable ifaces callback", mCurrent.poll(TIMEOUT_MS,
(cv) -> {
if (cv.callbackType != CallbackType.ON_TETHERABLE_IFACES) return false;
final List<String> interfaces = (List<String>) cv.callbackParam;
return isIfaceMatch(regexs, interfaces);
final Set<TetheringInterface> interfaces =
(Set<TetheringInterface>) cv.callbackParam;
return getFirstMatchingTetheringInterface(regexs, type, interfaces) != null;
}));
}
public void expectTetheredInterfacesChanged(@NonNull List<String> regexs) {
assertNotNull("No expected tethered ifaces callback", mCurrent.poll(TIMEOUT_MS,
(cv) -> {
public void expectNoTetheringActive() {
assertNotNull("At least one tethering type unexpectedly active",
mCurrent.poll(TIMEOUT_MS, (cv) -> {
if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) return false;
final List<String> interfaces = (List<String>) cv.callbackParam;
// Null regexs means no active tethering.
if (regexs == null) return interfaces.isEmpty();
return isIfaceMatch(regexs, interfaces);
return ((Set<TetheringInterface>) cv.callbackParam).isEmpty();
}));
}
public TetheringInterface expectTetheredInterfacesChanged(
@NonNull final List<String> regexs, final int type) {
while (true) {
final CallbackValue cv = mCurrent.poll(TIMEOUT_MS, c -> true);
if (cv == null) {
fail("No expected tethered ifaces callback, expected type: " + type);
}
if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) continue;
final Set<TetheringInterface> interfaces =
(Set<TetheringInterface>) cv.callbackParam;
final TetheringInterface iface =
getFirstMatchingTetheringInterface(regexs, type, interfaces);
if (iface != null) return iface;
}
}
public void expectCallbackStarted() {
// This method uses its own readhead because it just check whether last tethering status
// is updated after TetheringEventCallback get registered but do not check content
// of received callbacks. Using shared readhead (mCurrent) only when the callbacks the
// method polled is also not necessary for other methods which using shared readhead.
// All of methods using mCurrent is order mattered.
final ArrayTrackRecord<CallbackValue>.ReadHead history =
mHistory.newReadHead();
int receivedBitMap = 0;
// The each bit represent a type from CallbackType.ON_*.
// Expect all of callbacks except for ON_ERROR.
@@ -246,7 +325,7 @@ public final class CtsTetheringUtils {
// Receive ON_ERROR on started callback is not matter. It just means tethering is
// failed last time, should able to continue the test this time.
while ((receivedBitMap & expectedBitMap) != expectedBitMap) {
final CallbackValue cv = mCurrent.poll(TIMEOUT_MS, c -> true);
final CallbackValue cv = history.poll(TIMEOUT_MS, c -> true);
if (cv == null) {
fail("No expected callbacks, " + "expected bitmap: "
+ expectedBitMap + ", actual: " + receivedBitMap);
@@ -269,14 +348,14 @@ public final class CtsTetheringUtils {
}));
}
public void expectErrorOrTethered(final String iface) {
public void expectErrorOrTethered(final TetheringInterface iface) {
assertNotNull("No expected callback", mCurrent.poll(TIMEOUT_MS, (cv) -> {
if (cv.callbackType == CallbackType.ON_ERROR
&& iface.equals((String) cv.callbackParam)) {
&& iface.equals((TetheringInterface) cv.callbackParam)) {
return true;
}
if (cv.callbackType == CallbackType.ON_TETHERED_IFACES
&& ((List<String>) cv.callbackParam).contains(iface)) {
&& ((Set<TetheringInterface>) cv.callbackParam).contains(iface)) {
return true;
}
@@ -328,14 +407,6 @@ public final class CtsTetheringUtils {
public TetheringInterfaceRegexps getTetheringInterfaceRegexps() {
return mTetherableRegex;
}
public List<String> getTetherableInterfaces() {
return mTetherableIfaces;
}
public List<String> getTetheredInterfaces() {
return mTetheredIfaces;
}
}
private static void waitForWifiEnabled(final Context ctx) throws Exception {
@@ -386,10 +457,9 @@ public final class CtsTetheringUtils {
return !getWifiTetherableInterfaceRegexps(callback).isEmpty();
}
public void startWifiTethering(final TestTetheringEventCallback callback)
public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback)
throws InterruptedException {
final List<String> wifiRegexs = getWifiTetherableInterfaceRegexps(callback);
assertFalse(isIfaceMatch(wifiRegexs, callback.getTetheredInterfaces()));
final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
@@ -397,11 +467,14 @@ public final class CtsTetheringUtils {
mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
startTetheringCallback.verifyTetheringStarted();
callback.expectTetheredInterfacesChanged(wifiRegexs);
final TetheringInterface iface =
callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
callback.expectOneOfOffloadStatusChanged(
TETHER_HARDWARE_OFFLOAD_STARTED,
TETHER_HARDWARE_OFFLOAD_FAILED);
return iface;
}
private static class StopSoftApCallback implements SoftApCallback {
@@ -441,7 +514,7 @@ public final class CtsTetheringUtils {
public void stopWifiTethering(final TestTetheringEventCallback callback) {
mTm.stopTethering(TETHERING_WIFI);
expectSoftApDisabled();
callback.expectTetheredInterfacesChanged(null);
callback.expectNoTetheringActive();
callback.expectOneOfOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
}
}

View File

@@ -26,8 +26,7 @@ import static android.net.TetheringManager.TETHERING_WIFI_P2P;
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.cts.util.CtsTetheringUtils.isIfaceMatch;
import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported;
import static android.net.cts.util.CtsTetheringUtils.isAnyIfaceMatch;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -48,6 +47,7 @@ import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.TetheringInterface;
import android.net.TetheringManager;
import android.net.TetheringManager.OnTetheringEntitlementResultListener;
import android.net.TetheringManager.TetheringInterfaceRegexps;
@@ -190,13 +190,13 @@ public class TetheringManagerTest {
}
private boolean isIfaceActive(final String[] ifaceRegexs, final TetherState state) {
return isIfaceMatch(ifaceRegexs, state.mActive);
return isAnyIfaceMatch(ifaceRegexs, state.mActive);
}
private void assertNoErroredIfaces(final TetherState state, final String[] ifaceRegexs) {
if (state == null || state.mErrored == null) return;
if (isIfaceMatch(ifaceRegexs, state.mErrored)) {
if (isAnyIfaceMatch(ifaceRegexs, state.mErrored)) {
fail("Found failed tethering interfaces: " + Arrays.toString(state.mErrored.toArray()));
}
}
@@ -256,12 +256,13 @@ public class TetheringManagerTest {
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
tetherEventCallback.expectNoTetheringActive();
final TetheringInterface tetheredIface =
mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
final List<String> tetheredIfaces = tetherEventCallback.getTetheredInterfaces();
assertEquals(1, tetheredIfaces.size());
final String wifiTetheringIface = tetheredIfaces.get(0);
assertNotNull(tetheredIface);
final String wifiTetheringIface = tetheredIface.getInterface();
mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);
@@ -272,7 +273,8 @@ public class TetheringManagerTest {
if (ret == TETHER_ERROR_NO_ERROR) {
// If calling #tether successful, there is a callback to tell the result of
// tethering setup.
tetherEventCallback.expectErrorOrTethered(wifiTetheringIface);
tetherEventCallback.expectErrorOrTethered(
new TetheringInterface(TETHERING_WIFI, wifiTetheringIface));
}
} finally {
mTM.untether(wifiTetheringIface);
@@ -319,7 +321,7 @@ public class TetheringManagerTest {
mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
mTM.stopAllTethering();
tetherEventCallback.expectTetheredInterfacesChanged(null);
tetherEventCallback.expectNoTetheringActive();
} finally {
mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
}
@@ -417,6 +419,7 @@ public class TetheringManagerTest {
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
tetherEventCallback.expectNoTetheringActive();
previousWifiEnabledState = mWm.isWifiEnabled();
if (previousWifiEnabledState) {