OSDN Git Service

Move findPreferredUpstreamType into UNM
authorErik Kline <ek@google.com>
Mon, 5 Jun 2017 07:02:02 +0000 (16:02 +0900)
committerErik Kline <ek@google.com>
Thu, 15 Jun 2017 06:28:50 +0000 (15:28 +0900)
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)

services/core/java/com/android/server/connectivity/Tethering.java
services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
tests/net/java/com/android/server/connectivity/TetheringTest.java
tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java

index f9fb9d7..e825f90 100644 (file)
@@ -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;
index cd6038f..b2d5051 100644 (file)
@@ -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<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);
 
@@ -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<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;
+    }
 }
index f9a30e9..2137e55 100644 (file)
@@ -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));
index 9bb392a..fb6066e 100644 (file)
@@ -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<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());
@@ -254,6 +326,8 @@ public class UpstreamNetworkMonitorTest {
         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);
         }
@@ -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<Message> 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);
+    }
 }