NetworkCapabilities.java revision 06314e4eab4b78d971ff878de3984f2dc673c2d3
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    private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
159    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VPN;
160
161    /**
162     * Adds the given capability to this {@code NetworkCapability} instance.
163     * Multiple capabilities may be applied sequentially.  Note that when searching
164     * for a network to satisfy a request, all capabilities requested must be satisfied.
165     *
166     * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be added.
167     * @return This NetworkCapability to facilitate chaining.
168     * @hide
169     */
170    public NetworkCapabilities addCapability(int capability) {
171        if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
172            throw new IllegalArgumentException("NetworkCapability out of range");
173        }
174        mNetworkCapabilities |= 1 << capability;
175        return this;
176    }
177
178    /**
179     * Removes (if found) the given capability from this {@code NetworkCapability} instance.
180     *
181     * @param capability the {@code NetworkCapabilities.NET_CAPABILTIY_*} to be removed.
182     * @return This NetworkCapability to facilitate chaining.
183     * @hide
184     */
185    public NetworkCapabilities removeCapability(int capability) {
186        if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
187            throw new IllegalArgumentException("NetworkCapability out of range");
188        }
189        mNetworkCapabilities &= ~(1 << capability);
190        return this;
191    }
192
193    /**
194     * Gets all the capabilities set on this {@code NetworkCapability} instance.
195     *
196     * @return an array of {@code NetworkCapabilities.NET_CAPABILITY_*} values
197     *         for this instance.
198     * @hide
199     */
200    public int[] getCapabilities() {
201        return enumerateBits(mNetworkCapabilities);
202    }
203
204    /**
205     * Tests for the presence of a capabilitity on this instance.
206     *
207     * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be tested for.
208     * @return {@code true} if set on this instance.
209     */
210    public boolean hasCapability(int capability) {
211        if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
212            return false;
213        }
214        return ((mNetworkCapabilities & (1 << capability)) != 0);
215    }
216
217    private int[] enumerateBits(long val) {
218        int size = Long.bitCount(val);
219        int[] result = new int[size];
220        int index = 0;
221        int resource = 0;
222        while (val > 0) {
223            if ((val & 1) == 1) result[index++] = resource;
224            val = val >> 1;
225            resource++;
226        }
227        return result;
228    }
229
230    private void combineNetCapabilities(NetworkCapabilities nc) {
231        this.mNetworkCapabilities |= nc.mNetworkCapabilities;
232    }
233
234    private boolean satisfiedByNetCapabilities(NetworkCapabilities nc) {
235        return ((nc.mNetworkCapabilities & this.mNetworkCapabilities) == this.mNetworkCapabilities);
236    }
237
238    /** @hide */
239    public boolean equalsNetCapabilities(NetworkCapabilities nc) {
240        return (nc.mNetworkCapabilities == this.mNetworkCapabilities);
241    }
242
243    /**
244     * Representing the transport type.  Apps should generally not care about transport.  A
245     * request for a fast internet connection could be satisfied by a number of different
246     * transports.  If any are specified here it will be satisfied a Network that matches
247     * any of them.  If a caller doesn't care about the transport it should not specify any.
248     */
249    private long mTransportTypes;
250
251    /**
252     * Indicates this network uses a Cellular transport.
253     */
254    public static final int TRANSPORT_CELLULAR = 0;
255
256    /**
257     * Indicates this network uses a Wi-Fi transport.
258     */
259    public static final int TRANSPORT_WIFI = 1;
260
261    /**
262     * Indicates this network uses a Bluetooth transport.
263     */
264    public static final int TRANSPORT_BLUETOOTH = 2;
265
266    /**
267     * Indicates this network uses an Ethernet transport.
268     */
269    public static final int TRANSPORT_ETHERNET = 3;
270
271    /**
272     * Indicates this network uses a VPN transport.
273     */
274    public static final int TRANSPORT_VPN = 4;
275
276    private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
277    private static final int MAX_TRANSPORT = TRANSPORT_VPN;
278
279    /**
280     * Adds the given transport type to this {@code NetworkCapability} instance.
281     * Multiple transports may be applied sequentially.  Note that when searching
282     * for a network to satisfy a request, any listed in the request will satisfy the request.
283     * For example {@code TRANSPORT_WIFI} and {@code TRANSPORT_ETHERNET} added to a
284     * {@code NetworkCapabilities} would cause either a Wi-Fi network or an Ethernet network
285     * to be selected.  This is logically different than
286     * {@code NetworkCapabilities.NET_CAPABILITY_*} listed above.
287     *
288     * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be added.
289     * @return This NetworkCapability to facilitate chaining.
290     * @hide
291     */
292    public NetworkCapabilities addTransportType(int transportType) {
293        if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
294            throw new IllegalArgumentException("TransportType out of range");
295        }
296        mTransportTypes |= 1 << transportType;
297        setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
298        return this;
299    }
300
301    /**
302     * Removes (if found) the given transport from this {@code NetworkCapability} instance.
303     *
304     * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be removed.
305     * @return This NetworkCapability to facilitate chaining.
306     * @hide
307     */
308    public NetworkCapabilities removeTransportType(int transportType) {
309        if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
310            throw new IllegalArgumentException("TransportType out of range");
311        }
312        mTransportTypes &= ~(1 << transportType);
313        setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
314        return this;
315    }
316
317    /**
318     * Gets all the transports set on this {@code NetworkCapability} instance.
319     *
320     * @return an array of {@code NetworkCapabilities.TRANSPORT_*} values
321     *         for this instance.
322     * @hide
323     */
324    public int[] getTransportTypes() {
325        return enumerateBits(mTransportTypes);
326    }
327
328    /**
329     * Tests for the presence of a transport on this instance.
330     *
331     * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be tested for.
332     * @return {@code true} if set on this instance.
333     */
334    public boolean hasTransport(int transportType) {
335        if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
336            return false;
337        }
338        return ((mTransportTypes & (1 << transportType)) != 0);
339    }
340
341    private void combineTransportTypes(NetworkCapabilities nc) {
342        this.mTransportTypes |= nc.mTransportTypes;
343    }
344    private boolean satisfiedByTransportTypes(NetworkCapabilities nc) {
345        return ((this.mTransportTypes == 0) ||
346                ((this.mTransportTypes & nc.mTransportTypes) != 0));
347    }
348    /** @hide */
349    public boolean equalsTransportTypes(NetworkCapabilities nc) {
350        return (nc.mTransportTypes == this.mTransportTypes);
351    }
352
353    /**
354     * Passive link bandwidth.  This is a rough guide of the expected peak bandwidth
355     * for the first hop on the given transport.  It is not measured, but may take into account
356     * link parameters (Radio technology, allocated channels, etc).
357     */
358    private int mLinkUpBandwidthKbps;
359    private int mLinkDownBandwidthKbps;
360
361    /**
362     * Sets the upstream bandwidth for this network in Kbps.  This always only refers to
363     * the estimated first hop transport bandwidth.
364     * <p>
365     * Note that when used to request a network, this specifies the minimum acceptable.
366     * When received as the state of an existing network this specifies the typical
367     * first hop bandwidth expected.  This is never measured, but rather is inferred
368     * from technology type and other link parameters.  It could be used to differentiate
369     * between very slow 1xRTT cellular links and other faster networks or even between
370     * 802.11b vs 802.11AC wifi technologies.  It should not be used to differentiate between
371     * fast backhauls and slow backhauls.
372     *
373     * @param upKbps the estimated first hop upstream (device to network) bandwidth.
374     * @hide
375     */
376    public void setLinkUpstreamBandwidthKbps(int upKbps) {
377        mLinkUpBandwidthKbps = upKbps;
378    }
379
380    /**
381     * Retrieves the upstream bandwidth for this network in Kbps.  This always only refers to
382     * the estimated first hop transport bandwidth.
383     *
384     * @return The estimated first hop upstream (device to network) bandwidth.
385     */
386    public int getLinkUpstreamBandwidthKbps() {
387        return mLinkUpBandwidthKbps;
388    }
389
390    /**
391     * Sets the downstream bandwidth for this network in Kbps.  This always only refers to
392     * the estimated first hop transport bandwidth.
393     * <p>
394     * Note that when used to request a network, this specifies the minimum acceptable.
395     * When received as the state of an existing network this specifies the typical
396     * first hop bandwidth expected.  This is never measured, but rather is inferred
397     * from technology type and other link parameters.  It could be used to differentiate
398     * between very slow 1xRTT cellular links and other faster networks or even between
399     * 802.11b vs 802.11AC wifi technologies.  It should not be used to differentiate between
400     * fast backhauls and slow backhauls.
401     *
402     * @param downKbps the estimated first hop downstream (network to device) bandwidth.
403     * @hide
404     */
405    public void setLinkDownstreamBandwidthKbps(int downKbps) {
406        mLinkDownBandwidthKbps = downKbps;
407    }
408
409    /**
410     * Retrieves the downstream bandwidth for this network in Kbps.  This always only refers to
411     * the estimated first hop transport bandwidth.
412     *
413     * @return The estimated first hop downstream (network to device) bandwidth.
414     */
415    public int getLinkDownstreamBandwidthKbps() {
416        return mLinkDownBandwidthKbps;
417    }
418
419    private void combineLinkBandwidths(NetworkCapabilities nc) {
420        this.mLinkUpBandwidthKbps =
421                Math.max(this.mLinkUpBandwidthKbps, nc.mLinkUpBandwidthKbps);
422        this.mLinkDownBandwidthKbps =
423                Math.max(this.mLinkDownBandwidthKbps, nc.mLinkDownBandwidthKbps);
424    }
425    private boolean satisfiedByLinkBandwidths(NetworkCapabilities nc) {
426        return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps ||
427                this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps);
428    }
429    private boolean equalsLinkBandwidths(NetworkCapabilities nc) {
430        return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps &&
431                this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps);
432    }
433
434    private String mNetworkSpecifier;
435    /**
436     * Sets the optional bearer specific network specifier.
437     * This has no meaning if a single transport is also not specified, so calling
438     * this without a single transport set will generate an exception, as will
439     * subsequently adding or removing transports after this is set.
440     * </p>
441     * The interpretation of this {@code String} is bearer specific and bearers that use
442     * it should document their particulars.  For example, Bluetooth may use some sort of
443     * device id while WiFi could used SSID and/or BSSID.  Cellular may use carrier SPN (name)
444     * or Subscription ID.
445     *
446     * @param networkSpecifier An {@code String} of opaque format used to specify the bearer
447     *                         specific network specifier where the bearer has a choice of
448     *                         networks.
449     * @hide
450     */
451    public void setNetworkSpecifier(String networkSpecifier) {
452        if (TextUtils.isEmpty(networkSpecifier) == false && Long.bitCount(mTransportTypes) != 1) {
453            throw new IllegalStateException("Must have a single transport specified to use " +
454                    "setNetworkSpecifier");
455        }
456        mNetworkSpecifier = networkSpecifier;
457    }
458
459    /**
460     * Gets the optional bearer specific network specifier.
461     *
462     * @return The optional {@code String} specifying the bearer specific network specifier.
463     *         See {@link #setNetworkSpecifier}.
464     * @hide
465     */
466    public String getNetworkSpecifier() {
467        return mNetworkSpecifier;
468    }
469
470    private void combineSpecifiers(NetworkCapabilities nc) {
471        String otherSpecifier = nc.getNetworkSpecifier();
472        if (TextUtils.isEmpty(otherSpecifier)) return;
473        if (TextUtils.isEmpty(mNetworkSpecifier) == false) {
474            throw new IllegalStateException("Can't combine two networkSpecifiers");
475        }
476        setNetworkSpecifier(otherSpecifier);
477    }
478    private boolean satisfiedBySpecifier(NetworkCapabilities nc) {
479        return (TextUtils.isEmpty(mNetworkSpecifier) ||
480                mNetworkSpecifier.equals(nc.mNetworkSpecifier));
481    }
482    private boolean equalsSpecifier(NetworkCapabilities nc) {
483        if (TextUtils.isEmpty(mNetworkSpecifier)) {
484            return TextUtils.isEmpty(nc.mNetworkSpecifier);
485        } else {
486            return mNetworkSpecifier.equals(nc.mNetworkSpecifier);
487        }
488    }
489
490    /**
491     * Combine a set of Capabilities to this one.  Useful for coming up with the complete set
492     * {@hide}
493     */
494    public void combineCapabilities(NetworkCapabilities nc) {
495        combineNetCapabilities(nc);
496        combineTransportTypes(nc);
497        combineLinkBandwidths(nc);
498        combineSpecifiers(nc);
499    }
500
501    /**
502     * Check if our requirements are satisfied by the given Capabilities.
503     * {@hide}
504     */
505    public boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc) {
506        return (nc != null &&
507                satisfiedByNetCapabilities(nc) &&
508                satisfiedByTransportTypes(nc) &&
509                satisfiedByLinkBandwidths(nc) &&
510                satisfiedBySpecifier(nc));
511    }
512
513    @Override
514    public boolean equals(Object obj) {
515        if (obj == null || (obj instanceof NetworkCapabilities == false)) return false;
516        NetworkCapabilities that = (NetworkCapabilities)obj;
517        return (equalsNetCapabilities(that) &&
518                equalsTransportTypes(that) &&
519                equalsLinkBandwidths(that) &&
520                equalsSpecifier(that));
521    }
522
523    @Override
524    public int hashCode() {
525        return ((int)(mNetworkCapabilities & 0xFFFFFFFF) +
526                ((int)(mNetworkCapabilities >> 32) * 3) +
527                ((int)(mTransportTypes & 0xFFFFFFFF) * 5) +
528                ((int)(mTransportTypes >> 32) * 7) +
529                (mLinkUpBandwidthKbps * 11) +
530                (mLinkDownBandwidthKbps * 13) +
531                (TextUtils.isEmpty(mNetworkSpecifier) ? 0 : mNetworkSpecifier.hashCode() * 17));
532    }
533
534    @Override
535    public int describeContents() {
536        return 0;
537    }
538    @Override
539    public void writeToParcel(Parcel dest, int flags) {
540        dest.writeLong(mNetworkCapabilities);
541        dest.writeLong(mTransportTypes);
542        dest.writeInt(mLinkUpBandwidthKbps);
543        dest.writeInt(mLinkDownBandwidthKbps);
544        dest.writeString(mNetworkSpecifier);
545    }
546    public static final Creator<NetworkCapabilities> CREATOR =
547        new Creator<NetworkCapabilities>() {
548            @Override
549            public NetworkCapabilities createFromParcel(Parcel in) {
550                NetworkCapabilities netCap = new NetworkCapabilities();
551
552                netCap.mNetworkCapabilities = in.readLong();
553                netCap.mTransportTypes = in.readLong();
554                netCap.mLinkUpBandwidthKbps = in.readInt();
555                netCap.mLinkDownBandwidthKbps = in.readInt();
556                netCap.mNetworkSpecifier = in.readString();
557                return netCap;
558            }
559            @Override
560            public NetworkCapabilities[] newArray(int size) {
561                return new NetworkCapabilities[size];
562            }
563        };
564
565    @Override
566    public String toString() {
567        int[] types = getTransportTypes();
568        String transports = (types.length > 0 ? " Transports: " : "");
569        for (int i = 0; i < types.length;) {
570            switch (types[i]) {
571                case TRANSPORT_CELLULAR:    transports += "CELLULAR"; break;
572                case TRANSPORT_WIFI:        transports += "WIFI"; break;
573                case TRANSPORT_BLUETOOTH:   transports += "BLUETOOTH"; break;
574                case TRANSPORT_ETHERNET:    transports += "ETHERNET"; break;
575                case TRANSPORT_VPN:         transports += "VPN"; break;
576            }
577            if (++i < types.length) transports += "|";
578        }
579
580        types = getCapabilities();
581        String capabilities = (types.length > 0 ? " Capabilities: " : "");
582        for (int i = 0; i < types.length; ) {
583            switch (types[i]) {
584                case NET_CAPABILITY_MMS:            capabilities += "MMS"; break;
585                case NET_CAPABILITY_SUPL:           capabilities += "SUPL"; break;
586                case NET_CAPABILITY_DUN:            capabilities += "DUN"; break;
587                case NET_CAPABILITY_FOTA:           capabilities += "FOTA"; break;
588                case NET_CAPABILITY_IMS:            capabilities += "IMS"; break;
589                case NET_CAPABILITY_CBS:            capabilities += "CBS"; break;
590                case NET_CAPABILITY_WIFI_P2P:       capabilities += "WIFI_P2P"; break;
591                case NET_CAPABILITY_IA:             capabilities += "IA"; break;
592                case NET_CAPABILITY_RCS:            capabilities += "RCS"; break;
593                case NET_CAPABILITY_XCAP:           capabilities += "XCAP"; break;
594                case NET_CAPABILITY_EIMS:           capabilities += "EIMS"; break;
595                case NET_CAPABILITY_NOT_METERED:    capabilities += "NOT_METERED"; break;
596                case NET_CAPABILITY_INTERNET:       capabilities += "INTERNET"; break;
597                case NET_CAPABILITY_NOT_RESTRICTED: capabilities += "NOT_RESTRICTED"; break;
598                case NET_CAPABILITY_TRUSTED:        capabilities += "TRUSTED"; break;
599                case NET_CAPABILITY_NOT_VPN:        capabilities += "NOT_VPN"; break;
600            }
601            if (++i < types.length) capabilities += "&";
602        }
603
604        String upBand = ((mLinkUpBandwidthKbps > 0) ? " LinkUpBandwidth>=" +
605                mLinkUpBandwidthKbps + "Kbps" : "");
606        String dnBand = ((mLinkDownBandwidthKbps > 0) ? " LinkDnBandwidth>=" +
607                mLinkDownBandwidthKbps + "Kbps" : "");
608
609        String specifier = (mNetworkSpecifier == null ?
610                "" : " Specifier: <" + mNetworkSpecifier + ">");
611
612        return "[" + transports + capabilities + upBand + dnBand + specifier + "]";
613    }
614}
615