Add a new static library for mainline modules
Add a network static library for common utilities. This
library could be used by all mainline modules. Initially
create LinkPropertiesUtils and MacAddressUtils.
Bug: 139268426
Bug: 135998869
Bug: 138306002
Test: build lib pass
atest NetworkStaticLibTests
Change-Id: I8f79e4f836819ac83007acffb55103e5d69873e0
diff --git a/staticlibs/Android.bp b/staticlibs/Android.bp
new file mode 100644
index 0000000..ffc02c3
--- /dev/null
+++ b/staticlibs/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// File group "net-utils-framework-common-srcs" is also compiled into android framework.
+// Add jarjar rules here so that anything outside of framework could use this library
+// directly.
+
+filegroup {
+ name: "net-utils-framework-common-srcs",
+ srcs: ["src_frameworkcommon/**/*.java"],
+ visibility: ["//frameworks/base"],
+}
+
+java_library {
+ name: "net-utils-framework-common",
+ srcs: [":net-utils-framework-common-srcs" ],
+ jarjar_rules: "jarjar-rules-shared.txt",
+ visibility: [
+ "//frameworks/base/packages/Tethering",
+ "//frameworks/opt/net/wifi",
+ "//frameworks/opt/net/ike",
+ "//frameworks/opt/telephony",
+ "//packages/modules/NetworkStack",
+ "//packages/modules/CaptivePortalLogin",
+ ]
+}
diff --git a/staticlibs/jarjar-rules-shared.txt b/staticlibs/jarjar-rules-shared.txt
new file mode 100644
index 0000000..4a2b653
--- /dev/null
+++ b/staticlibs/jarjar-rules-shared.txt
@@ -0,0 +1 @@
+rule android.net.util.** com.android.net.module.util.@1
diff --git a/staticlibs/src_frameworkcommon/android/net/util/LinkPropertiesUtils.java b/staticlibs/src_frameworkcommon/android/net/util/LinkPropertiesUtils.java
new file mode 100644
index 0000000..59d88ac
--- /dev/null
+++ b/staticlibs/src_frameworkcommon/android/net/util/LinkPropertiesUtils.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.RouteInfo;
+import android.text.TextUtils;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Collection of link properties utilities.
+ */
+public final class LinkPropertiesUtils {
+
+ /**
+ * @param <T> The type of data to compare.
+ */
+ public static class CompareResult<T> {
+ public final List<T> removed = new ArrayList<>();
+ public final List<T> added = new ArrayList<>();
+
+ public CompareResult() {}
+
+ public CompareResult(@Nullable Collection<T> oldItems, @Nullable Collection<T> newItems) {
+ if (oldItems != null) {
+ removed.addAll(oldItems);
+ }
+ if (newItems != null) {
+ for (T newItem : newItems) {
+ if (!removed.remove(newItem)) {
+ added.add(newItem);
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "removed=[" + TextUtils.join(",", removed)
+ + "] added=[" + TextUtils.join(",", added)
+ + "]";
+ }
+ }
+
+ /**
+ * Compares the addresses in {@code left} LinkProperties with {@code right}
+ * LinkProperties, examining only addresses on the base link.
+ *
+ * @param left A LinkProperties with the old list of addresses.
+ * @param right A LinkProperties with the new list of addresses.
+ * @return the differences between the addresses.
+ */
+ public static @NonNull CompareResult<LinkAddress> compareAddresses(
+ @Nullable LinkProperties left, @Nullable LinkProperties right) {
+ /*
+ * Duplicate the LinkAddresses into removed, we will be removing
+ * address which are common between mLinkAddresses and target
+ * leaving the addresses that are different. And address which
+ * are in target but not in mLinkAddresses are placed in the
+ * addedAddresses.
+ */
+ return new CompareResult<>(left != null ? left.getLinkAddresses() : null,
+ right != null ? right.getLinkAddresses() : null);
+ }
+
+ /**
+ * Compares {@code left} {@code LinkProperties} interface addresses against the {@code right}.
+ *
+ * @param left A LinkProperties.
+ * @param right LinkProperties to be compared with {@code left}.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ */
+ public static boolean isIdenticalAddresses(@NonNull LinkProperties left,
+ @NonNull LinkProperties right) {
+ final Collection<InetAddress> leftAddresses = left.getAddresses();
+ final Collection<InetAddress> rightAddresses = right.getAddresses();
+ return (leftAddresses.size() == rightAddresses.size())
+ ? leftAddresses.containsAll(rightAddresses) : false;
+ }
+
+ /**
+ * Compares {@code left} {@code LinkProperties} DNS addresses against the {@code right}.
+ *
+ * @param left A LinkProperties.
+ * @param right A LinkProperties to be compared with {@code left}.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ */
+ public static boolean isIdenticalDnses(@NonNull LinkProperties left,
+ @NonNull LinkProperties right) {
+ final Collection<InetAddress> leftDnses = left.getDnsServers();
+ final Collection<InetAddress> rightDnses = right.getDnsServers();
+
+ final String leftDomains = left.getDomains();
+ final String rightDomains = right.getDomains();
+ if (leftDomains == null) {
+ if (rightDomains != null) return false;
+ } else {
+ if (!leftDomains.equals(rightDomains)) return false;
+ }
+ return (leftDnses.size() == rightDnses.size())
+ ? leftDnses.containsAll(rightDnses) : false;
+ }
+
+ /**
+ * Compares {@code left} {@code LinkProperties} HttpProxy against the {@code right}.
+ *
+ * @param left A LinkProperties.
+ * @param right A LinkProperties to be compared with {@code left}.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ */
+ public static boolean isIdenticalHttpProxy(@NonNull LinkProperties left,
+ @NonNull LinkProperties right) {
+ return Objects.equals(left.getHttpProxy(), right.getHttpProxy());
+ }
+
+ /**
+ * Compares {@code left} {@code LinkProperties} interface name against the {@code right}.
+ *
+ * @param left A LinkProperties.
+ * @param right A LinkProperties to be compared with {@code left}.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ */
+ public static boolean isIdenticalInterfaceName(@NonNull LinkProperties left,
+ @NonNull LinkProperties right) {
+ return TextUtils.equals(left.getInterfaceName(), right.getInterfaceName());
+ }
+
+ /**
+ * Compares {@code left} {@code LinkProperties} Routes against the {@code right}.
+ *
+ * @param left A LinkProperties.
+ * @param right A LinkProperties to be compared with {@code left}.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ */
+ public static boolean isIdenticalRoutes(@NonNull LinkProperties left,
+ @NonNull LinkProperties right) {
+ final Collection<RouteInfo> leftRoutes = left.getRoutes();
+ final Collection<RouteInfo> rightRoutes = right.getRoutes();
+ return (leftRoutes.size() == rightRoutes.size())
+ ? leftRoutes.containsAll(rightRoutes) : false;
+ }
+}
diff --git a/staticlibs/src_frameworkcommon/android/net/util/MacAddressUtils.java b/staticlibs/src_frameworkcommon/android/net/util/MacAddressUtils.java
new file mode 100644
index 0000000..5345789
--- /dev/null
+++ b/staticlibs/src_frameworkcommon/android/net/util/MacAddressUtils.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.MacAddress;
+
+import com.android.internal.util.BitUtils;
+import com.android.internal.util.Preconditions;
+
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Collection of MAC address utilities.
+ */
+public final class MacAddressUtils {
+
+ private static final long VALID_LONG_MASK = (1L << 48) - 1;
+ private static final long LOCALLY_ASSIGNED_MASK = longAddrFromByteAddr(
+ MacAddress.fromString("2:0:0:0:0:0").toByteArray());
+ private static final long MULTICAST_MASK = longAddrFromByteAddr(
+ MacAddress.fromString("1:0:0:0:0:0").toByteArray());
+ private static final long OUI_MASK = longAddrFromByteAddr(
+ MacAddress.fromString("ff:ff:ff:0:0:0").toByteArray());
+ private static final long NIC_MASK = longAddrFromByteAddr(
+ MacAddress.fromString("0:0:0:ff:ff:ff").toByteArray());
+ // Matches WifiInfo.DEFAULT_MAC_ADDRESS
+ private static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00";
+ private static final int ETHER_ADDR_LEN = 6;
+
+ /**
+ * @return true if this MacAddress is a multicast address.
+ */
+ public static boolean isMulticastAddress(@NonNull MacAddress address) {
+ return (longAddrFromByteAddr(address.toByteArray()) & MULTICAST_MASK) != 0;
+ }
+
+ /**
+ * Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the
+ * unicast bit, are randomly selected.
+ *
+ * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
+ *
+ * @return a random locally assigned, unicast MacAddress.
+ */
+ public static @NonNull MacAddress createRandomUnicastAddress() {
+ return createRandomUnicastAddress(null, new SecureRandom());
+ }
+
+ /**
+ * Returns a randomly generated MAC address using the given Random object and the same
+ * OUI values as the given MacAddress.
+ *
+ * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
+ *
+ * @param base a base MacAddress whose OUI is used for generating the random address.
+ * If base == null then the OUI will also be randomized.
+ * @param r a standard Java Random object used for generating the random address.
+ * @return a random locally assigned MacAddress.
+ */
+ public static @NonNull MacAddress createRandomUnicastAddress(@Nullable MacAddress base,
+ @NonNull Random r) {
+ long addr;
+
+ if (base == null) {
+ addr = r.nextLong() & VALID_LONG_MASK;
+ } else {
+ addr = (longAddrFromByteAddr(base.toByteArray()) & OUI_MASK)
+ | (NIC_MASK & r.nextLong());
+ }
+ addr |= LOCALLY_ASSIGNED_MASK;
+ addr &= ~MULTICAST_MASK;
+ MacAddress mac = MacAddress.fromBytes(byteAddrFromLongAddr(addr));
+ if (mac.equals(DEFAULT_MAC_ADDRESS)) {
+ return createRandomUnicastAddress(base, r);
+ }
+ return mac;
+ }
+
+ /**
+ * Convert a byte address to long address.
+ */
+ public static long longAddrFromByteAddr(byte[] addr) {
+ Preconditions.checkNotNull(addr);
+ if (!isMacAddress(addr)) {
+ throw new IllegalArgumentException(
+ Arrays.toString(addr) + " was not a valid MAC address");
+ }
+ long longAddr = 0;
+ for (byte b : addr) {
+ longAddr = (longAddr << 8) + BitUtils.uint8(b);
+ }
+ return longAddr;
+ }
+
+ /**
+ * Convert a long address to byte address.
+ */
+ public static byte[] byteAddrFromLongAddr(long addr) {
+ byte[] bytes = new byte[ETHER_ADDR_LEN];
+ int index = ETHER_ADDR_LEN;
+ while (index-- > 0) {
+ bytes[index] = (byte) addr;
+ addr = addr >> 8;
+ }
+ return bytes;
+ }
+
+ /**
+ * Returns true if the given byte array is a valid MAC address.
+ * A valid byte array representation for a MacAddress is a non-null array of length 6.
+ *
+ * @param addr a byte array.
+ * @return true if the given byte array is not null and has the length of a MAC address.
+ *
+ */
+ public static boolean isMacAddress(byte[] addr) {
+ return addr != null && addr.length == ETHER_ADDR_LEN;
+ }
+}
diff --git a/staticlibs/tests/unit/Android.bp b/staticlibs/tests/unit/Android.bp
new file mode 100644
index 0000000..4a51a8c
--- /dev/null
+++ b/staticlibs/tests/unit/Android.bp
@@ -0,0 +1,18 @@
+//########################################################################
+// Build NetworkStaticLibTests package
+//########################################################################
+
+android_test {
+ name: "NetworkStaticLibTests",
+ certificate: "platform",
+ srcs: ["src/**/*.java","src/**/*.kt"],
+ test_suites: ["device-tests"],
+ static_libs: [
+ "androidx.test.rules",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+}
+
diff --git a/staticlibs/tests/unit/AndroidManifest.xml b/staticlibs/tests/unit/AndroidManifest.xml
new file mode 100644
index 0000000..cc9e09e
--- /dev/null
+++ b/staticlibs/tests/unit/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.libnet.tests">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.libnet.tests"
+ android:label="Network Static Library Tests" />
+</manifest>
diff --git a/staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java b/staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java
new file mode 100644
index 0000000..6e06ee8
--- /dev/null
+++ b/staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.ProxyInfo;
+import android.net.RouteInfo;
+import android.net.util.LinkPropertiesUtils.CompareResult;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+
+@RunWith(AndroidJUnit4.class)
+public final class LinkPropertiesUtilsTest {
+ private static final IpPrefix PREFIX = new IpPrefix(toInetAddress("75.208.6.0"), 24);
+ private static final InetAddress V4_ADDR = toInetAddress("75.208.6.1");
+ private static final InetAddress V6_ADDR = toInetAddress(
+ "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ private static final InetAddress DNS1 = toInetAddress("75.208.7.1");
+ private static final InetAddress DNS2 = toInetAddress("69.78.7.1");
+
+ private static final InetAddress GATEWAY1 = toInetAddress("75.208.8.1");
+ private static final InetAddress GATEWAY2 = toInetAddress("69.78.8.1");
+
+ private static final String IF_NAME = "wlan0";
+ private static final LinkAddress V4_LINKADDR = new LinkAddress(V4_ADDR, 32);
+ private static final LinkAddress V6_LINKADDR = new LinkAddress(V6_ADDR, 128);
+ private static final RouteInfo RT_INFO1 = new RouteInfo(PREFIX, GATEWAY1, IF_NAME);
+ private static final RouteInfo RT_INFO2 = new RouteInfo(PREFIX, GATEWAY2, IF_NAME);
+ private static final String TEST_DOMAIN = "link.properties.com";
+
+ private static InetAddress toInetAddress(String addrString) {
+ return InetAddresses.parseNumericAddress(addrString);
+ }
+
+ private LinkProperties createTestObject() {
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(IF_NAME);
+ lp.addLinkAddress(V4_LINKADDR);
+ lp.addLinkAddress(V6_LINKADDR);
+ lp.addDnsServer(DNS1);
+ lp.addDnsServer(DNS2);
+ lp.setDomains(TEST_DOMAIN);
+ lp.addRoute(RT_INFO1);
+ lp.addRoute(RT_INFO2);
+ lp.setHttpProxy(ProxyInfo.buildDirectProxy("test", 8888));
+ return lp;
+ }
+
+ @Test
+ public void testLinkPropertiesIdenticalEqual() {
+ final LinkProperties source = createTestObject();
+ final LinkProperties target = new LinkProperties(source);
+
+ assertTrue(LinkPropertiesUtils.isIdenticalInterfaceName(source, target));
+ assertTrue(LinkPropertiesUtils.isIdenticalInterfaceName(target, source));
+
+ assertTrue(LinkPropertiesUtils.isIdenticalAddresses(source, target));
+ assertTrue(LinkPropertiesUtils.isIdenticalAddresses(target, source));
+
+ assertTrue(LinkPropertiesUtils.isIdenticalDnses(source, target));
+ assertTrue(LinkPropertiesUtils.isIdenticalDnses(target, source));
+
+ assertTrue(LinkPropertiesUtils.isIdenticalRoutes(source, target));
+ assertTrue(LinkPropertiesUtils.isIdenticalRoutes(target, source));
+
+ assertTrue(LinkPropertiesUtils.isIdenticalHttpProxy(source, target));
+ assertTrue(LinkPropertiesUtils.isIdenticalHttpProxy(target, source));
+
+ // Test different interface name.
+ target.setInterfaceName("lo");
+ assertFalse(LinkPropertiesUtils.isIdenticalInterfaceName(source, target));
+ assertFalse(LinkPropertiesUtils.isIdenticalInterfaceName(target, source));
+ // Restore interface name
+ target.setInterfaceName(IF_NAME);
+
+ // Compare addresses.size() not equals.
+ final LinkAddress testLinkAddr = new LinkAddress(toInetAddress("75.208.6.2"), 32);
+ target.addLinkAddress(testLinkAddr);
+ assertFalse(LinkPropertiesUtils.isIdenticalAddresses(source, target));
+ assertFalse(LinkPropertiesUtils.isIdenticalAddresses(target, source));
+
+ // Currently, target contains V4_LINKADDR, V6_LINKADDR and testLinkAddr.
+ // Compare addresses.size() equals but contains different address.
+ target.removeLinkAddress(V4_LINKADDR);
+ assertEquals(source.getAddresses().size(), target.getAddresses().size());
+ assertFalse(LinkPropertiesUtils.isIdenticalAddresses(source, target));
+ assertFalse(LinkPropertiesUtils.isIdenticalAddresses(target, source));
+ // Restore link address
+ target.addLinkAddress(V4_LINKADDR);
+ target.removeLinkAddress(testLinkAddr);
+
+ // Compare size not equals.
+ target.addDnsServer(toInetAddress("75.208.10.1"));
+ assertFalse(LinkPropertiesUtils.isIdenticalDnses(source, target));
+ assertFalse(LinkPropertiesUtils.isIdenticalDnses(target, source));
+
+ // Compare the same servers but target has different domains.
+ target.removeDnsServer(toInetAddress("75.208.10.1"));
+ target.setDomains("test.com");
+ assertFalse(LinkPropertiesUtils.isIdenticalDnses(source, target));
+ assertFalse(LinkPropertiesUtils.isIdenticalDnses(target, source));
+
+ // Test null domain.
+ target.setDomains(null);
+ assertFalse(LinkPropertiesUtils.isIdenticalDnses(source, target));
+ assertFalse(LinkPropertiesUtils.isIdenticalDnses(target, source));
+ // Restore domain
+ target.setDomains(TEST_DOMAIN);
+
+ // Compare size not equals.
+ final RouteInfo testRoute = new RouteInfo(toInetAddress("75.208.7.1"));
+ target.addRoute(testRoute);
+ assertFalse(LinkPropertiesUtils.isIdenticalRoutes(source, target));
+ assertFalse(LinkPropertiesUtils.isIdenticalRoutes(target, source));
+
+ // Currently, target contains RT_INFO1, RT_INFO2 and testRoute.
+ // Compare size equals but different routes.
+ target.removeRoute(RT_INFO1);
+ assertEquals(source.getRoutes().size(), target.getRoutes().size());
+ assertFalse(LinkPropertiesUtils.isIdenticalRoutes(source, target));
+ assertFalse(LinkPropertiesUtils.isIdenticalRoutes(target, source));
+ // Restore route
+ target.addRoute(RT_INFO1);
+ target.removeRoute(testRoute);
+
+ // Test different proxy.
+ target.setHttpProxy(ProxyInfo.buildDirectProxy("hello", 8888));
+ assertFalse(LinkPropertiesUtils.isIdenticalHttpProxy(source, target));
+ assertFalse(LinkPropertiesUtils.isIdenticalHttpProxy(target, source));
+
+ // Test null proxy.
+ target.setHttpProxy(null);
+ assertFalse(LinkPropertiesUtils.isIdenticalHttpProxy(source, target));
+ assertFalse(LinkPropertiesUtils.isIdenticalHttpProxy(target, source));
+ }
+
+ @Test
+ public void testCompareAddresses() {
+ final LinkProperties source = createTestObject();
+ final LinkProperties target = new LinkProperties(source);
+ final InetAddress addr1 = toInetAddress("75.208.6.2");
+ final LinkAddress linkAddr1 = new LinkAddress(addr1, 32);
+
+ CompareResult<LinkAddress> results = LinkPropertiesUtils.compareAddresses(source, target);
+ assertEquals(0, results.removed.size());
+ assertEquals(0, results.added.size());
+
+ source.addLinkAddress(linkAddr1);
+ results = LinkPropertiesUtils.compareAddresses(source, target);
+ assertEquals(1, results.removed.size());
+ assertEquals(linkAddr1, results.removed.get(0));
+ assertEquals(0, results.added.size());
+
+ final InetAddress addr2 = toInetAddress("75.208.6.3");
+ final LinkAddress linkAddr2 = new LinkAddress(addr2, 32);
+
+ target.addLinkAddress(linkAddr2);
+ results = LinkPropertiesUtils.compareAddresses(source, target);
+ assertEquals(linkAddr1, results.removed.get(0));
+ assertEquals(linkAddr2, results.added.get(0));
+ }
+}
diff --git a/staticlibs/tests/unit/src/android/net/util/MacAddressUtilsTest.java b/staticlibs/tests/unit/src/android/net/util/MacAddressUtilsTest.java
new file mode 100644
index 0000000..ad63b3c
--- /dev/null
+++ b/staticlibs/tests/unit/src/android/net/util/MacAddressUtilsTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.MacAddress;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public final class MacAddressUtilsTest {
+
+ // Matches WifiInfo.DEFAULT_MAC_ADDRESS
+ private static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00";
+
+ @Test
+ public void testIsMulticastAddress() {
+ MacAddress[] multicastAddresses = {
+ // broadcast address
+ MacAddress.fromString("ff:ff:ff:ff:ff:ff"),
+ MacAddress.fromString("07:00:d3:56:8a:c4"),
+ MacAddress.fromString("33:33:aa:bb:cc:dd"),
+ };
+ MacAddress[] unicastAddresses = {
+ // all zero address
+ MacAddress.fromString("00:00:00:00:00:00"),
+ MacAddress.fromString("00:01:44:55:66:77"),
+ MacAddress.fromString("08:00:22:33:44:55"),
+ MacAddress.fromString("06:00:00:00:00:00"),
+ };
+
+ for (MacAddress mac : multicastAddresses) {
+ String msg = mac.toString() + " expected to be a multicast address";
+ assertTrue(msg, MacAddressUtils.isMulticastAddress(mac));
+ }
+ for (MacAddress mac : unicastAddresses) {
+ String msg = mac.toString() + " expected not to be a multicast address";
+ assertFalse(msg, MacAddressUtils.isMulticastAddress(mac));
+ }
+ }
+
+ @Test
+ public void testMacAddressRandomGeneration() {
+ final int iterations = 1000;
+
+ for (int i = 0; i < iterations; i++) {
+ MacAddress mac = MacAddressUtils.createRandomUnicastAddress();
+ String stringRepr = mac.toString();
+
+ assertTrue(stringRepr + " expected to be a locally assigned address",
+ mac.isLocallyAssigned());
+ assertEquals(MacAddress.TYPE_UNICAST, mac.getAddressType());
+ assertFalse(mac.equals(DEFAULT_MAC_ADDRESS));
+ }
+ }
+}