1 /* 2 * Copyright (C) 2019 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.net.module.util; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.net.LinkAddress; 22 import android.net.LinkProperties; 23 import android.net.RouteInfo; 24 import android.text.TextUtils; 25 26 import java.net.InetAddress; 27 import java.util.ArrayList; 28 import java.util.Collection; 29 import java.util.HashMap; 30 import java.util.List; 31 import java.util.Objects; 32 import java.util.function.Function; 33 34 /** 35 * Collection of link properties utilities. 36 * @hide 37 */ 38 public final class LinkPropertiesUtils { 39 40 /** 41 * @param <T> The type of data to compare. 42 */ 43 public static class CompareResult<T> { 44 public final List<T> removed = new ArrayList<>(); 45 public final List<T> added = new ArrayList<>(); 46 CompareResult()47 public CompareResult() {} 48 CompareResult(@ullable Collection<T> oldItems, @Nullable Collection<T> newItems)49 public CompareResult(@Nullable Collection<T> oldItems, @Nullable Collection<T> newItems) { 50 if (oldItems != null) { 51 removed.addAll(oldItems); 52 } 53 if (newItems != null) { 54 for (T newItem : newItems) { 55 if (!removed.remove(newItem)) { 56 added.add(newItem); 57 } 58 } 59 } 60 } 61 62 @Override toString()63 public String toString() { 64 return "removed=[" + TextUtils.join(",", removed) 65 + "] added=[" + TextUtils.join(",", added) 66 + "]"; 67 } 68 } 69 70 /** 71 * Generic class to compare two lists of items of type {@code T} whose properties can change. 72 * The items to be compared must provide a way to calculate a corresponding key of type 73 * {@code K} such that if (and only if) an old and a new item have the same key, then the new 74 * item is an update of the old item. Both the old list and the new list may not contain more 75 * than one item with the same key, and may not contain any null items. 76 * 77 * @param <K> A class that represents the key of the items to be compared. 78 * @param <T> The class that represents the object to be compared. 79 */ 80 public static class CompareOrUpdateResult<K, T> { 81 public final List<T> added = new ArrayList<>(); 82 public final List<T> removed = new ArrayList<>(); 83 public final List<T> updated = new ArrayList<>(); 84 85 /** 86 * Compares two lists of items. 87 * @param oldItems the old list of items. 88 * @param newItems the new list of items. 89 * @param keyCalculator a {@link Function} that calculates an item's key. 90 */ CompareOrUpdateResult(Collection<T> oldItems, Collection<T> newItems, Function<T, K> keyCalculator)91 public CompareOrUpdateResult(Collection<T> oldItems, Collection<T> newItems, 92 Function<T, K> keyCalculator) { 93 HashMap<K, T> updateTracker = new HashMap<>(); 94 95 if (oldItems != null) { 96 for (T oldItem : oldItems) { 97 updateTracker.put(keyCalculator.apply(oldItem), oldItem); 98 } 99 } 100 101 if (newItems != null) { 102 for (T newItem : newItems) { 103 T oldItem = updateTracker.remove(keyCalculator.apply(newItem)); 104 if (oldItem != null) { 105 if (!oldItem.equals(newItem)) { 106 // Update of existing item. 107 updated.add(newItem); 108 } 109 } else { 110 // New item. 111 added.add(newItem); 112 } 113 } 114 } 115 116 removed.addAll(updateTracker.values()); 117 } 118 119 @Override toString()120 public String toString() { 121 return "removed=[" + TextUtils.join(",", removed) 122 + "] added=[" + TextUtils.join(",", added) 123 + "] updated=[" + TextUtils.join(",", updated) 124 + "]"; 125 } 126 } 127 128 /** 129 * Compares the addresses in {@code left} LinkProperties with {@code right} 130 * LinkProperties, examining only addresses on the base link. 131 * 132 * @param left A LinkProperties with the old list of addresses. 133 * @param right A LinkProperties with the new list of addresses. 134 * @return the differences between the addresses. 135 */ compareAddresses( @ullable LinkProperties left, @Nullable LinkProperties right)136 public static @NonNull CompareResult<LinkAddress> compareAddresses( 137 @Nullable LinkProperties left, @Nullable LinkProperties right) { 138 /* 139 * Duplicate the LinkAddresses into removed, we will be removing 140 * address which are common between mLinkAddresses and target 141 * leaving the addresses that are different. And address which 142 * are in target but not in mLinkAddresses are placed in the 143 * addedAddresses. 144 */ 145 return new CompareResult<>(left != null ? left.getLinkAddresses() : null, 146 right != null ? right.getLinkAddresses() : null); 147 } 148 149 /** 150 * Compares {@code left} {@code LinkProperties} interface addresses against the {@code right}. 151 * 152 * @param left A LinkProperties. 153 * @param right LinkProperties to be compared with {@code left}. 154 * @return {@code true} if both are identical, {@code false} otherwise. 155 */ isIdenticalAddresses(@onNull LinkProperties left, @NonNull LinkProperties right)156 public static boolean isIdenticalAddresses(@NonNull LinkProperties left, 157 @NonNull LinkProperties right) { 158 final Collection<InetAddress> leftAddresses = left.getAddresses(); 159 final Collection<InetAddress> rightAddresses = right.getAddresses(); 160 return (leftAddresses.size() == rightAddresses.size()) 161 ? leftAddresses.containsAll(rightAddresses) : false; 162 } 163 164 /** 165 * Compares {@code left} {@code LinkProperties} DNS addresses against the {@code right}. 166 * 167 * @param left A LinkProperties. 168 * @param right A LinkProperties to be compared with {@code left}. 169 * @return {@code true} if both are identical, {@code false} otherwise. 170 */ isIdenticalDnses(@onNull LinkProperties left, @NonNull LinkProperties right)171 public static boolean isIdenticalDnses(@NonNull LinkProperties left, 172 @NonNull LinkProperties right) { 173 final Collection<InetAddress> leftDnses = left.getDnsServers(); 174 final Collection<InetAddress> rightDnses = right.getDnsServers(); 175 176 final String leftDomains = left.getDomains(); 177 final String rightDomains = right.getDomains(); 178 if (leftDomains == null) { 179 if (rightDomains != null) return false; 180 } else { 181 if (!leftDomains.equals(rightDomains)) return false; 182 } 183 return (leftDnses.size() == rightDnses.size()) 184 ? leftDnses.containsAll(rightDnses) : false; 185 } 186 187 /** 188 * Compares {@code left} {@code LinkProperties} HttpProxy against the {@code right}. 189 * 190 * @param left A LinkProperties. 191 * @param right A LinkProperties to be compared with {@code left}. 192 * @return {@code true} if both are identical, {@code false} otherwise. 193 */ isIdenticalHttpProxy(@onNull LinkProperties left, @NonNull LinkProperties right)194 public static boolean isIdenticalHttpProxy(@NonNull LinkProperties left, 195 @NonNull LinkProperties right) { 196 return Objects.equals(left.getHttpProxy(), right.getHttpProxy()); 197 } 198 199 /** 200 * Compares {@code left} {@code LinkProperties} interface name against the {@code right}. 201 * 202 * @param left A LinkProperties. 203 * @param right A LinkProperties to be compared with {@code left}. 204 * @return {@code true} if both are identical, {@code false} otherwise. 205 */ isIdenticalInterfaceName(@onNull LinkProperties left, @NonNull LinkProperties right)206 public static boolean isIdenticalInterfaceName(@NonNull LinkProperties left, 207 @NonNull LinkProperties right) { 208 return TextUtils.equals(left.getInterfaceName(), right.getInterfaceName()); 209 } 210 211 /** 212 * Compares {@code left} {@code LinkProperties} Routes against the {@code right}. 213 * 214 * @param left A LinkProperties. 215 * @param right A LinkProperties to be compared with {@code left}. 216 * @return {@code true} if both are identical, {@code false} otherwise. 217 */ isIdenticalRoutes(@onNull LinkProperties left, @NonNull LinkProperties right)218 public static boolean isIdenticalRoutes(@NonNull LinkProperties left, 219 @NonNull LinkProperties right) { 220 final Collection<RouteInfo> leftRoutes = left.getRoutes(); 221 final Collection<RouteInfo> rightRoutes = right.getRoutes(); 222 return (leftRoutes.size() == rightRoutes.size()) 223 ? leftRoutes.containsAll(rightRoutes) : false; 224 } 225 } 226