From: Erik Kline Date: Mon, 5 Jun 2017 07:02:02 +0000 (+0900) Subject: Move findPreferredUpstreamType into UNM X-Git-Tag: android-x86-9.0-r1~813^2~531^2~1 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=3760999e347ae1cfa25087451638de8ddc79c9f6;p=android-x86%2Fframeworks-base.git Move findPreferredUpstreamType into UNM This changes the way in which available networks are found. Previously Tethering asked ConnectivityService for NetworkInfo and checked for whether or not it was in state CONNECTED. Here we use the fact that ConnectivityService will not call UNM's callbacks' onAvailable() methods until the networks in question have become connected. Test: as follows - built - flashed - booted - runtest framework-net passed Bug: 29337859 Bug: 32163131 Merged-In: I9937297727aa1a063e499fccd5833ace229b1e8a Merged-In: Ifa1a34a1fb32149085421a63cb0f2586d2862d6b Merged-In: Ia215e55b69b856f5511e5d4f852e39fa6c11462e Change-Id: I97abe225fdd3accb38bd9168f545445b761a90d8 (cherry picked from commit a1d368af2f01e2048240fb730f6cc399fc1cc1ab) --- diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index f9fb9d76891b..e825f904b104 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -1247,8 +1247,8 @@ public class Tethering extends BaseNetworkObserver { 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(); @@ -1260,58 +1260,6 @@ public class Tethering extends BaseNetworkObserver { 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; diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java index cd6038ffa0dd..b2d50515c39d 100644 --- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java @@ -16,6 +16,8 @@ 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; @@ -176,6 +178,41 @@ public class UpstreamNetworkMonitor { 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 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); @@ -365,4 +402,37 @@ public class UpstreamNetworkMonitor { 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 netStates, Iterable 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; + } } diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index f9a30e938023..2137e557cd0c 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -394,7 +394,6 @@ public class TetheringTest { any(NetworkCallback.class), any(Handler.class)); // In tethering mode, in the default configuration, an explicit request // for a mobile network is also made. - verify(mConnectivityManager, atLeastOnce()).getNetworkInfo(anyInt()); verify(mConnectivityManager, times(1)).requestNetwork( any(NetworkRequest.class), any(NetworkCallback.class), eq(0), anyInt(), any(Handler.class)); diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java index 9bb392a23d56..fb6066e46e66 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java @@ -18,7 +18,12 @@ package com.android.server.connectivity.tethering; 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; @@ -59,6 +64,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -240,6 +246,72 @@ public class UpstreamNetworkMonitorTest { assertFalse(mUNM.mobileNetworkRequested()); } + @Test + public void testSelectPreferredUpstreamType() throws Exception { + final Collection 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()); @@ -254,6 +326,8 @@ public class UpstreamNetworkMonitorTest { public Map requested = new HashMap<>(); public Map legacyTypeMap = new HashMap<>(); + private int mNetworkId = 100; + public TestConnectivityManager(Context ctx, IConnectivityManager svc) { super(ctx, svc); } @@ -287,6 +361,8 @@ public class UpstreamNetworkMonitorTest { return false; } + int getNetworkId() { return ++mNetworkId; } + @Override public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) { assertFalse(allCallbacks.containsKey(cb)); @@ -360,6 +436,35 @@ public class UpstreamNetworkMonitorTest { } } + 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 messages = new ArrayList<>(); private final State mLoggingState = new LoggingState(); @@ -382,4 +487,8 @@ public class UpstreamNetworkMonitorTest { super.start(); } } + + static NetworkCapabilities copy(NetworkCapabilities nc) { + return new NetworkCapabilities(nc); + } }