1/*
2 * Copyright (C) 2010 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
17package android.net;
18
19import android.net.ProxyProperties;
20import android.os.Parcelable;
21import android.os.Parcel;
22import android.text.TextUtils;
23
24import java.net.InetAddress;
25import java.net.UnknownHostException;
26import java.util.ArrayList;
27import java.util.Collection;
28import java.util.Collections;
29
30/**
31 * Describes the properties of a network link.
32 *
33 * A link represents a connection to a network.
34 * It may have multiple addresses and multiple gateways,
35 * multiple dns servers but only one http proxy.
36 *
37 * Because it's a single network, the dns's
38 * are interchangeable and don't need associating with
39 * particular addresses.  The gateways similarly don't
40 * need associating with particular addresses.
41 *
42 * A dual stack interface works fine in this model:
43 * each address has it's own prefix length to describe
44 * the local network.  The dns servers all return
45 * both v4 addresses and v6 addresses regardless of the
46 * address family of the server itself (rfc4213) and we
47 * don't care which is used.  The gateways will be
48 * selected based on the destination address and the
49 * source address has no relavence.
50 * @hide
51 */
52public class LinkProperties implements Parcelable {
53
54    String mIfaceName;
55    private Collection<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
56    private Collection<InetAddress> mDnses = new ArrayList<InetAddress>();
57    private Collection<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
58    private ProxyProperties mHttpProxy;
59
60    public static class CompareResult<T> {
61        public Collection<T> removed = new ArrayList<T>();
62        public Collection<T> added = new ArrayList<T>();
63
64        @Override
65        public String toString() {
66            String retVal = "removed=[";
67            for (T addr : removed) retVal += addr.toString() + ",";
68            retVal += "] added=[";
69            for (T addr : added) retVal += addr.toString() + ",";
70            retVal += "]";
71            return retVal;
72        }
73    }
74
75    public LinkProperties() {
76        clear();
77    }
78
79    // copy constructor instead of clone
80    public LinkProperties(LinkProperties source) {
81        if (source != null) {
82            mIfaceName = source.getInterfaceName();
83            for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
84            for (InetAddress i : source.getDnses()) mDnses.add(i);
85            for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
86            mHttpProxy = (source.getHttpProxy() == null)  ?
87                null : new ProxyProperties(source.getHttpProxy());
88        }
89    }
90
91    public void setInterfaceName(String iface) {
92        mIfaceName = iface;
93    }
94
95    public String getInterfaceName() {
96        return mIfaceName;
97    }
98
99    public Collection<InetAddress> getAddresses() {
100        Collection<InetAddress> addresses = new ArrayList<InetAddress>();
101        for (LinkAddress linkAddress : mLinkAddresses) {
102            addresses.add(linkAddress.getAddress());
103        }
104        return Collections.unmodifiableCollection(addresses);
105    }
106
107    public void addLinkAddress(LinkAddress address) {
108        if (address != null) mLinkAddresses.add(address);
109    }
110
111    public Collection<LinkAddress> getLinkAddresses() {
112        return Collections.unmodifiableCollection(mLinkAddresses);
113    }
114
115    public void addDns(InetAddress dns) {
116        if (dns != null) mDnses.add(dns);
117    }
118
119    public Collection<InetAddress> getDnses() {
120        return Collections.unmodifiableCollection(mDnses);
121    }
122
123    public void addRoute(RouteInfo route) {
124        if (route != null) mRoutes.add(route);
125    }
126    public Collection<RouteInfo> getRoutes() {
127        return Collections.unmodifiableCollection(mRoutes);
128    }
129
130    public void setHttpProxy(ProxyProperties proxy) {
131        mHttpProxy = proxy;
132    }
133    public ProxyProperties getHttpProxy() {
134        return mHttpProxy;
135    }
136
137    public void clear() {
138        mIfaceName = null;
139        mLinkAddresses.clear();
140        mDnses.clear();
141        mRoutes.clear();
142        mHttpProxy = null;
143    }
144
145    /**
146     * Implement the Parcelable interface
147     * @hide
148     */
149    public int describeContents() {
150        return 0;
151    }
152
153    @Override
154    public String toString() {
155        String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " ");
156
157        String linkAddresses = "LinkAddresses: [";
158        for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ",";
159        linkAddresses += "] ";
160
161        String dns = "DnsAddresses: [";
162        for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
163        dns += "] ";
164
165        String routes = "Routes: [";
166        for (RouteInfo route : mRoutes) routes += route.toString() + ",";
167        routes += "] ";
168        String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
169
170        return ifaceName + linkAddresses + routes + dns + proxy;
171    }
172
173    /**
174     * Compares this {@code LinkProperties} interface name against the target
175     *
176     * @param target LinkProperties to compare.
177     * @return {@code true} if both are identical, {@code false} otherwise.
178     */
179    public boolean isIdenticalInterfaceName(LinkProperties target) {
180        return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
181    }
182
183    /**
184     * Compares this {@code LinkProperties} interface name against the target
185     *
186     * @param target LinkProperties to compare.
187     * @return {@code true} if both are identical, {@code false} otherwise.
188     */
189    public boolean isIdenticalAddresses(LinkProperties target) {
190        Collection<InetAddress> targetAddresses = target.getAddresses();
191        Collection<InetAddress> sourceAddresses = getAddresses();
192        return (sourceAddresses.size() == targetAddresses.size()) ?
193                    sourceAddresses.containsAll(targetAddresses) : false;
194    }
195
196    /**
197     * Compares this {@code LinkProperties} DNS addresses against the target
198     *
199     * @param target LinkProperties to compare.
200     * @return {@code true} if both are identical, {@code false} otherwise.
201     */
202    public boolean isIdenticalDnses(LinkProperties target) {
203        Collection<InetAddress> targetDnses = target.getDnses();
204        return (mDnses.size() == targetDnses.size()) ?
205                    mDnses.containsAll(targetDnses) : false;
206    }
207
208    /**
209     * Compares this {@code LinkProperties} Routes against the target
210     *
211     * @param target LinkProperties to compare.
212     * @return {@code true} if both are identical, {@code false} otherwise.
213     */
214    public boolean isIdenticalRoutes(LinkProperties target) {
215        Collection<RouteInfo> targetRoutes = target.getRoutes();
216        return (mRoutes.size() == targetRoutes.size()) ?
217                    mRoutes.containsAll(targetRoutes) : false;
218    }
219
220    /**
221     * Compares this {@code LinkProperties} HttpProxy against the target
222     *
223     * @param target LinkProperties to compare.
224     * @return {@code true} if both are identical, {@code false} otherwise.
225     */
226    public boolean isIdenticalHttpProxy(LinkProperties target) {
227        return getHttpProxy() == null ? target.getHttpProxy() == null :
228                    getHttpProxy().equals(target.getHttpProxy());
229    }
230
231    @Override
232    /**
233     * Compares this {@code LinkProperties} instance against the target
234     * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
235     * all their fields are equal in values.
236     *
237     * For collection fields, such as mDnses, containsAll() is used to check
238     * if two collections contains the same elements, independent of order.
239     * There are two thoughts regarding containsAll()
240     * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal.
241     * 2. Worst case performance is O(n^2).
242     *
243     * @param obj the object to be tested for equality.
244     * @return {@code true} if both objects are equal, {@code false} otherwise.
245     */
246    public boolean equals(Object obj) {
247        if (this == obj) return true;
248
249        if (!(obj instanceof LinkProperties)) return false;
250
251        LinkProperties target = (LinkProperties) obj;
252
253        return isIdenticalInterfaceName(target) &&
254                isIdenticalAddresses(target) &&
255                isIdenticalDnses(target) &&
256                isIdenticalRoutes(target) &&
257                isIdenticalHttpProxy(target);
258    }
259
260    /**
261     * Return two lists, a list of addresses that would be removed from
262     * mLinkAddresses and a list of addresses that would be added to
263     * mLinkAddress which would then result in target and mLinkAddresses
264     * being the same list.
265     *
266     * @param target is a LinkProperties with the new list of addresses
267     * @return the removed and added lists.
268     */
269    public CompareResult<LinkAddress> compareAddresses(LinkProperties target) {
270        /*
271         * Duplicate the LinkAddresses into removed, we will be removing
272         * address which are common between mLinkAddresses and target
273         * leaving the addresses that are different. And address which
274         * are in target but not in mLinkAddresses are placed in the
275         * addedAddresses.
276         */
277        CompareResult<LinkAddress> result = new CompareResult<LinkAddress>();
278        result.removed = new ArrayList<LinkAddress>(mLinkAddresses);
279        result.added.clear();
280        if (target != null) {
281            for (LinkAddress newAddress : target.getLinkAddresses()) {
282                if (! result.removed.remove(newAddress)) {
283                    result.added.add(newAddress);
284                }
285            }
286        }
287        return result;
288    }
289
290    /**
291     * Return two lists, a list of dns addresses that would be removed from
292     * mDnses and a list of addresses that would be added to
293     * mDnses which would then result in target and mDnses
294     * being the same list.
295     *
296     * @param target is a LinkProperties with the new list of dns addresses
297     * @return the removed and added lists.
298     */
299    public CompareResult<InetAddress> compareDnses(LinkProperties target) {
300        /*
301         * Duplicate the InetAddresses into removed, we will be removing
302         * dns address which are common between mDnses and target
303         * leaving the addresses that are different. And dns address which
304         * are in target but not in mDnses are placed in the
305         * addedAddresses.
306         */
307        CompareResult<InetAddress> result = new CompareResult<InetAddress>();
308
309        result.removed = new ArrayList<InetAddress>(mDnses);
310        result.added.clear();
311        if (target != null) {
312            for (InetAddress newAddress : target.getDnses()) {
313                if (! result.removed.remove(newAddress)) {
314                    result.added.add(newAddress);
315                }
316            }
317        }
318        return result;
319    }
320
321    /**
322     * Return two lists, a list of routes that would be removed from
323     * mRoutes and a list of routes that would be added to
324     * mRoutes which would then result in target and mRoutes
325     * being the same list.
326     *
327     * @param target is a LinkProperties with the new list of routes
328     * @return the removed and added lists.
329     */
330    public CompareResult<RouteInfo> compareRoutes(LinkProperties target) {
331        /*
332         * Duplicate the RouteInfos into removed, we will be removing
333         * routes which are common between mDnses and target
334         * leaving the routes that are different. And route address which
335         * are in target but not in mRoutes are placed in added.
336         */
337        CompareResult<RouteInfo> result = new CompareResult<RouteInfo>();
338
339        result.removed = new ArrayList<RouteInfo>(mRoutes);
340        result.added.clear();
341        if (target != null) {
342            for (RouteInfo r : target.getRoutes()) {
343                if (! result.removed.remove(r)) {
344                    result.added.add(r);
345                }
346            }
347        }
348        return result;
349    }
350
351
352    @Override
353    /**
354     * generate hashcode based on significant fields
355     * Equal objects must produce the same hash code, while unequal objects
356     * may have the same hash codes.
357     */
358    public int hashCode() {
359        return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
360                + mLinkAddresses.size() * 31
361                + mDnses.size() * 37
362                + mRoutes.size() * 41
363                + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()));
364    }
365
366    /**
367     * Implement the Parcelable interface.
368     * @hide
369     */
370    public void writeToParcel(Parcel dest, int flags) {
371        dest.writeString(getInterfaceName());
372        dest.writeInt(mLinkAddresses.size());
373        for(LinkAddress linkAddress : mLinkAddresses) {
374            dest.writeParcelable(linkAddress, flags);
375        }
376
377        dest.writeInt(mDnses.size());
378        for(InetAddress d : mDnses) {
379            dest.writeByteArray(d.getAddress());
380        }
381
382        dest.writeInt(mRoutes.size());
383        for(RouteInfo route : mRoutes) {
384            dest.writeParcelable(route, flags);
385        }
386
387        if (mHttpProxy != null) {
388            dest.writeByte((byte)1);
389            dest.writeParcelable(mHttpProxy, flags);
390        } else {
391            dest.writeByte((byte)0);
392        }
393    }
394
395    /**
396     * Implement the Parcelable interface.
397     * @hide
398     */
399    public static final Creator<LinkProperties> CREATOR =
400        new Creator<LinkProperties>() {
401            public LinkProperties createFromParcel(Parcel in) {
402                LinkProperties netProp = new LinkProperties();
403                String iface = in.readString();
404                if (iface != null) {
405                    try {
406                        netProp.setInterfaceName(iface);
407                    } catch (Exception e) {
408                        return null;
409                    }
410                }
411                int addressCount = in.readInt();
412                for (int i=0; i<addressCount; i++) {
413                    netProp.addLinkAddress((LinkAddress)in.readParcelable(null));
414                }
415                addressCount = in.readInt();
416                for (int i=0; i<addressCount; i++) {
417                    try {
418                        netProp.addDns(InetAddress.getByAddress(in.createByteArray()));
419                    } catch (UnknownHostException e) { }
420                }
421                addressCount = in.readInt();
422                for (int i=0; i<addressCount; i++) {
423                    netProp.addRoute((RouteInfo)in.readParcelable(null));
424                }
425                if (in.readByte() == 1) {
426                    netProp.setHttpProxy((ProxyProperties)in.readParcelable(null));
427                }
428                return netProp;
429            }
430
431            public LinkProperties[] newArray(int size) {
432                return new LinkProperties[size];
433            }
434        };
435}
436