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 * Build the configuration using previously specified parameters. 274 */ build()275 public ProvisioningConfiguration build() { 276 if (mConfig.mIPv6ProvisioningMode == PROV_IPV6_LINKLOCAL 277 && mConfig.mIPv4ProvisioningMode != PROV_IPV4_DISABLED) { 278 throw new IllegalArgumentException("IPv4 must be disabled in IPv6 link-local" 279 + "only mode."); 280 } 281 return new ProvisioningConfiguration(mConfig); 282 } 283 } 284 285 /** 286 * Class wrapper of {@link android.net.wifi.ScanResult} to encapsulate the SSID and 287 * InformationElements fields of ScanResult. 288 */ 289 public static class ScanResultInfo { 290 @NonNull 291 private final String mSsid; 292 @NonNull 293 private final String mBssid; 294 @NonNull 295 private final List<InformationElement> mInformationElements; 296 297 /** 298 * Class wrapper of {@link android.net.wifi.ScanResult.InformationElement} to encapsulate 299 * the specific IE id and payload fields. 300 */ 301 public static class InformationElement { 302 private final int mId; 303 @NonNull 304 private final byte[] mPayload; 305 InformationElement(int id, @NonNull ByteBuffer payload)306 public InformationElement(int id, @NonNull ByteBuffer payload) { 307 mId = id; 308 mPayload = convertToByteArray(payload.asReadOnlyBuffer()); 309 } 310 311 /** 312 * Get the element ID of the information element. 313 */ getId()314 public int getId() { 315 return mId; 316 } 317 318 /** 319 * Get the specific content of the information element. 320 */ 321 @NonNull getPayload()322 public ByteBuffer getPayload() { 323 return ByteBuffer.wrap(mPayload).asReadOnlyBuffer(); 324 } 325 326 @Override equals(Object o)327 public boolean equals(Object o) { 328 if (o == this) return true; 329 if (!(o instanceof InformationElement)) return false; 330 InformationElement other = (InformationElement) o; 331 return mId == other.mId && Arrays.equals(mPayload, other.mPayload); 332 } 333 334 @Override hashCode()335 public int hashCode() { 336 return Objects.hash(mId, mPayload); 337 } 338 339 @Override toString()340 public String toString() { 341 return "ID: " + mId + ", " + Arrays.toString(mPayload); 342 } 343 344 /** 345 * Convert this InformationElement to a {@link InformationElementParcelable}. 346 */ toStableParcelable()347 public InformationElementParcelable toStableParcelable() { 348 final InformationElementParcelable p = new InformationElementParcelable(); 349 p.id = mId; 350 p.payload = mPayload != null ? mPayload.clone() : null; 351 return p; 352 } 353 354 /** 355 * Create an instance of {@link InformationElement} based on the contents of the 356 * specified {@link InformationElementParcelable}. 357 */ 358 @Nullable fromStableParcelable(InformationElementParcelable p)359 public static InformationElement fromStableParcelable(InformationElementParcelable p) { 360 if (p == null) return null; 361 return new InformationElement(p.id, 362 ByteBuffer.wrap(p.payload.clone()).asReadOnlyBuffer()); 363 } 364 } 365 ScanResultInfo(@onNull String ssid, @NonNull String bssid, @NonNull List<InformationElement> informationElements)366 public ScanResultInfo(@NonNull String ssid, @NonNull String bssid, 367 @NonNull List<InformationElement> informationElements) { 368 Objects.requireNonNull(ssid, "ssid must not be null."); 369 Objects.requireNonNull(bssid, "bssid must not be null."); 370 mSsid = ssid; 371 mBssid = bssid; 372 mInformationElements = 373 Collections.unmodifiableList(new ArrayList<>(informationElements)); 374 } 375 376 /** 377 * Get the scanned network name. 378 */ 379 @NonNull getSsid()380 public String getSsid() { 381 return mSsid; 382 } 383 384 /** 385 * Get the address of the access point. 386 */ 387 @NonNull getBssid()388 public String getBssid() { 389 return mBssid; 390 } 391 392 /** 393 * Get all information elements found in the beacon. 394 */ 395 @NonNull getInformationElements()396 public List<InformationElement> getInformationElements() { 397 return mInformationElements; 398 } 399 400 @Override toString()401 public String toString() { 402 StringBuffer str = new StringBuffer(); 403 str.append("SSID: ").append(mSsid); 404 str.append(", BSSID: ").append(mBssid); 405 str.append(", Information Elements: {"); 406 for (InformationElement ie : mInformationElements) { 407 str.append("[").append(ie.toString()).append("]"); 408 } 409 str.append("}"); 410 return str.toString(); 411 } 412 413 @Override equals(Object o)414 public boolean equals(Object o) { 415 if (o == this) return true; 416 if (!(o instanceof ScanResultInfo)) return false; 417 ScanResultInfo other = (ScanResultInfo) o; 418 return Objects.equals(mSsid, other.mSsid) 419 && Objects.equals(mBssid, other.mBssid) 420 && mInformationElements.equals(other.mInformationElements); 421 } 422 423 @Override hashCode()424 public int hashCode() { 425 return Objects.hash(mSsid, mBssid, mInformationElements); 426 } 427 428 /** 429 * Convert this ScanResultInfo to a {@link ScanResultInfoParcelable}. 430 */ toStableParcelable()431 public ScanResultInfoParcelable toStableParcelable() { 432 final ScanResultInfoParcelable p = new ScanResultInfoParcelable(); 433 p.ssid = mSsid; 434 p.bssid = mBssid; 435 p.informationElements = toParcelableArray(mInformationElements, 436 InformationElement::toStableParcelable, InformationElementParcelable.class); 437 return p; 438 } 439 440 /** 441 * Create an instance of {@link ScanResultInfo} based on the contents of the specified 442 * {@link ScanResultInfoParcelable}. 443 */ fromStableParcelable(ScanResultInfoParcelable p)444 public static ScanResultInfo fromStableParcelable(ScanResultInfoParcelable p) { 445 if (p == null) return null; 446 final List<InformationElement> ies = new ArrayList<InformationElement>(); 447 ies.addAll(fromParcelableArray(p.informationElements, 448 InformationElement::fromStableParcelable)); 449 return new ScanResultInfo(p.ssid, p.bssid, ies); 450 } 451 convertToByteArray(@onNull final ByteBuffer buffer)452 private static byte[] convertToByteArray(@NonNull final ByteBuffer buffer) { 453 final byte[] bytes = new byte[buffer.limit()]; 454 final ByteBuffer copy = buffer.asReadOnlyBuffer(); 455 try { 456 copy.position(0); 457 copy.get(bytes); 458 } catch (BufferUnderflowException e) { 459 Log.wtf(TAG, "Buffer under flow exception should never happen."); 460 } finally { 461 return bytes; 462 } 463 } 464 } 465 466 public boolean mEnablePreconnection = false; 467 public boolean mUsingMultinetworkPolicyTracker = true; 468 public boolean mUsingIpReachabilityMonitor = true; 469 public int mRequestedPreDhcpActionMs; 470 public InitialConfiguration mInitialConfig; 471 public StaticIpConfiguration mStaticIpConfig; 472 public ApfCapabilities mApfCapabilities; 473 public int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS; 474 public int mIPv6AddrGenMode = IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; 475 public Network mNetwork = null; 476 public String mDisplayName = null; 477 public ScanResultInfo mScanResultInfo; 478 public Layer2Information mLayer2Info; 479 public List<DhcpOption> mDhcpOptions; 480 public int mIPv4ProvisioningMode = PROV_IPV4_DHCP; 481 public int mIPv6ProvisioningMode = PROV_IPV6_SLAAC; 482 ProvisioningConfiguration()483 public ProvisioningConfiguration() {} // used by Builder 484 ProvisioningConfiguration(ProvisioningConfiguration other)485 public ProvisioningConfiguration(ProvisioningConfiguration other) { 486 mEnablePreconnection = other.mEnablePreconnection; 487 mUsingMultinetworkPolicyTracker = other.mUsingMultinetworkPolicyTracker; 488 mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor; 489 mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs; 490 mInitialConfig = InitialConfiguration.copy(other.mInitialConfig); 491 mStaticIpConfig = other.mStaticIpConfig == null 492 ? null 493 : new StaticIpConfiguration(other.mStaticIpConfig); 494 mApfCapabilities = other.mApfCapabilities; 495 mProvisioningTimeoutMs = other.mProvisioningTimeoutMs; 496 mIPv6AddrGenMode = other.mIPv6AddrGenMode; 497 mNetwork = other.mNetwork; 498 mDisplayName = other.mDisplayName; 499 mScanResultInfo = other.mScanResultInfo; 500 mLayer2Info = other.mLayer2Info; 501 mDhcpOptions = other.mDhcpOptions; 502 mIPv4ProvisioningMode = other.mIPv4ProvisioningMode; 503 mIPv6ProvisioningMode = other.mIPv6ProvisioningMode; 504 } 505 506 /** 507 * Create a ProvisioningConfigurationParcelable from this ProvisioningConfiguration. 508 */ toStableParcelable()509 public ProvisioningConfigurationParcelable toStableParcelable() { 510 final ProvisioningConfigurationParcelable p = new ProvisioningConfigurationParcelable(); 511 p.enableIPv4 = (mIPv4ProvisioningMode != PROV_IPV4_DISABLED); 512 p.ipv4ProvisioningMode = mIPv4ProvisioningMode; 513 p.enableIPv6 = (mIPv6ProvisioningMode != PROV_IPV6_DISABLED); 514 p.ipv6ProvisioningMode = mIPv6ProvisioningMode; 515 p.enablePreconnection = mEnablePreconnection; 516 p.usingMultinetworkPolicyTracker = mUsingMultinetworkPolicyTracker; 517 p.usingIpReachabilityMonitor = mUsingIpReachabilityMonitor; 518 p.requestedPreDhcpActionMs = mRequestedPreDhcpActionMs; 519 p.initialConfig = (mInitialConfig == null) ? null : mInitialConfig.toStableParcelable(); 520 p.staticIpConfig = (mStaticIpConfig == null) 521 ? null 522 : new StaticIpConfiguration(mStaticIpConfig); 523 p.apfCapabilities = mApfCapabilities; // ApfCapabilities is immutable 524 p.provisioningTimeoutMs = mProvisioningTimeoutMs; 525 p.ipv6AddrGenMode = mIPv6AddrGenMode; 526 p.network = mNetwork; 527 p.displayName = mDisplayName; 528 p.scanResultInfo = (mScanResultInfo == null) ? null : mScanResultInfo.toStableParcelable(); 529 p.layer2Info = (mLayer2Info == null) ? null : mLayer2Info.toStableParcelable(); 530 p.options = (mDhcpOptions == null) ? null : new ArrayList<>(mDhcpOptions); 531 return p; 532 } 533 534 /** 535 * Create a ProvisioningConfiguration from a ProvisioningConfigurationParcelable. 536 * 537 * @param p stable parcelable instance to be converted to a {@link ProvisioningConfiguration}. 538 * @param interfaceVersion IIpClientCallbacks interface version called by the remote peer, 539 * which is used to determine the appropriate parcelable members for 540 * backwards compatibility. 541 */ fromStableParcelable( @ullable ProvisioningConfigurationParcelable p, int interfaceVersion)542 public static ProvisioningConfiguration fromStableParcelable( 543 @Nullable ProvisioningConfigurationParcelable p, int interfaceVersion) { 544 if (p == null) return null; 545 final ProvisioningConfiguration config = new ProvisioningConfiguration(); 546 config.mEnablePreconnection = p.enablePreconnection; 547 config.mUsingMultinetworkPolicyTracker = p.usingMultinetworkPolicyTracker; 548 config.mUsingIpReachabilityMonitor = p.usingIpReachabilityMonitor; 549 config.mRequestedPreDhcpActionMs = p.requestedPreDhcpActionMs; 550 config.mInitialConfig = InitialConfiguration.fromStableParcelable(p.initialConfig); 551 config.mStaticIpConfig = (p.staticIpConfig == null) 552 ? null 553 : new StaticIpConfiguration(p.staticIpConfig); 554 config.mApfCapabilities = p.apfCapabilities; // ApfCapabilities is immutable 555 config.mProvisioningTimeoutMs = p.provisioningTimeoutMs; 556 config.mIPv6AddrGenMode = p.ipv6AddrGenMode; 557 config.mNetwork = p.network; 558 config.mDisplayName = p.displayName; 559 config.mScanResultInfo = ScanResultInfo.fromStableParcelable(p.scanResultInfo); 560 config.mLayer2Info = Layer2Information.fromStableParcelable(p.layer2Info); 561 config.mDhcpOptions = (p.options == null) ? null : new ArrayList<>(p.options); 562 if (interfaceVersion < VERSION_ADDED_PROVISIONING_ENUM) { 563 config.mIPv4ProvisioningMode = p.enableIPv4 ? PROV_IPV4_DHCP : PROV_IPV4_DISABLED; 564 config.mIPv6ProvisioningMode = p.enableIPv6 ? PROV_IPV6_SLAAC : PROV_IPV6_DISABLED; 565 } else { 566 config.mIPv4ProvisioningMode = p.ipv4ProvisioningMode; 567 config.mIPv6ProvisioningMode = p.ipv6ProvisioningMode; 568 } 569 return config; 570 } 571 572 @VisibleForTesting ipv4ProvisioningModeToString(int mode)573 static String ipv4ProvisioningModeToString(int mode) { 574 switch (mode) { 575 case PROV_IPV4_DISABLED: 576 return "disabled"; 577 case PROV_IPV4_STATIC: 578 return "static"; 579 case PROV_IPV4_DHCP: 580 return "dhcp"; 581 default: 582 return "unknown"; 583 } 584 } 585 586 @VisibleForTesting ipv6ProvisioningModeToString(int mode)587 static String ipv6ProvisioningModeToString(int mode) { 588 switch (mode) { 589 case PROV_IPV6_DISABLED: 590 return "disabled"; 591 case PROV_IPV6_SLAAC: 592 return "slaac"; 593 case PROV_IPV6_LINKLOCAL: 594 return "link-local"; 595 default: 596 return "unknown"; 597 } 598 } 599 600 @Override toString()601 public String toString() { 602 final String ipv4ProvisioningMode = ipv4ProvisioningModeToString(mIPv4ProvisioningMode); 603 final String ipv6ProvisioningMode = ipv6ProvisioningModeToString(mIPv6ProvisioningMode); 604 return new StringJoiner(", ", getClass().getSimpleName() + "{", "}") 605 .add("mEnablePreconnection: " + mEnablePreconnection) 606 .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker) 607 .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor) 608 .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs) 609 .add("mInitialConfig: " + mInitialConfig) 610 .add("mStaticIpConfig: " + mStaticIpConfig) 611 .add("mApfCapabilities: " + mApfCapabilities) 612 .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs) 613 .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode) 614 .add("mNetwork: " + mNetwork) 615 .add("mDisplayName: " + mDisplayName) 616 .add("mScanResultInfo: " + mScanResultInfo) 617 .add("mLayer2Info: " + mLayer2Info) 618 .add("mDhcpOptions: " + mDhcpOptions) 619 .add("mIPv4ProvisioningMode: " + ipv4ProvisioningMode) 620 .add("mIPv6ProvisioningMode: " + ipv6ProvisioningMode) 621 .toString(); 622 } 623 624 // TODO: mark DhcpOption stable parcelable with @JavaDerive(equals=true, toString=true) 625 // and @JavaOnlyImmutable. dhcpOptionEquals(@ullable DhcpOption obj1, @Nullable DhcpOption obj2)626 private static boolean dhcpOptionEquals(@Nullable DhcpOption obj1, @Nullable DhcpOption obj2) { 627 if (obj1 == obj2) return true; 628 if (obj1 == null || obj2 == null) return false; 629 return obj1.type == obj2.type && Arrays.equals(obj1.value, obj2.value); 630 } 631 632 // TODO: use Objects.equals(List<DhcpOption>, List<DhcpOption>) method instead once 633 // auto-generated equals() method of stable parcelable is supported in mainline-prod. dhcpOptionListEquals(@ullable List<DhcpOption> l1, @Nullable List<DhcpOption> l2)634 private static boolean dhcpOptionListEquals(@Nullable List<DhcpOption> l1, 635 @Nullable List<DhcpOption> l2) { 636 if (l1 == l2) return true; 637 if (l1 == null || l2 == null) return false; 638 if (l1.size() != l2.size()) return false; 639 640 for (int i = 0; i < l1.size(); i++) { 641 if (!dhcpOptionEquals(l1.get(i), l2.get(i))) return false; 642 } 643 return true; 644 } 645 646 @Override equals(Object obj)647 public boolean equals(Object obj) { 648 if (!(obj instanceof ProvisioningConfiguration)) return false; 649 final ProvisioningConfiguration other = (ProvisioningConfiguration) obj; 650 return mEnablePreconnection == other.mEnablePreconnection 651 && mUsingMultinetworkPolicyTracker == other.mUsingMultinetworkPolicyTracker 652 && mUsingIpReachabilityMonitor == other.mUsingIpReachabilityMonitor 653 && mRequestedPreDhcpActionMs == other.mRequestedPreDhcpActionMs 654 && Objects.equals(mInitialConfig, other.mInitialConfig) 655 && Objects.equals(mStaticIpConfig, other.mStaticIpConfig) 656 && Objects.equals(mApfCapabilities, other.mApfCapabilities) 657 && mProvisioningTimeoutMs == other.mProvisioningTimeoutMs 658 && mIPv6AddrGenMode == other.mIPv6AddrGenMode 659 && Objects.equals(mNetwork, other.mNetwork) 660 && Objects.equals(mDisplayName, other.mDisplayName) 661 && Objects.equals(mScanResultInfo, other.mScanResultInfo) 662 && Objects.equals(mLayer2Info, other.mLayer2Info) 663 && dhcpOptionListEquals(mDhcpOptions, other.mDhcpOptions) 664 && mIPv4ProvisioningMode == other.mIPv4ProvisioningMode 665 && mIPv6ProvisioningMode == other.mIPv6ProvisioningMode; 666 } 667 isValid()668 public boolean isValid() { 669 return (mInitialConfig == null) || mInitialConfig.isValid(); 670 } 671 } 672