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
This commit is contained in:
Aaron Huang 2019-12-06 18:12:24 +08:00
commit f73ff8cfce
8 changed files with 649 additions and 0 deletions

37
staticlibs/Android.bp Normal file
View file

@ -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",
]
}

View file

@ -0,0 +1 @@
rule android.net.util.** com.android.net.module.util.@1

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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",
],
}

View file

@ -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>

View file

@ -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));
}
}

View file

@ -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));
}
}
}