protected void chooseUpstreamType(boolean tryCell) {
updateConfiguration(); // TODO - remove?
- final int upstreamType = findPreferredUpstreamType(
- getConnectivityManager(), mConfig);
+ final int upstreamType = mUpstreamNetworkMonitor.selectPreferredUpstreamType(
+ mConfig.preferredUpstreamIfaceTypes);
if (upstreamType == ConnectivityManager.TYPE_NONE) {
if (tryCell) {
mUpstreamNetworkMonitor.registerMobileNetworkRequest();
setUpstreamByType(upstreamType);
}
- // TODO: Move this function into UpstreamNetworkMonitor.
- protected int findPreferredUpstreamType(ConnectivityManager cm,
- TetheringConfiguration cfg) {
- int upType = ConnectivityManager.TYPE_NONE;
-
- if (VDBG) {
- Log.d(TAG, "chooseUpstreamType has upstream iface types:");
- for (Integer netType : cfg.preferredUpstreamIfaceTypes) {
- Log.d(TAG, " " + netType);
- }
- }
-
- for (Integer netType : cfg.preferredUpstreamIfaceTypes) {
- NetworkInfo info = cm.getNetworkInfo(netType.intValue());
- // TODO: if the network is suspended we should consider
- // that to be the same as connected here.
- if ((info != null) && info.isConnected()) {
- upType = netType.intValue();
- break;
- }
- }
-
- final int preferredUpstreamMobileApn = cfg.isDunRequired
- ? ConnectivityManager.TYPE_MOBILE_DUN
- : ConnectivityManager.TYPE_MOBILE_HIPRI;
- mLog.log(String.format(
- "findPreferredUpstreamType(), preferredApn=%s, got type=%s",
- getNetworkTypeName(preferredUpstreamMobileApn),
- getNetworkTypeName(upType)));
-
- switch (upType) {
- case ConnectivityManager.TYPE_MOBILE_DUN:
- case ConnectivityManager.TYPE_MOBILE_HIPRI:
- // If we're on DUN, put our own grab on it.
- mUpstreamNetworkMonitor.registerMobileNetworkRequest();
- break;
- case ConnectivityManager.TYPE_NONE:
- break;
- default:
- /* If we've found an active upstream connection that's not DUN/HIPRI
- * we should stop any outstanding DUN/HIPRI start requests.
- *
- * If we found NONE we don't want to do this as we want any previous
- * requests to keep trying to bring up something we can use.
- */
- mUpstreamNetworkMonitor.releaseMobileNetworkRequest();
- break;
- }
-
- return upType;
- }
-
protected void setUpstreamByType(int upType) {
final ConnectivityManager cm = getConnectivityManager();
Network network = null;
package com.android.server.connectivity.tethering;
+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;
return (network != null) ? mNetworkMap.get(network) : null;
}
+ // So many TODOs here, but chief among them is: make this functionality an
+ // integral part of this class such that whenever a higher priority network
+ // becomes available and useful we (a) file a request to keep it up as
+ // necessary and (b) change all upstream tracking state accordingly (by
+ // passing LinkProperties up to Tethering).
+ //
+ // Next TODO: return NetworkState instead of just the type.
+ public int selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
+ final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType(
+ mNetworkMap.values(), preferredTypes);
+
+ mLog.log("preferred upstream type: " + getNetworkTypeName(typeStatePair.type));
+
+ switch (typeStatePair.type) {
+ case TYPE_MOBILE_DUN:
+ case TYPE_MOBILE_HIPRI:
+ // If we're on DUN, put our own grab on it.
+ registerMobileNetworkRequest();
+ break;
+ case TYPE_NONE:
+ break;
+ default:
+ /* If we've found an active upstream connection that's not DUN/HIPRI
+ * we should stop any outstanding DUN/HIPRI start requests.
+ *
+ * If we found NONE we don't want to do this as we want any previous
+ * requests to keep trying to bring up something we can use.
+ */
+ releaseMobileNetworkRequest();
+ break;
+ }
+
+ return typeStatePair.type;
+ }
+
private void handleAvailable(int callbackType, Network network) {
if (VDBG) Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
private void notifyTarget(int which, NetworkState netstate) {
mTarget.sendMessage(mWhat, which, 0, netstate);
}
+
+ static private class TypeStatePair {
+ public int type = TYPE_NONE;
+ public NetworkState ns = null;
+ }
+
+ static private TypeStatePair findFirstAvailableUpstreamByType(
+ Iterable<NetworkState> netStates, Iterable<Integer> preferredTypes) {
+ final TypeStatePair result = new TypeStatePair();
+
+ for (int type : preferredTypes) {
+ NetworkCapabilities nc;
+ try {
+ nc = ConnectivityManager.networkCapabilitiesForType(type);
+ } catch (IllegalArgumentException iae) {
+ Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " +
+ ConnectivityManager.getNetworkTypeName(type));
+ continue;
+ }
+
+ for (NetworkState value : netStates) {
+ if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) {
+ continue;
+ }
+
+ result.type = type;
+ result.ns = value;
+ return result;
+ }
+ }
+
+ return result;
+ }
}
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.ConnectivityManager.TYPE_NONE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
assertFalse(mUNM.mobileNetworkRequested());
}
+ @Test
+ public void testSelectPreferredUpstreamType() throws Exception {
+ final Collection<Integer> preferredTypes = new ArrayList<>();
+ preferredTypes.add(TYPE_WIFI);
+
+ mUNM.start();
+ // There are no networks, so there is nothing to select.
+ assertEquals(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
+
+ final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
+ wifiAgent.fakeConnect();
+ // WiFi is up, we should prefer it.
+ assertEquals(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
+ wifiAgent.fakeDisconnect();
+ // There are no networks, so there is nothing to select.
+ assertEquals(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
+
+ final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+ cellAgent.fakeConnect();
+ assertEquals(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
+
+ preferredTypes.add(TYPE_MOBILE_DUN);
+ // This is coupled with preferred types in TetheringConfiguration.
+ mUNM.updateMobileRequiresDun(true);
+ // DUN is available, but only use regular cell: no upstream selected.
+ assertEquals(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
+ preferredTypes.remove(TYPE_MOBILE_DUN);
+ // No WiFi, but our preferred flavour of cell is up.
+ preferredTypes.add(TYPE_MOBILE_HIPRI);
+ // This is coupled with preferred types in TetheringConfiguration.
+ mUNM.updateMobileRequiresDun(false);
+ assertEquals(TYPE_MOBILE_HIPRI, mUNM.selectPreferredUpstreamType(preferredTypes));
+ // Check to see we filed an explicit request.
+ assertEquals(1, mCM.requested.size());
+ NetworkRequest netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
+ assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
+
+ wifiAgent.fakeConnect();
+ // WiFi is up, and we should prefer it over cell.
+ assertEquals(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
+ assertEquals(0, mCM.requested.size());
+
+ preferredTypes.remove(TYPE_MOBILE_HIPRI);
+ preferredTypes.add(TYPE_MOBILE_DUN);
+ // This is coupled with preferred types in TetheringConfiguration.
+ mUNM.updateMobileRequiresDun(true);
+ assertEquals(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
+
+ final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+ dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
+ dunAgent.fakeConnect();
+
+ // WiFi is still preferred.
+ assertEquals(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
+
+ // WiFi goes down, cell and DUN are still up but only DUN is preferred.
+ wifiAgent.fakeDisconnect();
+ assertEquals(TYPE_MOBILE_DUN, mUNM.selectPreferredUpstreamType(preferredTypes));
+ // Check to see we filed an explicit request.
+ assertEquals(1, mCM.requested.size());
+ netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
+ assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR));
+ assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
+ }
+
private void assertUpstreamTypeRequested(int upstreamType) throws Exception {
assertEquals(1, mCM.requested.size());
assertEquals(1, mCM.legacyTypeMap.size());
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);
}
return false;
}
+ int getNetworkId() { return ++mNetworkId; }
+
@Override
public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
assertFalse(allCallbacks.containsKey(cb));
}
}
+ public static class TestNetworkAgent {
+ public final TestConnectivityManager cm;
+ public final Network networkId;
+ public final int transportType;
+ public final NetworkCapabilities networkCapabilities;
+
+ 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);
+ }
+
+ public void fakeConnect() {
+ for (NetworkCallback cb : cm.listening.keySet()) {
+ cb.onAvailable(networkId);
+ cb.onCapabilitiesChanged(networkId, copy(networkCapabilities));
+ }
+ }
+
+ public void fakeDisconnect() {
+ for (NetworkCallback cb : cm.listening.keySet()) {
+ cb.onLost(networkId);
+ }
+ }
+ }
+
public static class TestStateMachine extends StateMachine {
public final ArrayList<Message> messages = new ArrayList<>();
private final State mLoggingState = new LoggingState();
super.start();
}
}
+
+ static NetworkCapabilities copy(NetworkCapabilities nc) {
+ return new NetworkCapabilities(nc);
+ }
}