1 /*
2  * Copyright (C) 2011 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.wifi.p2p;
18 
19 import android.annotation.FlaggedApi;
20 import android.annotation.IntDef;
21 import android.annotation.IntRange;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.SystemApi;
25 import android.compat.annotation.UnsupportedAppUsage;
26 import android.net.MacAddress;
27 import android.net.wifi.OuiKeyedData;
28 import android.net.wifi.ParcelUtil;
29 import android.net.wifi.WpsInfo;
30 import android.os.Build;
31 import android.os.Parcel;
32 import android.os.Parcelable;
33 import android.text.TextUtils;
34 
35 import androidx.annotation.RequiresApi;
36 
37 import com.android.modules.utils.build.SdkLevel;
38 import com.android.wifi.flags.Flags;
39 
40 import java.lang.annotation.Retention;
41 import java.lang.annotation.RetentionPolicy;
42 import java.nio.charset.StandardCharsets;
43 import java.util.ArrayList;
44 import java.util.Collections;
45 import java.util.List;
46 import java.util.regex.PatternSyntaxException;
47 
48 /**
49  * A class representing a Wi-Fi P2p configuration for setting up a connection
50  *
51  * {@see WifiP2pManager}
52  */
53 public class WifiP2pConfig implements Parcelable {
54 
55     /**
56      * The device MAC address uniquely identifies a Wi-Fi p2p device
57      */
58     public String deviceAddress = "";
59 
60     /**
61      * Wi-Fi Protected Setup information
62      */
63     public WpsInfo wps;
64 
65     /** Get the network name of this P2P configuration, or null if unset. */
66     @Nullable
getNetworkName()67     public String getNetworkName() {
68         return networkName;
69     }
70 
71     /** @hide */
72     public String networkName = "";
73 
74     /** Get the passphrase of this P2P configuration, or null if unset. */
75     @Nullable
getPassphrase()76     public String getPassphrase() {
77         return passphrase;
78     }
79 
80     /** @hide */
81     public String passphrase = "";
82 
83     /**
84      * Get the required band for the group owner.
85      * The result will be one of the following:
86      * {@link #GROUP_OWNER_BAND_AUTO},
87      * {@link #GROUP_OWNER_BAND_2GHZ},
88      * {@link #GROUP_OWNER_BAND_5GHZ}
89      */
90     @GroupOperatingBandType
getGroupOwnerBand()91     public int getGroupOwnerBand() {
92         return groupOwnerBand;
93     }
94 
95     /** @hide */
96     @GroupOperatingBandType
97     public int groupOwnerBand = GROUP_OWNER_BAND_AUTO;
98 
99     /** @hide */
100     @IntDef(flag = false, prefix = { "GROUP_OWNER_BAND_" }, value = {
101         GROUP_OWNER_BAND_AUTO,
102         GROUP_OWNER_BAND_2GHZ,
103         GROUP_OWNER_BAND_5GHZ
104     })
105     @Retention(RetentionPolicy.SOURCE)
106     public @interface GroupOperatingBandType {}
107 
108     /**
109      * IP provisioning via IPv4 DHCP, when joining a group as a group client.
110      */
111     public static final int GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP = 0;
112 
113     /**
114      * IP provisioning via IPv6 link-local, when joining a group as a group client.
115      */
116     public static final int GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL = 1;
117 
118     /**
119      * Allow the system to pick the operating frequency from all supported bands.
120      */
121     public static final int GROUP_OWNER_BAND_AUTO = 0;
122     /**
123      * Allow the system to pick the operating frequency from the 2.4 GHz band.
124      */
125     public static final int GROUP_OWNER_BAND_2GHZ = 1;
126     /**
127      * Allow the system to pick the operating frequency from the 5 GHz band.
128      */
129     public static final int GROUP_OWNER_BAND_5GHZ = 2;
130 
131     /**
132      * The least inclination to be a group owner, to be filled in the field
133      * {@link #groupOwnerIntent}.
134      */
135     public static final int GROUP_OWNER_INTENT_MIN = 0;
136 
137     /**
138      * The most inclination to be a group owner, to be filled in the field
139      * {@link #groupOwnerIntent}.
140      */
141     public static final int GROUP_OWNER_INTENT_MAX = 15;
142 
143     /**
144      * The system can choose an appropriate owner intent value, to be filled in the field
145      * {@link #groupOwnerIntent}.
146      */
147     public static final int GROUP_OWNER_INTENT_AUTO = -1;
148 
149     /**
150      * This is an integer value between {@link #GROUP_OWNER_INTENT_MIN} and
151      * {@link #GROUP_OWNER_INTENT_MAX} where
152      * {@link #GROUP_OWNER_INTENT_MIN} indicates the least inclination to be a group owner and
153      * {@link #GROUP_OWNER_INTENT_MAX} indicates the highest inclination to be a group owner.
154      *
155      * A value of {@link #GROUP_OWNER_INTENT_AUTO} indicates the system can choose an appropriate
156      * value.
157      *
158      * By default this field is set to {@link #GROUP_OWNER_INTENT_AUTO}.
159      */
160     @IntRange(from = 0, to = 15)
161     public int groupOwnerIntent = GROUP_OWNER_INTENT_AUTO;
162 
163     /** @hide */
164     @IntDef(prefix = { "GROUP_CLIENT_IP_PROVISIONING_MODE_" }, value = {
165             GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP,
166             GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL
167     })
168     @Retention(RetentionPolicy.SOURCE)
169     public @interface GroupClientIpProvisioningMode {}
170 
171     @GroupClientIpProvisioningMode
172     private int mGroupClientIpProvisioningMode = GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP;
173 
174     /**
175      * Query whether or not join existing group is enabled/disabled.
176      * @see #setJoinExistingGroup(boolean)
177      *
178      * @return true if configured to trigger the join existing group logic. False otherwise.
179      * @hide
180      */
181     @SystemApi
isJoinExistingGroup()182     public boolean isJoinExistingGroup() {
183         return mJoinExistingGroup;
184     }
185 
186     /**
187      * Join an existing group as a client.
188      */
189     private boolean mJoinExistingGroup = false;
190 
191     /** @hide */
192     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
193     public int netId = WifiP2pGroup.NETWORK_ID_PERSISTENT;
194 
195     /**
196      * Get the network ID of this P2P configuration.
197      * @return either a non-negative network ID, or one of
198      * {@link WifiP2pGroup#NETWORK_ID_PERSISTENT} or {@link WifiP2pGroup#NETWORK_ID_TEMPORARY}.
199      */
getNetworkId()200     public int getNetworkId() {
201         return netId;
202     }
203 
204     /** List of {@link OuiKeyedData} providing vendor-specific configuration data. */
205     private @NonNull List<OuiKeyedData> mVendorData = Collections.emptyList();
206 
207     /**
208      * Set additional vendor-provided configuration data.
209      *
210      * @param vendorData List of {@link android.net.wifi.OuiKeyedData} containing the
211      *                   vendor-provided configuration data. Note that multiple elements with
212      *                   the same OUI are allowed.
213      * @hide
214      */
215     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
216     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
217     @SystemApi
setVendorData(@onNull List<OuiKeyedData> vendorData)218     public void setVendorData(@NonNull List<OuiKeyedData> vendorData) {
219         if (!SdkLevel.isAtLeastV()) {
220             throw new UnsupportedOperationException();
221         }
222         if (vendorData == null) {
223             throw new IllegalArgumentException("setVendorData received a null value");
224         }
225         mVendorData = new ArrayList<>(vendorData);
226     }
227 
228     /**
229      * Return the vendor-provided configuration data, if it exists. See also {@link
230      * #setVendorData(List)}
231      *
232      * @return Vendor configuration data, or empty list if it does not exist.
233      * @hide
234      */
235     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
236     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
237     @SystemApi
238     @NonNull
getVendorData()239     public List<OuiKeyedData> getVendorData() {
240         if (!SdkLevel.isAtLeastV()) {
241             throw new UnsupportedOperationException();
242         }
243         return mVendorData;
244     }
245 
WifiP2pConfig()246     public WifiP2pConfig() {
247         //set defaults
248         wps = new WpsInfo();
249         wps.setup = WpsInfo.PBC;
250     }
251 
252     /** @hide */
invalidate()253     public void invalidate() {
254         deviceAddress = "";
255     }
256 
257     /** P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 {@hide}*/
258     @UnsupportedAppUsage
WifiP2pConfig(String supplicantEvent)259     public WifiP2pConfig(String supplicantEvent) throws IllegalArgumentException {
260         String[] tokens = supplicantEvent.split(" ");
261 
262         if (tokens.length < 2 || !tokens[0].equals("P2P-GO-NEG-REQUEST")) {
263             throw new IllegalArgumentException("Malformed supplicant event");
264         }
265 
266         deviceAddress = tokens[1];
267         wps = new WpsInfo();
268 
269         if (tokens.length > 2) {
270             String[] nameVal = tokens[2].split("=");
271             int devPasswdId;
272             try {
273                 devPasswdId = Integer.parseInt(nameVal[1]);
274             } catch (NumberFormatException e) {
275                 devPasswdId = 0;
276             }
277             //Based on definitions in wps/wps_defs.h
278             switch (devPasswdId) {
279                 //DEV_PW_USER_SPECIFIED = 0x0001,
280                 case 0x01:
281                     wps.setup = WpsInfo.DISPLAY;
282                     break;
283                 //DEV_PW_PUSHBUTTON = 0x0004,
284                 case 0x04:
285                     wps.setup = WpsInfo.PBC;
286                     break;
287                 //DEV_PW_REGISTRAR_SPECIFIED = 0x0005
288                 case 0x05:
289                     wps.setup = WpsInfo.KEYPAD;
290                     break;
291                 default:
292                     wps.setup = WpsInfo.PBC;
293                     break;
294             }
295         }
296     }
297 
298     /**
299      * Get the IP provisioning mode when joining a group as a group client.
300      * The result will be one of the following:
301      * {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP},
302      * {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL}
303      */
304     @GroupClientIpProvisioningMode
getGroupClientIpProvisioningMode()305     public int getGroupClientIpProvisioningMode() {
306         return mGroupClientIpProvisioningMode;
307     }
308 
toString()309     public String toString() {
310         StringBuffer sbuf = new StringBuffer();
311         sbuf.append("\n address: ").append(deviceAddress);
312         sbuf.append("\n wps: ").append(wps);
313         sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent);
314         sbuf.append("\n persist: ").append(netId);
315         sbuf.append("\n networkName: ").append(networkName);
316         sbuf.append("\n passphrase: ").append(
317                 TextUtils.isEmpty(passphrase) ? "<empty>" : "<non-empty>");
318         sbuf.append("\n groupOwnerBand: ").append(groupOwnerBand);
319         sbuf.append("\n groupClientIpProvisioningMode: ").append(mGroupClientIpProvisioningMode);
320         sbuf.append("\n joinExistingGroup: ").append(mJoinExistingGroup);
321         sbuf.append("\n vendorData: ").append(mVendorData);
322         return sbuf.toString();
323     }
324 
325     /** Implement the Parcelable interface */
describeContents()326     public int describeContents() {
327         return 0;
328     }
329 
330     /** copy constructor */
WifiP2pConfig(WifiP2pConfig source)331     public WifiP2pConfig(WifiP2pConfig source) {
332         if (source != null) {
333             deviceAddress = source.deviceAddress;
334             wps = new WpsInfo(source.wps);
335             groupOwnerIntent = source.groupOwnerIntent;
336             netId = source.netId;
337             networkName = source.networkName;
338             passphrase = source.passphrase;
339             groupOwnerBand = source.groupOwnerBand;
340             mGroupClientIpProvisioningMode = source.mGroupClientIpProvisioningMode;
341             mJoinExistingGroup = source.mJoinExistingGroup;
342             mVendorData = new ArrayList<>(source.mVendorData);
343         }
344     }
345 
346     /** Implement the Parcelable interface */
writeToParcel(Parcel dest, int flags)347     public void writeToParcel(Parcel dest, int flags) {
348         dest.writeString(deviceAddress);
349         dest.writeParcelable(wps, flags);
350         dest.writeInt(groupOwnerIntent);
351         dest.writeInt(netId);
352         dest.writeString(networkName);
353         dest.writeString(passphrase);
354         dest.writeInt(groupOwnerBand);
355         dest.writeInt(mGroupClientIpProvisioningMode);
356         dest.writeBoolean(mJoinExistingGroup);
357         dest.writeList(mVendorData);
358     }
359 
360     /** Implement the Parcelable interface */
361     @NonNull
362     public static final Creator<WifiP2pConfig> CREATOR =
363         new Creator<WifiP2pConfig>() {
364             public WifiP2pConfig createFromParcel(Parcel in) {
365                 WifiP2pConfig config = new WifiP2pConfig();
366                 config.deviceAddress = in.readString();
367                 config.wps = (WpsInfo) in.readParcelable(null);
368                 config.groupOwnerIntent = in.readInt();
369                 config.netId = in.readInt();
370                 config.networkName = in.readString();
371                 config.passphrase = in.readString();
372                 config.groupOwnerBand = in.readInt();
373                 config.mGroupClientIpProvisioningMode = in.readInt();
374                 config.mJoinExistingGroup = in.readBoolean();
375                 config.mVendorData = ParcelUtil.readOuiKeyedDataList(in);
376                 return config;
377             }
378 
379             public WifiP2pConfig[] newArray(int size) {
380                 return new WifiP2pConfig[size];
381             }
382         };
383 
384     /**
385      * Builder used to build {@link WifiP2pConfig} objects for
386      * creating or joining a group.
387      *
388      * The WifiP2pConfig can be constructed for two use-cases:
389      * <ul>
390      * <li>SSID + Passphrase are known: use {@link #setNetworkName(String)} and
391      *   {@link #setPassphrase(String)}.</li>
392      * <li>SSID or Passphrase is unknown, in such a case the MAC address must be known and
393      *   specified using {@link #setDeviceAddress(MacAddress)}.</li>
394      * </ul>
395      */
396     public static final class Builder {
397 
398         private static final MacAddress MAC_ANY_ADDRESS =
399                 MacAddress.fromString("02:00:00:00:00:00");
400         /**
401          * Maximum number of bytes allowed for a SSID.
402          */
403         private static final int MAX_SSID_BYTES = 32;
404 
405         private MacAddress mDeviceAddress = MAC_ANY_ADDRESS;
406         private String mNetworkName = "";
407         private String mPassphrase = "";
408         private int mGroupOperatingBand = GROUP_OWNER_BAND_AUTO;
409         private int mGroupOperatingFrequency = GROUP_OWNER_BAND_AUTO;
410         private int mNetId = WifiP2pGroup.NETWORK_ID_TEMPORARY;
411         private int mGroupClientIpProvisioningMode = GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP;
412         private boolean mJoinExistingGroup = false;
413 
414         /**
415          * Specify the peer's MAC address. If not set, the device will
416          * try to find a peer whose SSID matches the network name as
417          * specified by {@link #setNetworkName(String)}. Specifying null will
418          * reset the peer's MAC address to "02:00:00:00:00:00".
419          * <p>
420          *     Optional. "02:00:00:00:00:00" by default.
421          *
422          * <p> If the network name is not set, the peer's MAC address is mandatory.
423          *
424          * @param deviceAddress the peer's MAC address.
425          * @return The builder to facilitate chaining
426          *         {@code builder.setXXX(..).setXXX(..)}.
427          */
428         @NonNull
setDeviceAddress(@ullable MacAddress deviceAddress)429         public Builder setDeviceAddress(@Nullable MacAddress deviceAddress) {
430             if (deviceAddress == null) {
431                 mDeviceAddress = MAC_ANY_ADDRESS;
432             } else {
433                 mDeviceAddress = deviceAddress;
434             }
435             return this;
436         }
437 
438         /**
439          * Specify the network name, a.k.a. group name,
440          * for creating or joining a group.
441          * <p>
442          * A network name shall begin with "DIRECT-xy". x and y are selected
443          * from the following character set: upper case letters, lower case
444          * letters and numbers. Any byte values allowed for an SSID according to
445          * IEEE802.11-2012 [1] may be included after the string "DIRECT-xy"
446          * (including none).
447          * <p>
448          *     Must be called - an empty network name or an network name
449          *     not conforming to the P2P Group ID naming rule is not valid.
450          *
451          * @param networkName network name of a group.
452          * @return The builder to facilitate chaining
453          *         {@code builder.setXXX(..).setXXX(..)}.
454          */
455         @NonNull
setNetworkName(@onNull String networkName)456         public Builder setNetworkName(@NonNull String networkName) {
457             if (TextUtils.isEmpty(networkName)) {
458                 throw new IllegalArgumentException(
459                         "network name must be non-empty.");
460             }
461             if (networkName.getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) {
462                 throw new IllegalArgumentException(
463                         "network name exceeds " + MAX_SSID_BYTES + " bytes.");
464             }
465             try {
466                 if (!networkName.matches("^DIRECT-[a-zA-Z0-9]{2}.*")) {
467                     throw new IllegalArgumentException(
468                             "network name must starts with the prefix DIRECT-xy.");
469                 }
470             } catch (PatternSyntaxException e) {
471                 // can never happen (fixed pattern)
472             }
473             mNetworkName = networkName;
474             return this;
475         }
476 
477         /**
478          * Specify the passphrase for creating or joining a group.
479          * <p>
480          * The passphrase must be an ASCII string whose length is between 8
481          * and 63.
482          * <p>
483          *     Must be called - an empty passphrase is not valid.
484          *
485          * @param passphrase the passphrase of a group.
486          * @return The builder to facilitate chaining
487          *         {@code builder.setXXX(..).setXXX(..)}.
488          */
489         @NonNull
setPassphrase(@onNull String passphrase)490         public Builder setPassphrase(@NonNull String passphrase) {
491             if (TextUtils.isEmpty(passphrase)) {
492                 throw new IllegalArgumentException(
493                         "passphrase must be non-empty.");
494             }
495             if (passphrase.length() < 8 || passphrase.length() > 63) {
496                 throw new IllegalArgumentException(
497                         "The length of a passphrase must be between 8 and 63.");
498             }
499             mPassphrase = passphrase;
500             return this;
501         }
502 
503         /**
504          * Specify the band to use for creating the group or joining the group. The band should
505          * be {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ} or
506          * {@link #GROUP_OWNER_BAND_AUTO}.
507          * <p>
508          * When creating a group as Group Owner using {@link
509          * WifiP2pManager#createGroup(WifiP2pManager.Channel,
510          * WifiP2pConfig, WifiP2pManager.ActionListener)},
511          * specifying {@link #GROUP_OWNER_BAND_AUTO} allows the system to pick the operating
512          * frequency from all supported bands.
513          * Specifying {@link #GROUP_OWNER_BAND_2GHZ} or {@link #GROUP_OWNER_BAND_5GHZ}
514          * only allows the system to pick the operating frequency in the specified band.
515          * If the Group Owner cannot create a group in the specified band, the operation will fail.
516          * <p>
517          * When joining a group as Group Client using {@link
518          * WifiP2pManager#connect(WifiP2pManager.Channel, WifiP2pConfig,
519          * WifiP2pManager.ActionListener)},
520          * specifying {@link #GROUP_OWNER_BAND_AUTO} allows the system to scan all supported
521          * frequencies to find the desired group. Specifying {@link #GROUP_OWNER_BAND_2GHZ} or
522          * {@link #GROUP_OWNER_BAND_5GHZ} only allows the system to scan the specified band.
523          * <p>
524          *     {@link #setGroupOperatingBand(int)} and {@link #setGroupOperatingFrequency(int)} are
525          *     mutually exclusive. Setting operating band and frequency both is invalid.
526          * <p>
527          *     Optional. {@link #GROUP_OWNER_BAND_AUTO} by default.
528          *
529          * @param band the operating band of the group.
530          *             This should be one of {@link #GROUP_OWNER_BAND_AUTO},
531          *             {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ}.
532          * @return The builder to facilitate chaining
533          *         {@code builder.setXXX(..).setXXX(..)}.
534          */
535         @NonNull
setGroupOperatingBand(@roupOperatingBandType int band)536         public Builder setGroupOperatingBand(@GroupOperatingBandType int band) {
537             switch (band) {
538                 case GROUP_OWNER_BAND_AUTO:
539                 case GROUP_OWNER_BAND_2GHZ:
540                 case GROUP_OWNER_BAND_5GHZ:
541                     mGroupOperatingBand = band;
542                     break;
543                 default:
544                     throw new IllegalArgumentException(
545                         "Invalid constant for the group operating band!");
546             }
547             return this;
548         }
549 
550         /**
551          * Specify the frequency, in MHz, to use for creating the group or joining the group.
552          * <p>
553          * When creating a group as Group Owner using {@link WifiP2pManager#createGroup(
554          * WifiP2pManager.Channel, WifiP2pConfig, WifiP2pManager.ActionListener)},
555          * specifying a frequency only allows the system to pick the specified frequency.
556          * If the Group Owner cannot create a group at the specified frequency,
557          * the operation will fail.
558          * When not specifying a frequency, it allows the system to pick operating frequency
559          * from all supported bands.
560          * <p>
561          * When joining a group as Group Client using {@link WifiP2pManager#connect(
562          * WifiP2pManager.Channel, WifiP2pConfig, WifiP2pManager.ActionListener)},
563          * specifying a frequency only allows the system to scan the specified frequency.
564          * If the frequency is not supported or invalid, the operation will fail.
565          * When not specifying a frequency, it allows the system to scan all supported
566          * frequencies to find the desired group.
567          * <p>
568          *     {@link #setGroupOperatingBand(int)} and {@link #setGroupOperatingFrequency(int)} are
569          *     mutually exclusive. Setting operating band and frequency both is invalid.
570          * <p>
571          *     Optional. 0 by default.
572          *
573          * @param frequency the operating frequency of the group.
574          * @return The builder to facilitate chaining
575          *         {@code builder.setXXX(..).setXXX(..)}.
576          */
577         @NonNull
setGroupOperatingFrequency(int frequency)578         public Builder setGroupOperatingFrequency(int frequency) {
579             if (frequency < 0) {
580                 throw new IllegalArgumentException(
581                     "Invalid group operating frequency!");
582             }
583             mGroupOperatingFrequency = frequency;
584             return this;
585         }
586 
587         /**
588          * Specify that the group configuration be persisted (i.e. saved).
589          * By default the group configuration will not be saved.
590          * <p>
591          *     Optional. false by default.
592          *
593          * @param persistent is this group persistent group.
594          * @return The builder to facilitate chaining
595          *         {@code builder.setXXX(..).setXXX(..)}.
596          */
597         @NonNull
enablePersistentMode(boolean persistent)598         public Builder enablePersistentMode(boolean persistent) {
599             if (persistent) {
600                 mNetId = WifiP2pGroup.NETWORK_ID_PERSISTENT;
601             } else {
602                 mNetId = WifiP2pGroup.NETWORK_ID_TEMPORARY;
603             }
604             return this;
605         }
606 
607         /**
608          * Specify the IP provisioning mode when joining a group as a group client. The IP
609          * provisioning mode should be {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP} or
610          * {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL}.
611          * <p>
612          * When joining a group as group client using {@link
613          * WifiP2pManager#connect(WifiP2pManager.Channel, WifiP2pConfig,
614          * WifiP2pManager.ActionListener)},
615          * specifying {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP} directs the system to
616          * assign a IPv4 to the group client using DHCP. Specifying
617          * {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL} directs the system to assign
618          * a link-local IPv6 to the group client.
619          * <p>
620          *     Optional. {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP} by default.
621          * <p>
622          *
623          * If {@link WifiP2pManager#isGroupOwnerIPv6LinkLocalAddressProvided()} is {@code true} and
624          * {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL} is used then the system will
625          * discover the group owner's IPv6 link-local address and broadcast it using the
626          * {@link WifiP2pManager#EXTRA_WIFI_P2P_INFO} extra of the
627          * {@link WifiP2pManager#WIFI_P2P_CONNECTION_CHANGED_ACTION} broadcast. Otherwise, if
628          * {@link WifiP2pManager#isGroupOwnerIPv6LinkLocalAddressProvided()} is
629          * {@code false} then the group owner's IPv6 link-local address is not discovered and it is
630          * the responsibility of the caller to obtain it in some other way, e.g. via out-of-band
631          * communication.
632          *
633          * @param groupClientIpProvisioningMode the IP provisioning mode of the group client.
634          *             This should be one of {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP},
635          *             {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL}.
636          * @return The builder to facilitate chaining
637          *         {@code builder.setXXX(..).setXXX(..)}.
638          * @see WifiP2pManager#isGroupOwnerIPv6LinkLocalAddressProvided()
639          */
640         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
641         @NonNull
setGroupClientIpProvisioningMode( @roupClientIpProvisioningMode int groupClientIpProvisioningMode)642         public Builder setGroupClientIpProvisioningMode(
643                 @GroupClientIpProvisioningMode int groupClientIpProvisioningMode) {
644             // Since group client IP provisioning modes use NetworkStack functionalities introduced
645             // in T, hence we need at least T sdk for this to be supported.
646             if (!SdkLevel.isAtLeastT()) {
647                 throw new UnsupportedOperationException(
648                         "IPv6 link-local provisioning not supported");
649             }
650             switch (groupClientIpProvisioningMode) {
651                 case GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP:
652                 case GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL:
653                     mGroupClientIpProvisioningMode = groupClientIpProvisioningMode;
654                     break;
655                 default:
656                     throw new IllegalArgumentException(
657                             "Invalid constant for the group client IP provisioning mode!");
658             }
659             return this;
660         }
661 
662         /**
663          * Specify that the device wants to join an existing group as client.
664          * Usually group owner sets the group owner capability bit in beacons/probe responses. But
665          * there are deployed devices which don't set the group owner capability bit.
666          * This API is for applications which can get the peer group owner capability via OOB
667          * (out of band) mechanisms and forcefully trigger the join existing group logic.
668          * <p>
669          *     Optional. false by default.
670          *
671          * @param join true to forcefully trigger the join existing group logic, false to let
672          *             device decide whether to join a group or form a group.
673          * @return The builder to facilitate chaining
674          *         {@code builder.setXXX(..).setXXX(..)}.
675          * @hide
676          */
677         @SystemApi
678         @NonNull
setJoinExistingGroup(boolean join)679         public Builder setJoinExistingGroup(boolean join) {
680             mJoinExistingGroup = join;
681             return this;
682         }
683 
684         /**
685          * Build {@link WifiP2pConfig} given the current requests made on the builder.
686          * @return {@link WifiP2pConfig} constructed based on builder method calls.
687          */
688         @NonNull
build()689         public WifiP2pConfig build() {
690             if ((TextUtils.isEmpty(mNetworkName) && !TextUtils.isEmpty(mPassphrase))
691                     || (!TextUtils.isEmpty(mNetworkName) && TextUtils.isEmpty(mPassphrase))) {
692                 throw new IllegalStateException(
693                         "network name and passphrase must be non-empty or empty both.");
694             }
695             if (TextUtils.isEmpty(mNetworkName)
696                     && mDeviceAddress.equals(MAC_ANY_ADDRESS)) {
697                 throw new IllegalStateException(
698                         "peer address must be set if network name and pasphrase are not set.");
699             }
700 
701             if (mGroupOperatingFrequency > 0 && mGroupOperatingBand > 0) {
702                 throw new IllegalStateException(
703                         "Preferred frequency and band are mutually exclusive.");
704             }
705 
706             WifiP2pConfig config = new WifiP2pConfig();
707             config.deviceAddress = mDeviceAddress.toString();
708             config.networkName = mNetworkName;
709             config.passphrase = mPassphrase;
710             config.groupOwnerBand = GROUP_OWNER_BAND_AUTO;
711             if (mGroupOperatingFrequency > 0) {
712                 config.groupOwnerBand = mGroupOperatingFrequency;
713             } else if (mGroupOperatingBand > 0) {
714                 config.groupOwnerBand = mGroupOperatingBand;
715             }
716             config.netId = mNetId;
717             config.mGroupClientIpProvisioningMode = mGroupClientIpProvisioningMode;
718             config.mJoinExistingGroup = mJoinExistingGroup;
719             return config;
720         }
721     }
722 }
723