Add support for caching networks based on NetworkCallback
This change adds a cache for networks in the SimpleNetworkCallback that is already registered, allowing the cache to be used preferentially as opposed to the deprecated getAllNetworks call. This is a fork of https://webrtc-review.googlesource.com/c/src/+/251401 - adds field trials for new behavior - removes test that did not work - add (poor) test of field trials - remove the "network_monitor_java" build target (that I could not find any reference to...) Bug: webrtc:13741 Change-Id: I2829c2f1940d4b42455d8e1a2217cf15c133e22b Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/252284 Reviewed-by: Xavier Lepaul <xalep@webrtc.org> Commit-Queue: Jonas Oreland <jonaso@webrtc.org> Cr-Commit-Position: refs/heads/main@{#36121}
This commit is contained in:

committed by
WebRTC LUCI CQ

parent
b663cfaae4
commit
3e64739a76
@ -537,24 +537,6 @@ if (is_android) {
|
|||||||
"//third_party/androidx:androidx_annotation_annotation_java",
|
"//third_party/androidx:androidx_annotation_annotation_java",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc_android_library("network_monitor_java") {
|
|
||||||
visibility = [ "*" ]
|
|
||||||
sources = [
|
|
||||||
"api/org/webrtc/NetworkChangeDetector.java",
|
|
||||||
"api/org/webrtc/NetworkChangeDetectorFactory.java",
|
|
||||||
"api/org/webrtc/NetworkMonitor.java",
|
|
||||||
"api/org/webrtc/NetworkMonitorAutoDetect.java",
|
|
||||||
]
|
|
||||||
deps = [
|
|
||||||
":base_java",
|
|
||||||
":logging_java",
|
|
||||||
"//rtc_base:base_java",
|
|
||||||
"//third_party/android_deps:com_android_support_support_annotations_java",
|
|
||||||
"//third_party/androidx:androidx_annotation_annotation_java",
|
|
||||||
]
|
|
||||||
srcjar_deps = [ "//rtc_base:network_monitor_enums" ]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_os == "linux" || is_android) {
|
if (current_os == "linux" || is_android) {
|
||||||
|
@ -29,13 +29,18 @@ import android.net.wifi.p2p.WifiP2pGroup;
|
|||||||
import android.net.wifi.p2p.WifiP2pManager;
|
import android.net.wifi.p2p.WifiP2pManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
|
import androidx.annotation.GuardedBy;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.NetworkInterface;
|
import java.net.NetworkInterface;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Borrowed from Chromium's
|
* Borrowed from Chromium's
|
||||||
@ -87,16 +92,23 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver implements Netwo
|
|||||||
return underlyingNetworkSubtypeForVpn;
|
return underlyingNetworkSubtypeForVpn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* The methods in this class get called when the network changes if the callback
|
|
||||||
* is registered with a proper network request. It is only available in Android Lollipop
|
|
||||||
* and above.
|
|
||||||
*/
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
private class SimpleNetworkCallback extends NetworkCallback {
|
@VisibleForTesting()
|
||||||
|
class SimpleNetworkCallback extends NetworkCallback {
|
||||||
|
@GuardedBy("availableNetworks") final Set<Network> availableNetworks;
|
||||||
|
|
||||||
|
SimpleNetworkCallback(Set<Network> availableNetworks) {
|
||||||
|
this.availableNetworks = availableNetworks;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAvailable(Network network) {
|
public void onAvailable(Network network) {
|
||||||
Logging.d(TAG, "Network becomes available: " + network.toString());
|
Logging.d(TAG, "Network becomes available: " + network.toString());
|
||||||
|
|
||||||
|
synchronized (availableNetworks) {
|
||||||
|
availableNetworks.add(network);
|
||||||
|
}
|
||||||
onNetworkChanged(network);
|
onNetworkChanged(network);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +142,10 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver implements Netwo
|
|||||||
@Override
|
@Override
|
||||||
public void onLost(Network network) {
|
public void onLost(Network network) {
|
||||||
Logging.d(TAG, "Network " + network.toString() + " is disconnected");
|
Logging.d(TAG, "Network " + network.toString() + " is disconnected");
|
||||||
|
|
||||||
|
synchronized (availableNetworks) {
|
||||||
|
availableNetworks.remove(network);
|
||||||
|
}
|
||||||
observer.onNetworkDisconnect(networkToNetId(network));
|
observer.onNetworkDisconnect(networkToNetId(network));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,15 +165,40 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver implements Netwo
|
|||||||
*/
|
*/
|
||||||
@Nullable private final ConnectivityManager connectivityManager;
|
@Nullable private final ConnectivityManager connectivityManager;
|
||||||
|
|
||||||
ConnectivityManagerDelegate(Context context) {
|
/**
|
||||||
connectivityManager =
|
* Note: The availableNetworks set is instantiated in NetworkMonitorAutoDetect
|
||||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
* and the instance is mutated by SimpleNetworkCallback.
|
||||||
|
*/
|
||||||
|
@NonNull @GuardedBy("availableNetworks") private final Set<Network> availableNetworks;
|
||||||
|
|
||||||
|
/** field trials */
|
||||||
|
private final boolean getAllNetworksFromCache;
|
||||||
|
private final boolean requestVPN;
|
||||||
|
private final boolean includeOtherUidNetworks;
|
||||||
|
|
||||||
|
ConnectivityManagerDelegate(Context context, Set<Network> availableNetworks) {
|
||||||
|
this((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
|
||||||
|
availableNetworks,
|
||||||
|
PeerConnectionFactory.fieldTrialsFindFullName("WebRTC-NetworkMonitorAutoDetect"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// For testing.
|
@VisibleForTesting
|
||||||
ConnectivityManagerDelegate() {
|
ConnectivityManagerDelegate(ConnectivityManager connectivityManager,
|
||||||
// All the methods below should be overridden.
|
Set<Network> availableNetworks, String fieldTrials) {
|
||||||
connectivityManager = null;
|
this.connectivityManager = connectivityManager;
|
||||||
|
this.availableNetworks = availableNetworks;
|
||||||
|
this.getAllNetworksFromCache = checkFieldTrial(fieldTrials, "getAllNetworksFromCache", false);
|
||||||
|
this.requestVPN = checkFieldTrial(fieldTrials, "requestVPN", false);
|
||||||
|
this.includeOtherUidNetworks = checkFieldTrial(fieldTrials, "includeOtherUidNetworks", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkFieldTrial(String fieldTrials, String key, boolean defaultValue) {
|
||||||
|
if (fieldTrials.contains(key + "/Enabled")) {
|
||||||
|
return true;
|
||||||
|
} else if (fieldTrials.contains(key + "/Disabled")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -265,6 +306,13 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver implements Netwo
|
|||||||
if (connectivityManager == null) {
|
if (connectivityManager == null) {
|
||||||
return new Network[0];
|
return new Network[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (supportNetworkCallback() && getAllNetworksFromCache) {
|
||||||
|
synchronized (availableNetworks) {
|
||||||
|
return availableNetworks.toArray(new Network[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return connectivityManager.getAllNetworks();
|
return connectivityManager.getAllNetworks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,14 +433,26 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver implements Netwo
|
|||||||
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
|
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
@VisibleForTesting()
|
||||||
|
NetworkRequest createNetworkRequest() {
|
||||||
|
// Requests the following capabilities by default: NOT_VPN, NOT_RESTRICTED, TRUSTED
|
||||||
|
NetworkRequest.Builder builder =
|
||||||
|
new NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
|
||||||
|
|
||||||
|
if (requestVPN) {
|
||||||
|
builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && includeOtherUidNetworks) {
|
||||||
|
builder.setIncludeOtherUidNetworks(true);
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
/** Only callable on Lollipop and newer releases. */
|
/** Only callable on Lollipop and newer releases. */
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
public void registerNetworkCallback(NetworkCallback networkCallback) {
|
public void registerNetworkCallback(NetworkCallback networkCallback) {
|
||||||
connectivityManager.registerNetworkCallback(
|
connectivityManager.registerNetworkCallback(createNetworkRequest(), networkCallback);
|
||||||
new NetworkRequest.Builder()
|
|
||||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
|
||||||
.build(),
|
|
||||||
networkCallback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Only callable on Lollipop and newer releases. */
|
/** Only callable on Lollipop and newer releases. */
|
||||||
@ -424,7 +484,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver implements Netwo
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean supportNetworkCallback() {
|
public boolean supportNetworkCallback() {
|
||||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && connectivityManager != null;
|
return connectivityManager != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,6 +627,8 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver implements Netwo
|
|||||||
private WifiDirectManagerDelegate wifiDirectManagerDelegate;
|
private WifiDirectManagerDelegate wifiDirectManagerDelegate;
|
||||||
private static boolean includeWifiDirect;
|
private static boolean includeWifiDirect;
|
||||||
|
|
||||||
|
@GuardedBy("availableNetworks") final Set<Network> availableNetworks = new HashSet<>();
|
||||||
|
|
||||||
private boolean isRegistered;
|
private boolean isRegistered;
|
||||||
private NetworkChangeDetector.ConnectionType connectionType;
|
private NetworkChangeDetector.ConnectionType connectionType;
|
||||||
private String wifiSSID;
|
private String wifiSSID;
|
||||||
@ -576,7 +638,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver implements Netwo
|
|||||||
public NetworkMonitorAutoDetect(NetworkChangeDetector.Observer observer, Context context) {
|
public NetworkMonitorAutoDetect(NetworkChangeDetector.Observer observer, Context context) {
|
||||||
this.observer = observer;
|
this.observer = observer;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
connectivityManagerDelegate = new ConnectivityManagerDelegate(context);
|
connectivityManagerDelegate = new ConnectivityManagerDelegate(context, availableNetworks);
|
||||||
wifiManagerDelegate = new WifiManagerDelegate(context);
|
wifiManagerDelegate = new WifiManagerDelegate(context);
|
||||||
|
|
||||||
final NetworkState networkState = connectivityManagerDelegate.getNetworkState();
|
final NetworkState networkState = connectivityManagerDelegate.getNetworkState();
|
||||||
@ -600,7 +662,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver implements Netwo
|
|||||||
tempNetworkCallback = null;
|
tempNetworkCallback = null;
|
||||||
}
|
}
|
||||||
mobileNetworkCallback = tempNetworkCallback;
|
mobileNetworkCallback = tempNetworkCallback;
|
||||||
allNetworkCallback = new SimpleNetworkCallback();
|
allNetworkCallback = new SimpleNetworkCallback(availableNetworks);
|
||||||
connectivityManagerDelegate.registerNetworkCallback(allNetworkCallback);
|
connectivityManagerDelegate.registerNetworkCallback(allNetworkCallback);
|
||||||
} else {
|
} else {
|
||||||
mobileNetworkCallback = null;
|
mobileNetworkCallback = null;
|
||||||
|
@ -10,17 +10,22 @@
|
|||||||
|
|
||||||
package org.webrtc;
|
package org.webrtc;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Mockito.CALLS_REAL_METHODS;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
|
import android.net.NetworkCapabilities;
|
||||||
|
import android.net.NetworkRequest;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@ -28,7 +33,11 @@ import android.support.test.InstrumentationRegistry;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.test.filters.MediumTest;
|
import androidx.test.filters.MediumTest;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import org.chromium.base.test.BaseJUnit4ClassRunner;
|
import org.chromium.base.test.BaseJUnit4ClassRunner;
|
||||||
import org.chromium.base.test.UiThreadTest;
|
import org.chromium.base.test.UiThreadTest;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@ -39,6 +48,7 @@ import org.webrtc.NetworkChangeDetector.ConnectionType;
|
|||||||
import org.webrtc.NetworkChangeDetector.NetworkInformation;
|
import org.webrtc.NetworkChangeDetector.NetworkInformation;
|
||||||
import org.webrtc.NetworkMonitorAutoDetect.ConnectivityManagerDelegate;
|
import org.webrtc.NetworkMonitorAutoDetect.ConnectivityManagerDelegate;
|
||||||
import org.webrtc.NetworkMonitorAutoDetect.NetworkState;
|
import org.webrtc.NetworkMonitorAutoDetect.NetworkState;
|
||||||
|
import org.webrtc.NetworkMonitorAutoDetect.SimpleNetworkCallback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for org.webrtc.NetworkMonitor.
|
* Tests for org.webrtc.NetworkMonitor.
|
||||||
@ -83,6 +93,14 @@ public class NetworkMonitorTest {
|
|||||||
private int underlyingNetworkTypeForVpn;
|
private int underlyingNetworkTypeForVpn;
|
||||||
private int underlyingNetworkSubtypeForVpn;
|
private int underlyingNetworkSubtypeForVpn;
|
||||||
|
|
||||||
|
MockConnectivityManagerDelegate() {
|
||||||
|
this(new HashSet<>(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
MockConnectivityManagerDelegate(Set<Network> availableNetworks, String fieldTrialsString) {
|
||||||
|
super(null, availableNetworks, fieldTrialsString);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NetworkState getNetworkState() {
|
public NetworkState getNetworkState() {
|
||||||
return new NetworkState(activeNetworkExists, networkType, networkSubtype,
|
return new NetworkState(activeNetworkExists, networkType, networkSubtype,
|
||||||
@ -275,8 +293,8 @@ public class NetworkMonitorTest {
|
|||||||
@UiThreadTest
|
@UiThreadTest
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public void testConnectivityManagerDelegateDoesNotCrash() {
|
public void testConnectivityManagerDelegateDoesNotCrash() {
|
||||||
ConnectivityManagerDelegate delegate =
|
ConnectivityManagerDelegate delegate = new ConnectivityManagerDelegate(
|
||||||
new ConnectivityManagerDelegate(InstrumentationRegistry.getTargetContext());
|
InstrumentationRegistry.getTargetContext(), new HashSet<>());
|
||||||
delegate.getNetworkState();
|
delegate.getNetworkState();
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
Network[] networks = delegate.getAllNetworks();
|
Network[] networks = delegate.getAllNetworks();
|
||||||
@ -288,6 +306,72 @@ public class NetworkMonitorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Tests that ConnectivityManagerDelegate preferentially reads from the cache */
|
||||||
|
@Test
|
||||||
|
@SmallTest
|
||||||
|
public void testConnectivityManagerDelegatePreferentiallyReadsFromCache() {
|
||||||
|
final Set<Network> availableNetworks = new HashSet<>();
|
||||||
|
ConnectivityManagerDelegate delegate = new ConnectivityManagerDelegate(
|
||||||
|
(ConnectivityManager) InstrumentationRegistry.getTargetContext().getSystemService(
|
||||||
|
Context.CONNECTIVITY_SERVICE),
|
||||||
|
availableNetworks, "getAllNetworksFromCache/Enabled/");
|
||||||
|
|
||||||
|
Network[] networks = delegate.getAllNetworks();
|
||||||
|
assertTrue(networks.length == 0);
|
||||||
|
|
||||||
|
final Network mockNetwork = mock(Network.class);
|
||||||
|
availableNetworks.add(mockNetwork);
|
||||||
|
|
||||||
|
assertArrayEquals(new Network[] {mockNetwork}, delegate.getAllNetworks());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests field trial parsing */
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SmallTest
|
||||||
|
public void testConnectivityManager_requestVPN_disabled() {
|
||||||
|
NetworkRequest request = getNetworkRequestForFieldTrials("requestVPN/Disabled");
|
||||||
|
assertTrue(request.equals(new NetworkRequest.Builder()
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||||
|
.build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SmallTest
|
||||||
|
public void testConnectivityManager_requestVPN_enabled() {
|
||||||
|
NetworkRequest request = getNetworkRequestForFieldTrials("requestVPN/Enabled");
|
||||||
|
assertTrue(request.equals(new NetworkRequest.Builder()
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||||
|
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
|
||||||
|
.build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SmallTest
|
||||||
|
public void testConnectivityManager_includeOtherUidNetworks_disabled() {
|
||||||
|
NetworkRequest request = getNetworkRequestForFieldTrials("includeOtherUidNetworks/Disabled");
|
||||||
|
assertTrue(request.equals(new NetworkRequest.Builder()
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||||
|
.build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SmallTest
|
||||||
|
public void testConnectivityManager_includeOtherUidNetworks_enabled() {
|
||||||
|
NetworkRequest request = getNetworkRequestForFieldTrials("includeOtherUidNetworks/Enabled");
|
||||||
|
NetworkRequest.Builder builder =
|
||||||
|
new NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
builder.setIncludeOtherUidNetworks(true);
|
||||||
|
}
|
||||||
|
assertTrue(request.equals(builder.build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private NetworkRequest getNetworkRequestForFieldTrials(String fieldTrials) {
|
||||||
|
return new ConnectivityManagerDelegate(null, new HashSet<>(), fieldTrials)
|
||||||
|
.createNetworkRequest();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that NetworkMonitorAutoDetect queryable APIs don't crash. This test cannot rely
|
* Tests that NetworkMonitorAutoDetect queryable APIs don't crash. This test cannot rely
|
||||||
* on having any active network connections so it cannot usefully check results, but it can at
|
* on having any active network connections so it cannot usefully check results, but it can at
|
||||||
|
Reference in New Issue
Block a user