1/*
2 * Copyright (C) 2014 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.os.Parcel;
20import android.os.Parcelable;
21import android.text.TextUtils;
22import java.lang.IllegalArgumentException;
23
24/**
25 * This class represents the capabilities of a network.  This is used both to specify
26 * needs to {@link ConnectivityManager} and when inspecting a network.
27 *
28 * Note that this replaces the old {@link ConnectivityManager#TYPE_MOBILE} method
29 * of network selection.  Rather than indicate a need for Wi-Fi because an application
30 * needs high bandwidth and risk obsolescence when a new, fast network appears (like LTE),
31 * the application should specify it needs high bandwidth.  Similarly if an application
32 * needs an unmetered network for a bulk transfer it can specify that rather than assuming
33 * all cellular based connections are metered and all Wi-Fi based connections are not.
34 */
35public final class NetworkCapabilities implements Parcelable {
36    /**
37     * @hide
38     */
39    public NetworkCapabilities() {
40    }
41
42    public NetworkCapabilities(NetworkCapabilities nc) {
43        if (nc != null) {
44            mNetworkCapabilities = nc.mNetworkCapabilities;
45            mTransportTypes = nc.mTransportTypes;
46            mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
47            mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
48            mNetworkSpecifier = nc.mNetworkSpecifier;
49        }
50    }
51
52    /**
53     * Represents the network's capabilities.  If any are specified they will be satisfied
54     * by any Network that matches all of them.
55     */
56    private long mNetworkCapabilities = (1 << NET_CAPABILITY_NOT_RESTRICTED) |
57            (1 << NET_CAPABILITY_TRUSTED) | (1 << NET_CAPABILITY_NOT_VPN);
58
59    /**
60     * Indicates this is a network that has the ability to reach the
61     * carrier's MMSC for sending and receiving MMS messages.
62     */
63    public static final int NET_CAPABILITY_MMS            = 0;
64
65    /**
66     * Indicates this is a network that has the ability to reach the carrier's
67     * SUPL server, used to retrieve GPS information.
68     */
69    public static final int NET_CAPABILITY_SUPL           = 1;
70
71    /**
72     * Indicates this is a network that has the ability to reach the carrier's
73     * DUN or tethering gateway.
74     */
75    public static final int NET_CAPABILITY_DUN            = 2;
76
77    /**
78     * Indicates this is a network that has the ability to reach the carrier's
79     * FOTA portal, used for over the air updates.
80     */
81    public static final int NET_CAPABILITY_FOTA           = 3;
82
83    /**
84     * Indicates this is a network that has the ability to reach the carrier's
85     * IMS servers, used for network registration and signaling.
86     */
87    public static final int NET_CAPABILITY_IMS            = 4;
88
89    /**
90     * Indicates this is a network that has the ability to reach the carrier's
91     * CBS servers, used for carrier specific services.
92     */
93    public static final int NET_CAPABILITY_CBS            = 5;
94
95    /**
96     * Indicates this is a network that has the ability to reach a Wi-Fi direct
97     * peer.
98     */
99    public static final int NET_CAPABILITY_WIFI_P2P       = 6;
100
101    /**
102     * Indicates this is a network that has the ability to reach a carrier's
103     * Initial Attach servers.
104     */
105    public static final int NET_CAPABILITY_IA             = 7;
106
107    /**
108     * Indicates this is a network that has the ability to reach a carrier's
109     * RCS servers, used for Rich Communication Services.
110     */
111    public static final int NET_CAPABILITY_RCS            = 8;
112
113    /**
114     * Indicates this is a network that has the ability to reach a carrier's
115     * XCAP servers, used for configuration and control.
116     */
117    public static final int NET_CAPABILITY_XCAP           = 9;
118
119    /**
120     * Indicates this is a network that has the ability to reach a carrier's
121     * Emergency IMS servers, used for network signaling during emergency calls.
122     */
123    public static final int NET_CAPABILITY_EIMS           = 10;
124
125    /**
126     * Indicates that this network is unmetered.
127     */
128    public static final int NET_CAPABILITY_NOT_METERED    = 11;
129
130    /**
131     * Indicates that this network should be able to reach the internet.
132     */
133    public static final int NET_CAPABILITY_INTERNET       = 12;
134
135    /**
136     * Indicates that this network is available for general use.  If this is not set
137     * applications should not attempt to communicate on this network.  Note that this
138     * is simply informative and not enforcement - enforcement is handled via other means.
139     * Set by default.
140     */
141    public static final int NET_CAPABILITY_NOT_RESTRICTED = 13;
142
143    /**
144     * Indicates that the user has indicated implicit trust of this network.  This
145     * generally means it's a sim-selected carrier, a plugged in ethernet, a paired
146     * BT device or a wifi the user asked to connect to.  Untrusted networks
147     * are probably limited to unknown wifi AP.  Set by default.
148     */
149    public static final int NET_CAPABILITY_TRUSTED        = 14;
150
151    /*
152     * Indicates that this network is not a VPN.  This capability is set by default and should be
153     * explicitly cleared when creating VPN networks.
154     */
155    public static final int NET_CAPABILITY_NOT_VPN        = 15;
156
157    /**
158     * Indicates that connectivity on this network was successfully validated. For example, for a
159     * network with NET_CAPABILITY_INTERNET, it means that Internet connectivity was successfully
160     * detected.
161     * @hide
162     */
163    public static final int NET_CAPABILITY_VALIDATED      = 16;
164
165    private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
166    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_VALIDATED;
167
168    /**
169     * Adds the given capability to this {@code NetworkCapability} instance.
170     * Multiple capabilities may be applied sequentially.  Note that when searching
171     * for a network to satisfy a request, all capabilities requested must be satisfied.
172     *
173     * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be added.
174     * @return This NetworkCapability to facilitate chaining.
175     * @hide
176     */
177    public NetworkCapabilities addCapability(int capability) {
178        if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
179            throw new IllegalArgumentException("NetworkCapability out of range");
180        }
181        mNetworkCapabilities |= 1 << capability;
182        return this;
183    }
184
185    /**
186     * Removes (if found) the given capability from this {@code NetworkCapability} instance.
187     *
188     * @param capability the {@code NetworkCapabilities.NET_CAPABILTIY_*} to be removed.
189     * @return This NetworkCapability to facilitate chaining.
190     * @hide
191     */
192    public NetworkCapabilities removeCapability(int capability) {
193        if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
194            throw new IllegalArgumentException("NetworkCapability out of range");
195        }
196        mNetworkCapabilities &= ~(1 << capability);
197        return this;
198    }
199
200    /**
201     * Gets all the capabilities set on this {@code NetworkCapability} instance.
202     *
203     * @return an array of {@code NetworkCapabilities.NET_CAPABILITY_*} values
204     *         for this instance.
205     * @hide
206     */
207    public int[] getCapabilities() {
208        return enumerateBits(mNetworkCapabilities);
209    }
210
211    /**
212     * Tests for the presence of a capabilitity on this instance.
213     *
214     * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be tested for.
215     * @return {@code true} if set on this instance.
216     */
217    public boolean hasCapability(int capability) {
218        if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
219            return false;
220        }
221        return ((mNetworkCapabilities & (1 << capability)) != 0);
222    }
223
224    private int[] enumerateBits(long val) {
225        int size = Long.bitCount(val);
226        int[] result = new int[size];
227        int index = 0;
228        int resource = 0;
229        while (val > 0) {
230            if ((val & 1) == 1) result[index++] = resource;
231            val = val >> 1;
232            resource++;
233        }
234        return result;
235    }
236
237    private void combineNetCapabilities(NetworkCapabilities nc) {
238        this.mNetworkCapabilities |= nc.mNetworkCapabilities;
239    }
240
241    private boolean satisfiedByNetCapabilities(NetworkCapabilities nc) {
242        return ((nc.mNetworkCapabilities & this.mNetworkCapabilities) == this.mNetworkCapabilities);
243    }
244
245    /** @hide */
246    public boolean equalsNetCapabilities(NetworkCapabilities nc) {
247        return (nc.mNetworkCapabilities == this.mNetworkCapabilities);
248    }
249
250    /**
251     * Representing the transport type.  Apps should generally not care about transport.  A
252     * request for a fast internet connection could be satisfied by a number of different
253     * transports.  If any are specified here it will be satisfied a Network that matches
254     * any of them.  If a caller doesn't care about the transport it should not specify any.
255     */
256    private long mTransportTypes;
257
258    /**
259     * Indicates this network uses a Cellular transport.
260     */
261    public static final int TRANSPORT_CELLULAR = 0;
262
263    /**
264     * Indicates this network uses a Wi-Fi transport.
265     */
266    public static final int TRANSPORT_WIFI = 1;
267
268    /**
269     * Indicates this network uses a Bluetooth transport.
270     */
271    public static final int TRANSPORT_BLUETOOTH = 2;
272
273    /**
274     * Indicates this network uses an Ethernet transport.
275     */
276    public static final int TRANSPORT_ETHERNET = 3;
277
278    /**
279     * Indicates this network uses a VPN transport.
280     */
281    public static final int TRANSPORT_VPN = 4;
282
283    private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
284    private static final int MAX_TRANSPORT = TRANSPORT_VPN;
285
286    /**
287     * Adds the given transport type to this {@code NetworkCapability} instance.
288     * Multiple transports may be applied sequentially.  Note that when searching
289     * for a network to satisfy a request, any listed in the request will satisfy the request.
290     * For example {@code TRANSPORT_WIFI} and {@code TRANSPORT_ETHERNET} added to a
291     * {@code NetworkCapabilities} would cause either a Wi-Fi network or an Ethernet network
292     * to be selected.  This is logically different than
293     * {@code NetworkCapabilities.NET_CAPABILITY_*} listed above.
294     *
295     * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be added.
296     * @return This NetworkCapability to facilitate chaining.
297     * @hide
298     */
299    public NetworkCapabilities addTransportType(int transportType) {
300        if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
301            throw new IllegalArgumentException("TransportType out of range");
302        }
303        mTransportTypes |= 1 << transportType;
304        setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
305        return this;
306    }
307
308    /**
309     * Removes (if found) the given transport from this {@code NetworkCapability} instance.
310     *
311     * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be removed.
312     * @return This NetworkCapability to facilitate chaining.
313     * @hide
314     */
315    public NetworkCapabilities removeTransportType(int transportType) {
316        if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
317            throw new IllegalArgumentException("TransportType out of range");
318        }
319        mTransportTypes &= ~(1 << transportType);
320        setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
321        return this;
322    }
323
324    /**
325     * Gets all the transports set on this {@code NetworkCapability} instance.
326     *
327     * @return an array of {@code NetworkCapabilities.TRANSPORT_*} values
328     *         for this instance.
329     * @hide
330     */
331    public int[] getTransportTypes() {
332        return enumerateBits(mTransportTypes);
333    }
334
335    /**
336     * Tests for the presence of a transport on this instance.
337     *
338     * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be tested for.
339     * @return {@code true} if set on this instance.
340     */
341    public boolean hasTransport(int transportType) {
342        if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
343            return false;
344        }
345        return ((mTransportTypes & (1 << transportType)) != 0);
346    }
347
348    private void combineTransportTypes(NetworkCapabilities nc) {
349        this.mTransportTypes |= nc.mTransportTypes;
350    }
351    private boolean satisfiedByTransportTypes(NetworkCapabilities nc) {
352        return ((this.mTransportTypes == 0) ||
353                ((this.mTransportTypes & nc.mTransportTypes) != 0));
354    }
355    /** @hide */
356    public boolean equalsTransportTypes(NetworkCapabilities nc) {
357        return (nc.mTransportTypes == this.mTransportTypes);
358    }
359
360    /**
361     * Passive link bandwidth.  This is a rough guide of the expected peak bandwidth
362     * for the first hop on the given transport.  It is not measured, but may take into account
363     * link parameters (Radio technology, allocated channels, etc).
364     */
365    private int mLinkUpBandwidthKbps;
366    private int mLinkDownBandwidthKbps;
367
368    /**
369     * Sets the upstream bandwidth for this network in Kbps.  This always only refers to
370     * the estimated first hop transport bandwidth.
371     * <p>
372     * Note that when used to request a network, this specifies the minimum acceptable.
373     * When received as the state of an existing network this specifies the typical
374     * first hop bandwidth expected.  This is never measured, but rather is inferred
375     * from technology type and other link parameters.  It could be used to differentiate
376     * between very slow 1xRTT cellular links and other faster networks or even between
377     * 802.11b vs 802.11AC wifi technologies.  It should not be used to differentiate between
378     * fast backhauls and slow backhauls.
379     *
380     * @param upKbps the estimated first hop upstream (device to network) bandwidth.
381     * @hide
382     */
383    public void setLinkUpstreamBandwidthKbps(int upKbps) {
384        mLinkUpBandwidthKbps = upKbps;
385    }
386
387    /**
388     * Retrieves the upstream bandwidth for this network in Kbps.  This always only refers to
389     * the estimated first hop transport bandwidth.
390     *
391     * @return The estimated first hop upstream (device to network) bandwidth.
392     */
393    public int getLinkUpstreamBandwidthKbps() {
394        return mLinkUpBandwidthKbps;
395    }
396
397    /**
398     * Sets the downstream bandwidth for this network in Kbps.  This always only refers to
399     * the estimated first hop transport bandwidth.
400     * <p>
401     * Note that when used to request a network, this specifies the minimum acceptable.
402     * When received as the state of an existing network this specifies the typical
403     * first hop bandwidth expected.  This is never measured, but rather is inferred
404     * from technology type and other link parameters.  It could be used to differentiate
405     * between very slow 1xRTT cellular links and other faster networks or even between
406     * 802.11b vs 802.11AC wifi technologies.  It should not be used to differentiate between
407     * fast backhauls and slow backhauls.
408     *
409     * @param downKbps the estimated first hop downstream (network to device) bandwidth.
410     * @hide
411     */
412    public void setLinkDownstreamBandwidthKbps(int downKbps) {
413        mLinkDownBandwidthKbps = downKbps;
414    }
415
416    /**
417     * Retrieves the downstream bandwidth for this network in Kbps.  This always only refers to
418     * the estimated first hop transport bandwidth.
419     *
420     * @return The estimated first hop downstream (network to device) bandwidth.
421     */
422    public int getLinkDownstreamBandwidthKbps() {
423        return mLinkDownBandwidthKbps;
424    }
425
426    private void combineLinkBandwidths(NetworkCapabilities nc) {
427        this.mLinkUpBandwidthKbps =
428                Math.max(this.mLinkUpBandwidthKbps, nc.mLinkUpBandwidthKbps);
429        this.mLinkDownBandwidthKbps =
430                Math.max(this.mLinkDownBandwidthKbps, nc.mLinkDownBandwidthKbps);
431    }
432    private boolean satisfiedByLinkBandwidths(NetworkCapabilities nc) {
433        return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps ||
434                this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps);
435    }
436    private boolean equalsLinkBandwidths(NetworkCapabilities nc) {
437        return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps &&
438                this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps);
439    }
440
441    private String mNetworkSpecifier;
442    /**
443     * Sets the optional bearer specific network specifier.
444     * This has no meaning if a single transport is also not specified, so calling
445     * this without a single transport set will generate an exception, as will
446     * subsequently adding or removing transports after this is set.
447     * </p>
448     * The interpretation of this {@code String} is bearer specific and bearers that use
449     * it should document their particulars.  For example, Bluetooth may use some sort of
450     * device id while WiFi could used SSID and/or BSSID.  Cellular may use carrier SPN (name)
451     * or Subscription ID.
452     *
453     * @param networkSpecifier An {@code String} of opaque format used to specify the bearer
454     *                         specific network specifier where the bearer has a choice of
455     *                         networks.
456     * @hide
457     */
458    public void setNetworkSpecifier(String networkSpecifier) {
459        if (TextUtils.isEmpty(networkSpecifier) == false && Long.bitCount(mTransportTypes) != 1) {
460            throw new IllegalStateException("Must have a single transport specified to use " +
461                    "setNetworkSpecifier");
462        }
463        mNetworkSpecifier = networkSpecifier;
464    }
465
466    /**
467     * Gets the optional bearer specific network specifier.
468     *
469     * @return The optional {@code String} specifying the bearer specific network specifier.
470     *         See {@link #setNetworkSpecifier}.
471     * @hide
472     */
473    public String getNetworkSpecifier() {
474        return mNetworkSpecifier;
475    }
476
477    private void combineSpecifiers(NetworkCapabilities nc) {
478        String otherSpecifier = nc.getNetworkSpecifier();
479        if (TextUtils.isEmpty(otherSpecifier)) return;
480        if (TextUtils.isEmpty(mNetworkSpecifier) == false) {
481            throw new IllegalStateException("Can't combine two networkSpecifiers");
482        }
483        setNetworkSpecifier(otherSpecifier);
484    }
485    private boolean satisfiedBySpecifier(NetworkCapabilities nc) {
486        return (TextUtils.isEmpty(mNetworkSpecifier) ||
487                mNetworkSpecifier.equals(nc.mNetworkSpecifier));
488    }
489    private boolean equalsSpecifier(NetworkCapabilities nc) {
490        if (TextUtils.isEmpty(mNetworkSpecifier)) {
491            return TextUtils.isEmpty(nc.mNetworkSpecifier);
492        } else {
493            return mNetworkSpecifier.equals(nc.mNetworkSpecifier);
494        }
495    }
496
497    /**
498     * Combine a set of Capabilities to this one.  Useful for coming up with the complete set
499     * {@hide}
500     */
501    public void combineCapabilities(NetworkCapabilities nc) {
502        combineNetCapabilities(nc);
503        combineTransportTypes(nc);
504        combineLinkBandwidths(nc);
505        combineSpecifiers(nc);
506    }
507
508    /**
509     * Check if our requirements are satisfied by the given Capabilities.
510     * {@hide}
511     */
512    public boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc) {
513        return (nc != null &&
514                satisfiedByNetCapabilities(nc) &&
515                satisfiedByTransportTypes(nc) &&
516                satisfiedByLinkBandwidths(nc) &&
517                satisfiedBySpecifier(nc));
518    }
519
520    @Override
521    public boolean equals(Object obj) {
522        if (obj == null || (obj instanceof NetworkCapabilities == false)) return false;
523        NetworkCapabilities that = (NetworkCapabilities)obj;
524        return (equalsNetCapabilities(that) &&
525                equalsTransportTypes(that) &&
526                equalsLinkBandwidths(that) &&
527                equalsSpecifier(that));
528    }
529
530    @Override
531    public int hashCode() {
532        return ((int)(mNetworkCapabilities & 0xFFFFFFFF) +
533                ((int)(mNetworkCapabilities >> 32) * 3) +
534                ((int)(mTransportTypes & 0xFFFFFFFF) * 5) +
535                ((int)(mTransportTypes >> 32) * 7) +
536                (mLinkUpBandwidthKbps * 11) +
537                (mLinkDownBandwidthKbps * 13) +
538                (TextUtils.isEmpty(mNetworkSpecifier) ? 0 : mNetworkSpecifier.hashCode() * 17));
539    }
540
541    @Override
542    public int describeContents() {
543        return 0;
544    }
545    @Override
546    public void writeToParcel(Parcel dest, int flags) {
547        dest.writeLong(mNetworkCapabilities);
548        dest.writeLong(mTransportTypes);
549        dest.writeInt(mLinkUpBandwidthKbps);
550        dest.writeInt(mLinkDownBandwidthKbps);
551        dest.writeString(mNetworkSpecifier);
552    }
553    public static final Creator<NetworkCapabilities> CREATOR =
554        new Creator<NetworkCapabilities>() {
555            @Override
556            public NetworkCapabilities createFromParcel(Parcel in) {
557                NetworkCapabilities netCap = new NetworkCapabilities();
558
559                netCap.mNetworkCapabilities = in.readLong();
560                netCap.mTransportTypes = in.readLong();
561                netCap.mLinkUpBandwidthKbps = in.readInt();
562                netCap.mLinkDownBandwidthKbps = in.readInt();
563                netCap.mNetworkSpecifier = in.readString();
564                return netCap;
565            }
566            @Override
567            public NetworkCapabilities[] newArray(int size) {
568                return new NetworkCapabilities[size];
569            }
570        };
571
572    @Override
573    public String toString() {
574        int[] types = getTransportTypes();
575        String transports = (types.length > 0 ? " Transports: " : "");
576        for (int i = 0; i < types.length;) {
577            switch (types[i]) {
578                case TRANSPORT_CELLULAR:    transports += "CELLULAR"; break;
579                case TRANSPORT_WIFI:        transports += "WIFI"; break;
580                case TRANSPORT_BLUETOOTH:   transports += "BLUETOOTH"; break;
581                case TRANSPORT_ETHERNET:    transports += "ETHERNET"; break;
582                case TRANSPORT_VPN:         transports += "VPN"; break;
583            }
584            if (++i < types.length) transports += "|";
585        }
586
587        types = getCapabilities();
588        String capabilities = (types.length > 0 ? " Capabilities: " : "");
589        for (int i = 0; i < types.length; ) {
590            switch (types[i]) {
591                case NET_CAPABILITY_MMS:            capabilities += "MMS"; break;
592                case NET_CAPABILITY_SUPL:           capabilities += "SUPL"; break;
593                case NET_CAPABILITY_DUN:            capabilities += "DUN"; break;
594                case NET_CAPABILITY_FOTA:           capabilities += "FOTA"; break;
595                case NET_CAPABILITY_IMS:            capabilities += "IMS"; break;
596                case NET_CAPABILITY_CBS:            capabilities += "CBS"; break;
597                case NET_CAPABILITY_WIFI_P2P:       capabilities += "WIFI_P2P"; break;
598                case NET_CAPABILITY_IA:             capabilities += "IA"; break;
599                case NET_CAPABILITY_RCS:            capabilities += "RCS"; break;
600                case NET_CAPABILITY_XCAP:           capabilities += "XCAP"; break;
601                case NET_CAPABILITY_EIMS:           capabilities += "EIMS"; break;
602                case NET_CAPABILITY_NOT_METERED:    capabilities += "NOT_METERED"; break;
603                case NET_CAPABILITY_INTERNET:       capabilities += "INTERNET"; break;
604                case NET_CAPABILITY_NOT_RESTRICTED: capabilities += "NOT_RESTRICTED"; break;
605                case NET_CAPABILITY_TRUSTED:        capabilities += "TRUSTED"; break;
606                case NET_CAPABILITY_NOT_VPN:        capabilities += "NOT_VPN"; break;
607            }
608            if (++i < types.length) capabilities += "&";
609        }
610
611        String upBand = ((mLinkUpBandwidthKbps > 0) ? " LinkUpBandwidth>=" +
612                mLinkUpBandwidthKbps + "Kbps" : "");
613        String dnBand = ((mLinkDownBandwidthKbps > 0) ? " LinkDnBandwidth>=" +
614                mLinkDownBandwidthKbps + "Kbps" : "");
615
616        String specifier = (mNetworkSpecifier == null ?
617                "" : " Specifier: <" + mNetworkSpecifier + ">");
618
619        return "[" + transports + capabilities + upBand + dnBand + specifier + "]";
620    }
621}
622