blob: 282fa10d2bd870de82b85cecad2197610512e9ac [file] [log] [blame]
/*
* 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.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
/**
* Collection of link properties utilities.
* @hide
*/
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)
+ "]";
}
}
/**
* Generic class to compare two lists of items of type {@code T} whose properties can change.
* The items to be compared must provide a way to calculate a corresponding key of type
* {@code K} such that if (and only if) an old and a new item have the same key, then the new
* item is an update of the old item. Both the old list and the new list may not contain more
* than one item with the same key, and may not contain any null items.
*
* @param <K> A class that represents the key of the items to be compared.
* @param <T> The class that represents the object to be compared.
*/
public static class CompareOrUpdateResult<K, T> {
public final List<T> added = new ArrayList<>();
public final List<T> removed = new ArrayList<>();
public final List<T> updated = new ArrayList<>();
/**
* Compares two lists of items.
* @param oldItems the old list of items.
* @param newItems the new list of items.
* @param keyCalculator a {@link Function} that calculates an item's key.
*/
public CompareOrUpdateResult(Collection<T> oldItems, Collection<T> newItems,
Function<T, K> keyCalculator) {
HashMap<K, T> updateTracker = new HashMap<>();
if (oldItems != null) {
for (T oldItem : oldItems) {
updateTracker.put(keyCalculator.apply(oldItem), oldItem);
}
}
if (newItems != null) {
for (T newItem : newItems) {
T oldItem = updateTracker.remove(keyCalculator.apply(newItem));
if (oldItem != null) {
if (!oldItem.equals(newItem)) {
// Update of existing item.
updated.add(newItem);
}
} else {
// New item.
added.add(newItem);
}
}
}
removed.addAll(updateTracker.values());
}
@Override
public String toString() {
return "removed=[" + TextUtils.join(",", removed)
+ "] added=[" + TextUtils.join(",", added)
+ "] updated=[" + TextUtils.join(",", updated)
+ "]";
}
}
/**
* 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;
}
}