Make TestConnectivityManager usable by other tethering tests.
Currently, this class is a static inner class of UpstreamNetworkMonitorTest. Extract it to its own top-level class so it can be used by other tests. Bug: 173068192 Test: atest TetheringTests Change-Id: I6bdb090a99781ac2530b3924ac5c4cf78de315b0
This commit is contained in:
parent
bc1bdf4a85
commit
d025c562f0
2 changed files with 250 additions and 196 deletions
|
|
@ -0,0 +1,247 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.networkstack.tethering;
|
||||
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.IConnectivityManager;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkRequest;
|
||||
import android.os.Handler;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Simulates upstream switching and sending NetworkCallbacks and CONNECTIVITY_ACTION broadcasts.
|
||||
*
|
||||
* TODO: this duplicates a fair amount of code from ConnectivityManager and ConnectivityService.
|
||||
* Consider using a ConnectivityService object instead, as used in ConnectivityServiceTest.
|
||||
*
|
||||
* Things to consider:
|
||||
* - ConnectivityService uses a real handler for realism, and these test use TestLooper (or even
|
||||
* invoke callbacks directly inline) for determinism. Using a real ConnectivityService would
|
||||
* require adding dispatchAll() calls and migrating to handlers.
|
||||
* - ConnectivityService does not provide a way to order CONNECTIVITY_ACTION before or after the
|
||||
* NetworkCallbacks for the same network change. That ability is useful because the upstream
|
||||
* selection code in Tethering is vulnerable to race conditions, due to its reliance on multiple
|
||||
* separate NetworkCallbacks and BroadcastReceivers, each of which trigger different types of
|
||||
* updates. If/when the upstream selection code is refactored to a more level-triggered model
|
||||
* (e.g., with an idempotent function that takes into account all state every time any part of
|
||||
* that state changes), this may become less important or unnecessary.
|
||||
*/
|
||||
public class TestConnectivityManager extends ConnectivityManager {
|
||||
public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>();
|
||||
public Set<NetworkCallback> trackingDefault = new HashSet<>();
|
||||
public TestNetworkAgent defaultNetwork = null;
|
||||
public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
|
||||
public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
|
||||
public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>();
|
||||
|
||||
private final NetworkRequest mDefaultRequest;
|
||||
private int mNetworkId = 100;
|
||||
|
||||
public TestConnectivityManager(Context ctx, IConnectivityManager svc,
|
||||
NetworkRequest defaultRequest) {
|
||||
super(ctx, svc);
|
||||
mDefaultRequest = defaultRequest;
|
||||
}
|
||||
|
||||
boolean hasNoCallbacks() {
|
||||
return allCallbacks.isEmpty()
|
||||
&& trackingDefault.isEmpty()
|
||||
&& listening.isEmpty()
|
||||
&& requested.isEmpty()
|
||||
&& legacyTypeMap.isEmpty();
|
||||
}
|
||||
|
||||
boolean onlyHasDefaultCallbacks() {
|
||||
return (allCallbacks.size() == 1)
|
||||
&& (trackingDefault.size() == 1)
|
||||
&& listening.isEmpty()
|
||||
&& requested.isEmpty()
|
||||
&& legacyTypeMap.isEmpty();
|
||||
}
|
||||
|
||||
boolean isListeningForAll() {
|
||||
final NetworkCapabilities empty = new NetworkCapabilities();
|
||||
empty.clearAll();
|
||||
|
||||
for (NetworkRequest req : listening.values()) {
|
||||
if (req.networkCapabilities.equalRequestableCapabilities(empty)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int getNetworkId() {
|
||||
return ++mNetworkId;
|
||||
}
|
||||
|
||||
void makeDefaultNetwork(TestNetworkAgent agent) {
|
||||
if (Objects.equals(defaultNetwork, agent)) return;
|
||||
|
||||
final TestNetworkAgent formerDefault = defaultNetwork;
|
||||
defaultNetwork = agent;
|
||||
|
||||
for (NetworkCallback cb : trackingDefault) {
|
||||
if (defaultNetwork != null) {
|
||||
cb.onAvailable(defaultNetwork.networkId);
|
||||
cb.onCapabilitiesChanged(
|
||||
defaultNetwork.networkId, defaultNetwork.networkCapabilities);
|
||||
cb.onLinkPropertiesChanged(
|
||||
defaultNetwork.networkId, defaultNetwork.linkProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
|
||||
assertFalse(allCallbacks.containsKey(cb));
|
||||
allCallbacks.put(cb, h);
|
||||
if (mDefaultRequest.equals(req)) {
|
||||
assertFalse(trackingDefault.contains(cb));
|
||||
trackingDefault.add(cb);
|
||||
} else {
|
||||
assertFalse(requested.containsKey(cb));
|
||||
requested.put(cb, req);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestNetwork(NetworkRequest req, NetworkCallback cb) {
|
||||
fail("Should never be called.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestNetwork(NetworkRequest req,
|
||||
int timeoutMs, int legacyType, Handler h, NetworkCallback cb) {
|
||||
assertFalse(allCallbacks.containsKey(cb));
|
||||
allCallbacks.put(cb, h);
|
||||
assertFalse(requested.containsKey(cb));
|
||||
requested.put(cb, req);
|
||||
assertFalse(legacyTypeMap.containsKey(cb));
|
||||
if (legacyType != ConnectivityManager.TYPE_NONE) {
|
||||
legacyTypeMap.put(cb, legacyType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h) {
|
||||
assertFalse(allCallbacks.containsKey(cb));
|
||||
allCallbacks.put(cb, h);
|
||||
assertFalse(listening.containsKey(cb));
|
||||
listening.put(cb, req);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) {
|
||||
fail("Should never be called.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) {
|
||||
fail("Should never be called.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDefaultNetworkCallback(NetworkCallback cb) {
|
||||
fail("Should never be called.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterNetworkCallback(NetworkCallback cb) {
|
||||
if (trackingDefault.contains(cb)) {
|
||||
trackingDefault.remove(cb);
|
||||
} else if (listening.containsKey(cb)) {
|
||||
listening.remove(cb);
|
||||
} else if (requested.containsKey(cb)) {
|
||||
requested.remove(cb);
|
||||
legacyTypeMap.remove(cb);
|
||||
} else {
|
||||
fail("Unexpected callback removed");
|
||||
}
|
||||
allCallbacks.remove(cb);
|
||||
|
||||
assertFalse(allCallbacks.containsKey(cb));
|
||||
assertFalse(trackingDefault.contains(cb));
|
||||
assertFalse(listening.containsKey(cb));
|
||||
assertFalse(requested.containsKey(cb));
|
||||
}
|
||||
|
||||
public static class TestNetworkAgent {
|
||||
public final TestConnectivityManager cm;
|
||||
public final Network networkId;
|
||||
public final int transportType;
|
||||
public final NetworkCapabilities networkCapabilities;
|
||||
public final LinkProperties linkProperties;
|
||||
|
||||
public TestNetworkAgent(TestConnectivityManager cm, int transportType) {
|
||||
this.cm = cm;
|
||||
this.networkId = new Network(cm.getNetworkId());
|
||||
this.transportType = transportType;
|
||||
networkCapabilities = new NetworkCapabilities();
|
||||
networkCapabilities.addTransportType(transportType);
|
||||
networkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
|
||||
linkProperties = new LinkProperties();
|
||||
}
|
||||
|
||||
public void fakeConnect() {
|
||||
for (NetworkCallback cb : cm.listening.keySet()) {
|
||||
cb.onAvailable(networkId);
|
||||
cb.onCapabilitiesChanged(networkId, copy(networkCapabilities));
|
||||
cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
|
||||
}
|
||||
}
|
||||
|
||||
public void fakeDisconnect() {
|
||||
for (NetworkCallback cb : cm.listening.keySet()) {
|
||||
cb.onLost(networkId);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendLinkProperties() {
|
||||
for (NetworkCallback cb : cm.listening.keySet()) {
|
||||
cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("TestNetworkAgent: %s %s", networkId, networkCapabilities);
|
||||
}
|
||||
}
|
||||
|
||||
static NetworkCapabilities copy(NetworkCapabilities nc) {
|
||||
return new NetworkCapabilities(nc);
|
||||
}
|
||||
|
||||
static LinkProperties copy(LinkProperties lp) {
|
||||
return new LinkProperties(lp);
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,6 @@ import static com.android.networkstack.tethering.UpstreamNetworkMonitor.TYPE_NON
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
|
|
@ -48,7 +47,6 @@ import android.net.IConnectivityManager;
|
|||
import android.net.IpPrefix;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkRequest;
|
||||
import android.net.util.SharedLog;
|
||||
|
|
@ -60,6 +58,7 @@ import androidx.test.runner.AndroidJUnit4;
|
|||
|
||||
import com.android.internal.util.State;
|
||||
import com.android.internal.util.StateMachine;
|
||||
import com.android.networkstack.tethering.TestConnectivityManager.TestNetworkAgent;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
|
@ -71,10 +70,7 @@ import org.mockito.MockitoAnnotations;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
|
|
@ -106,7 +102,7 @@ public class UpstreamNetworkMonitorTest {
|
|||
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
|
||||
when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
|
||||
|
||||
mCM = spy(new TestConnectivityManager(mContext, mCS));
|
||||
mCM = spy(new TestConnectivityManager(mContext, mCS, sDefaultRequest));
|
||||
mSM = new TestStateMachine();
|
||||
mUNM = new UpstreamNetworkMonitor(
|
||||
(ConnectivityManager) mCM, mSM, mLog, EVENT_UNM_UPDATE);
|
||||
|
|
@ -567,187 +563,6 @@ public class UpstreamNetworkMonitorTest {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static class TestConnectivityManager extends ConnectivityManager {
|
||||
public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>();
|
||||
public Set<NetworkCallback> trackingDefault = new HashSet<>();
|
||||
public TestNetworkAgent defaultNetwork = null;
|
||||
public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
|
||||
public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
|
||||
public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>();
|
||||
|
||||
private int mNetworkId = 100;
|
||||
|
||||
public TestConnectivityManager(Context ctx, IConnectivityManager svc) {
|
||||
super(ctx, svc);
|
||||
}
|
||||
|
||||
boolean hasNoCallbacks() {
|
||||
return allCallbacks.isEmpty()
|
||||
&& trackingDefault.isEmpty()
|
||||
&& listening.isEmpty()
|
||||
&& requested.isEmpty()
|
||||
&& legacyTypeMap.isEmpty();
|
||||
}
|
||||
|
||||
boolean onlyHasDefaultCallbacks() {
|
||||
return (allCallbacks.size() == 1)
|
||||
&& (trackingDefault.size() == 1)
|
||||
&& listening.isEmpty()
|
||||
&& requested.isEmpty()
|
||||
&& legacyTypeMap.isEmpty();
|
||||
}
|
||||
|
||||
boolean isListeningForAll() {
|
||||
final NetworkCapabilities empty = new NetworkCapabilities();
|
||||
empty.clearAll();
|
||||
|
||||
for (NetworkRequest req : listening.values()) {
|
||||
if (req.networkCapabilities.equalRequestableCapabilities(empty)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int getNetworkId() {
|
||||
return ++mNetworkId;
|
||||
}
|
||||
|
||||
void makeDefaultNetwork(TestNetworkAgent agent) {
|
||||
if (Objects.equals(defaultNetwork, agent)) return;
|
||||
|
||||
final TestNetworkAgent formerDefault = defaultNetwork;
|
||||
defaultNetwork = agent;
|
||||
|
||||
for (NetworkCallback cb : trackingDefault) {
|
||||
if (defaultNetwork != null) {
|
||||
cb.onAvailable(defaultNetwork.networkId);
|
||||
cb.onCapabilitiesChanged(
|
||||
defaultNetwork.networkId, defaultNetwork.networkCapabilities);
|
||||
cb.onLinkPropertiesChanged(
|
||||
defaultNetwork.networkId, defaultNetwork.linkProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
|
||||
assertFalse(allCallbacks.containsKey(cb));
|
||||
allCallbacks.put(cb, h);
|
||||
if (sDefaultRequest.equals(req)) {
|
||||
assertFalse(trackingDefault.contains(cb));
|
||||
trackingDefault.add(cb);
|
||||
} else {
|
||||
assertFalse(requested.containsKey(cb));
|
||||
requested.put(cb, req);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestNetwork(NetworkRequest req, NetworkCallback cb) {
|
||||
fail("Should never be called.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestNetwork(NetworkRequest req,
|
||||
int timeoutMs, int legacyType, Handler h, NetworkCallback cb) {
|
||||
assertFalse(allCallbacks.containsKey(cb));
|
||||
allCallbacks.put(cb, h);
|
||||
assertFalse(requested.containsKey(cb));
|
||||
requested.put(cb, req);
|
||||
assertFalse(legacyTypeMap.containsKey(cb));
|
||||
if (legacyType != ConnectivityManager.TYPE_NONE) {
|
||||
legacyTypeMap.put(cb, legacyType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h) {
|
||||
assertFalse(allCallbacks.containsKey(cb));
|
||||
allCallbacks.put(cb, h);
|
||||
assertFalse(listening.containsKey(cb));
|
||||
listening.put(cb, req);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) {
|
||||
fail("Should never be called.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) {
|
||||
fail("Should never be called.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDefaultNetworkCallback(NetworkCallback cb) {
|
||||
fail("Should never be called.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterNetworkCallback(NetworkCallback cb) {
|
||||
if (trackingDefault.contains(cb)) {
|
||||
trackingDefault.remove(cb);
|
||||
} else if (listening.containsKey(cb)) {
|
||||
listening.remove(cb);
|
||||
} else if (requested.containsKey(cb)) {
|
||||
requested.remove(cb);
|
||||
legacyTypeMap.remove(cb);
|
||||
} else {
|
||||
fail("Unexpected callback removed");
|
||||
}
|
||||
allCallbacks.remove(cb);
|
||||
|
||||
assertFalse(allCallbacks.containsKey(cb));
|
||||
assertFalse(trackingDefault.contains(cb));
|
||||
assertFalse(listening.containsKey(cb));
|
||||
assertFalse(requested.containsKey(cb));
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestNetworkAgent {
|
||||
public final TestConnectivityManager cm;
|
||||
public final Network networkId;
|
||||
public final int transportType;
|
||||
public final NetworkCapabilities networkCapabilities;
|
||||
public final LinkProperties linkProperties;
|
||||
|
||||
public TestNetworkAgent(TestConnectivityManager cm, int transportType) {
|
||||
this.cm = cm;
|
||||
this.networkId = new Network(cm.getNetworkId());
|
||||
this.transportType = transportType;
|
||||
networkCapabilities = new NetworkCapabilities();
|
||||
networkCapabilities.addTransportType(transportType);
|
||||
networkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
|
||||
linkProperties = new LinkProperties();
|
||||
}
|
||||
|
||||
public void fakeConnect() {
|
||||
for (NetworkCallback cb : cm.listening.keySet()) {
|
||||
cb.onAvailable(networkId);
|
||||
cb.onCapabilitiesChanged(networkId, copy(networkCapabilities));
|
||||
cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
|
||||
}
|
||||
}
|
||||
|
||||
public void fakeDisconnect() {
|
||||
for (NetworkCallback cb : cm.listening.keySet()) {
|
||||
cb.onLost(networkId);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendLinkProperties() {
|
||||
for (NetworkCallback cb : cm.listening.keySet()) {
|
||||
cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("TestNetworkAgent: %s %s", networkId, networkCapabilities);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestStateMachine extends StateMachine {
|
||||
public final ArrayList<Message> messages = new ArrayList<>();
|
||||
private final State mLoggingState = new LoggingState();
|
||||
|
|
@ -775,14 +590,6 @@ public class UpstreamNetworkMonitorTest {
|
|||
}
|
||||
}
|
||||
|
||||
static NetworkCapabilities copy(NetworkCapabilities nc) {
|
||||
return new NetworkCapabilities(nc);
|
||||
}
|
||||
|
||||
static LinkProperties copy(LinkProperties lp) {
|
||||
return new LinkProperties(lp);
|
||||
}
|
||||
|
||||
static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected) {
|
||||
final Set<String> expectedSet = new HashSet<>();
|
||||
Collections.addAll(expectedSet, expected);
|
||||
|
|
@ -797,4 +604,4 @@ public class UpstreamNetworkMonitorTest {
|
|||
expectation, prefixes.contains(new IpPrefix(expectedPrefix)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue