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