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 org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotSame;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyString;
26 import static org.mockito.ArgumentMatchers.argThat;
27 import static org.mockito.ArgumentMatchers.eq;
28 import static org.mockito.Mockito.clearInvocations;
29 import static org.mockito.Mockito.doAnswer;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
33 
34 import android.annotation.NonNull;
35 import android.app.test.MockAnswerUtil.AnswerWithArguments;
36 import android.content.Context;
37 import android.content.res.Resources;
38 import android.net.ConnectivityManager;
39 import android.net.EthernetNetworkSpecifier;
40 import android.net.IpConfiguration;
41 import android.net.LinkAddress;
42 import android.net.LinkProperties;
43 import android.net.Network;
44 import android.net.NetworkAgentConfig;
45 import android.net.NetworkCapabilities;
46 import android.net.NetworkProvider;
47 import android.net.NetworkProvider.NetworkOfferCallback;
48 import android.net.NetworkRequest;
49 import android.net.StaticIpConfiguration;
50 import android.net.ip.IpClientCallbacks;
51 import android.net.ip.IpClientManager;
52 import android.os.Build;
53 import android.os.Handler;
54 import android.os.Looper;
55 import android.os.test.TestLooper;
56 
57 import androidx.test.filters.SmallTest;
58 
59 import com.android.net.module.util.InterfaceParams;
60 import com.android.testutils.DevSdkIgnoreRule;
61 import com.android.testutils.DevSdkIgnoreRunner;
62 
63 import org.junit.After;
64 import org.junit.Before;
65 import org.junit.Test;
66 import org.junit.runner.RunWith;
67 import org.mockito.ArgumentCaptor;
68 import org.mockito.Mock;
69 import org.mockito.MockitoAnnotations;
70 
71 import java.util.Objects;
72 
73 @SmallTest
74 @RunWith(DevSdkIgnoreRunner.class)
75 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
76 public class EthernetNetworkFactoryTest {
77     private static final int TIMEOUT_MS = 2_000;
78     private static final String TEST_IFACE = "test123";
79     private static final String IP_ADDR = "192.0.2.2/25";
80     private static final LinkAddress LINK_ADDR = new LinkAddress(IP_ADDR);
81     private static final String HW_ADDR = "01:02:03:04:05:06";
82     private TestLooper mLooper;
83     private Handler mHandler;
84     private EthernetNetworkFactory mNetFactory = null;
85     private IpClientCallbacks mIpClientCallbacks;
86     private NetworkOfferCallback mNetworkOfferCallback;
87     private NetworkRequest mRequestToKeepNetworkUp;
88     @Mock private Context mContext;
89     @Mock private Resources mResources;
90     @Mock private EthernetNetworkFactory.Dependencies mDeps;
91     @Mock private IpClientManager mIpClient;
92     @Mock private EthernetNetworkAgent mNetworkAgent;
93     @Mock private InterfaceParams mInterfaceParams;
94     @Mock private Network mMockNetwork;
95     @Mock private NetworkProvider mNetworkProvider;
96 
97     @Before
setUp()98     public void setUp() throws Exception {
99         MockitoAnnotations.initMocks(this);
100         setupNetworkAgentMock();
101         setupIpClientMock();
102         setupContext();
103     }
104 
105     //TODO: Move away from usage of TestLooper in order to move this logic back into @Before.
initEthernetNetworkFactory()106     private void initEthernetNetworkFactory() {
107         mLooper = new TestLooper();
108         mHandler = new Handler(mLooper.getLooper());
109         mNetFactory = new EthernetNetworkFactory(mHandler, mContext, mNetworkProvider, mDeps);
110     }
111 
setupNetworkAgentMock()112     private void setupNetworkAgentMock() {
113         when(mDeps.makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any()))
114                 .thenAnswer(new AnswerWithArguments() {
115                                        public EthernetNetworkAgent answer(
116                                                Context context,
117                                                Looper looper,
118                                                NetworkCapabilities nc,
119                                                LinkProperties lp,
120                                                NetworkAgentConfig config,
121                                                NetworkProvider provider,
122                                                EthernetNetworkAgent.Callbacks cb) {
123                                            when(mNetworkAgent.getCallbacks()).thenReturn(cb);
124                                            when(mNetworkAgent.getNetwork())
125                                                    .thenReturn(mMockNetwork);
126                                            return mNetworkAgent;
127                                        }
128                                    }
129         );
130     }
131 
setupIpClientMock()132     private void setupIpClientMock() throws Exception {
133         doAnswer(inv -> {
134             // these tests only support one concurrent IpClient, so make sure we do not accidentally
135             // create a mess.
136             assertNull("An IpClient has already been created.", mIpClientCallbacks);
137 
138             mIpClientCallbacks = inv.getArgument(2);
139             mIpClientCallbacks.onIpClientCreated(null);
140             mLooper.dispatchAll();
141             return null;
142         }).when(mDeps).makeIpClient(any(Context.class), anyString(), any());
143 
144         doAnswer(inv -> {
145             mIpClientCallbacks.onQuit();
146             mLooper.dispatchAll();
147             mIpClientCallbacks = null;
148             return null;
149         }).when(mIpClient).shutdown();
150 
151         when(mDeps.makeIpClientManager(any())).thenReturn(mIpClient);
152     }
153 
triggerOnProvisioningSuccess()154     private void triggerOnProvisioningSuccess() {
155         mIpClientCallbacks.onProvisioningSuccess(new LinkProperties());
156         mLooper.dispatchAll();
157     }
158 
triggerOnProvisioningFailure()159     private void triggerOnProvisioningFailure() {
160         mIpClientCallbacks.onProvisioningFailure(new LinkProperties());
161         mLooper.dispatchAll();
162     }
163 
triggerOnReachabilityLost()164     private void triggerOnReachabilityLost() {
165         mIpClientCallbacks.onReachabilityLost("ReachabilityLost");
166         mLooper.dispatchAll();
167     }
168 
setupContext()169     private void setupContext() {
170         when(mDeps.getTcpBufferSizesFromResource(eq(mContext))).thenReturn("");
171     }
172 
173     @After
tearDown()174     public void tearDown() {
175         // looper is shared with the network agents, so there may still be messages to dispatch on
176         // tear down.
177         mLooper.dispatchAll();
178     }
179 
createDefaultFilterCaps()180     private NetworkCapabilities createDefaultFilterCaps() {
181         return NetworkCapabilities.Builder.withoutDefaultCapabilities()
182                 .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
183                 .build();
184     }
185 
createInterfaceCapsBuilder(final int transportType)186     private NetworkCapabilities.Builder createInterfaceCapsBuilder(final int transportType) {
187         return new NetworkCapabilities.Builder()
188                 .addTransportType(transportType)
189                 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
190                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
191     }
192 
createDefaultRequestBuilder()193     private NetworkRequest.Builder createDefaultRequestBuilder() {
194         return new NetworkRequest.Builder()
195                 .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
196                 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
197     }
198 
createDefaultRequest()199     private NetworkRequest createDefaultRequest() {
200         return createDefaultRequestBuilder().build();
201     }
202 
createDefaultIpConfig()203     private IpConfiguration createDefaultIpConfig() {
204         IpConfiguration ipConfig = new IpConfiguration();
205         ipConfig.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
206         ipConfig.setProxySettings(IpConfiguration.ProxySettings.NONE);
207         return ipConfig;
208     }
209 
210     /**
211      * Create an {@link IpConfiguration} with an associated {@link StaticIpConfiguration}.
212      *
213      * @return {@link IpConfiguration} with its {@link StaticIpConfiguration} set.
214      */
createStaticIpConfig()215     private IpConfiguration createStaticIpConfig() {
216         final IpConfiguration ipConfig = new IpConfiguration();
217         ipConfig.setIpAssignment(IpConfiguration.IpAssignment.STATIC);
218         ipConfig.setStaticIpConfiguration(
219                 new StaticIpConfiguration.Builder().setIpAddress(LINK_ADDR).build());
220         return ipConfig;
221     }
222 
223     // creates an interface with provisioning in progress (since updating the interface link state
224     // automatically starts the provisioning process)
createInterfaceUndergoingProvisioning(String iface)225     private void createInterfaceUndergoingProvisioning(String iface) {
226         // Default to the ethernet transport type.
227         createInterfaceUndergoingProvisioning(iface, NetworkCapabilities.TRANSPORT_ETHERNET);
228     }
229 
createInterfaceUndergoingProvisioning( @onNull final String iface, final int transportType)230     private void createInterfaceUndergoingProvisioning(
231             @NonNull final String iface, final int transportType) {
232         final IpConfiguration ipConfig = createDefaultIpConfig();
233         mNetFactory.addInterface(iface, HW_ADDR, ipConfig,
234                 createInterfaceCapsBuilder(transportType).build());
235         assertTrue(mNetFactory.updateInterfaceLinkState(iface, true));
236 
237         ArgumentCaptor<NetworkOfferCallback> captor = ArgumentCaptor.forClass(
238                 NetworkOfferCallback.class);
239         verify(mNetworkProvider).registerNetworkOffer(any(), any(), any(), captor.capture());
240         mRequestToKeepNetworkUp = createDefaultRequest();
241         mNetworkOfferCallback = captor.getValue();
242         mNetworkOfferCallback.onNetworkNeeded(mRequestToKeepNetworkUp);
243 
244         verifyStart(ipConfig);
245         clearInvocations(mDeps);
246         clearInvocations(mIpClient);
247         clearInvocations(mNetworkProvider);
248     }
249 
250     // creates a provisioned interface
createAndVerifyProvisionedInterface(String iface)251     private void createAndVerifyProvisionedInterface(String iface) throws Exception {
252         // Default to the ethernet transport type.
253         createAndVerifyProvisionedInterface(iface, NetworkCapabilities.TRANSPORT_ETHERNET,
254                 ConnectivityManager.TYPE_ETHERNET);
255     }
256 
createVerifyAndRemoveProvisionedInterface(final int transportType, final int expectedLegacyType)257     private void createVerifyAndRemoveProvisionedInterface(final int transportType,
258             final int expectedLegacyType) throws Exception {
259         createAndVerifyProvisionedInterface(TEST_IFACE, transportType,
260                 expectedLegacyType);
261         mNetFactory.removeInterface(TEST_IFACE);
262     }
263 
createAndVerifyProvisionedInterface( @onNull final String iface, final int transportType, final int expectedLegacyType)264     private void createAndVerifyProvisionedInterface(
265             @NonNull final String iface, final int transportType, final int expectedLegacyType)
266             throws Exception {
267         createInterfaceUndergoingProvisioning(iface, transportType);
268         triggerOnProvisioningSuccess();
269         // provisioning succeeded, verify that the network agent is created, registered, marked
270         // as connected and legacy type are correctly set.
271         final ArgumentCaptor<NetworkCapabilities> ncCaptor = ArgumentCaptor.forClass(
272                 NetworkCapabilities.class);
273         verify(mDeps).makeEthernetNetworkAgent(any(), any(), ncCaptor.capture(), any(),
274                 argThat(x -> x.getLegacyType() == expectedLegacyType), any(), any());
275         assertEquals(
276                 new EthernetNetworkSpecifier(iface), ncCaptor.getValue().getNetworkSpecifier());
277         verifyNetworkAgentRegistersAndConnects();
278         clearInvocations(mDeps);
279         clearInvocations(mNetworkAgent);
280     }
281 
282     // creates an unprovisioned interface
createUnprovisionedInterface(String iface)283     private void createUnprovisionedInterface(String iface) throws Exception {
284         // To create an unprovisioned interface, provision and then "stop" it, i.e. stop its
285         // NetworkAgent and IpClient. One way this can be done is by provisioning an interface and
286         // then calling onNetworkUnwanted.
287         mNetFactory.addInterface(iface, HW_ADDR, createDefaultIpConfig(),
288                 createInterfaceCapsBuilder(NetworkCapabilities.TRANSPORT_ETHERNET).build());
289         assertTrue(mNetFactory.updateInterfaceLinkState(iface, true));
290 
291         clearInvocations(mIpClient);
292         clearInvocations(mNetworkAgent);
293     }
294 
295     @Test
testUpdateInterfaceLinkStateForActiveProvisioningInterface()296     public void testUpdateInterfaceLinkStateForActiveProvisioningInterface() throws Exception {
297         initEthernetNetworkFactory();
298         createInterfaceUndergoingProvisioning(TEST_IFACE);
299 
300         // verify that the IpClient gets shut down when interface state changes to down.
301         final boolean ret = mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */);
302 
303         assertTrue(ret);
304         verify(mIpClient).shutdown();
305     }
306 
307     @Test
testUpdateInterfaceLinkStateForNonExistingInterface()308     public void testUpdateInterfaceLinkStateForNonExistingInterface() throws Exception {
309         initEthernetNetworkFactory();
310 
311         // if interface was never added, link state cannot be updated.
312         final boolean ret = mNetFactory.updateInterfaceLinkState(TEST_IFACE, true /* up */);
313 
314         assertFalse(ret);
315         verifyNoStopOrStart();
316     }
317 
318     @Test
testProvisioningLoss()319     public void testProvisioningLoss() throws Exception {
320         initEthernetNetworkFactory();
321         when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
322         createAndVerifyProvisionedInterface(TEST_IFACE);
323 
324         triggerOnProvisioningFailure();
325         verifyStop();
326         // provisioning loss should trigger a retry, since the interface is still there
327         verify(mIpClient).startProvisioning(any());
328     }
329 
330     @Test
testProvisioningLossForDisappearedInterface()331     public void testProvisioningLossForDisappearedInterface() throws Exception {
332         initEthernetNetworkFactory();
333         // mocked method returns null by default, but just to be explicit in the test:
334         when(mDeps.getNetworkInterfaceByName(eq(TEST_IFACE))).thenReturn(null);
335 
336         createAndVerifyProvisionedInterface(TEST_IFACE);
337         triggerOnProvisioningFailure();
338 
339         // the interface disappeared and getNetworkInterfaceByName returns null, we should not retry
340         verify(mIpClient, never()).startProvisioning(any());
341         verifyNoStopOrStart();
342     }
343 
verifyNoStopOrStart()344     private void verifyNoStopOrStart() {
345         verify(mNetworkAgent, never()).register();
346         verify(mIpClient, never()).shutdown();
347         verify(mNetworkAgent, never()).unregister();
348         verify(mIpClient, never()).startProvisioning(any());
349     }
350 
351     @Test
testNetworkUnwanted()352     public void testNetworkUnwanted() throws Exception {
353         initEthernetNetworkFactory();
354         createAndVerifyProvisionedInterface(TEST_IFACE);
355 
356         mNetworkAgent.getCallbacks().onNetworkUnwanted();
357         mLooper.dispatchAll();
358         verifyStop();
359     }
360 
361     @Test
testNetworkUnwantedWithStaleNetworkAgent()362     public void testNetworkUnwantedWithStaleNetworkAgent() throws Exception {
363         initEthernetNetworkFactory();
364         // ensures provisioning is restarted after provisioning loss
365         when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
366         createAndVerifyProvisionedInterface(TEST_IFACE);
367 
368         EthernetNetworkAgent.Callbacks oldCbs = mNetworkAgent.getCallbacks();
369         // replace network agent in EthernetNetworkFactory
370         // Loss of provisioning will restart the ip client and network agent.
371         triggerOnProvisioningFailure();
372         verify(mDeps).makeIpClient(any(), any(), any());
373 
374         triggerOnProvisioningSuccess();
375         verify(mDeps).makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any());
376 
377         // verify that unwanted is ignored
378         clearInvocations(mIpClient);
379         clearInvocations(mNetworkAgent);
380         oldCbs.onNetworkUnwanted();
381         verify(mIpClient, never()).shutdown();
382         verify(mNetworkAgent, never()).unregister();
383     }
384 
385     @Test
testTransportOverrideIsCorrectlySet()386     public void testTransportOverrideIsCorrectlySet() throws Exception {
387         initEthernetNetworkFactory();
388         // createProvisionedInterface() has verifications in place for transport override
389         // functionality which for EthernetNetworkFactory is network score and legacy type mappings.
390         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_ETHERNET,
391                 ConnectivityManager.TYPE_ETHERNET);
392         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_BLUETOOTH,
393                 ConnectivityManager.TYPE_BLUETOOTH);
394         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_WIFI,
395                 ConnectivityManager.TYPE_WIFI);
396         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_CELLULAR,
397                 ConnectivityManager.TYPE_MOBILE);
398         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_LOWPAN,
399                 ConnectivityManager.TYPE_NONE);
400         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_WIFI_AWARE,
401                 ConnectivityManager.TYPE_NONE);
402         createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_TEST,
403                 ConnectivityManager.TYPE_NONE);
404     }
405 
406     @Test
testReachabilityLoss()407     public void testReachabilityLoss() throws Exception {
408         initEthernetNetworkFactory();
409         createAndVerifyProvisionedInterface(TEST_IFACE);
410 
411         triggerOnReachabilityLost();
412 
413         // Reachability loss should trigger a stop and start, since the interface is still there
414         verifyRestart(createDefaultIpConfig());
415     }
416 
getStaleIpClientCallbacks()417     private IpClientCallbacks getStaleIpClientCallbacks() throws Exception {
418         createAndVerifyProvisionedInterface(TEST_IFACE);
419         final IpClientCallbacks staleIpClientCallbacks = mIpClientCallbacks;
420         mNetFactory.removeInterface(TEST_IFACE);
421         verifyStop();
422         assertNotSame(mIpClientCallbacks, staleIpClientCallbacks);
423         return staleIpClientCallbacks;
424     }
425 
426     @Test
testIgnoreOnIpLayerStartedCallbackForStaleCallback()427     public void testIgnoreOnIpLayerStartedCallbackForStaleCallback() throws Exception {
428         initEthernetNetworkFactory();
429         final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks();
430 
431         staleIpClientCallbacks.onProvisioningSuccess(new LinkProperties());
432         mLooper.dispatchAll();
433 
434         verify(mIpClient, never()).startProvisioning(any());
435         verify(mNetworkAgent, never()).register();
436     }
437 
438     @Test
testIgnoreOnIpLayerStoppedCallbackForStaleCallback()439     public void testIgnoreOnIpLayerStoppedCallbackForStaleCallback() throws Exception {
440         initEthernetNetworkFactory();
441         when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
442         final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks();
443 
444         staleIpClientCallbacks.onProvisioningFailure(new LinkProperties());
445         mLooper.dispatchAll();
446 
447         verify(mIpClient, never()).startProvisioning(any());
448     }
449 
450     @Test
testIgnoreLinkPropertiesCallbackForStaleCallback()451     public void testIgnoreLinkPropertiesCallbackForStaleCallback() throws Exception {
452         initEthernetNetworkFactory();
453         final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks();
454         final LinkProperties lp = new LinkProperties();
455 
456         staleIpClientCallbacks.onLinkPropertiesChange(lp);
457         mLooper.dispatchAll();
458 
459         verify(mNetworkAgent, never()).sendLinkPropertiesImpl(eq(lp));
460     }
461 
462     @Test
testIgnoreNeighborLossCallbackForStaleCallback()463     public void testIgnoreNeighborLossCallbackForStaleCallback() throws Exception {
464         initEthernetNetworkFactory();
465         final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks();
466 
467         staleIpClientCallbacks.onReachabilityLost("Neighbor Lost");
468         mLooper.dispatchAll();
469 
470         verify(mIpClient, never()).startProvisioning(any());
471         verify(mNetworkAgent, never()).register();
472     }
473 
verifyRestart(@onNull final IpConfiguration ipConfig)474     private void verifyRestart(@NonNull final IpConfiguration ipConfig) {
475         verifyStop();
476         verifyStart(ipConfig);
477     }
478 
verifyStart(@onNull final IpConfiguration ipConfig)479     private void verifyStart(@NonNull final IpConfiguration ipConfig) {
480         verify(mDeps).makeIpClient(any(Context.class), anyString(), any());
481         verify(mIpClient).startProvisioning(
482                 argThat(x -> Objects.equals(x.mStaticIpConfig, ipConfig.getStaticIpConfiguration()))
483         );
484     }
485 
verifyStop()486     private void verifyStop() {
487         verify(mIpClient).shutdown();
488         verify(mNetworkAgent).unregister();
489     }
490 
verifyNetworkAgentRegistersAndConnects()491     private void verifyNetworkAgentRegistersAndConnects() {
492         verify(mNetworkAgent).register();
493         verify(mNetworkAgent).markConnected();
494     }
495 
496     @Test
testUpdateInterfaceRestartsAgentCorrectly()497     public void testUpdateInterfaceRestartsAgentCorrectly() throws Exception {
498         initEthernetNetworkFactory();
499         createAndVerifyProvisionedInterface(TEST_IFACE);
500         final NetworkCapabilities capabilities = createDefaultFilterCaps();
501         final IpConfiguration ipConfiguration = createStaticIpConfig();
502 
503         mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities);
504         triggerOnProvisioningSuccess();
505 
506         verify(mDeps).makeEthernetNetworkAgent(any(), any(),
507                 eq(capabilities), any(), any(), any(), any());
508         verifyRestart(ipConfiguration);
509     }
510 
511     @Test
testUpdateInterfaceForNonExistingInterface()512     public void testUpdateInterfaceForNonExistingInterface() throws Exception {
513         initEthernetNetworkFactory();
514         // No interface exists due to not calling createAndVerifyProvisionedInterface(...).
515         final NetworkCapabilities capabilities = createDefaultFilterCaps();
516         final IpConfiguration ipConfiguration = createStaticIpConfig();
517 
518         mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities);
519 
520         verifyNoStopOrStart();
521     }
522 
523     @Test
testUpdateInterfaceWithNullIpConfiguration()524     public void testUpdateInterfaceWithNullIpConfiguration() throws Exception {
525         initEthernetNetworkFactory();
526         createAndVerifyProvisionedInterface(TEST_IFACE);
527 
528         final IpConfiguration initialIpConfig = createStaticIpConfig();
529         mNetFactory.updateInterface(TEST_IFACE, initialIpConfig, null /*capabilities*/);
530 
531         triggerOnProvisioningSuccess();
532         verifyRestart(initialIpConfig);
533 
534         // TODO: have verifyXyz functions clear invocations.
535         clearInvocations(mDeps);
536         clearInvocations(mIpClient);
537         clearInvocations(mNetworkAgent);
538 
539 
540         // verify that sending a null ipConfig does not update the current ipConfig.
541         mNetFactory.updateInterface(TEST_IFACE, null /*ipConfig*/, null /*capabilities*/);
542         triggerOnProvisioningSuccess();
543         verifyRestart(initialIpConfig);
544     }
545 
546     @Test
testOnNetworkNeededOnStaleNetworkOffer()547     public void testOnNetworkNeededOnStaleNetworkOffer() throws Exception {
548         initEthernetNetworkFactory();
549         createAndVerifyProvisionedInterface(TEST_IFACE);
550         mNetFactory.updateInterfaceLinkState(TEST_IFACE, false);
551         verify(mNetworkProvider).unregisterNetworkOffer(mNetworkOfferCallback);
552         // It is possible that even after a network offer is unregistered, CS still sends it
553         // onNetworkNeeded() callbacks.
554         mNetworkOfferCallback.onNetworkNeeded(createDefaultRequest());
555         verify(mIpClient, never()).startProvisioning(any());
556     }
557 
558     @Test
testGetMacAddressProvisionedInterface()559     public void testGetMacAddressProvisionedInterface() throws Exception {
560         initEthernetNetworkFactory();
561         createAndVerifyProvisionedInterface(TEST_IFACE);
562 
563         final String result = mNetFactory.getHwAddress(TEST_IFACE);
564         assertEquals(HW_ADDR, result);
565     }
566 
567     @Test
testGetMacAddressForNonExistingInterface()568     public void testGetMacAddressForNonExistingInterface() {
569         initEthernetNetworkFactory();
570 
571         final String result = mNetFactory.getHwAddress(TEST_IFACE);
572         // No interface exists due to not calling createAndVerifyProvisionedInterface(...).
573         assertNull(result);
574     }
575 }
576