Home | History | Annotate | Download | only in tethering
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.connectivity.tethering;
     18 
     19 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
     20 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
     21 import static android.net.ConnectivityManager.TYPE_NONE;
     22 import static android.net.ConnectivityManager.TYPE_WIFI;
     23 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
     24 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
     25 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
     26 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
     27 import static org.junit.Assert.assertEquals;
     28 import static org.junit.Assert.assertFalse;
     29 import static org.junit.Assert.assertTrue;
     30 import static org.junit.Assert.fail;
     31 import static org.mockito.Mockito.any;
     32 import static org.mockito.Mockito.anyInt;
     33 import static org.mockito.Mockito.anyString;
     34 import static org.mockito.Mockito.reset;
     35 import static org.mockito.Mockito.spy;
     36 import static org.mockito.Mockito.times;
     37 import static org.mockito.Mockito.verify;
     38 import static org.mockito.Mockito.verifyNoMoreInteractions;
     39 import static org.mockito.Mockito.when;
     40 
     41 import android.content.Context;
     42 import android.os.Handler;
     43 import android.os.Message;
     44 import android.net.ConnectivityManager;
     45 import android.net.ConnectivityManager.NetworkCallback;
     46 import android.net.IConnectivityManager;
     47 import android.net.IpPrefix;
     48 import android.net.LinkAddress;
     49 import android.net.LinkProperties;
     50 import android.net.Network;
     51 import android.net.NetworkCapabilities;
     52 import android.net.NetworkRequest;
     53 import android.net.NetworkState;
     54 import android.net.util.SharedLog;
     55 
     56 import android.support.test.filters.SmallTest;
     57 import android.support.test.runner.AndroidJUnit4;
     58 
     59 import com.android.internal.util.State;
     60 import com.android.internal.util.StateMachine;
     61 
     62 import org.junit.After;
     63 import org.junit.Before;
     64 import org.junit.runner.RunWith;
     65 import org.junit.Test;
     66 import org.mockito.Mock;
     67 import org.mockito.Mockito;
     68 import org.mockito.MockitoAnnotations;
     69 
     70 import java.util.ArrayList;
     71 import java.util.Collection;
     72 import java.util.Collections;
     73 import java.util.HashMap;
     74 import java.util.HashSet;
     75 import java.util.Map;
     76 import java.util.Set;
     77 
     78 
     79 @RunWith(AndroidJUnit4.class)
     80 @SmallTest
     81 public class UpstreamNetworkMonitorTest {
     82     private static final int EVENT_UNM_UPDATE = 1;
     83 
     84     private static final boolean INCLUDES = true;
     85     private static final boolean EXCLUDES = false;
     86 
     87     @Mock private Context mContext;
     88     @Mock private IConnectivityManager mCS;
     89     @Mock private SharedLog mLog;
     90 
     91     private TestStateMachine mSM;
     92     private TestConnectivityManager mCM;
     93     private UpstreamNetworkMonitor mUNM;
     94 
     95     @Before public void setUp() throws Exception {
     96         MockitoAnnotations.initMocks(this);
     97         reset(mContext);
     98         reset(mCS);
     99         reset(mLog);
    100         when(mLog.forSubComponent(anyString())).thenReturn(mLog);
    101 
    102         mCM = spy(new TestConnectivityManager(mContext, mCS));
    103         mSM = new TestStateMachine();
    104         mUNM = new UpstreamNetworkMonitor(
    105                 (ConnectivityManager) mCM, mSM, mLog, EVENT_UNM_UPDATE);
    106     }
    107 
    108     @After public void tearDown() throws Exception {
    109         if (mSM != null) {
    110             mSM.quit();
    111             mSM = null;
    112         }
    113     }
    114 
    115     @Test
    116     public void testDoesNothingBeforeStarted() {
    117         assertTrue(mCM.hasNoCallbacks());
    118         assertFalse(mUNM.mobileNetworkRequested());
    119 
    120         mUNM.updateMobileRequiresDun(true);
    121         assertTrue(mCM.hasNoCallbacks());
    122         mUNM.updateMobileRequiresDun(false);
    123         assertTrue(mCM.hasNoCallbacks());
    124     }
    125 
    126     @Test
    127     public void testDefaultNetworkIsTracked() throws Exception {
    128         assertEquals(0, mCM.trackingDefault.size());
    129 
    130         mUNM.start();
    131         assertEquals(1, mCM.trackingDefault.size());
    132 
    133         mUNM.stop();
    134         assertTrue(mCM.hasNoCallbacks());
    135     }
    136 
    137     @Test
    138     public void testListensForAllNetworks() throws Exception {
    139         assertTrue(mCM.listening.isEmpty());
    140 
    141         mUNM.start();
    142         assertFalse(mCM.listening.isEmpty());
    143         assertTrue(mCM.isListeningForAll());
    144 
    145         mUNM.stop();
    146         assertTrue(mCM.hasNoCallbacks());
    147     }
    148 
    149     @Test
    150     public void testRequestsMobileNetwork() throws Exception {
    151         assertFalse(mUNM.mobileNetworkRequested());
    152         assertEquals(0, mCM.requested.size());
    153 
    154         mUNM.start();
    155         assertFalse(mUNM.mobileNetworkRequested());
    156         assertEquals(0, mCM.requested.size());
    157 
    158         mUNM.updateMobileRequiresDun(false);
    159         assertFalse(mUNM.mobileNetworkRequested());
    160         assertEquals(0, mCM.requested.size());
    161 
    162         mUNM.registerMobileNetworkRequest();
    163         assertTrue(mUNM.mobileNetworkRequested());
    164         assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
    165         assertFalse(mCM.isDunRequested());
    166 
    167         mUNM.stop();
    168         assertFalse(mUNM.mobileNetworkRequested());
    169         assertTrue(mCM.hasNoCallbacks());
    170     }
    171 
    172     @Test
    173     public void testDuplicateMobileRequestsIgnored() throws Exception {
    174         assertFalse(mUNM.mobileNetworkRequested());
    175         assertEquals(0, mCM.requested.size());
    176 
    177         mUNM.start();
    178         verify(mCM, Mockito.times(1)).registerNetworkCallback(
    179                 any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
    180         verify(mCM, Mockito.times(1)).registerDefaultNetworkCallback(
    181                 any(NetworkCallback.class), any(Handler.class));
    182         assertFalse(mUNM.mobileNetworkRequested());
    183         assertEquals(0, mCM.requested.size());
    184 
    185         mUNM.updateMobileRequiresDun(true);
    186         mUNM.registerMobileNetworkRequest();
    187         verify(mCM, Mockito.times(1)).requestNetwork(
    188                 any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt(),
    189                 any(Handler.class));
    190 
    191         assertTrue(mUNM.mobileNetworkRequested());
    192         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
    193         assertTrue(mCM.isDunRequested());
    194 
    195         // Try a few things that must not result in any state change.
    196         mUNM.registerMobileNetworkRequest();
    197         mUNM.updateMobileRequiresDun(true);
    198         mUNM.registerMobileNetworkRequest();
    199 
    200         assertTrue(mUNM.mobileNetworkRequested());
    201         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
    202         assertTrue(mCM.isDunRequested());
    203 
    204         mUNM.stop();
    205         verify(mCM, times(3)).unregisterNetworkCallback(any(NetworkCallback.class));
    206 
    207         verifyNoMoreInteractions(mCM);
    208     }
    209 
    210     @Test
    211     public void testRequestsDunNetwork() throws Exception {
    212         assertFalse(mUNM.mobileNetworkRequested());
    213         assertEquals(0, mCM.requested.size());
    214 
    215         mUNM.start();
    216         assertFalse(mUNM.mobileNetworkRequested());
    217         assertEquals(0, mCM.requested.size());
    218 
    219         mUNM.updateMobileRequiresDun(true);
    220         assertFalse(mUNM.mobileNetworkRequested());
    221         assertEquals(0, mCM.requested.size());
    222 
    223         mUNM.registerMobileNetworkRequest();
    224         assertTrue(mUNM.mobileNetworkRequested());
    225         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
    226         assertTrue(mCM.isDunRequested());
    227 
    228         mUNM.stop();
    229         assertFalse(mUNM.mobileNetworkRequested());
    230         assertTrue(mCM.hasNoCallbacks());
    231     }
    232 
    233     @Test
    234     public void testUpdateMobileRequiresDun() throws Exception {
    235         mUNM.start();
    236 
    237         // Test going from no-DUN to DUN correctly re-registers callbacks.
    238         mUNM.updateMobileRequiresDun(false);
    239         mUNM.registerMobileNetworkRequest();
    240         assertTrue(mUNM.mobileNetworkRequested());
    241         assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
    242         assertFalse(mCM.isDunRequested());
    243         mUNM.updateMobileRequiresDun(true);
    244         assertTrue(mUNM.mobileNetworkRequested());
    245         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
    246         assertTrue(mCM.isDunRequested());
    247 
    248         // Test going from DUN to no-DUN correctly re-registers callbacks.
    249         mUNM.updateMobileRequiresDun(false);
    250         assertTrue(mUNM.mobileNetworkRequested());
    251         assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
    252         assertFalse(mCM.isDunRequested());
    253 
    254         mUNM.stop();
    255         assertFalse(mUNM.mobileNetworkRequested());
    256     }
    257 
    258     @Test
    259     public void testSelectPreferredUpstreamType() throws Exception {
    260         final Collection<Integer> preferredTypes = new ArrayList<>();
    261         preferredTypes.add(TYPE_WIFI);
    262 
    263         mUNM.start();
    264         // There are no networks, so there is nothing to select.
    265         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
    266 
    267         final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
    268         wifiAgent.fakeConnect();
    269         // WiFi is up, we should prefer it.
    270         assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
    271         wifiAgent.fakeDisconnect();
    272         // There are no networks, so there is nothing to select.
    273         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
    274 
    275         final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
    276         cellAgent.fakeConnect();
    277         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
    278 
    279         preferredTypes.add(TYPE_MOBILE_DUN);
    280         // This is coupled with preferred types in TetheringConfiguration.
    281         mUNM.updateMobileRequiresDun(true);
    282         // DUN is available, but only use regular cell: no upstream selected.
    283         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
    284         preferredTypes.remove(TYPE_MOBILE_DUN);
    285         // No WiFi, but our preferred flavour of cell is up.
    286         preferredTypes.add(TYPE_MOBILE_HIPRI);
    287         // This is coupled with preferred types in TetheringConfiguration.
    288         mUNM.updateMobileRequiresDun(false);
    289         assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,
    290                 mUNM.selectPreferredUpstreamType(preferredTypes));
    291         // Check to see we filed an explicit request.
    292         assertEquals(1, mCM.requested.size());
    293         NetworkRequest netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
    294         assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR));
    295         assertFalse(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
    296 
    297         wifiAgent.fakeConnect();
    298         // WiFi is up, and we should prefer it over cell.
    299         assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
    300         assertEquals(0, mCM.requested.size());
    301 
    302         preferredTypes.remove(TYPE_MOBILE_HIPRI);
    303         preferredTypes.add(TYPE_MOBILE_DUN);
    304         // This is coupled with preferred types in TetheringConfiguration.
    305         mUNM.updateMobileRequiresDun(true);
    306         assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
    307 
    308         final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
    309         dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
    310         dunAgent.fakeConnect();
    311 
    312         // WiFi is still preferred.
    313         assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
    314 
    315         // WiFi goes down, cell and DUN are still up but only DUN is preferred.
    316         wifiAgent.fakeDisconnect();
    317         assertSatisfiesLegacyType(TYPE_MOBILE_DUN,
    318                 mUNM.selectPreferredUpstreamType(preferredTypes));
    319         // Check to see we filed an explicit request.
    320         assertEquals(1, mCM.requested.size());
    321         netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
    322         assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR));
    323         assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
    324     }
    325 
    326     @Test
    327     public void testLocalPrefixes() throws Exception {
    328         mUNM.start();
    329 
    330         // [0] Test minimum set of local prefixes.
    331         Set<IpPrefix> local = mUNM.getLocalPrefixes();
    332         assertTrue(local.isEmpty());
    333 
    334         final Set<String> alreadySeen = new HashSet<>();
    335 
    336         // [1] Pretend Wi-Fi connects.
    337         final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
    338         final LinkProperties wifiLp = new LinkProperties();
    339         wifiLp.setInterfaceName("wlan0");
    340         final String[] WIFI_ADDRS = {
    341                 "fe80::827a:bfff:fe6f:374d", "100.112.103.18",
    342                 "2001:db8:4:fd00:827a:bfff:fe6f:374d",
    343                 "2001:db8:4:fd00:6dea:325a:fdae:4ef4",
    344                 "fd6a:a640:60bf:e985::123",  // ULA address for good measure.
    345         };
    346         for (String addrStr : WIFI_ADDRS) {
    347             final String cidr = addrStr.contains(":") ? "/64" : "/20";
    348             wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr));
    349         }
    350         wifiAgent.fakeConnect();
    351         wifiAgent.sendLinkProperties(wifiLp);
    352 
    353         local = mUNM.getLocalPrefixes();
    354         assertPrefixSet(local, INCLUDES, alreadySeen);
    355         final String[] wifiLinkPrefixes = {
    356                 // Link-local prefixes are excluded and dealt with elsewhere.
    357                 "100.112.96.0/20", "2001:db8:4:fd00::/64", "fd6a:a640:60bf:e985::/64",
    358         };
    359         assertPrefixSet(local, INCLUDES, wifiLinkPrefixes);
    360         Collections.addAll(alreadySeen, wifiLinkPrefixes);
    361         assertEquals(alreadySeen.size(), local.size());
    362 
    363         // [2] Pretend mobile connects.
    364         final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
    365         final LinkProperties cellLp = new LinkProperties();
    366         cellLp.setInterfaceName("rmnet_data0");
    367         final String[] CELL_ADDRS = {
    368                 "10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d",
    369         };
    370         for (String addrStr : CELL_ADDRS) {
    371             final String cidr = addrStr.contains(":") ? "/64" : "/27";
    372             cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
    373         }
    374         cellAgent.fakeConnect();
    375         cellAgent.sendLinkProperties(cellLp);
    376 
    377         local = mUNM.getLocalPrefixes();
    378         assertPrefixSet(local, INCLUDES, alreadySeen);
    379         final String[] cellLinkPrefixes = { "10.102.211.32/27", "2001:db8:0:1::/64" };
    380         assertPrefixSet(local, INCLUDES, cellLinkPrefixes);
    381         Collections.addAll(alreadySeen, cellLinkPrefixes);
    382         assertEquals(alreadySeen.size(), local.size());
    383 
    384         // [3] Pretend DUN connects.
    385         final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
    386         dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
    387         final LinkProperties dunLp = new LinkProperties();
    388         dunLp.setInterfaceName("rmnet_data1");
    389         final String[] DUN_ADDRS = {
    390                 "192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d",
    391         };
    392         for (String addrStr : DUN_ADDRS) {
    393             final String cidr = addrStr.contains(":") ? "/64" : "/27";
    394             cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
    395         }
    396         dunAgent.fakeConnect();
    397         dunAgent.sendLinkProperties(dunLp);
    398 
    399         local = mUNM.getLocalPrefixes();
    400         assertPrefixSet(local, INCLUDES, alreadySeen);
    401         final String[] dunLinkPrefixes = { "192.0.2.32/27", "2001:db8:1:2::/64" };
    402         assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
    403         Collections.addAll(alreadySeen, dunLinkPrefixes);
    404         assertEquals(alreadySeen.size(), local.size());
    405 
    406         // [4] Pretend Wi-Fi disconnected.  It's addresses/prefixes should no
    407         // longer be included (should be properly removed).
    408         wifiAgent.fakeDisconnect();
    409         local = mUNM.getLocalPrefixes();
    410         assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes);
    411         assertPrefixSet(local, INCLUDES, cellLinkPrefixes);
    412         assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
    413     }
    414 
    415     private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) {
    416         if (legacyType == TYPE_NONE) {
    417             assertTrue(ns == null);
    418             return;
    419         }
    420 
    421         final NetworkCapabilities nc = ConnectivityManager.networkCapabilitiesForType(legacyType);
    422         assertTrue(nc.satisfiedByNetworkCapabilities(ns.networkCapabilities));
    423     }
    424 
    425     private void assertUpstreamTypeRequested(int upstreamType) throws Exception {
    426         assertEquals(1, mCM.requested.size());
    427         assertEquals(1, mCM.legacyTypeMap.size());
    428         assertEquals(Integer.valueOf(upstreamType),
    429                 mCM.legacyTypeMap.values().iterator().next());
    430     }
    431 
    432     public static class TestConnectivityManager extends ConnectivityManager {
    433         public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>();
    434         public Set<NetworkCallback> trackingDefault = new HashSet<>();
    435         public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
    436         public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
    437         public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>();
    438 
    439         private int mNetworkId = 100;
    440 
    441         public TestConnectivityManager(Context ctx, IConnectivityManager svc) {
    442             super(ctx, svc);
    443         }
    444 
    445         boolean hasNoCallbacks() {
    446             return allCallbacks.isEmpty() &&
    447                    trackingDefault.isEmpty() &&
    448                    listening.isEmpty() &&
    449                    requested.isEmpty() &&
    450                    legacyTypeMap.isEmpty();
    451         }
    452 
    453         boolean isListeningForAll() {
    454             final NetworkCapabilities empty = new NetworkCapabilities();
    455             empty.clearAll();
    456 
    457             for (NetworkRequest req : listening.values()) {
    458                 if (req.networkCapabilities.equalRequestableCapabilities(empty)) {
    459                     return true;
    460                 }
    461             }
    462             return false;
    463         }
    464 
    465         boolean isDunRequested() {
    466             for (NetworkRequest req : requested.values()) {
    467                 if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) {
    468                     return true;
    469                 }
    470             }
    471             return false;
    472         }
    473 
    474         int getNetworkId() { return ++mNetworkId; }
    475 
    476         @Override
    477         public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
    478             assertFalse(allCallbacks.containsKey(cb));
    479             allCallbacks.put(cb, h);
    480             assertFalse(requested.containsKey(cb));
    481             requested.put(cb, req);
    482         }
    483 
    484         @Override
    485         public void requestNetwork(NetworkRequest req, NetworkCallback cb) {
    486             fail("Should never be called.");
    487         }
    488 
    489         @Override
    490         public void requestNetwork(NetworkRequest req, NetworkCallback cb,
    491                 int timeoutMs, int legacyType, Handler h) {
    492             assertFalse(allCallbacks.containsKey(cb));
    493             allCallbacks.put(cb, h);
    494             assertFalse(requested.containsKey(cb));
    495             requested.put(cb, req);
    496             assertFalse(legacyTypeMap.containsKey(cb));
    497             if (legacyType != ConnectivityManager.TYPE_NONE) {
    498                 legacyTypeMap.put(cb, legacyType);
    499             }
    500         }
    501 
    502         @Override
    503         public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h) {
    504             assertFalse(allCallbacks.containsKey(cb));
    505             allCallbacks.put(cb, h);
    506             assertFalse(listening.containsKey(cb));
    507             listening.put(cb, req);
    508         }
    509 
    510         @Override
    511         public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) {
    512             fail("Should never be called.");
    513         }
    514 
    515         @Override
    516         public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) {
    517             assertFalse(allCallbacks.containsKey(cb));
    518             allCallbacks.put(cb, h);
    519             assertFalse(trackingDefault.contains(cb));
    520             trackingDefault.add(cb);
    521         }
    522 
    523         @Override
    524         public void registerDefaultNetworkCallback(NetworkCallback cb) {
    525             fail("Should never be called.");
    526         }
    527 
    528         @Override
    529         public void unregisterNetworkCallback(NetworkCallback cb) {
    530             if (trackingDefault.contains(cb)) {
    531                 trackingDefault.remove(cb);
    532             } else if (listening.containsKey(cb)) {
    533                 listening.remove(cb);
    534             } else if (requested.containsKey(cb)) {
    535                 requested.remove(cb);
    536                 legacyTypeMap.remove(cb);
    537             } else {
    538                 fail("Unexpected callback removed");
    539             }
    540             allCallbacks.remove(cb);
    541 
    542             assertFalse(allCallbacks.containsKey(cb));
    543             assertFalse(trackingDefault.contains(cb));
    544             assertFalse(listening.containsKey(cb));
    545             assertFalse(requested.containsKey(cb));
    546         }
    547     }
    548 
    549     public static class TestNetworkAgent {
    550         public final TestConnectivityManager cm;
    551         public final Network networkId;
    552         public final int transportType;
    553         public final NetworkCapabilities networkCapabilities;
    554 
    555         public TestNetworkAgent(TestConnectivityManager cm, int transportType) {
    556             this.cm = cm;
    557             this.networkId = new Network(cm.getNetworkId());
    558             this.transportType = transportType;
    559             networkCapabilities = new NetworkCapabilities();
    560             networkCapabilities.addTransportType(transportType);
    561             networkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
    562         }
    563 
    564         public void fakeConnect() {
    565             for (NetworkCallback cb : cm.listening.keySet()) {
    566                 cb.onAvailable(networkId);
    567                 cb.onCapabilitiesChanged(networkId, copy(networkCapabilities));
    568             }
    569         }
    570 
    571         public void fakeDisconnect() {
    572             for (NetworkCallback cb : cm.listening.keySet()) {
    573                 cb.onLost(networkId);
    574             }
    575         }
    576 
    577         public void sendLinkProperties(LinkProperties lp) {
    578             for (NetworkCallback cb : cm.listening.keySet()) {
    579                 cb.onLinkPropertiesChanged(networkId, lp);
    580             }
    581         }
    582     }
    583 
    584     public static class TestStateMachine extends StateMachine {
    585         public final ArrayList<Message> messages = new ArrayList<>();
    586         private final State mLoggingState = new LoggingState();
    587 
    588         class LoggingState extends State {
    589             @Override public void enter() { messages.clear(); }
    590 
    591             @Override public void exit() { messages.clear(); }
    592 
    593             @Override public boolean processMessage(Message msg) {
    594                 messages.add(msg);
    595                 return true;
    596             }
    597         }
    598 
    599         public TestStateMachine() {
    600             super("UpstreamNetworkMonitor.TestStateMachine");
    601             addState(mLoggingState);
    602             setInitialState(mLoggingState);
    603             super.start();
    604         }
    605     }
    606 
    607     static NetworkCapabilities copy(NetworkCapabilities nc) {
    608         return new NetworkCapabilities(nc);
    609     }
    610 
    611     static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected) {
    612         final Set<String> expectedSet = new HashSet<>();
    613         Collections.addAll(expectedSet, expected);
    614         assertPrefixSet(prefixes, expectation, expectedSet);
    615     }
    616 
    617     static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, Set<String> expected) {
    618         for (String expectedPrefix : expected) {
    619             final String errStr = expectation ? "did not find" : "found";
    620             assertEquals(
    621                     String.format("Failed expectation: %s prefix: %s", errStr, expectedPrefix),
    622                     expectation, prefixes.contains(new IpPrefix(expectedPrefix)));
    623         }
    624     }
    625 }
    626