• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.ethernet;
18 
19 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotSame;
24 import static org.junit.Assert.assertNull;
25 import static org.junit.Assert.assertThrows;
26 import static org.junit.Assert.assertTrue;
27 import static org.mockito.ArgumentMatchers.any;
28 import static org.mockito.ArgumentMatchers.anyString;
29 import static org.mockito.ArgumentMatchers.argThat;
30 import static org.mockito.ArgumentMatchers.eq;
31 import static org.mockito.ArgumentMatchers.same;
32 import static org.mockito.Mockito.clearInvocations;
33 import static org.mockito.Mockito.doAnswer;
34 import static org.mockito.Mockito.never;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when;
37 
38 import android.annotation.NonNull;
39 import android.app.test.MockAnswerUtil.AnswerWithArguments;
40 import android.content.Context;
41 import android.content.res.Resources;
42 import android.net.ConnectivityManager;
43 import android.net.EthernetNetworkManagementException;
44 import android.net.EthernetNetworkSpecifier;
45 import android.net.INetworkInterfaceOutcomeReceiver;
46 import android.net.IpConfiguration;
47 import android.net.LinkAddress;
48 import android.net.LinkProperties;
49 import android.net.Network;
50 import android.net.NetworkAgentConfig;
51 import android.net.NetworkCapabilities;
52 import android.net.NetworkProvider;
53 import android.net.NetworkProvider.NetworkOfferCallback;
54 import android.net.NetworkRequest;
55 import android.net.StaticIpConfiguration;
56 import android.net.ip.IpClientCallbacks;
57 import android.net.ip.IpClientManager;
58 import android.os.Handler;
59 import android.os.IBinder;
60 import android.os.Looper;
61 import android.os.test.TestLooper;
62 
63 import androidx.test.filters.SmallTest;
64 import androidx.test.runner.AndroidJUnit4;
65 
66 import com.android.net.module.util.InterfaceParams;
67 import com.android.testutils.DevSdkIgnoreRule;
68 
69 import org.junit.After;
70 import org.junit.Before;
71 import org.junit.Test;
72 import org.junit.runner.RunWith;
73 import org.mockito.ArgumentCaptor;
74 import org.mockito.Mock;
75 import org.mockito.MockitoAnnotations;
76 
77 import java.util.Objects;
78 import java.util.concurrent.CompletableFuture;
79 import java.util.concurrent.ExecutionException;
80 import java.util.concurrent.TimeUnit;
81 
82 @RunWith(AndroidJUnit4.class)
83 @SmallTest
84 public class EthernetNetworkFactoryTest {
85     private static final int TIMEOUT_MS = 2_000;
86     private static final String TEST_IFACE = "test123";
87     private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null;
88     private static final String IP_ADDR = "192.0.2.2/25";
89     private static final LinkAddress LINK_ADDR = new LinkAddress(IP_ADDR);
90     private static final String HW_ADDR = "01:02:03:04:05:06";
91     private TestLooper mLooper;
92     private Handler mHandler;
93     private EthernetNetworkFactory mNetFactory = null;
94     private IpClientCallbacks mIpClientCallbacks;
95     private NetworkOfferCallback mNetworkOfferCallback;
96     private NetworkRequest mRequestToKeepNetworkUp;
97     @Mock private Context mContext;
98     @Mock private Resources mResources;
99     @Mock private EthernetNetworkFactory.Dependencies mDeps;
100     @Mock private IpClientManager mIpClient;
101     @Mock private EthernetNetworkAgent mNetworkAgent;
102     @Mock private InterfaceParams mInterfaceParams;
103     @Mock private Network mMockNetwork;
104     @Mock private NetworkProvider mNetworkProvider;
105 
106     @Before
setUp()107     public void setUp() throws Exception {
108         MockitoAnnotations.initMocks(this);
109         setupNetworkAgentMock();
110         setupIpClientMock();
111         setupContext();
112     }
113 
114     //TODO: Move away from usage of TestLooper in order to move this logic back into @Before.
initEthernetNetworkFactory()115     private void initEthernetNetworkFactory() {
116         mLooper = new TestLooper();
117         mHandler = new Handler(mLooper.getLooper());
118         mNetFactory = new EthernetNetworkFactory(mHandler, mContext, mNetworkProvider, mDeps);
119     }
120 
setupNetworkAgentMock()121     private void setupNetworkAgentMock() {
122         when(mDeps.makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any()))
123                 .thenAnswer(new AnswerWithArguments() {
124                                        public EthernetNetworkAgent answer(
125                                                Context context,
126                                                Looper looper,
127                                                NetworkCapabilities nc,
128                                                LinkProperties lp,
129                                                NetworkAgentConfig config,
130                                                NetworkProvider provider,
131                                                EthernetNetworkAgent.Callbacks cb) {
132                                            when(mNetworkAgent.getCallbacks()).thenReturn(cb);
133                                            when(mNetworkAgent.getNetwork())
134                                                    .thenReturn(mMockNetwork);
135                                            return mNetworkAgent;
136                                        }
137                                    }
138         );
139     }
140 
setupIpClientMock()141     private void setupIpClientMock() throws Exception {
142         doAnswer(inv -> {
143             // these tests only support one concurrent IpClient, so make sure we do not accidentally
144             // create a mess.
145             assertNull("An IpClient has already been created.", mIpClientCallbacks);
146 
147             mIpClientCallbacks = inv.getArgument(2);
148             mIpClientCallbacks.onIpClientCreated(null);
149             mLooper.dispatchAll();
150             return null;
151         }).when(mDeps).makeIpClient(any(Context.class), anyString(), any());
152 
153         doAnswer(inv -> {
154             mIpClientCallbacks.onQuit();
155             mLooper.dispatchAll();
156             mIpClientCallbacks = null;
157             return null;
158         }).when(mIpClient).shutdown();
159 
160         when(mDeps.makeIpClientManager(any())).thenReturn(mIpClient);
161     }
162 
triggerOnProvisioningSuccess()163     private void triggerOnProvisioningSuccess() {
164         mIpClientCallbacks.onProvisioningSuccess(new LinkProperties());
165         mLooper.dispatchAll();
166     }
167 
triggerOnProvisioningFailure()168     private void triggerOnProvisioningFailure() {
169         mIpClientCallbacks.onProvisioningFailure(new LinkProperties());
170         mLooper.dispatchAll();
171     }
172 
triggerOnReachabilityLost()173     private void triggerOnReachabilityLost() {
174         mIpClientCallbacks.onReachabilityLost("ReachabilityLost");
175         mLooper.dispatchAll();
176     }
177 
setupContext()178     private void setupContext() {
179         when(mDeps.getTcpBufferSizesFromResource(eq(mContext))).thenReturn("");
180     }
181 
182     @After
tearDown()183     public void tearDown() {
184         // looper is shared with the network agents, so there may still be messages to dispatch on
185         // tear down.
186         mLooper.dispatchAll();
187     }
188 
createDefaultFilterCaps()189     private NetworkCapabilities createDefaultFilterCaps() {
190         return NetworkCapabilities.Builder.withoutDefaultCapabilities()
191                 .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
192                 .build();
193     }
194 
createInterfaceCapsBuilder(final int transportType)195     private NetworkCapabilities.Builder createInterfaceCapsBuilder(final int transportType) {
196         return new NetworkCapabilities.Builder()
197                 .addTransportType(transportType)
198                 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
199                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
200     }
201 
createDefaultRequestBuilder()202     private NetworkRequest.Builder createDefaultRequestBuilder() {
203         return new NetworkRequest.Builder()
204                 .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
205                 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
206     }
207 
createDefaultRequest()208     private NetworkRequest createDefaultRequest() {
209         return createDefaultRequestBuilder().build();
210     }
211 
createDefaultIpConfig()212     private IpConfiguration createDefaultIpConfig() {
213         IpConfiguration ipConfig = new IpConfiguration();
214         ipConfig.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
215         ipConfig.setProxySettings(IpConfiguration.ProxySettings.NONE);
216         return ipConfig;
217     }
218 
219     /**
220      * Create an {@link IpConfiguration} with an associated {@link StaticIpConfiguration}.
221      *
222      * @return {@link IpConfiguration} with its {@link StaticIpConfiguration} set.
223      */
createStaticIpConfig()224     private IpConfiguration createStaticIpConfig() {
225         final IpConfiguration ipConfig = new IpConfiguration();
226         ipConfig.setIpAssignment(IpConfiguration.IpAssignment.STATIC);
227         ipConfig.setStaticIpConfiguration(
228                 new StaticIpConfiguration.Builder().setIpAddress(LINK_ADDR).build());
229         return ipConfig;
230     }
231 
232     // creates an interface with provisioning in progress (since updating the interface link state
233     // automatically starts the provisioning process)
createInterfaceUndergoingProvisioning(String iface)234     private void createInterfaceUndergoingProvisioning(String iface) {
235         // Default to the ethernet transport type.
236         createInterfaceUndergoingProvisioning(iface, NetworkCapabilities.TRANSPORT_ETHERNET);
237     }
238 
createInterfaceUndergoingProvisioning( @onNull final String iface, final int transportType)239     private void createInterfaceUndergoingProvisioning(
240             @NonNull final String iface, final int transportType) {
241         final IpConfiguration ipConfig = createDefaultIpConfig();
242         mNetFactory.addInterface(iface, HW_ADDR, ipConfig,
243                 createInterfaceCapsBuilder(transportType).build());
244         assertTrue(mNetFactory.updateInterfaceLinkState(iface, true, NULL_LISTENER));
245 
246         ArgumentCaptor<NetworkOfferCallback> captor = ArgumentCaptor.forClass(
247                 NetworkOfferCallback.class);
248         verify(mNetworkProvider).registerNetworkOffer(any(), any(), any(), captor.capture());
249         mRequestToKeepNetworkUp = createDefaultRequest();
250         mNetworkOfferCallback = captor.getValue();
251         mNetworkOfferCallback.onNetworkNeeded(mRequestToKeepNetworkUp);
252 
253         verifyStart(ipConfig);
254         clearInvocations(mDeps);
255         clearInvocations(mIpClient);
256         clearInvocations(mNetworkProvider);
257     }
258 
259     // creates a provisioned interface
createAndVerifyProvisionedInterface(String iface)260     private void createAndVerifyProvisionedInterface(String iface) throws Exception {
261         // Default to the ethernet transport type.
262         createAndVerifyProvisionedInterface(iface, NetworkCapabilities.TRANSPORT_ETHERNET,
263                 ConnectivityManager.TYPE_ETHERNET);
264     }
265 
createVerifyAndRemoveProvisionedInterface(final int transportType, final int expectedLegacyType)266     private void createVerifyAndRemoveProvisionedInterface(final int transportType,
267             final int expectedLegacyType) throws Exception {
268         createAndVerifyProvisionedInterface(TEST_IFACE, transportType,
269                 expectedLegacyType);
270         mNetFactory.removeInterface(TEST_IFACE);
271     }
272 
createAndVerifyProvisionedInterface( @onNull final String iface, final int transportType, final int expectedLegacyType)273     private void createAndVerifyProvisionedInterface(
274             @NonNull final String iface, final int transportType, final int expectedLegacyType)
275             throws Exception {
276         createInterfaceUndergoingProvisioning(iface, transportType);
277         triggerOnProvisioningSuccess();
278         // provisioning succeeded, verify that the network agent is created, registered, marked
279         // as connected and legacy type are correctly set.
280         final ArgumentCaptor<NetworkCapabilities> ncCaptor = ArgumentCaptor.forClass(
281                 NetworkCapabilities.class);
282         verify(mDeps).makeEthernetNetworkAgent(any(), any(), ncCaptor.capture(), any(),
283                 argThat(x -> x.getLegacyType() == expectedLegacyType), any(), any());
284         assertEquals(
285                 new EthernetNetworkSpecifier(iface), ncCaptor.getValue().getNetworkSpecifier());
286         verifyNetworkAgentRegistersAndConnects();
287         clearInvocations(mDeps);
288         clearInvocations(mNetworkAgent);
289     }
290 
291     // creates an unprovisioned interface
createUnprovisionedInterface(String iface)292     private void createUnprovisionedInterface(String iface) throws Exception {
293         // To create an unprovisioned interface, provision and then "stop" it, i.e. stop its
294         // NetworkAgent and IpClient. One way this can be done is by provisioning an interface and
295         // then calling onNetworkUnwanted.
296         mNetFactory.addInterface(iface, HW_ADDR, createDefaultIpConfig(),
297                 createInterfaceCapsBuilder(NetworkCapabilities.TRANSPORT_ETHERNET).build());
298         assertTrue(mNetFactory.updateInterfaceLinkState(iface, true, NULL_LISTENER));
299 
300         clearInvocations(mIpClient);
301         clearInvocations(mNetworkAgent);
302     }
303 
304     @Test
testUpdateInterfaceLinkStateForActiveProvisioningInterface()305     public void testUpdateInterfaceLinkStateForActiveProvisioningInterface() throws Exception {
306         initEthernetNetworkFactory();
307         createInterfaceUndergoingProvisioning(TEST_IFACE);
308         final TestNetworkManagementListener listener = new TestNetworkManagementListener();
309 
310         // verify that the IpClient gets shut down when interface state changes to down.
311         final boolean ret =
312                 mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */, listener);
313 
314         assertTrue(ret);
315         verify(mIpClient).shutdown();
316         assertEquals(listener.expectOnResult(), TEST_IFACE);
317     }
318 
319     @Test
testUpdateInterfaceLinkStateForProvisionedInterface()320     public void testUpdateInterfaceLinkStateForProvisionedInterface() throws Exception {
321         initEthernetNetworkFactory();
322         createAndVerifyProvisionedInterface(TEST_IFACE);
323         final TestNetworkManagementListener listenerDown = new TestNetworkManagementListener();
324         final TestNetworkManagementListener listenerUp = new TestNetworkManagementListener();
325 
326         final boolean retDown =
327                 mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */, listenerDown);
328 
329         assertTrue(retDown);
330         verifyStop();
331         assertEquals(listenerDown.expectOnResult(), TEST_IFACE);
332 
333         final boolean retUp =
334                 mNetFactory.updateInterfaceLinkState(TEST_IFACE, true /* up */, listenerUp);
335 
336         assertTrue(retUp);
337         assertEquals(listenerUp.expectOnResult(), TEST_IFACE);
338     }
339 
340     @Test
testUpdateInterfaceLinkStateForUnprovisionedInterface()341     public void testUpdateInterfaceLinkStateForUnprovisionedInterface() throws Exception {
342         initEthernetNetworkFactory();
343         createUnprovisionedInterface(TEST_IFACE);
344         final TestNetworkManagementListener listener = new TestNetworkManagementListener();
345 
346         final boolean ret =
347                 mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */, listener);
348 
349         assertTrue(ret);
350         // There should not be an active IPClient or NetworkAgent.
351         verify(mDeps, never()).makeIpClient(any(), any(), any());
352         verify(mDeps, never())
353                 .makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any());
354         assertEquals(listener.expectOnResult(), TEST_IFACE);
355     }
356 
357     @Test
testUpdateInterfaceLinkStateForNonExistingInterface()358     public void testUpdateInterfaceLinkStateForNonExistingInterface() throws Exception {
359         initEthernetNetworkFactory();
360         final TestNetworkManagementListener listener = new TestNetworkManagementListener();
361 
362         // if interface was never added, link state cannot be updated.
363         final boolean ret =
364                 mNetFactory.updateInterfaceLinkState(TEST_IFACE, true /* up */, listener);
365 
366         assertFalse(ret);
367         verifyNoStopOrStart();
368         listener.expectOnError();
369     }
370 
371     @Test
testUpdateInterfaceLinkStateWithNoChanges()372     public void testUpdateInterfaceLinkStateWithNoChanges() throws Exception {
373         initEthernetNetworkFactory();
374         createAndVerifyProvisionedInterface(TEST_IFACE);
375         final TestNetworkManagementListener listener = new TestNetworkManagementListener();
376 
377         final boolean ret =
378                 mNetFactory.updateInterfaceLinkState(TEST_IFACE, true /* up */, listener);
379 
380         assertFalse(ret);
381         verifyNoStopOrStart();
382         listener.expectOnError();
383     }
384 
385     @Test
testProvisioningLoss()386     public void testProvisioningLoss() throws Exception {
387         initEthernetNetworkFactory();
388         when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
389         createAndVerifyProvisionedInterface(TEST_IFACE);
390 
391         triggerOnProvisioningFailure();
392         verifyStop();
393         // provisioning loss should trigger a retry, since the interface is still there
394         verify(mIpClient).startProvisioning(any());
395     }
396 
397     @Test
testProvisioningLossForDisappearedInterface()398     public void testProvisioningLossForDisappearedInterface() throws Exception {
399         initEthernetNetworkFactory();
400         // mocked method returns null by default, but just to be explicit in the test:
401         when(mDeps.getNetworkInterfaceByName(eq(TEST_IFACE))).thenReturn(null);
402 
403         createAndVerifyProvisionedInterface(TEST_IFACE);
404         triggerOnProvisioningFailure();
405 
406         // the interface disappeared and getNetworkInterfaceByName returns null, we should not retry
407         verify(mIpClient, never()).startProvisioning(any());
408         verifyNoStopOrStart();
409     }
410 
verifyNoStopOrStart()411     private void verifyNoStopOrStart() {
412         verify(mNetworkAgent, never()).register();
413         verify(mIpClient, never()).shutdown();
414         verify(mNetworkAgent, never()).unregister();
415         verify(mIpClient, never()).startProvisioning(any());
416     }
417 
418     @Test
testLinkPropertiesChanged()419     public void testLinkPropertiesChanged() throws Exception {
420         initEthernetNetworkFactory();
421         createAndVerifyProvisionedInterface(TEST_IFACE);
422 
423         LinkProperties lp = new LinkProperties();
424         mIpClientCallbacks.onLinkPropertiesChange(lp);
425         mLooper.dispatchAll();
426         verify(mNetworkAgent).sendLinkPropertiesImpl(same(lp));
427     }
428 
429     @Test
testNetworkUnwanted()430     public void testNetworkUnwanted() throws Exception {
431         initEthernetNetworkFactory();
432         createAndVerifyProvisionedInterface(TEST_IFACE);
433 
434         mNetworkAgent.getCallbacks().onNetworkUnwanted();
435         mLooper.dispatchAll();
436         verifyStop();
437     }
438 
439     @Test
testNetworkUnwantedWithStaleNetworkAgent()440     public void testNetworkUnwantedWithStaleNetworkAgent() throws Exception {
441         initEthernetNetworkFactory();
442         // ensures provisioning is restarted after provisioning loss
443         when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
444         createAndVerifyProvisionedInterface(TEST_IFACE);
445 
446         EthernetNetworkAgent.Callbacks oldCbs = mNetworkAgent.getCallbacks();
447         // replace network agent in EthernetNetworkFactory
448         // Loss of provisioning will restart the ip client and network agent.
449         triggerOnProvisioningFailure();
450         verify(mDeps).makeIpClient(any(), any(), any());
451 
452         triggerOnProvisioningSuccess();
453         verify(mDeps).makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any());
454 
455         // verify that unwanted is ignored
456         clearInvocations(mIpClient);
457         clearInvocations(mNetworkAgent);
458         oldCbs.onNetworkUnwanted();
459         verify(mIpClient, never()).shutdown();
460         verify(mNetworkAgent, never()).unregister();
461     }
462 
463     @Test
testTransportOverrideIsCorrectlySet()464     public void testTransportOverrideIsCorrectlySet() throws Exception {
465         initEthernetNetworkFactory();
466         // createProvisionedInterface() has verifications in place for transport override
467         // functionality which for EthernetNetworkFactory is network score and legacy type mappings.
468         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_ETHERNET,
469                 ConnectivityManager.TYPE_ETHERNET);
470         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_BLUETOOTH,
471                 ConnectivityManager.TYPE_BLUETOOTH);
472         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_WIFI,
473                 ConnectivityManager.TYPE_WIFI);
474         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_CELLULAR,
475                 ConnectivityManager.TYPE_MOBILE);
476         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_LOWPAN,
477                 ConnectivityManager.TYPE_NONE);
478         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_WIFI_AWARE,
479                 ConnectivityManager.TYPE_NONE);
480         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_TEST,
481                 ConnectivityManager.TYPE_NONE);
482     }
483 
484     @Test
testReachabilityLoss()485     public void testReachabilityLoss() throws Exception {
486         initEthernetNetworkFactory();
487         createAndVerifyProvisionedInterface(TEST_IFACE);
488 
489         triggerOnReachabilityLost();
490 
491         // Reachability loss should trigger a stop and start, since the interface is still there
492         verifyRestart(createDefaultIpConfig());
493     }
494 
getStaleIpClientCallbacks()495     private IpClientCallbacks getStaleIpClientCallbacks() throws Exception {
496         createAndVerifyProvisionedInterface(TEST_IFACE);
497         final IpClientCallbacks staleIpClientCallbacks = mIpClientCallbacks;
498         mNetFactory.removeInterface(TEST_IFACE);
499         verifyStop();
500         assertNotSame(mIpClientCallbacks, staleIpClientCallbacks);
501         return staleIpClientCallbacks;
502     }
503 
504     @Test
testIgnoreOnIpLayerStartedCallbackForStaleCallback()505     public void testIgnoreOnIpLayerStartedCallbackForStaleCallback() throws Exception {
506         initEthernetNetworkFactory();
507         final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks();
508 
509         staleIpClientCallbacks.onProvisioningSuccess(new LinkProperties());
510         mLooper.dispatchAll();
511 
512         verify(mIpClient, never()).startProvisioning(any());
513         verify(mNetworkAgent, never()).register();
514     }
515 
516     @Test
testIgnoreOnIpLayerStoppedCallbackForStaleCallback()517     public void testIgnoreOnIpLayerStoppedCallbackForStaleCallback() throws Exception {
518         initEthernetNetworkFactory();
519         when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
520         final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks();
521 
522         staleIpClientCallbacks.onProvisioningFailure(new LinkProperties());
523         mLooper.dispatchAll();
524 
525         verify(mIpClient, never()).startProvisioning(any());
526     }
527 
528     @Test
testIgnoreLinkPropertiesCallbackForStaleCallback()529     public void testIgnoreLinkPropertiesCallbackForStaleCallback() throws Exception {
530         initEthernetNetworkFactory();
531         final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks();
532         final LinkProperties lp = new LinkProperties();
533 
534         staleIpClientCallbacks.onLinkPropertiesChange(lp);
535         mLooper.dispatchAll();
536 
537         verify(mNetworkAgent, never()).sendLinkPropertiesImpl(eq(lp));
538     }
539 
540     @Test
testIgnoreNeighborLossCallbackForStaleCallback()541     public void testIgnoreNeighborLossCallbackForStaleCallback() throws Exception {
542         initEthernetNetworkFactory();
543         final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks();
544 
545         staleIpClientCallbacks.onReachabilityLost("Neighbor Lost");
546         mLooper.dispatchAll();
547 
548         verify(mIpClient, never()).startProvisioning(any());
549         verify(mNetworkAgent, never()).register();
550     }
551 
verifyRestart(@onNull final IpConfiguration ipConfig)552     private void verifyRestart(@NonNull final IpConfiguration ipConfig) {
553         verifyStop();
554         verifyStart(ipConfig);
555     }
556 
verifyStart(@onNull final IpConfiguration ipConfig)557     private void verifyStart(@NonNull final IpConfiguration ipConfig) {
558         verify(mDeps).makeIpClient(any(Context.class), anyString(), any());
559         verify(mIpClient).startProvisioning(
560                 argThat(x -> Objects.equals(x.mStaticIpConfig, ipConfig.getStaticIpConfiguration()))
561         );
562     }
563 
verifyStop()564     private void verifyStop() {
565         verify(mIpClient).shutdown();
566         verify(mNetworkAgent).unregister();
567     }
568 
verifyNetworkAgentRegistersAndConnects()569     private void verifyNetworkAgentRegistersAndConnects() {
570         verify(mNetworkAgent).register();
571         verify(mNetworkAgent).markConnected();
572     }
573 
574     private static final class TestNetworkManagementListener
575             implements INetworkInterfaceOutcomeReceiver {
576         private final CompletableFuture<String> mResult = new CompletableFuture<>();
577 
578         @Override
onResult(@onNull String iface)579         public void onResult(@NonNull String iface) {
580             mResult.complete(iface);
581         }
582 
583         @Override
onError(@onNull EthernetNetworkManagementException exception)584         public void onError(@NonNull EthernetNetworkManagementException exception) {
585             mResult.completeExceptionally(exception);
586         }
587 
expectOnResult()588         String expectOnResult() throws Exception {
589             return mResult.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
590         }
591 
expectOnError()592         void expectOnError() throws Exception {
593             assertThrows(EthernetNetworkManagementException.class, () -> {
594                 try {
595                     mResult.get();
596                 } catch (ExecutionException e) {
597                     throw e.getCause();
598                 }
599             });
600         }
601 
602         @Override
asBinder()603         public IBinder asBinder() {
604             return null;
605         }
606     }
607 
608     @Test
testUpdateInterfaceCallsListenerCorrectlyOnSuccess()609     public void testUpdateInterfaceCallsListenerCorrectlyOnSuccess() throws Exception {
610         initEthernetNetworkFactory();
611         createAndVerifyProvisionedInterface(TEST_IFACE);
612         final NetworkCapabilities capabilities = createDefaultFilterCaps();
613         final IpConfiguration ipConfiguration = createStaticIpConfig();
614         final TestNetworkManagementListener listener = new TestNetworkManagementListener();
615 
616         mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener);
617         triggerOnProvisioningSuccess();
618 
619         assertEquals(listener.expectOnResult(), TEST_IFACE);
620     }
621 
622     @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
623     @Test
testUpdateInterfaceAbortsOnConcurrentRemoveInterface()624     public void testUpdateInterfaceAbortsOnConcurrentRemoveInterface() throws Exception {
625         initEthernetNetworkFactory();
626         verifyNetworkManagementCallIsAbortedWhenInterrupted(
627                 TEST_IFACE,
628                 () -> mNetFactory.removeInterface(TEST_IFACE));
629     }
630 
631     @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
632     @Test
testUpdateInterfaceAbortsOnConcurrentUpdateInterfaceLinkState()633     public void testUpdateInterfaceAbortsOnConcurrentUpdateInterfaceLinkState() throws Exception {
634         initEthernetNetworkFactory();
635         verifyNetworkManagementCallIsAbortedWhenInterrupted(
636                 TEST_IFACE,
637                 () -> mNetFactory.updateInterfaceLinkState(TEST_IFACE, false, NULL_LISTENER));
638     }
639 
640     @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
641     @Test
testUpdateInterfaceAbortsOnNetworkUneededRemovesAllRequests()642     public void testUpdateInterfaceAbortsOnNetworkUneededRemovesAllRequests() throws Exception {
643         initEthernetNetworkFactory();
644         verifyNetworkManagementCallIsAbortedWhenInterrupted(
645                 TEST_IFACE,
646                 () -> mNetworkOfferCallback.onNetworkUnneeded(mRequestToKeepNetworkUp));
647     }
648 
649     @Test
testUpdateInterfaceCallsListenerCorrectlyOnConcurrentRequests()650     public void testUpdateInterfaceCallsListenerCorrectlyOnConcurrentRequests() throws Exception {
651         initEthernetNetworkFactory();
652         final NetworkCapabilities capabilities = createDefaultFilterCaps();
653         final IpConfiguration ipConfiguration = createStaticIpConfig();
654         final TestNetworkManagementListener successfulListener =
655                 new TestNetworkManagementListener();
656 
657         // If two calls come in before the first one completes, the first listener will be aborted
658         // and the second one will be successful.
659         verifyNetworkManagementCallIsAbortedWhenInterrupted(
660                 TEST_IFACE,
661                 () -> {
662                     mNetFactory.updateInterface(
663                             TEST_IFACE, ipConfiguration, capabilities, successfulListener);
664                     triggerOnProvisioningSuccess();
665                 });
666 
667         assertEquals(successfulListener.expectOnResult(), TEST_IFACE);
668     }
669 
verifyNetworkManagementCallIsAbortedWhenInterrupted( @onNull final String iface, @NonNull final Runnable interruptingRunnable)670     private void verifyNetworkManagementCallIsAbortedWhenInterrupted(
671             @NonNull final String iface,
672             @NonNull final Runnable interruptingRunnable) throws Exception {
673         createAndVerifyProvisionedInterface(iface);
674         final NetworkCapabilities capabilities = createDefaultFilterCaps();
675         final IpConfiguration ipConfiguration = createStaticIpConfig();
676         final TestNetworkManagementListener failedListener = new TestNetworkManagementListener();
677 
678         // An active update request will be aborted on interrupt prior to provisioning completion.
679         mNetFactory.updateInterface(iface, ipConfiguration, capabilities, failedListener);
680         interruptingRunnable.run();
681 
682         failedListener.expectOnError();
683     }
684 
685     @Test
testUpdateInterfaceRestartsAgentCorrectly()686     public void testUpdateInterfaceRestartsAgentCorrectly() throws Exception {
687         initEthernetNetworkFactory();
688         createAndVerifyProvisionedInterface(TEST_IFACE);
689         final NetworkCapabilities capabilities = createDefaultFilterCaps();
690         final IpConfiguration ipConfiguration = createStaticIpConfig();
691         final TestNetworkManagementListener listener = new TestNetworkManagementListener();
692 
693         mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener);
694         triggerOnProvisioningSuccess();
695 
696         assertEquals(listener.expectOnResult(), TEST_IFACE);
697         verify(mDeps).makeEthernetNetworkAgent(any(), any(),
698                 eq(capabilities), any(), any(), any(), any());
699         verifyRestart(ipConfiguration);
700     }
701 
702     @Test
testUpdateInterfaceForNonExistingInterface()703     public void testUpdateInterfaceForNonExistingInterface() throws Exception {
704         initEthernetNetworkFactory();
705         // No interface exists due to not calling createAndVerifyProvisionedInterface(...).
706         final NetworkCapabilities capabilities = createDefaultFilterCaps();
707         final IpConfiguration ipConfiguration = createStaticIpConfig();
708         final TestNetworkManagementListener listener = new TestNetworkManagementListener();
709 
710         mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener);
711 
712         verifyNoStopOrStart();
713         listener.expectOnError();
714     }
715 
716     @Test
testUpdateInterfaceWithNullIpConfiguration()717     public void testUpdateInterfaceWithNullIpConfiguration() throws Exception {
718         initEthernetNetworkFactory();
719         createAndVerifyProvisionedInterface(TEST_IFACE);
720 
721         final IpConfiguration initialIpConfig = createStaticIpConfig();
722         mNetFactory.updateInterface(TEST_IFACE, initialIpConfig, null /*capabilities*/,
723                 null /*listener*/);
724         triggerOnProvisioningSuccess();
725         verifyRestart(initialIpConfig);
726 
727         // TODO: have verifyXyz functions clear invocations.
728         clearInvocations(mDeps);
729         clearInvocations(mIpClient);
730         clearInvocations(mNetworkAgent);
731 
732 
733         // verify that sending a null ipConfig does not update the current ipConfig.
734         mNetFactory.updateInterface(TEST_IFACE, null /*ipConfig*/, null /*capabilities*/,
735                 null /*listener*/);
736         triggerOnProvisioningSuccess();
737         verifyRestart(initialIpConfig);
738     }
739 }
740