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 android.net;
18 
19 import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
20 import static android.Manifest.permission.NETWORK_SETTINGS;
21 import static android.Manifest.permission.TETHER_PRIVILEGED;
22 import static android.net.TetheringManager.TETHERING_ETHERNET;
23 
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.assertNull;
27 import static org.junit.Assert.assertTrue;
28 import static org.junit.Assert.fail;
29 import static org.junit.Assume.assumeFalse;
30 import static org.junit.Assume.assumeTrue;
31 
32 import android.app.UiAutomation;
33 import android.content.Context;
34 import android.net.EthernetManager.TetheredInterfaceCallback;
35 import android.net.EthernetManager.TetheredInterfaceRequest;
36 import android.net.TetheringManager.StartTetheringCallback;
37 import android.net.TetheringManager.TetheringEventCallback;
38 import android.net.TetheringManager.TetheringRequest;
39 import android.net.dhcp.DhcpAckPacket;
40 import android.net.dhcp.DhcpOfferPacket;
41 import android.net.dhcp.DhcpPacket;
42 import android.os.Handler;
43 import android.os.HandlerThread;
44 import android.os.SystemClock;
45 import android.os.SystemProperties;
46 import android.system.Os;
47 import android.util.Log;
48 
49 import androidx.test.InstrumentationRegistry;
50 import androidx.test.filters.MediumTest;
51 import androidx.test.runner.AndroidJUnit4;
52 
53 import com.android.testutils.HandlerUtilsKt;
54 import com.android.testutils.TapPacketReader;
55 
56 import org.junit.After;
57 import org.junit.Before;
58 import org.junit.Test;
59 import org.junit.runner.RunWith;
60 
61 import java.io.FileDescriptor;
62 import java.net.Inet4Address;
63 import java.net.InterfaceAddress;
64 import java.net.NetworkInterface;
65 import java.net.SocketException;
66 import java.nio.ByteBuffer;
67 import java.util.Collection;
68 import java.util.List;
69 import java.util.Random;
70 import java.util.concurrent.CompletableFuture;
71 import java.util.concurrent.CountDownLatch;
72 import java.util.concurrent.TimeUnit;
73 import java.util.concurrent.TimeoutException;
74 
75 @RunWith(AndroidJUnit4.class)
76 @MediumTest
77 public class EthernetTetheringTest {
78 
79     private static final String TAG = EthernetTetheringTest.class.getSimpleName();
80     private static final int TIMEOUT_MS = 5000;
81     private static final int PACKET_READ_TIMEOUT_MS = 100;
82     private static final int DHCP_DISCOVER_ATTEMPTS = 10;
83     private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] {
84             DhcpPacket.DHCP_SUBNET_MASK,
85             DhcpPacket.DHCP_ROUTER,
86             DhcpPacket.DHCP_DNS_SERVER,
87             DhcpPacket.DHCP_LEASE_TIME,
88     };
89     private static final String DHCP_HOSTNAME = "testhostname";
90 
91     private final Context mContext = InstrumentationRegistry.getContext();
92     private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class);
93     private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class);
94 
95     private TestNetworkInterface mTestIface;
96     private HandlerThread mHandlerThread;
97     private Handler mHandler;
98     private TapPacketReader mTapPacketReader;
99 
100     private TetheredInterfaceRequester mTetheredInterfaceRequester;
101     private MyTetheringEventCallback mTetheringEventCallback;
102 
103     private UiAutomation mUiAutomation =
104             InstrumentationRegistry.getInstrumentation().getUiAutomation();
105     private boolean mRunTests;
106 
107     @Before
setUp()108     public void setUp() throws Exception {
109         // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive
110         // tethered client callbacks.
111         mUiAutomation.adoptShellPermissionIdentity(
112                 MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED);
113         mRunTests = mTm.isTetheringSupported() && mEm != null;
114         assumeTrue(mRunTests);
115 
116         mHandlerThread = new HandlerThread(getClass().getSimpleName());
117         mHandlerThread.start();
118         mHandler = new Handler(mHandlerThread.getLooper());
119         mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
120     }
121 
cleanUp()122     private void cleanUp() throws Exception {
123         mTm.stopTethering(TETHERING_ETHERNET);
124         if (mTetheringEventCallback != null) {
125             mTetheringEventCallback.awaitInterfaceUntethered();
126             mTetheringEventCallback.unregister();
127             mTetheringEventCallback = null;
128         }
129         if (mTapPacketReader != null) {
130             TapPacketReader reader = mTapPacketReader;
131             mHandler.post(() -> reader.stop());
132             mTapPacketReader = null;
133         }
134         mHandlerThread.quitSafely();
135         mTetheredInterfaceRequester.release();
136         mEm.setIncludeTestInterfaces(false);
137         maybeDeleteTestInterface();
138     }
139 
140     @After
tearDown()141     public void tearDown() throws Exception {
142         try {
143             if (mRunTests) cleanUp();
144         } finally {
145             mUiAutomation.dropShellPermissionIdentity();
146         }
147     }
148 
149     @Test
testVirtualEthernetAlreadyExists()150     public void testVirtualEthernetAlreadyExists() throws Exception {
151         // This test requires manipulating packets. Skip if there is a physical Ethernet connected.
152         assumeFalse(mEm.isAvailable());
153 
154         mTestIface = createTestInterface();
155         // This must be done now because as soon as setIncludeTestInterfaces(true) is called, the
156         // interface will be placed in client mode, which will delete the link-local address.
157         // At that point NetworkInterface.getByName() will cease to work on the interface, because
158         // starting in R NetworkInterface can no longer see interfaces without IP addresses.
159         int mtu = getMTU(mTestIface);
160 
161         Log.d(TAG, "Including test interfaces");
162         mEm.setIncludeTestInterfaces(true);
163 
164         final String iface = mTetheredInterfaceRequester.getInterface();
165         assertEquals("TetheredInterfaceCallback for unexpected interface",
166                 mTestIface.getInterfaceName(), iface);
167 
168         checkVirtualEthernet(mTestIface, mtu);
169     }
170 
171     @Test
testVirtualEthernet()172     public void testVirtualEthernet() throws Exception {
173         // This test requires manipulating packets. Skip if there is a physical Ethernet connected.
174         assumeFalse(mEm.isAvailable());
175 
176         CompletableFuture<String> futureIface = mTetheredInterfaceRequester.requestInterface();
177 
178         mEm.setIncludeTestInterfaces(true);
179 
180         mTestIface = createTestInterface();
181 
182         final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
183         assertEquals("TetheredInterfaceCallback for unexpected interface",
184                 mTestIface.getInterfaceName(), iface);
185 
186         checkVirtualEthernet(mTestIface, getMTU(mTestIface));
187     }
188 
189     @Test
testStaticIpv4()190     public void testStaticIpv4() throws Exception {
191         assumeFalse(mEm.isAvailable());
192 
193         mEm.setIncludeTestInterfaces(true);
194 
195         mTestIface = createTestInterface();
196 
197         final String iface = mTetheredInterfaceRequester.getInterface();
198         assertEquals("TetheredInterfaceCallback for unexpected interface",
199                 mTestIface.getInterfaceName(), iface);
200 
201         assertInvalidStaticIpv4Request(iface, null, null);
202         assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64");
203         assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28");
204         assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28");
205         assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null);
206         assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28");
207         assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28");
208 
209         final String localAddr = "192.0.2.3/28";
210         final String clientAddr = "192.0.2.2/28";
211         mTetheringEventCallback = enableEthernetTethering(iface,
212                 requestWithStaticIpv4(localAddr, clientAddr));
213 
214         mTetheringEventCallback.awaitInterfaceTethered();
215         assertInterfaceHasIpAddress(iface, localAddr);
216 
217         byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray();
218         byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray();
219 
220         FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor();
221         mTapPacketReader = makePacketReader(fd, getMTU(mTestIface));
222         DhcpResults dhcpResults = runDhcp(fd, client1);
223         assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress);
224 
225         try {
226             runDhcp(fd, client2);
227             fail("Only one client should get an IP address");
228         } catch (TimeoutException expected) { }
229 
230     }
231 
isAdbOverNetwork()232     private boolean isAdbOverNetwork() {
233         // If adb TCP port opened, this test may running by adb over network.
234         return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1)
235                 || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1);
236     }
237 
238     @Test
testPhysicalEthernet()239     public void testPhysicalEthernet() throws Exception {
240         assumeTrue(mEm.isAvailable());
241         // Do not run this test if adb is over network and ethernet is connected.
242         // It is likely the adb run over ethernet, the adb would break when ethernet is switching
243         // from client mode to server mode. See b/160389275.
244         assumeFalse(isAdbOverNetwork());
245 
246         // Get an interface to use.
247         final String iface = mTetheredInterfaceRequester.getInterface();
248 
249         // Enable Ethernet tethering and check that it starts.
250         mTetheringEventCallback = enableEthernetTethering(iface);
251 
252         // There is nothing more we can do on a physical interface without connecting an actual
253         // client, which is not possible in this test.
254     }
255 
256     private static final class MyTetheringEventCallback implements TetheringEventCallback {
257         private final TetheringManager mTm;
258         private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1);
259         private final CountDownLatch mTetheringStoppedLatch = new CountDownLatch(1);
260         private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1);
261         private final String mIface;
262 
263         private volatile boolean mInterfaceWasTethered = false;
264         private volatile boolean mUnregistered = false;
265         private volatile Collection<TetheredClient> mClients = null;
266 
MyTetheringEventCallback(TetheringManager tm, String iface)267         MyTetheringEventCallback(TetheringManager tm, String iface) {
268             mTm = tm;
269             mIface = iface;
270         }
271 
unregister()272         public void unregister() {
273             mTm.unregisterTetheringEventCallback(this);
274             mUnregistered = true;
275         }
276 
277         @Override
onTetheredInterfacesChanged(List<String> interfaces)278         public void onTetheredInterfacesChanged(List<String> interfaces) {
279             // Ignore stale callbacks registered by previous test cases.
280             if (mUnregistered) return;
281 
282             final boolean wasTethered = mTetheringStartedLatch.getCount() == 0;
283             if (!mInterfaceWasTethered && (mIface == null || interfaces.contains(mIface))) {
284                 // This interface is being tethered for the first time.
285                 Log.d(TAG, "Tethering started: " + interfaces);
286                 mInterfaceWasTethered = true;
287                 mTetheringStartedLatch.countDown();
288             } else if (mInterfaceWasTethered && !interfaces.contains(mIface)) {
289                 Log.d(TAG, "Tethering stopped: " + interfaces);
290                 mTetheringStoppedLatch.countDown();
291             }
292         }
293 
awaitInterfaceTethered()294         public void awaitInterfaceTethered() throws Exception {
295             assertTrue("Ethernet not tethered after " + TIMEOUT_MS + "ms",
296                     mTetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
297         }
298 
awaitInterfaceUntethered()299         public void awaitInterfaceUntethered() throws Exception {
300             // Don't block teardown if the interface was never tethered.
301             // This is racy because the interface might become tethered right after this check, but
302             // that can only happen in tearDown if startTethering timed out, which likely means
303             // the test has already failed.
304             if (!mInterfaceWasTethered) return;
305 
306             assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms",
307                     mTetheringStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
308         }
309 
310         @Override
onError(String ifName, int error)311         public void onError(String ifName, int error) {
312             // Ignore stale callbacks registered by previous test cases.
313             if (mUnregistered) return;
314 
315             fail("TetheringEventCallback got error:" + error + " on iface " + ifName);
316         }
317 
318         @Override
onClientsChanged(Collection<TetheredClient> clients)319         public void onClientsChanged(Collection<TetheredClient> clients) {
320             // Ignore stale callbacks registered by previous test cases.
321             if (mUnregistered) return;
322 
323             Log.d(TAG, "Got clients changed: " + clients);
324             mClients = clients;
325             if (clients.size() > 0) {
326                 mClientConnectedLatch.countDown();
327             }
328         }
329 
awaitClientConnected()330         public Collection<TetheredClient> awaitClientConnected() throws Exception {
331             assertTrue("Did not receive client connected callback after " + TIMEOUT_MS + "ms",
332                     mClientConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
333             return mClients;
334         }
335     }
336 
enableEthernetTethering(String iface, TetheringRequest request)337     private MyTetheringEventCallback enableEthernetTethering(String iface,
338             TetheringRequest request) throws Exception {
339         MyTetheringEventCallback callback = new MyTetheringEventCallback(mTm, iface);
340         mTm.registerTetheringEventCallback(mHandler::post, callback);
341 
342         StartTetheringCallback startTetheringCallback = new StartTetheringCallback() {
343             @Override
344             public void onTetheringFailed(int resultCode) {
345                 fail("Unexpectedly got onTetheringFailed");
346             }
347         };
348         Log.d(TAG, "Starting Ethernet tethering");
349         mTm.startTethering(request, mHandler::post /* executor */,  startTetheringCallback);
350         callback.awaitInterfaceTethered();
351         return callback;
352     }
353 
enableEthernetTethering(String iface)354     private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception {
355         return enableEthernetTethering(iface,
356                 new TetheringRequest.Builder(TETHERING_ETHERNET)
357                 .setShouldShowEntitlementUi(false).build());
358     }
359 
getMTU(TestNetworkInterface iface)360     private int getMTU(TestNetworkInterface iface) throws SocketException {
361         NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName());
362         assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif);
363         return nif.getMTU();
364     }
365 
makePacketReader(FileDescriptor fd, int mtu)366     private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) {
367         final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu);
368         mHandler.post(() -> reader.start());
369         HandlerUtilsKt.waitForIdle(mHandler, TIMEOUT_MS);
370         return reader;
371     }
372 
checkVirtualEthernet(TestNetworkInterface iface, int mtu)373     private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception {
374         FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
375         mTapPacketReader = makePacketReader(fd, mtu);
376         mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName());
377         checkTetheredClientCallbacks(fd);
378     }
379 
runDhcp(FileDescriptor fd, byte[] clientMacAddr)380     private DhcpResults runDhcp(FileDescriptor fd, byte[] clientMacAddr) throws Exception {
381         // We have to retransmit DHCP requests because IpServer declares itself to be ready before
382         // its DhcpServer is actually started. TODO: fix this race and remove this loop.
383         DhcpPacket offerPacket = null;
384         for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) {
385             Log.d(TAG, "Sending DHCP discover");
386             sendDhcpDiscover(fd, clientMacAddr);
387             offerPacket = getNextDhcpPacket();
388             if (offerPacket instanceof DhcpOfferPacket) break;
389         }
390         if (!(offerPacket instanceof DhcpOfferPacket)) {
391             throw new TimeoutException("No DHCPOFFER received on interface within timeout");
392         }
393 
394         sendDhcpRequest(fd, offerPacket, clientMacAddr);
395         DhcpPacket ackPacket = getNextDhcpPacket();
396         if (!(ackPacket instanceof DhcpAckPacket)) {
397             throw new TimeoutException("No DHCPACK received on interface within timeout");
398         }
399 
400         return ackPacket.toDhcpResults();
401     }
402 
checkTetheredClientCallbacks(FileDescriptor fd)403     private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception {
404         // Create a fake client.
405         byte[] clientMacAddr = new byte[6];
406         new Random().nextBytes(clientMacAddr);
407 
408         DhcpResults dhcpResults = runDhcp(fd, clientMacAddr);
409 
410         final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected();
411         assertEquals(1, clients.size());
412         final TetheredClient client = clients.iterator().next();
413 
414         // Check the MAC address.
415         assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress());
416         assertEquals(TETHERING_ETHERNET, client.getTetheringType());
417 
418         // Check the hostname.
419         assertEquals(1, client.getAddresses().size());
420         TetheredClient.AddressInfo info = client.getAddresses().get(0);
421         assertEquals(DHCP_HOSTNAME, info.getHostname());
422 
423         // Check the address is the one that was handed out in the DHCP ACK.
424         assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress());
425 
426         // Check that the lifetime is correct +/- 10s.
427         final long now = SystemClock.elapsedRealtime();
428         final long actualLeaseDuration = (info.getAddress().getExpirationTime() - now) / 1000;
429         final String msg = String.format("IP address should have lifetime of %d, got %d",
430                 dhcpResults.leaseDuration, actualLeaseDuration);
431         assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10);
432     }
433 
getNextDhcpPacket()434     private DhcpPacket getNextDhcpPacket() throws ParseException {
435         byte[] packet;
436         while ((packet = mTapPacketReader.popPacket(PACKET_READ_TIMEOUT_MS)) != null) {
437             try {
438                 return DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2);
439             } catch (DhcpPacket.ParseException e) {
440                 // Not a DHCP packet. Continue.
441             }
442         }
443         return null;
444     }
445 
446     private static final class TetheredInterfaceRequester implements TetheredInterfaceCallback {
447         private final CountDownLatch mInterfaceAvailableLatch = new CountDownLatch(1);
448         private final Handler mHandler;
449         private final EthernetManager mEm;
450 
451         private TetheredInterfaceRequest mRequest;
452         private final CompletableFuture<String> mFuture = new CompletableFuture<>();
453 
TetheredInterfaceRequester(Handler handler, EthernetManager em)454         TetheredInterfaceRequester(Handler handler, EthernetManager em) {
455             mHandler = handler;
456             mEm = em;
457         }
458 
459         @Override
onAvailable(String iface)460         public void onAvailable(String iface) {
461             Log.d(TAG, "Ethernet interface available: " + iface);
462             mFuture.complete(iface);
463         }
464 
465         @Override
onUnavailable()466         public void onUnavailable() {
467             mFuture.completeExceptionally(new IllegalStateException("onUnavailable received"));
468         }
469 
requestInterface()470         public CompletableFuture<String> requestInterface() {
471             assertNull("BUG: more than one tethered interface request", mRequest);
472             Log.d(TAG, "Requesting tethered interface");
473             mRequest = mEm.requestTetheredInterface(mHandler::post, this);
474             return mFuture;
475         }
476 
getInterface()477         public String getInterface() throws Exception {
478             return requestInterface().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
479         }
480 
release()481         public void release() {
482             if (mRequest != null) {
483                 mFuture.obtrudeException(new IllegalStateException("Request already released"));
484                 mRequest.release();
485                 mRequest = null;
486             }
487         }
488     }
489 
sendDhcpDiscover(FileDescriptor fd, byte[] macAddress)490     private void sendDhcpDiscover(FileDescriptor fd, byte[] macAddress) throws Exception {
491         ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2,
492                 new Random().nextInt() /* transactionId */, (short) 0 /* secs */,
493                 macAddress,  false /* unicast */, DHCP_REQUESTED_PARAMS,
494                 false /* rapid commit */,  DHCP_HOSTNAME);
495         sendPacket(fd, packet);
496     }
497 
sendDhcpRequest(FileDescriptor fd, DhcpPacket offerPacket, byte[] macAddress)498     private void sendDhcpRequest(FileDescriptor fd, DhcpPacket offerPacket, byte[] macAddress)
499             throws Exception {
500         DhcpResults results = offerPacket.toDhcpResults();
501         Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress();
502         Inet4Address serverIdentifier = results.serverAddress;
503         ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2,
504                 0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */,
505                 false /* broadcast */, macAddress, clientIp /* requestedIpAddress */,
506                 serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME);
507         sendPacket(fd, packet);
508     }
509 
sendPacket(FileDescriptor fd, ByteBuffer packet)510     private void sendPacket(FileDescriptor fd, ByteBuffer packet) throws Exception {
511         assertNotNull("Only tests on virtual interfaces can send packets", fd);
512         Os.write(fd, packet);
513     }
514 
assertLinkAddressMatches(LinkAddress l1, LinkAddress l2)515     public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) {
516         // Check all fields except the deprecation and expiry times.
517         String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2);
518         assertTrue(msg, l1.isSameAddressAs(l2));
519         assertEquals("LinkAddress flags do not match", l1.getFlags(), l2.getFlags());
520         assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope());
521     }
522 
requestWithStaticIpv4(String local, String client)523     private TetheringRequest requestWithStaticIpv4(String local, String client) {
524         LinkAddress localAddr = local == null ? null : new LinkAddress(local);
525         LinkAddress clientAddr = client == null ? null : new LinkAddress(client);
526         return new TetheringRequest.Builder(TETHERING_ETHERNET)
527                 .setStaticIpv4Addresses(localAddr, clientAddr)
528                 .setShouldShowEntitlementUi(false).build();
529     }
530 
assertInvalidStaticIpv4Request(String iface, String local, String client)531     private void assertInvalidStaticIpv4Request(String iface, String local, String client)
532             throws Exception {
533         try {
534             enableEthernetTethering(iface, requestWithStaticIpv4(local, client));
535             fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client);
536         } catch (IllegalArgumentException | NullPointerException expected) { }
537     }
538 
assertInterfaceHasIpAddress(String iface, String expected)539     private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception {
540         LinkAddress expectedAddr = new LinkAddress(expected);
541         NetworkInterface nif = NetworkInterface.getByName(iface);
542         for (InterfaceAddress ia : nif.getInterfaceAddresses()) {
543             final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength());
544             if (expectedAddr.equals(addr)) {
545                 return;
546             }
547         }
548         fail("Expected " + iface + " to have IP address " + expected + ", found "
549                 + nif.getInterfaceAddresses());
550     }
551 
createTestInterface()552     private TestNetworkInterface createTestInterface() throws Exception {
553         TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class);
554         TestNetworkInterface iface = tnm.createTapInterface();
555         Log.d(TAG, "Created test interface " + iface.getInterfaceName());
556         assertNotNull(NetworkInterface.getByName(iface.getInterfaceName()));
557         return iface;
558     }
559 
maybeDeleteTestInterface()560     private void maybeDeleteTestInterface() throws Exception {
561         if (mTestIface != null) {
562             mTestIface.getFileDescriptor().close();
563             Log.d(TAG, "Deleted test interface " + mTestIface.getInterfaceName());
564             mTestIface = null;
565         }
566     }
567 }
568