1 /* 2 * Copyright (C) 2019 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 17 package android.net.shared; 18 19 import static android.net.ip.IIpClient.PROV_IPV4_DHCP; 20 import static android.net.ip.IIpClient.PROV_IPV4_DISABLED; 21 import static android.net.ip.IIpClient.PROV_IPV4_STATIC; 22 import static android.net.ip.IIpClient.PROV_IPV6_DISABLED; 23 import static android.net.ip.IIpClient.PROV_IPV6_LINKLOCAL; 24 import static android.net.ip.IIpClient.PROV_IPV6_SLAAC; 25 import static android.net.shared.ParcelableUtil.fromParcelableArray; 26 import static android.net.shared.ParcelableUtil.toParcelableArray; 27 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.net.InformationElementParcelable; 31 import android.net.Network; 32 import android.net.ProvisioningConfigurationParcelable; 33 import android.net.ScanResultInfoParcelable; 34 import android.net.StaticIpConfiguration; 35 import android.net.apf.ApfCapabilities; 36 import android.net.ip.IIpClient; 37 import android.net.networkstack.aidl.dhcp.DhcpOption; 38 import android.util.Log; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 42 import java.nio.BufferUnderflowException; 43 import java.nio.ByteBuffer; 44 import java.util.ArrayList; 45 import java.util.Arrays; 46 import java.util.Collections; 47 import java.util.List; 48 import java.util.Objects; 49 import java.util.StringJoiner; 50 51 /** 52 * This class encapsulates parameters to be passed to 53 * IpClient#startProvisioning(). A defensive copy is made by IpClient 54 * and the values specified herein are in force until IpClient#stop() 55 * is called. 56 * 57 * Example use: 58 * 59 * final ProvisioningConfiguration config = 60 * new ProvisioningConfiguration.Builder() 61 * .withPreDhcpAction() 62 * .withProvisioningTimeoutMs(36 * 1000) 63 * .build(); 64 * mIpClient.startProvisioning(config.toStableParcelable()); 65 * ... 66 * mIpClient.stop(); 67 * 68 * The specified provisioning configuration will only be active until 69 * IIpClient#stop() is called. Future calls to IIpClient#startProvisioning() 70 * must specify the configuration again. 71 * @hide 72 */ 73 public class ProvisioningConfiguration { 74 private static final String TAG = "ProvisioningConfiguration"; 75 76 // TODO: Delete this default timeout once those callers that care are 77 // fixed to pass in their preferred timeout. 78 // 79 // We pick 18 seconds so we can send DHCP requests at 80 // 81 // t=0, t=1, t=3, t=7, t=16 82 // 83 // allowing for 10% jitter. 84 private static final int DEFAULT_TIMEOUT_MS = 18 * 1000; 85 86 // TODO: These cannot be imported from INetd.aidl, because networkstack-client cannot depend on 87 // INetd, as there are users of IpClient that depend on INetd directly (potentially at a 88 // different version, which is not allowed by the build system). 89 // Find a better way to express these constants. 90 public static final int IPV6_ADDR_GEN_MODE_EUI64 = 0; 91 public static final int IPV6_ADDR_GEN_MODE_STABLE_PRIVACY = 2; 92 93 // ipv4ProvisioningMode and ipv6ProvisioningMode members are introduced since 94 // networkstack-aidl-interfaces-v12. 95 public static final int VERSION_ADDED_PROVISIONING_ENUM = 12; 96 97 /** 98 * Builder to create a {@link ProvisioningConfiguration}. 99 */ 100 public static class Builder { 101 protected ProvisioningConfiguration mConfig = new ProvisioningConfiguration(); 102 103 /** 104 * Specify that the configuration should not enable IPv4. It is enabled by default. 105 */ withoutIPv4()106 public Builder withoutIPv4() { 107 mConfig.mIPv4ProvisioningMode = PROV_IPV4_DISABLED; 108 return this; 109 } 110 111 /** 112 * Specify that the configuration should not enable IPv6. It is enabled by default. 113 */ withoutIPv6()114 public Builder withoutIPv6() { 115 mConfig.mIPv6ProvisioningMode = PROV_IPV6_DISABLED; 116 return this; 117 } 118 119 /** 120 * Specify that the configuration should not use a MultinetworkPolicyTracker. It is used 121 * by default. 122 */ withoutMultinetworkPolicyTracker()123 public Builder withoutMultinetworkPolicyTracker() { 124 mConfig.mUsingMultinetworkPolicyTracker = false; 125 return this; 126 } 127 128 /** 129 * Specify that the configuration should not use a IpReachabilityMonitor. It is used by 130 * default. 131 */ withoutIpReachabilityMonitor()132 public Builder withoutIpReachabilityMonitor() { 133 mConfig.mUsingIpReachabilityMonitor = false; 134 return this; 135 } 136 137 /** 138 * Identical to {@link #withPreDhcpAction(int)}, using a default timeout. 139 * @see #withPreDhcpAction(int) 140 */ withPreDhcpAction()141 public Builder withPreDhcpAction() { 142 mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS; 143 return this; 144 } 145 146 /** 147 * Specify that {@link IpClientCallbacks#onPreDhcpAction()} should be called. Clients must 148 * call {@link IIpClient#completedPreDhcpAction()} when the callback called. This behavior 149 * is disabled by default. 150 * @param dhcpActionTimeoutMs Timeout for clients to call completedPreDhcpAction(). 151 */ withPreDhcpAction(int dhcpActionTimeoutMs)152 public Builder withPreDhcpAction(int dhcpActionTimeoutMs) { 153 mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs; 154 return this; 155 } 156 157 /** 158 * Specify that preconnection feature would be enabled. It's not used by default. 159 */ withPreconnection()160 public Builder withPreconnection() { 161 mConfig.mEnablePreconnection = true; 162 return this; 163 } 164 165 /** 166 * Specify the initial provisioning configuration. 167 */ withInitialConfiguration(InitialConfiguration initialConfig)168 public Builder withInitialConfiguration(InitialConfiguration initialConfig) { 169 mConfig.mInitialConfig = initialConfig; 170 return this; 171 } 172 173 /** 174 * Specify a static configuration for provisioning. 175 */ withStaticConfiguration(StaticIpConfiguration staticConfig)176 public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) { 177 mConfig.mIPv4ProvisioningMode = PROV_IPV4_STATIC; 178 mConfig.mStaticIpConfig = staticConfig; 179 return this; 180 } 181 182 /** 183 * Specify ApfCapabilities. 184 */ withApfCapabilities(ApfCapabilities apfCapabilities)185 public Builder withApfCapabilities(ApfCapabilities apfCapabilities) { 186 mConfig.mApfCapabilities = apfCapabilities; 187 return this; 188 } 189 190 /** 191 * Specify the timeout to use for provisioning. 192 */ withProvisioningTimeoutMs(int timeoutMs)193 public Builder withProvisioningTimeoutMs(int timeoutMs) { 194 mConfig.mProvisioningTimeoutMs = timeoutMs; 195 return this; 196 } 197 198 /** 199 * Specify that IPv6 address generation should use a random MAC address. 200 */ withRandomMacAddress()201 public Builder withRandomMacAddress() { 202 mConfig.mIPv6AddrGenMode = IPV6_ADDR_GEN_MODE_EUI64; 203 return this; 204 } 205 206 /** 207 * Specify that IPv6 address generation should use a stable MAC address. 208 */ withStableMacAddress()209 public Builder withStableMacAddress() { 210 mConfig.mIPv6AddrGenMode = IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; 211 return this; 212 } 213 214 /** 215 * Specify the network to use for provisioning. 216 */ withNetwork(Network network)217 public Builder withNetwork(Network network) { 218 mConfig.mNetwork = network; 219 return this; 220 } 221 222 /** 223 * Specify the display name that the IpClient should use. 224 */ withDisplayName(String displayName)225 public Builder withDisplayName(String displayName) { 226 mConfig.mDisplayName = displayName; 227 return this; 228 } 229 230 /** 231 * Specify the information elements included in wifi scan result that was obtained 232 * prior to connecting to the access point, if this is a WiFi network. 233 * 234 * <p>The scan result can be used to infer whether the network is metered. 235 */ withScanResultInfo(ScanResultInfo scanResultInfo)236 public Builder withScanResultInfo(ScanResultInfo scanResultInfo) { 237 mConfig.mScanResultInfo = scanResultInfo; 238 return this; 239 } 240 241 /** 242 * Specify the L2 information(bssid, l2key and cluster) that the IpClient should use. 243 */ withLayer2Information(Layer2Information layer2Info)244 public Builder withLayer2Information(Layer2Information layer2Info) { 245 mConfig.mLayer2Info = layer2Info; 246 return this; 247 } 248 249 /** 250 * Specify the customized DHCP options to be put in the PRL or in the DHCP packet. Options 251 * with null value will be put in the PRL. 252 * 253 * @param: options customized DHCP option stable parcelable list. 254 */ withDhcpOptions(@ullable List<DhcpOption> options)255 public Builder withDhcpOptions(@Nullable List<DhcpOption> options) { 256 mConfig.mDhcpOptions = options; 257 return this; 258 } 259 260 /** 261 * Specify that the configuration should enable IPv6 link-local only mode used for 262 * WiFi Neighbor Aware Networking and other link-local-only technologies. It's not 263 * used by default, and IPv4 must be disabled when this mode is enabled. 264 * 265 * @note This API is only supported since Android T. 266 */ withIpv6LinkLocalOnly()267 public Builder withIpv6LinkLocalOnly() { 268 mConfig.mIPv6ProvisioningMode = PROV_IPV6_LINKLOCAL; 269 return this; 270 } 271 272 /** 273 * Specify that the configuration is for a network that only uses unique EUI-64 274 * addresses (e.g., a link-local-only network where addresses are generated via 275 * EUI-64 and where MAC addresses are guaranteed to be unique). 276 * This will disable duplicate address detection if withLinkLocalOnly() and 277 * withRandomMacAddress are also called. 278 */ withUniqueEui64AddressesOnly()279 public Builder withUniqueEui64AddressesOnly() { 280 mConfig.mUniqueEui64AddressesOnly = true; 281 return this; 282 } 283 284 /** 285 * Build the configuration using previously specified parameters. 286 */ build()287 public ProvisioningConfiguration build() { 288 if (mConfig.mIPv6ProvisioningMode == PROV_IPV6_LINKLOCAL 289 && mConfig.mIPv4ProvisioningMode != PROV_IPV4_DISABLED) { 290 throw new IllegalArgumentException("IPv4 must be disabled in IPv6 link-local" 291 + "only mode."); 292 } 293 return new ProvisioningConfiguration(mConfig); 294 } 295 } 296 297 /** 298 * Class wrapper of {@link android.net.wifi.ScanResult} to encapsulate the SSID and 299 * InformationElements fields of ScanResult. 300 */ 301 public static class ScanResultInfo { 302 @NonNull 303 private final String mSsid; 304 @NonNull 305 private final String mBssid; 306 @NonNull 307 private final List<InformationElement> mInformationElements; 308 309 /** 310 * Class wrapper of {@link android.net.wifi.ScanResult.InformationElement} to encapsulate 311 * the specific IE id and payload fields. 312 */ 313 public static class InformationElement { 314 private final int mId; 315 @NonNull 316 private final byte[] mPayload; 317 InformationElement(int id, @NonNull ByteBuffer payload)318 public InformationElement(int id, @NonNull ByteBuffer payload) { 319 mId = id; 320 mPayload = convertToByteArray(payload.asReadOnlyBuffer()); 321 } 322 323 /** 324 * Get the element ID of the information element. 325 */ getId()326 public int getId() { 327 return mId; 328 } 329 330 /** 331 * Get the specific content of the information element. 332 */ 333 @NonNull getPayload()334 public ByteBuffer getPayload() { 335 return ByteBuffer.wrap(mPayload).asReadOnlyBuffer(); 336 } 337 338 @Override equals(Object o)339 public boolean equals(Object o) { 340 if (o == this) return true; 341 if (!(o instanceof InformationElement)) return false; 342 InformationElement other = (InformationElement) o; 343 return mId == other.mId && Arrays.equals(mPayload, other.mPayload); 344 } 345 346 @Override hashCode()347 public int hashCode() { 348 return Objects.hash(mId, Arrays.hashCode(mPayload)); 349 } 350 351 @Override toString()352 public String toString() { 353 return "ID: " + mId + ", " + Arrays.toString(mPayload); 354 } 355 356 /** 357 * Convert this InformationElement to a {@link InformationElementParcelable}. 358 */ toStableParcelable()359 public InformationElementParcelable toStableParcelable() { 360 final InformationElementParcelable p = new InformationElementParcelable(); 361 p.id = mId; 362 p.payload = mPayload != null ? mPayload.clone() : null; 363 return p; 364 } 365 366 /** 367 * Create an instance of {@link InformationElement} based on the contents of the 368 * specified {@link InformationElementParcelable}. 369 */ 370 @Nullable fromStableParcelable(InformationElementParcelable p)371 public static InformationElement fromStableParcelable(InformationElementParcelable p) { 372 if (p == null) return null; 373 return new InformationElement(p.id, 374 ByteBuffer.wrap(p.payload.clone()).asReadOnlyBuffer()); 375 } 376 } 377 ScanResultInfo(@onNull String ssid, @NonNull String bssid, @NonNull List<InformationElement> informationElements)378 public ScanResultInfo(@NonNull String ssid, @NonNull String bssid, 379 @NonNull List<InformationElement> informationElements) { 380 Objects.requireNonNull(ssid, "ssid must not be null."); 381 Objects.requireNonNull(bssid, "bssid must not be null."); 382 mSsid = ssid; 383 mBssid = bssid; 384 mInformationElements = 385 Collections.unmodifiableList(new ArrayList<>(informationElements)); 386 } 387 388 /** 389 * Get the scanned network name. 390 */ 391 @NonNull getSsid()392 public String getSsid() { 393 return mSsid; 394 } 395 396 /** 397 * Get the address of the access point. 398 */ 399 @NonNull getBssid()400 public String getBssid() { 401 return mBssid; 402 } 403 404 /** 405 * Get all information elements found in the beacon. 406 */ 407 @NonNull getInformationElements()408 public List<InformationElement> getInformationElements() { 409 return mInformationElements; 410 } 411 412 @Override toString()413 public String toString() { 414 StringBuffer str = new StringBuffer(); 415 str.append("SSID: ").append(mSsid); 416 str.append(", BSSID: ").append(mBssid); 417 str.append(", Information Elements: {"); 418 for (InformationElement ie : mInformationElements) { 419 str.append("[").append(ie.toString()).append("]"); 420 } 421 str.append("}"); 422 return str.toString(); 423 } 424 425 @Override equals(Object o)426 public boolean equals(Object o) { 427 if (o == this) return true; 428 if (!(o instanceof ScanResultInfo)) return false; 429 ScanResultInfo other = (ScanResultInfo) o; 430 return Objects.equals(mSsid, other.mSsid) 431 && Objects.equals(mBssid, other.mBssid) 432 && mInformationElements.equals(other.mInformationElements); 433 } 434 435 @Override hashCode()436 public int hashCode() { 437 return Objects.hash(mSsid, mBssid, mInformationElements); 438 } 439 440 /** 441 * Convert this ScanResultInfo to a {@link ScanResultInfoParcelable}. 442 */ toStableParcelable()443 public ScanResultInfoParcelable toStableParcelable() { 444 final ScanResultInfoParcelable p = new ScanResultInfoParcelable(); 445 p.ssid = mSsid; 446 p.bssid = mBssid; 447 p.informationElements = toParcelableArray(mInformationElements, 448 InformationElement::toStableParcelable, InformationElementParcelable.class); 449 return p; 450 } 451 452 /** 453 * Create an instance of {@link ScanResultInfo} based on the contents of the specified 454 * {@link ScanResultInfoParcelable}. 455 */ fromStableParcelable(ScanResultInfoParcelable p)456 public static ScanResultInfo fromStableParcelable(ScanResultInfoParcelable p) { 457 if (p == null) return null; 458 final List<InformationElement> ies = new ArrayList<InformationElement>(); 459 ies.addAll(fromParcelableArray(p.informationElements, 460 InformationElement::fromStableParcelable)); 461 return new ScanResultInfo(p.ssid, p.bssid, ies); 462 } 463 convertToByteArray(@onNull final ByteBuffer buffer)464 private static byte[] convertToByteArray(@NonNull final ByteBuffer buffer) { 465 final byte[] bytes = new byte[buffer.limit()]; 466 final ByteBuffer copy = buffer.asReadOnlyBuffer(); 467 try { 468 copy.position(0); 469 copy.get(bytes); 470 } catch (BufferUnderflowException e) { 471 Log.wtf(TAG, "Buffer under flow exception should never happen."); 472 } finally { 473 return bytes; 474 } 475 } 476 } 477 478 public boolean mUniqueEui64AddressesOnly = false; 479 public boolean mEnablePreconnection = false; 480 public boolean mUsingMultinetworkPolicyTracker = true; 481 public boolean mUsingIpReachabilityMonitor = true; 482 public int mRequestedPreDhcpActionMs; 483 public InitialConfiguration mInitialConfig; 484 public StaticIpConfiguration mStaticIpConfig; 485 public ApfCapabilities mApfCapabilities; 486 public int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS; 487 public int mIPv6AddrGenMode = IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; 488 public Network mNetwork = null; 489 public String mDisplayName = null; 490 public ScanResultInfo mScanResultInfo; 491 public Layer2Information mLayer2Info; 492 public List<DhcpOption> mDhcpOptions; 493 public int mIPv4ProvisioningMode = PROV_IPV4_DHCP; 494 public int mIPv6ProvisioningMode = PROV_IPV6_SLAAC; 495 ProvisioningConfiguration()496 public ProvisioningConfiguration() {} // used by Builder 497 ProvisioningConfiguration(ProvisioningConfiguration other)498 public ProvisioningConfiguration(ProvisioningConfiguration other) { 499 mUniqueEui64AddressesOnly = other.mUniqueEui64AddressesOnly; 500 mEnablePreconnection = other.mEnablePreconnection; 501 mUsingMultinetworkPolicyTracker = other.mUsingMultinetworkPolicyTracker; 502 mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor; 503 mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs; 504 mInitialConfig = InitialConfiguration.copy(other.mInitialConfig); 505 mStaticIpConfig = other.mStaticIpConfig == null 506 ? null 507 : new StaticIpConfiguration(other.mStaticIpConfig); 508 mApfCapabilities = other.mApfCapabilities; 509 mProvisioningTimeoutMs = other.mProvisioningTimeoutMs; 510 mIPv6AddrGenMode = other.mIPv6AddrGenMode; 511 mNetwork = other.mNetwork; 512 mDisplayName = other.mDisplayName; 513 mScanResultInfo = other.mScanResultInfo; 514 mLayer2Info = other.mLayer2Info; 515 mDhcpOptions = other.mDhcpOptions; 516 mIPv4ProvisioningMode = other.mIPv4ProvisioningMode; 517 mIPv6ProvisioningMode = other.mIPv6ProvisioningMode; 518 } 519 520 /** 521 * Create a ProvisioningConfigurationParcelable from this ProvisioningConfiguration. 522 */ toStableParcelable()523 public ProvisioningConfigurationParcelable toStableParcelable() { 524 final ProvisioningConfigurationParcelable p = new ProvisioningConfigurationParcelable(); 525 p.enableIPv4 = (mIPv4ProvisioningMode != PROV_IPV4_DISABLED); 526 p.ipv4ProvisioningMode = mIPv4ProvisioningMode; 527 p.enableIPv6 = (mIPv6ProvisioningMode != PROV_IPV6_DISABLED); 528 p.ipv6ProvisioningMode = mIPv6ProvisioningMode; 529 p.uniqueEui64AddressesOnly = mUniqueEui64AddressesOnly; 530 p.enablePreconnection = mEnablePreconnection; 531 p.usingMultinetworkPolicyTracker = mUsingMultinetworkPolicyTracker; 532 p.usingIpReachabilityMonitor = mUsingIpReachabilityMonitor; 533 p.requestedPreDhcpActionMs = mRequestedPreDhcpActionMs; 534 p.initialConfig = (mInitialConfig == null) ? null : mInitialConfig.toStableParcelable(); 535 p.staticIpConfig = (mStaticIpConfig == null) 536 ? null 537 : new StaticIpConfiguration(mStaticIpConfig); 538 p.apfCapabilities = mApfCapabilities; // ApfCapabilities is immutable 539 p.provisioningTimeoutMs = mProvisioningTimeoutMs; 540 p.ipv6AddrGenMode = mIPv6AddrGenMode; 541 p.network = mNetwork; 542 p.displayName = mDisplayName; 543 p.scanResultInfo = (mScanResultInfo == null) ? null : mScanResultInfo.toStableParcelable(); 544 p.layer2Info = (mLayer2Info == null) ? null : mLayer2Info.toStableParcelable(); 545 p.options = (mDhcpOptions == null) ? null : new ArrayList<>(mDhcpOptions); 546 return p; 547 } 548 549 /** 550 * Create a ProvisioningConfiguration from a ProvisioningConfigurationParcelable. 551 * 552 * @param p stable parcelable instance to be converted to a {@link ProvisioningConfiguration}. 553 * @param interfaceVersion IIpClientCallbacks interface version called by the remote peer, 554 * which is used to determine the appropriate parcelable members for 555 * backwards compatibility. 556 */ fromStableParcelable( @ullable ProvisioningConfigurationParcelable p, int interfaceVersion)557 public static ProvisioningConfiguration fromStableParcelable( 558 @Nullable ProvisioningConfigurationParcelable p, int interfaceVersion) { 559 if (p == null) return null; 560 final ProvisioningConfiguration config = new ProvisioningConfiguration(); 561 config.mUniqueEui64AddressesOnly = p.uniqueEui64AddressesOnly; 562 config.mEnablePreconnection = p.enablePreconnection; 563 config.mUsingMultinetworkPolicyTracker = p.usingMultinetworkPolicyTracker; 564 config.mUsingIpReachabilityMonitor = p.usingIpReachabilityMonitor; 565 config.mRequestedPreDhcpActionMs = p.requestedPreDhcpActionMs; 566 config.mInitialConfig = InitialConfiguration.fromStableParcelable(p.initialConfig); 567 config.mStaticIpConfig = (p.staticIpConfig == null) 568 ? null 569 : new StaticIpConfiguration(p.staticIpConfig); 570 config.mApfCapabilities = p.apfCapabilities; // ApfCapabilities is immutable 571 config.mProvisioningTimeoutMs = p.provisioningTimeoutMs; 572 config.mIPv6AddrGenMode = p.ipv6AddrGenMode; 573 config.mNetwork = p.network; 574 config.mDisplayName = p.displayName; 575 config.mScanResultInfo = ScanResultInfo.fromStableParcelable(p.scanResultInfo); 576 config.mLayer2Info = Layer2Information.fromStableParcelable(p.layer2Info); 577 config.mDhcpOptions = (p.options == null) ? null : new ArrayList<>(p.options); 578 if (interfaceVersion < VERSION_ADDED_PROVISIONING_ENUM) { 579 config.mIPv4ProvisioningMode = p.enableIPv4 ? PROV_IPV4_DHCP : PROV_IPV4_DISABLED; 580 config.mIPv6ProvisioningMode = p.enableIPv6 ? PROV_IPV6_SLAAC : PROV_IPV6_DISABLED; 581 } else { 582 config.mIPv4ProvisioningMode = p.ipv4ProvisioningMode; 583 config.mIPv6ProvisioningMode = p.ipv6ProvisioningMode; 584 } 585 return config; 586 } 587 588 @VisibleForTesting ipv4ProvisioningModeToString(int mode)589 static String ipv4ProvisioningModeToString(int mode) { 590 switch (mode) { 591 case PROV_IPV4_DISABLED: 592 return "disabled"; 593 case PROV_IPV4_STATIC: 594 return "static"; 595 case PROV_IPV4_DHCP: 596 return "dhcp"; 597 default: 598 return "unknown"; 599 } 600 } 601 602 @VisibleForTesting ipv6ProvisioningModeToString(int mode)603 static String ipv6ProvisioningModeToString(int mode) { 604 switch (mode) { 605 case PROV_IPV6_DISABLED: 606 return "disabled"; 607 case PROV_IPV6_SLAAC: 608 return "slaac"; 609 case PROV_IPV6_LINKLOCAL: 610 return "link-local"; 611 default: 612 return "unknown"; 613 } 614 } 615 616 @Override toString()617 public String toString() { 618 final String ipv4ProvisioningMode = ipv4ProvisioningModeToString(mIPv4ProvisioningMode); 619 final String ipv6ProvisioningMode = ipv6ProvisioningModeToString(mIPv6ProvisioningMode); 620 return new StringJoiner(", ", getClass().getSimpleName() + "{", "}") 621 .add("mUniqueEui64AddressesOnly: " + mUniqueEui64AddressesOnly) 622 .add("mEnablePreconnection: " + mEnablePreconnection) 623 .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker) 624 .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor) 625 .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs) 626 .add("mInitialConfig: " + mInitialConfig) 627 .add("mStaticIpConfig: " + mStaticIpConfig) 628 .add("mApfCapabilities: " + mApfCapabilities) 629 .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs) 630 .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode) 631 .add("mNetwork: " + mNetwork) 632 .add("mDisplayName: " + mDisplayName) 633 .add("mScanResultInfo: " + mScanResultInfo) 634 .add("mLayer2Info: " + mLayer2Info) 635 .add("mDhcpOptions: " + mDhcpOptions) 636 .add("mIPv4ProvisioningMode: " + ipv4ProvisioningMode) 637 .add("mIPv6ProvisioningMode: " + ipv6ProvisioningMode) 638 .toString(); 639 } 640 641 // TODO: mark DhcpOption stable parcelable with @JavaDerive(equals=true, toString=true) 642 // and @JavaOnlyImmutable. dhcpOptionEquals(@ullable DhcpOption obj1, @Nullable DhcpOption obj2)643 private static boolean dhcpOptionEquals(@Nullable DhcpOption obj1, @Nullable DhcpOption obj2) { 644 if (obj1 == obj2) return true; 645 if (obj1 == null || obj2 == null) return false; 646 return obj1.type == obj2.type && Arrays.equals(obj1.value, obj2.value); 647 } 648 649 // TODO: use Objects.equals(List<DhcpOption>, List<DhcpOption>) method instead once 650 // auto-generated equals() method of stable parcelable is supported in mainline-prod. dhcpOptionListEquals(@ullable List<DhcpOption> l1, @Nullable List<DhcpOption> l2)651 private static boolean dhcpOptionListEquals(@Nullable List<DhcpOption> l1, 652 @Nullable List<DhcpOption> l2) { 653 if (l1 == l2) return true; 654 if (l1 == null || l2 == null) return false; 655 if (l1.size() != l2.size()) return false; 656 657 for (int i = 0; i < l1.size(); i++) { 658 if (!dhcpOptionEquals(l1.get(i), l2.get(i))) return false; 659 } 660 return true; 661 } 662 663 @Override equals(Object obj)664 public boolean equals(Object obj) { 665 if (!(obj instanceof ProvisioningConfiguration)) return false; 666 final ProvisioningConfiguration other = (ProvisioningConfiguration) obj; 667 return mUniqueEui64AddressesOnly == other.mUniqueEui64AddressesOnly 668 && mEnablePreconnection == other.mEnablePreconnection 669 && mUsingMultinetworkPolicyTracker == other.mUsingMultinetworkPolicyTracker 670 && mUsingIpReachabilityMonitor == other.mUsingIpReachabilityMonitor 671 && mRequestedPreDhcpActionMs == other.mRequestedPreDhcpActionMs 672 && Objects.equals(mInitialConfig, other.mInitialConfig) 673 && Objects.equals(mStaticIpConfig, other.mStaticIpConfig) 674 && Objects.equals(mApfCapabilities, other.mApfCapabilities) 675 && mProvisioningTimeoutMs == other.mProvisioningTimeoutMs 676 && mIPv6AddrGenMode == other.mIPv6AddrGenMode 677 && Objects.equals(mNetwork, other.mNetwork) 678 && Objects.equals(mDisplayName, other.mDisplayName) 679 && Objects.equals(mScanResultInfo, other.mScanResultInfo) 680 && Objects.equals(mLayer2Info, other.mLayer2Info) 681 && dhcpOptionListEquals(mDhcpOptions, other.mDhcpOptions) 682 && mIPv4ProvisioningMode == other.mIPv4ProvisioningMode 683 && mIPv6ProvisioningMode == other.mIPv6ProvisioningMode; 684 } 685 isValid()686 public boolean isValid() { 687 return (mInitialConfig == null) || mInitialConfig.isValid(); 688 } 689 } 690