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