Merge "Prefer default Internet network for upstream tethering." into pi-dev
am: 1e1635a45e
Change-Id: I9cddf1fb7aa3b8d56bf048c563556244e74808c2
This commit is contained in:
@@ -491,13 +491,21 @@
|
||||
|
||||
Note also: the order of this is important. The first upstream type
|
||||
for which a satisfying network exists is used.
|
||||
-->
|
||||
-->
|
||||
<integer-array translatable="false" name="config_tether_upstream_types">
|
||||
<item>1</item>
|
||||
<item>7</item>
|
||||
<item>0</item>
|
||||
</integer-array>
|
||||
|
||||
<!-- When true, the tethering upstream network follows the current default
|
||||
Internet network (except when the current default network is mobile,
|
||||
in which case a DUN network will be used if required).
|
||||
|
||||
When true, overrides the config_tether_upstream_types setting above.
|
||||
-->
|
||||
<bool translatable="false" name="config_tether_upstream_automatic">false</bool>
|
||||
|
||||
<!-- If the DUN connection for this CDMA device supports more than just DUN -->
|
||||
<!-- traffic you should list them here. -->
|
||||
<!-- If this device is not CDMA this is ignored. If this list is empty on -->
|
||||
|
||||
@@ -1828,6 +1828,7 @@
|
||||
<java-symbol type="array" name="config_tether_bluetooth_regexs" />
|
||||
<java-symbol type="array" name="config_tether_dhcp_range" />
|
||||
<java-symbol type="array" name="config_tether_upstream_types" />
|
||||
<java-symbol type="bool" name="config_tether_upstream_automatic" />
|
||||
<java-symbol type="array" name="config_tether_apndata" />
|
||||
<java-symbol type="array" name="config_tether_usb_regexs" />
|
||||
<java-symbol type="array" name="config_tether_wifi_regexs" />
|
||||
|
||||
@@ -898,6 +898,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
public boolean isTetheringSupported() {
|
||||
return ConnectivityService.this.isTetheringSupported();
|
||||
}
|
||||
@Override
|
||||
public NetworkRequest getDefaultNetworkRequest() {
|
||||
return mDefaultRequest;
|
||||
}
|
||||
};
|
||||
return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
|
||||
IoThread.get().getLooper(), new MockableSystemProperties(),
|
||||
@@ -915,7 +919,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
|
||||
private NetworkRequest createDefaultInternetRequestForTransport(
|
||||
int transportType, NetworkRequest.Type type) {
|
||||
NetworkCapabilities netCap = new NetworkCapabilities();
|
||||
final NetworkCapabilities netCap = new NetworkCapabilities();
|
||||
netCap.addCapability(NET_CAPABILITY_INTERNET);
|
||||
netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
|
||||
if (transportType > -1) {
|
||||
|
||||
@@ -1350,8 +1350,11 @@ public class Tethering extends BaseNetworkObserver {
|
||||
// do not currently know how to watch for changes in DUN settings.
|
||||
maybeUpdateConfiguration();
|
||||
|
||||
final NetworkState ns = mUpstreamNetworkMonitor.selectPreferredUpstreamType(
|
||||
mConfig.preferredUpstreamIfaceTypes);
|
||||
final TetheringConfiguration config = mConfig;
|
||||
final NetworkState ns = (config.chooseUpstreamAutomatically)
|
||||
? mUpstreamNetworkMonitor.getCurrentPreferredUpstream()
|
||||
: mUpstreamNetworkMonitor.selectPreferredUpstreamType(
|
||||
config.preferredUpstreamIfaceTypes);
|
||||
if (ns == null) {
|
||||
if (tryCell) {
|
||||
mUpstreamNetworkMonitor.registerMobileNetworkRequest();
|
||||
@@ -1380,9 +1383,7 @@ public class Tethering extends BaseNetworkObserver {
|
||||
}
|
||||
notifyDownstreamsOfNewUpstreamIface(ifaces);
|
||||
if (ns != null && pertainsToCurrentUpstream(ns)) {
|
||||
// If we already have NetworkState for this network examine
|
||||
// it immediately, because there likely will be no second
|
||||
// EVENT_ON_AVAILABLE (it was already received).
|
||||
// If we already have NetworkState for this network update it immediately.
|
||||
handleNewUpstreamNetworkState(ns);
|
||||
} else if (mCurrentUpstreamIfaceSet == null) {
|
||||
// There are no available upstream networks.
|
||||
@@ -1498,15 +1499,6 @@ public class Tethering extends BaseNetworkObserver {
|
||||
}
|
||||
|
||||
switch (arg1) {
|
||||
case UpstreamNetworkMonitor.EVENT_ON_AVAILABLE:
|
||||
// The default network changed, or DUN connected
|
||||
// before this callback was processed. Updates
|
||||
// for the current NetworkCapabilities and
|
||||
// LinkProperties have been requested (default
|
||||
// request) or are being sent shortly (DUN). Do
|
||||
// nothing until they arrive; if no updates
|
||||
// arrive there's nothing to do.
|
||||
break;
|
||||
case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
|
||||
handleNewUpstreamNetworkState(ns);
|
||||
break;
|
||||
@@ -1539,7 +1531,7 @@ public class Tethering extends BaseNetworkObserver {
|
||||
}
|
||||
|
||||
mSimChange.startListening();
|
||||
mUpstreamNetworkMonitor.start();
|
||||
mUpstreamNetworkMonitor.start(mDeps.getDefaultNetworkRequest());
|
||||
|
||||
// TODO: De-duplicate with updateUpstreamWanted() below.
|
||||
if (upstreamWanted()) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import static com.android.internal.R.array.config_tether_dhcp_range;
|
||||
import static com.android.internal.R.array.config_tether_usb_regexs;
|
||||
import static com.android.internal.R.array.config_tether_upstream_types;
|
||||
import static com.android.internal.R.array.config_tether_wifi_regexs;
|
||||
import static com.android.internal.R.bool.config_tether_upstream_automatic;
|
||||
import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -86,6 +87,7 @@ public class TetheringConfiguration {
|
||||
public final String[] tetherableBluetoothRegexs;
|
||||
public final int dunCheck;
|
||||
public final boolean isDunRequired;
|
||||
public final boolean chooseUpstreamAutomatically;
|
||||
public final Collection<Integer> preferredUpstreamIfaceTypes;
|
||||
public final String[] dhcpRanges;
|
||||
public final String[] defaultIPv4DNS;
|
||||
@@ -106,6 +108,7 @@ public class TetheringConfiguration {
|
||||
dunCheck = checkDunRequired(ctx);
|
||||
configLog.log("DUN check returned: " + dunCheckString(dunCheck));
|
||||
|
||||
chooseUpstreamAutomatically = getResourceBoolean(ctx, config_tether_upstream_automatic);
|
||||
preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck);
|
||||
isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN);
|
||||
|
||||
@@ -142,6 +145,8 @@ public class TetheringConfiguration {
|
||||
pw.print("isDunRequired: ");
|
||||
pw.println(isDunRequired);
|
||||
|
||||
pw.print("chooseUpstreamAutomatically: ");
|
||||
pw.println(chooseUpstreamAutomatically);
|
||||
dumpStringArray(pw, "preferredUpstreamIfaceTypes",
|
||||
preferredUpstreamNames(preferredUpstreamIfaceTypes));
|
||||
|
||||
@@ -160,6 +165,7 @@ public class TetheringConfiguration {
|
||||
sj.add(String.format("tetherableBluetoothRegexs:%s",
|
||||
makeString(tetherableBluetoothRegexs)));
|
||||
sj.add(String.format("isDunRequired:%s", isDunRequired));
|
||||
sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically));
|
||||
sj.add(String.format("preferredUpstreamIfaceTypes:%s",
|
||||
makeString(preferredUpstreamNames(preferredUpstreamIfaceTypes))));
|
||||
sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
|
||||
@@ -286,6 +292,14 @@ public class TetheringConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean getResourceBoolean(Context ctx, int resId) {
|
||||
try {
|
||||
return ctx.getResources().getBoolean(resId);
|
||||
} catch (Resources.NotFoundException e404) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] getResourceStringArray(Context ctx, int resId) {
|
||||
try {
|
||||
final String[] strArray = ctx.getResources().getStringArray(resId);
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.connectivity.tethering;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.INetd;
|
||||
import android.net.NetworkRequest;
|
||||
import android.net.ip.RouterAdvertisementDaemon;
|
||||
import android.net.util.InterfaceParams;
|
||||
import android.net.util.NetdService;
|
||||
@@ -64,4 +65,8 @@ public class TetheringDependencies {
|
||||
public boolean isTetheringSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public NetworkRequest getDefaultNetworkRequest() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ import static android.net.ConnectivityManager.getNetworkTypeName;
|
||||
import static android.net.ConnectivityManager.TYPE_NONE;
|
||||
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
|
||||
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
@@ -74,14 +77,13 @@ public class UpstreamNetworkMonitor {
|
||||
private static final boolean DBG = false;
|
||||
private static final boolean VDBG = false;
|
||||
|
||||
public static final int EVENT_ON_AVAILABLE = 1;
|
||||
public static final int EVENT_ON_CAPABILITIES = 2;
|
||||
public static final int EVENT_ON_LINKPROPERTIES = 3;
|
||||
public static final int EVENT_ON_LOST = 4;
|
||||
public static final int EVENT_ON_CAPABILITIES = 1;
|
||||
public static final int EVENT_ON_LINKPROPERTIES = 2;
|
||||
public static final int EVENT_ON_LOST = 3;
|
||||
public static final int NOTIFY_LOCAL_PREFIXES = 10;
|
||||
|
||||
private static final int CALLBACK_LISTEN_ALL = 1;
|
||||
private static final int CALLBACK_TRACK_DEFAULT = 2;
|
||||
private static final int CALLBACK_DEFAULT_INTERNET = 2;
|
||||
private static final int CALLBACK_MOBILE_REQUEST = 3;
|
||||
|
||||
private final Context mContext;
|
||||
@@ -117,7 +119,7 @@ public class UpstreamNetworkMonitor {
|
||||
mCM = cm;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
public void start(NetworkRequest defaultNetworkRequest) {
|
||||
stop();
|
||||
|
||||
final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
|
||||
@@ -125,8 +127,16 @@ public class UpstreamNetworkMonitor {
|
||||
mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
|
||||
cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
|
||||
|
||||
mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_TRACK_DEFAULT);
|
||||
cm().registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
|
||||
if (defaultNetworkRequest != null) {
|
||||
// This is not really a "request", just a way of tracking the system default network.
|
||||
// It's guaranteed not to actually bring up any networks because it's the same request
|
||||
// as the ConnectivityService default request, and thus shares fate with it. We can't
|
||||
// use registerDefaultNetworkCallback because it will not track the system default
|
||||
// network if there is a VPN that applies to our UID.
|
||||
final NetworkRequest trackDefaultRequest = new NetworkRequest(defaultNetworkRequest);
|
||||
mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
|
||||
cm().requestNetwork(trackDefaultRequest, mDefaultNetworkCallback, mHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
@@ -225,6 +235,20 @@ public class UpstreamNetworkMonitor {
|
||||
return typeStatePair.ns;
|
||||
}
|
||||
|
||||
// Returns null if no current upstream available.
|
||||
public NetworkState getCurrentPreferredUpstream() {
|
||||
final NetworkState dfltState = (mDefaultInternetNetwork != null)
|
||||
? mNetworkMap.get(mDefaultInternetNetwork)
|
||||
: null;
|
||||
if (!mDunRequired) return dfltState;
|
||||
|
||||
if (isNetworkUsableAndNotCellular(dfltState)) return dfltState;
|
||||
|
||||
// Find a DUN network. Note that code in Tethering causes a DUN request
|
||||
// to be filed, but this might be moved into this class in future.
|
||||
return findFirstDunNetwork(mNetworkMap.values());
|
||||
}
|
||||
|
||||
public void setCurrentUpstream(Network upstream) {
|
||||
mTetheringUpstreamNetwork = upstream;
|
||||
}
|
||||
@@ -233,72 +257,16 @@ public class UpstreamNetworkMonitor {
|
||||
return (Set<IpPrefix>) mLocalPrefixes.clone();
|
||||
}
|
||||
|
||||
private void handleAvailable(int callbackType, Network network) {
|
||||
if (VDBG) Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
|
||||
private void handleAvailable(Network network) {
|
||||
if (mNetworkMap.containsKey(network)) return;
|
||||
|
||||
if (!mNetworkMap.containsKey(network)) {
|
||||
mNetworkMap.put(network,
|
||||
new NetworkState(null, null, null, network, null, null));
|
||||
}
|
||||
|
||||
// Always request whatever extra information we can, in case this
|
||||
// was already up when start() was called, in which case we would
|
||||
// not have been notified of any information that had not changed.
|
||||
switch (callbackType) {
|
||||
case CALLBACK_LISTEN_ALL:
|
||||
break;
|
||||
|
||||
case CALLBACK_TRACK_DEFAULT:
|
||||
if (mDefaultNetworkCallback == null) {
|
||||
// The callback was unregistered in the interval between
|
||||
// ConnectivityService enqueueing onAvailable() and our
|
||||
// handling of it here on the mHandler thread.
|
||||
//
|
||||
// Clean-up of this network entry is deferred to the
|
||||
// handling of onLost() by other callbacks.
|
||||
//
|
||||
// These request*() calls can be deleted post oag/339444.
|
||||
return;
|
||||
}
|
||||
mDefaultInternetNetwork = network;
|
||||
break;
|
||||
|
||||
case CALLBACK_MOBILE_REQUEST:
|
||||
if (mMobileNetworkCallback == null) {
|
||||
// The callback was unregistered in the interval between
|
||||
// ConnectivityService enqueueing onAvailable() and our
|
||||
// handling of it here on the mHandler thread.
|
||||
//
|
||||
// Clean-up of this network entry is deferred to the
|
||||
// handling of onLost() by other callbacks.
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Requesting updates for mListenAllCallback is not currently possible
|
||||
// because it's a "listen". Two possible solutions to getting updates
|
||||
// about networks without waiting for a change (which might never come)
|
||||
// are:
|
||||
//
|
||||
// [1] extend request{NetworkCapabilities,LinkProperties}() to
|
||||
// take a Network argument and have ConnectivityService do
|
||||
// what's required (if the network satisfies the request)
|
||||
//
|
||||
// [2] explicitly file a NetworkRequest for each connectivity type
|
||||
// listed as a preferred upstream and wait for these callbacks
|
||||
// to be notified (requires tracking many more callbacks).
|
||||
//
|
||||
// Until this is addressed, networks that exist prior to the "listen"
|
||||
// registration and which do not subsequently change will not cause
|
||||
// us to learn their NetworkCapabilities nor their LinkProperties.
|
||||
|
||||
// TODO: If sufficient information is available to select a more
|
||||
// preferable upstream, do so now and notify the target.
|
||||
notifyTarget(EVENT_ON_AVAILABLE, network);
|
||||
if (VDBG) Log.d(TAG, "onAvailable for " + network);
|
||||
mNetworkMap.put(network, new NetworkState(null, null, null, network, null, null));
|
||||
}
|
||||
|
||||
private void handleNetCap(Network network, NetworkCapabilities newNc) {
|
||||
private void handleNetCap(int callbackType, Network network, NetworkCapabilities newNc) {
|
||||
if (callbackType == CALLBACK_DEFAULT_INTERNET) mDefaultInternetNetwork = network;
|
||||
|
||||
final NetworkState prev = mNetworkMap.get(network);
|
||||
if (prev == null || newNc.equals(prev.networkCapabilities)) {
|
||||
// Ignore notifications about networks for which we have not yet
|
||||
@@ -360,13 +328,17 @@ public class UpstreamNetworkMonitor {
|
||||
}
|
||||
|
||||
private void handleLost(int callbackType, Network network) {
|
||||
if (callbackType == CALLBACK_TRACK_DEFAULT) {
|
||||
if (network.equals(mDefaultInternetNetwork)) {
|
||||
mDefaultInternetNetwork = null;
|
||||
// Receiving onLost() for a default network does not necessarily
|
||||
// mean the network is gone. We wait for a separate notification
|
||||
// on either the LISTEN_ALL or MOBILE_REQUEST callbacks before
|
||||
// clearing all state.
|
||||
return;
|
||||
// There are few TODOs within ConnectivityService's rematching code
|
||||
// pertaining to spurious onLost() notifications.
|
||||
//
|
||||
// TODO: simplify this, probably if favor of code that:
|
||||
// - selects a new upstream if mTetheringUpstreamNetwork has
|
||||
// been lost (by any callback)
|
||||
// - deletes the entry from the map only when the LISTEN_ALL
|
||||
// callback gets notified.
|
||||
if (callbackType == CALLBACK_DEFAULT_INTERNET) return;
|
||||
}
|
||||
|
||||
if (!mNetworkMap.containsKey(network)) {
|
||||
@@ -416,17 +388,19 @@ public class UpstreamNetworkMonitor {
|
||||
|
||||
@Override
|
||||
public void onAvailable(Network network) {
|
||||
handleAvailable(mCallbackType, network);
|
||||
handleAvailable(network);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
|
||||
handleNetCap(network, newNc);
|
||||
handleNetCap(mCallbackType, network, newNc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
|
||||
handleLinkProp(network, newLp);
|
||||
// TODO(b/110335330): reduce the number of times this is called by
|
||||
// only recomputing on the LISTEN_ALL callback.
|
||||
recomputeLocalPrefixes();
|
||||
}
|
||||
|
||||
@@ -443,6 +417,8 @@ public class UpstreamNetworkMonitor {
|
||||
@Override
|
||||
public void onLost(Network network) {
|
||||
handleLost(mCallbackType, network);
|
||||
// TODO(b/110335330): reduce the number of times this is called by
|
||||
// only recomputing on the LISTEN_ALL callback.
|
||||
recomputeLocalPrefixes();
|
||||
}
|
||||
}
|
||||
@@ -509,4 +485,31 @@ public class UpstreamNetworkMonitor {
|
||||
if (nc == null || !nc.hasSignalStrength()) return "unknown";
|
||||
return Integer.toString(nc.getSignalStrength());
|
||||
}
|
||||
|
||||
private static boolean isCellular(NetworkState ns) {
|
||||
return (ns != null) && isCellular(ns.networkCapabilities);
|
||||
}
|
||||
|
||||
private static boolean isCellular(NetworkCapabilities nc) {
|
||||
return (nc != null) && nc.hasTransport(TRANSPORT_CELLULAR) &&
|
||||
nc.hasCapability(NET_CAPABILITY_NOT_VPN);
|
||||
}
|
||||
|
||||
private static boolean hasCapability(NetworkState ns, int netCap) {
|
||||
return (ns != null) && (ns.networkCapabilities != null) &&
|
||||
ns.networkCapabilities.hasCapability(netCap);
|
||||
}
|
||||
|
||||
private static boolean isNetworkUsableAndNotCellular(NetworkState ns) {
|
||||
return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null) &&
|
||||
!isCellular(ns.networkCapabilities);
|
||||
}
|
||||
|
||||
private static NetworkState findFirstDunNetwork(Iterable<NetworkState> netStates) {
|
||||
for (NetworkState ns : netStates) {
|
||||
if (isCellular(ns) && hasCapability(ns, NET_CAPABILITY_DUN)) return ns;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ import android.net.MacAddress;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.NetworkRequest;
|
||||
import android.net.NetworkState;
|
||||
import android.net.NetworkUtils;
|
||||
import android.net.RouteInfo;
|
||||
@@ -128,6 +129,10 @@ public class TetheringTest {
|
||||
private static final String TEST_USB_IFNAME = "test_rndis0";
|
||||
private static final String TEST_WLAN_IFNAME = "test_wlan0";
|
||||
|
||||
// Actual contents of the request don't matter for this test. The lack of
|
||||
// any specific TRANSPORT_* is sufficient to identify this request.
|
||||
private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
|
||||
|
||||
@Mock private ApplicationInfo mApplicationInfo;
|
||||
@Mock private Context mContext;
|
||||
@Mock private INetworkManagementService mNMService;
|
||||
@@ -238,6 +243,11 @@ public class TetheringTest {
|
||||
isTetheringSupportedCalls++;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkRequest getDefaultNetworkRequest() {
|
||||
return mDefaultRequest;
|
||||
}
|
||||
}
|
||||
|
||||
private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
|
||||
@@ -305,6 +315,8 @@ public class TetheringTest {
|
||||
.thenReturn(new String[0]);
|
||||
when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
|
||||
.thenReturn(new int[0]);
|
||||
when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
|
||||
.thenReturn(false);
|
||||
when(mNMService.listInterfaces())
|
||||
.thenReturn(new String[] {
|
||||
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME});
|
||||
@@ -458,6 +470,7 @@ public class TetheringTest {
|
||||
}
|
||||
|
||||
private void prepareUsbTethering(NetworkState upstreamState) {
|
||||
when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
|
||||
when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
|
||||
.thenReturn(upstreamState);
|
||||
|
||||
@@ -519,7 +532,7 @@ public class TetheringTest {
|
||||
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
|
||||
verifyNoMoreInteractions(mWifiManager);
|
||||
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
|
||||
verify(mUpstreamNetworkMonitor, times(1)).start();
|
||||
verify(mUpstreamNetworkMonitor, times(1)).start(any(NetworkRequest.class));
|
||||
// TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
|
||||
assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
|
||||
|
||||
@@ -655,6 +668,24 @@ public class TetheringTest {
|
||||
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception {
|
||||
when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
|
||||
.thenReturn(true);
|
||||
sendConfigurationChanged();
|
||||
|
||||
// Setup IPv6
|
||||
final NetworkState upstreamState = buildMobileIPv6UpstreamState();
|
||||
runUsbTethering(upstreamState);
|
||||
|
||||
// UpstreamNetworkMonitor should choose upstream automatically
|
||||
// (in this specific case: choose the default network).
|
||||
verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream();
|
||||
verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any());
|
||||
|
||||
verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception {
|
||||
workingLocalOnlyHotspotEnrichedApBroadcast(true);
|
||||
@@ -718,7 +749,7 @@ public class TetheringTest {
|
||||
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
|
||||
verifyNoMoreInteractions(mWifiManager);
|
||||
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
|
||||
verify(mUpstreamNetworkMonitor, times(1)).start();
|
||||
verify(mUpstreamNetworkMonitor, times(1)).start(any(NetworkRequest.class));
|
||||
// In tethering mode, in the default configuration, an explicit request
|
||||
// for a mobile network is also made.
|
||||
verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
|
||||
|
||||
@@ -31,6 +31,7 @@ import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -73,6 +74,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
@@ -84,6 +86,10 @@ public class UpstreamNetworkMonitorTest {
|
||||
private static final boolean INCLUDES = true;
|
||||
private static final boolean EXCLUDES = false;
|
||||
|
||||
// Actual contents of the request don't matter for this test. The lack of
|
||||
// any specific TRANSPORT_* is sufficient to identify this request.
|
||||
private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
|
||||
|
||||
@Mock private Context mContext;
|
||||
@Mock private IConnectivityManager mCS;
|
||||
@Mock private SharedLog mLog;
|
||||
@@ -112,6 +118,13 @@ public class UpstreamNetworkMonitorTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopWithoutStartIsNonFatal() {
|
||||
mUNM.stop();
|
||||
mUNM.stop();
|
||||
mUNM.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNothingBeforeStarted() {
|
||||
assertTrue(mCM.hasNoCallbacks());
|
||||
@@ -127,7 +140,7 @@ public class UpstreamNetworkMonitorTest {
|
||||
public void testDefaultNetworkIsTracked() throws Exception {
|
||||
assertEquals(0, mCM.trackingDefault.size());
|
||||
|
||||
mUNM.start();
|
||||
mUNM.start(mDefaultRequest);
|
||||
assertEquals(1, mCM.trackingDefault.size());
|
||||
|
||||
mUNM.stop();
|
||||
@@ -138,7 +151,7 @@ public class UpstreamNetworkMonitorTest {
|
||||
public void testListensForAllNetworks() throws Exception {
|
||||
assertTrue(mCM.listening.isEmpty());
|
||||
|
||||
mUNM.start();
|
||||
mUNM.start(mDefaultRequest);
|
||||
assertFalse(mCM.listening.isEmpty());
|
||||
assertTrue(mCM.isListeningForAll());
|
||||
|
||||
@@ -148,9 +161,11 @@ public class UpstreamNetworkMonitorTest {
|
||||
|
||||
@Test
|
||||
public void testCallbacksRegistered() {
|
||||
mUNM.start();
|
||||
verify(mCM, times(1)).registerNetworkCallback(any(), any(), any());
|
||||
verify(mCM, times(1)).registerDefaultNetworkCallback(any(), any());
|
||||
mUNM.start(mDefaultRequest);
|
||||
verify(mCM, times(1)).registerNetworkCallback(
|
||||
any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
|
||||
verify(mCM, times(1)).requestNetwork(
|
||||
eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
|
||||
|
||||
mUNM.stop();
|
||||
verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
|
||||
@@ -161,7 +176,7 @@ public class UpstreamNetworkMonitorTest {
|
||||
assertFalse(mUNM.mobileNetworkRequested());
|
||||
assertEquals(0, mCM.requested.size());
|
||||
|
||||
mUNM.start();
|
||||
mUNM.start(mDefaultRequest);
|
||||
assertFalse(mUNM.mobileNetworkRequested());
|
||||
assertEquals(0, mCM.requested.size());
|
||||
|
||||
@@ -184,17 +199,17 @@ public class UpstreamNetworkMonitorTest {
|
||||
assertFalse(mUNM.mobileNetworkRequested());
|
||||
assertEquals(0, mCM.requested.size());
|
||||
|
||||
mUNM.start();
|
||||
verify(mCM, Mockito.times(1)).registerNetworkCallback(
|
||||
mUNM.start(mDefaultRequest);
|
||||
verify(mCM, times(1)).registerNetworkCallback(
|
||||
any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
|
||||
verify(mCM, Mockito.times(1)).registerDefaultNetworkCallback(
|
||||
any(NetworkCallback.class), any(Handler.class));
|
||||
verify(mCM, times(1)).requestNetwork(
|
||||
eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
|
||||
assertFalse(mUNM.mobileNetworkRequested());
|
||||
assertEquals(0, mCM.requested.size());
|
||||
|
||||
mUNM.updateMobileRequiresDun(true);
|
||||
mUNM.registerMobileNetworkRequest();
|
||||
verify(mCM, Mockito.times(1)).requestNetwork(
|
||||
verify(mCM, times(1)).requestNetwork(
|
||||
any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt(),
|
||||
any(Handler.class));
|
||||
|
||||
@@ -222,7 +237,7 @@ public class UpstreamNetworkMonitorTest {
|
||||
assertFalse(mUNM.mobileNetworkRequested());
|
||||
assertEquals(0, mCM.requested.size());
|
||||
|
||||
mUNM.start();
|
||||
mUNM.start(mDefaultRequest);
|
||||
assertFalse(mUNM.mobileNetworkRequested());
|
||||
assertEquals(0, mCM.requested.size());
|
||||
|
||||
@@ -242,7 +257,7 @@ public class UpstreamNetworkMonitorTest {
|
||||
|
||||
@Test
|
||||
public void testUpdateMobileRequiresDun() throws Exception {
|
||||
mUNM.start();
|
||||
mUNM.start(mDefaultRequest);
|
||||
|
||||
// Test going from no-DUN to DUN correctly re-registers callbacks.
|
||||
mUNM.updateMobileRequiresDun(false);
|
||||
@@ -270,7 +285,7 @@ public class UpstreamNetworkMonitorTest {
|
||||
final Collection<Integer> preferredTypes = new ArrayList<>();
|
||||
preferredTypes.add(TYPE_WIFI);
|
||||
|
||||
mUNM.start();
|
||||
mUNM.start(mDefaultRequest);
|
||||
// There are no networks, so there is nothing to select.
|
||||
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
|
||||
|
||||
@@ -333,9 +348,48 @@ public class UpstreamNetworkMonitorTest {
|
||||
assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCurrentPreferredUpstream() throws Exception {
|
||||
mUNM.start(mDefaultRequest);
|
||||
mUNM.updateMobileRequiresDun(false);
|
||||
|
||||
// [0] Mobile connects, DUN not required -> mobile selected.
|
||||
final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
|
||||
cellAgent.fakeConnect();
|
||||
mCM.makeDefaultNetwork(cellAgent);
|
||||
assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
||||
|
||||
// [1] WiFi connects but not validated/promoted to default -> mobile selected.
|
||||
final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
|
||||
wifiAgent.fakeConnect();
|
||||
assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
||||
|
||||
// [2] WiFi validates and is promoted to the default network -> WiFi selected.
|
||||
mCM.makeDefaultNetwork(wifiAgent);
|
||||
assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
||||
|
||||
// [3] DUN required, no other changes -> WiFi still selected
|
||||
mUNM.updateMobileRequiresDun(true);
|
||||
assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
||||
|
||||
// [4] WiFi no longer validated, mobile becomes default, DUN required -> null selected.
|
||||
mCM.makeDefaultNetwork(cellAgent);
|
||||
assertEquals(null, mUNM.getCurrentPreferredUpstream());
|
||||
// TODO: make sure that a DUN request has been filed. This is currently
|
||||
// triggered by code over in Tethering, but once that has been moved
|
||||
// into UNM we should test for this here.
|
||||
|
||||
// [5] DUN network arrives -> DUN selected
|
||||
final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
|
||||
dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
|
||||
dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
|
||||
dunAgent.fakeConnect();
|
||||
assertEquals(dunAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalPrefixes() throws Exception {
|
||||
mUNM.start();
|
||||
mUNM.start(mDefaultRequest);
|
||||
|
||||
// [0] Test minimum set of local prefixes.
|
||||
Set<IpPrefix> local = mUNM.getLocalPrefixes();
|
||||
@@ -345,7 +399,7 @@ public class UpstreamNetworkMonitorTest {
|
||||
|
||||
// [1] Pretend Wi-Fi connects.
|
||||
final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
|
||||
final LinkProperties wifiLp = new LinkProperties();
|
||||
final LinkProperties wifiLp = wifiAgent.linkProperties;
|
||||
wifiLp.setInterfaceName("wlan0");
|
||||
final String[] WIFI_ADDRS = {
|
||||
"fe80::827a:bfff:fe6f:374d", "100.112.103.18",
|
||||
@@ -358,7 +412,7 @@ public class UpstreamNetworkMonitorTest {
|
||||
wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr));
|
||||
}
|
||||
wifiAgent.fakeConnect();
|
||||
wifiAgent.sendLinkProperties(wifiLp);
|
||||
wifiAgent.sendLinkProperties();
|
||||
|
||||
local = mUNM.getLocalPrefixes();
|
||||
assertPrefixSet(local, INCLUDES, alreadySeen);
|
||||
@@ -372,7 +426,7 @@ public class UpstreamNetworkMonitorTest {
|
||||
|
||||
// [2] Pretend mobile connects.
|
||||
final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
|
||||
final LinkProperties cellLp = new LinkProperties();
|
||||
final LinkProperties cellLp = cellAgent.linkProperties;
|
||||
cellLp.setInterfaceName("rmnet_data0");
|
||||
final String[] CELL_ADDRS = {
|
||||
"10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d",
|
||||
@@ -382,7 +436,7 @@ public class UpstreamNetworkMonitorTest {
|
||||
cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
|
||||
}
|
||||
cellAgent.fakeConnect();
|
||||
cellAgent.sendLinkProperties(cellLp);
|
||||
cellAgent.sendLinkProperties();
|
||||
|
||||
local = mUNM.getLocalPrefixes();
|
||||
assertPrefixSet(local, INCLUDES, alreadySeen);
|
||||
@@ -394,17 +448,18 @@ public class UpstreamNetworkMonitorTest {
|
||||
// [3] Pretend DUN connects.
|
||||
final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
|
||||
dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
|
||||
final LinkProperties dunLp = new LinkProperties();
|
||||
dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
|
||||
final LinkProperties dunLp = dunAgent.linkProperties;
|
||||
dunLp.setInterfaceName("rmnet_data1");
|
||||
final String[] DUN_ADDRS = {
|
||||
"192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d",
|
||||
};
|
||||
for (String addrStr : DUN_ADDRS) {
|
||||
final String cidr = addrStr.contains(":") ? "/64" : "/27";
|
||||
cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
|
||||
dunLp.addLinkAddress(new LinkAddress(addrStr + cidr));
|
||||
}
|
||||
dunAgent.fakeConnect();
|
||||
dunAgent.sendLinkProperties(dunLp);
|
||||
dunAgent.sendLinkProperties();
|
||||
|
||||
local = mUNM.getLocalPrefixes();
|
||||
assertPrefixSet(local, INCLUDES, alreadySeen);
|
||||
@@ -442,6 +497,7 @@ public class UpstreamNetworkMonitorTest {
|
||||
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<>();
|
||||
@@ -483,12 +539,34 @@ public class UpstreamNetworkMonitorTest {
|
||||
|
||||
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);
|
||||
assertFalse(requested.containsKey(cb));
|
||||
requested.put(cb, req);
|
||||
if (mDefaultRequest.equals(req)) {
|
||||
assertFalse(trackingDefault.contains(cb));
|
||||
trackingDefault.add(cb);
|
||||
} else {
|
||||
assertFalse(requested.containsKey(cb));
|
||||
requested.put(cb, req);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -524,10 +602,7 @@ public class UpstreamNetworkMonitorTest {
|
||||
|
||||
@Override
|
||||
public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) {
|
||||
assertFalse(allCallbacks.containsKey(cb));
|
||||
allCallbacks.put(cb, h);
|
||||
assertFalse(trackingDefault.contains(cb));
|
||||
trackingDefault.add(cb);
|
||||
fail("Should never be called.");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -561,6 +636,7 @@ public class UpstreamNetworkMonitorTest {
|
||||
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;
|
||||
@@ -569,12 +645,14 @@ public class UpstreamNetworkMonitorTest {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,11 +662,16 @@ public class UpstreamNetworkMonitorTest {
|
||||
}
|
||||
}
|
||||
|
||||
public void sendLinkProperties(LinkProperties lp) {
|
||||
public void sendLinkProperties() {
|
||||
for (NetworkCallback cb : cm.listening.keySet()) {
|
||||
cb.onLinkPropertiesChanged(networkId, lp);
|
||||
cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("TestNetworkAgent: %s %s", networkId, networkCapabilities);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestStateMachine extends StateMachine {
|
||||
@@ -618,6 +701,10 @@ public class UpstreamNetworkMonitorTest {
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user