Merge "Refactor requestNetworkAndGenerateTraffic by using synchronized methods" into main
diff --git a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
index 11fc6df..fef085d 100644
--- a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
@@ -41,6 +41,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.app.Instrumentation;
import android.app.usage.NetworkStats;
@@ -68,13 +69,16 @@
import android.util.Log;
import androidx.test.InstrumentationRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.compatibility.common.util.SystemUtil;
import com.android.modules.utils.build.SdkLevel;
+import com.android.testutils.AutoReleaseNetworkCallbackRule;
import com.android.testutils.ConnectivityModuleTest;
import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+import com.android.testutils.RecorderCallback.CallbackEntry;
+import com.android.testutils.TestableNetworkCallback;
import org.junit.After;
import org.junit.Before;
@@ -95,12 +99,18 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;
-@ConnectivityModuleTest
+// TODO: Fix thread leaks in testCallback and annotating with @MonitorThreadLeak.
@AppModeFull(reason = "instant apps cannot be granted USAGE_STATS")
-@RunWith(AndroidJUnit4.class)
+@ConnectivityModuleTest
+@DevSdkIgnoreRunner.RestoreDefaultNetwork
+@RunWith(DevSdkIgnoreRunner.class)
public class NetworkStatsManagerTest {
- @Rule
+ @Rule(order = 1)
public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(Build.VERSION_CODES.Q);
+ @Rule(order = 2)
+ public final AutoReleaseNetworkCallbackRule
+ networkCallbackRule = new AutoReleaseNetworkCallbackRule();
+
private static final String LOG_TAG = "NetworkStatsManagerTest";
private static final String APPOPS_SET_SHELL_COMMAND = "appops set {0} {1} {2}";
@@ -119,12 +129,19 @@
private static final long LONG_TOLERANCE = MINUTE * 120;
private abstract class NetworkInterfaceToTest {
+
+ final TestableNetworkCallback mRequestNetworkCb = new TestableNetworkCallback();
private boolean mMetered;
private boolean mRoaming;
private boolean mIsDefault;
abstract int getNetworkType();
- abstract int getTransportType();
+
+ abstract Network requestNetwork();
+
+ void unrequestNetwork() {
+ networkCallbackRule.unregisterNetworkCallback(mRequestNetworkCb);
+ }
public boolean getMetered() {
return mMetered;
@@ -151,7 +168,13 @@
}
abstract String getSystemFeature();
- abstract String getErrorMessage();
+
+ @NonNull NetworkRequest buildRequestForTransport(int transport) {
+ return new NetworkRequest.Builder()
+ .addTransportType(transport)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+ }
}
private final NetworkInterfaceToTest[] mNetworkInterfacesToTest =
@@ -163,19 +186,20 @@
}
@Override
- public int getTransportType() {
- return NetworkCapabilities.TRANSPORT_WIFI;
+ public Network requestNetwork() {
+ networkCallbackRule.requestNetwork(buildRequestForTransport(
+ NetworkCapabilities.TRANSPORT_WIFI),
+ mRequestNetworkCb, TIMEOUT_MILLIS);
+ return mRequestNetworkCb.expect(CallbackEntry.AVAILABLE,
+ "Wifi network not available. "
+ + "Please ensure the device has working wifi."
+ ).getNetwork();
}
@Override
public String getSystemFeature() {
return PackageManager.FEATURE_WIFI;
}
-
- @Override
- public String getErrorMessage() {
- return " Please make sure you are connected to a WiFi access point.";
- }
},
new NetworkInterfaceToTest() {
@Override
@@ -184,22 +208,20 @@
}
@Override
- public int getTransportType() {
- return NetworkCapabilities.TRANSPORT_CELLULAR;
+ public Network requestNetwork() {
+ networkCallbackRule.requestNetwork(buildRequestForTransport(
+ NetworkCapabilities.TRANSPORT_CELLULAR),
+ mRequestNetworkCb, TIMEOUT_MILLIS);
+ return mRequestNetworkCb.expect(CallbackEntry.AVAILABLE,
+ "Cell network not available. "
+ + "Please ensure the device has working mobile data."
+ ).getNetwork();
}
@Override
public String getSystemFeature() {
return PackageManager.FEATURE_TELEPHONY;
}
-
- @Override
- public String getErrorMessage() {
- return " Please make sure you have added a SIM card with data plan to"
- + " your phone, have enabled data over cellular and in case of"
- + " dual SIM devices, have selected the right SIM "
- + "for data connection.";
- }
}
};
@@ -215,7 +237,22 @@
private String mWriteSettingsMode;
private String mUsageStatsMode;
- private void exerciseRemoteHost(Network network, URL url) throws Exception {
+ // The test host only has IPv4. So on a dual-stack network where IPv6 connects before IPv4,
+ // we need to wait until IPv4 is available or the test will spuriously fail.
+ private static void waitForHostResolution(@NonNull Network network, @NonNull URL url) {
+ for (int i = 0; i < HOST_RESOLUTION_RETRIES; i++) {
+ try {
+ network.getAllByName(url.getHost());
+ return;
+ } catch (UnknownHostException e) {
+ SystemClock.sleep(HOST_RESOLUTION_INTERVAL_MS);
+ }
+ }
+ fail(String.format("%s could not be resolved on network %s (%d attempts %dms apart)",
+ url.getHost(), network, HOST_RESOLUTION_RETRIES, HOST_RESOLUTION_INTERVAL_MS));
+ }
+
+ private void exerciseRemoteHost(@NonNull Network network, @NonNull URL url) throws Exception {
NetworkInfo networkInfo = mCm.getNetworkInfo(network);
if (networkInfo == null) {
Log.w(LOG_TAG, "Network info is null");
@@ -311,97 +348,44 @@
return result.contains("FOREGROUND");
}
- private class NetworkCallback extends ConnectivityManager.NetworkCallback {
- private long mTolerance;
- private URL mUrl;
- public boolean success;
- public boolean metered;
- public boolean roaming;
- public boolean isDefault;
-
- NetworkCallback(long tolerance, URL url) {
- mTolerance = tolerance;
- mUrl = url;
- success = false;
- metered = false;
- roaming = false;
- isDefault = false;
- }
-
- // The test host only has IPv4. So on a dual-stack network where IPv6 connects before IPv4,
- // we need to wait until IPv4 is available or the test will spuriously fail.
- private void waitForHostResolution(Network network) {
- for (int i = 0; i < HOST_RESOLUTION_RETRIES; i++) {
- try {
- network.getAllByName(mUrl.getHost());
- return;
- } catch (UnknownHostException e) {
- SystemClock.sleep(HOST_RESOLUTION_INTERVAL_MS);
- }
- }
- fail(String.format("%s could not be resolved on network %s (%d attempts %dms apart)",
- mUrl.getHost(), network, HOST_RESOLUTION_RETRIES, HOST_RESOLUTION_INTERVAL_MS));
- }
-
- @Override
- public void onAvailable(Network network) {
- try {
- mStartTime = System.currentTimeMillis() - mTolerance;
- isDefault = network.equals(mCm.getActiveNetwork());
- waitForHostResolution(network);
- exerciseRemoteHost(network, mUrl);
- mEndTime = System.currentTimeMillis() + mTolerance;
- success = true;
- metered = !mCm.getNetworkCapabilities(network)
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- roaming = !mCm.getNetworkCapabilities(network)
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
- synchronized (NetworkStatsManagerTest.this) {
- NetworkStatsManagerTest.this.notify();
- }
- } catch (Exception e) {
- Log.w(LOG_TAG, "exercising remote host failed.", e);
- success = false;
- }
- }
- }
-
private boolean shouldTestThisNetworkType(int networkTypeIndex) {
return mPm.hasSystemFeature(mNetworkInterfacesToTest[networkTypeIndex].getSystemFeature());
}
+ @NonNull
+ private Network requestNetworkAndSetAttributes(
+ @NonNull NetworkInterfaceToTest networkInterface) {
+ final Network network = networkInterface.requestNetwork();
+
+ // These attributes are needed when performing NetworkStats queries.
+ // Fetch caps from the first capabilities changed event since the
+ // interested attributes are not mutable, and not expected to be
+ // changed during the test.
+ final NetworkCapabilities caps = networkInterface.mRequestNetworkCb.expect(
+ CallbackEntry.NETWORK_CAPS_UPDATED, network).getCaps();
+ networkInterface.setMetered(!caps.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
+ networkInterface.setRoaming(!caps.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING));
+ networkInterface.setIsDefault(network.equals(mCm.getActiveNetwork()));
+
+ return network;
+ }
+
private void requestNetworkAndGenerateTraffic(int networkTypeIndex, final long tolerance)
throws Exception {
final NetworkInterfaceToTest networkInterface = mNetworkInterfacesToTest[networkTypeIndex];
- final NetworkCallback callback = new NetworkCallback(tolerance,
- new URL(CHECK_CONNECTIVITY_URL));
- mCm.requestNetwork(new NetworkRequest.Builder()
- .addTransportType(networkInterface.getTransportType())
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build(), callback);
- synchronized (this) {
- long now = System.currentTimeMillis();
- final long deadline = (long) (now + TIMEOUT_MILLIS * 2.4);
- while (!callback.success && now < deadline) {
- try {
- wait(deadline - now);
- } catch (InterruptedException e) {
- }
- now = System.currentTimeMillis();
- }
- }
- mCm.unregisterNetworkCallback(callback);
- if (!callback.success) {
- fail(networkInterface.getSystemFeature()
- + " is a reported system feature, however no corresponding "
- + "connected network interface was found or the attempt "
- + "to connect and read has timed out (timeout = " + (TIMEOUT_MILLIS * 2.4)
- + "ms)." + networkInterface.getErrorMessage());
- }
+ final Network network = requestNetworkAndSetAttributes(networkInterface);
- networkInterface.setMetered(callback.metered);
- networkInterface.setRoaming(callback.roaming);
- networkInterface.setIsDefault(callback.isDefault);
+ mStartTime = System.currentTimeMillis() - tolerance;
+ waitForHostResolution(network, new URL(CHECK_CONNECTIVITY_URL));
+ exerciseRemoteHost(network, new URL(CHECK_CONNECTIVITY_URL));
+ mEndTime = System.currentTimeMillis() + tolerance;
+
+ // It is fine if the test fails and this line is not reached.
+ // The AutoReleaseNetworkCallbackRule will eventually release
+ // all unwanted callbacks.
+ networkInterface.unrequestNetwork();
}
private String getSubscriberId(int networkIndex) {