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