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