• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.net.wifi.util.Environment;
31 import android.os.Build;
32 import android.os.Parcel;
33 import android.os.Parcelable;
34 import android.text.TextUtils;
35 
36 import androidx.annotation.RequiresApi;
37 
38 import com.android.modules.utils.build.SdkLevel;
39 import com.android.wifi.flags.Flags;
40 
41 import java.lang.annotation.Retention;
42 import java.lang.annotation.RetentionPolicy;
43 import java.nio.charset.StandardCharsets;
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.util.List;
47 import java.util.Objects;
48 import java.util.regex.PatternSyntaxException;
49 
50 /**
51  * A class representing a Wi-Fi P2p configuration for setting up a connection
52  *
53  * {@see WifiP2pManager}
54  */
55 public class WifiP2pConfig implements Parcelable {
56 
57     static final int PSK_PASSWORD_MIN_LEN = 8;
58     static final int PSK_PASSWORD_MAX_LEN = 63;
59     static final int SAE_PASSWORD_MAX_LEN = 128;
60 
61     /**
62      * The device MAC address uniquely identifies a Wi-Fi p2p device
63      */
64     public String deviceAddress = "";
65 
66     /**
67      * Wi-Fi Protected Setup information
68      */
69     public WpsInfo wps;
70 
71     /** Get the network name of this P2P configuration, or null if unset. */
72     @Nullable
getNetworkName()73     public String getNetworkName() {
74         return networkName;
75     }
76 
77     /** @hide */
78     public String networkName = "";
79 
80     /** Get the passphrase of this P2P configuration, or null if unset. */
81     @Nullable
getPassphrase()82     public String getPassphrase() {
83         return passphrase;
84     }
85 
86     /** @hide */
87     public String passphrase = "";
88 
89     /**
90      * Get the required band for the group owner.
91      * The result will be one of the following:
92      * {@link #GROUP_OWNER_BAND_AUTO},
93      * {@link #GROUP_OWNER_BAND_2GHZ},
94      * {@link #GROUP_OWNER_BAND_5GHZ},
95      * {@link #GROUP_OWNER_BAND_6GHZ}
96      */
97     @GroupOperatingBandType
getGroupOwnerBand()98     public int getGroupOwnerBand() {
99         return groupOwnerBand;
100     }
101 
102     /** @hide */
103     @GroupOperatingBandType
104     public int groupOwnerBand = GROUP_OWNER_BAND_AUTO;
105 
106     /** @hide */
107     @IntDef(flag = false, prefix = { "GROUP_OWNER_BAND_" }, value = {
108             GROUP_OWNER_BAND_AUTO,
109             GROUP_OWNER_BAND_2GHZ,
110             GROUP_OWNER_BAND_5GHZ,
111             GROUP_OWNER_BAND_6GHZ,
112     })
113     @Retention(RetentionPolicy.SOURCE)
114     public @interface GroupOperatingBandType {}
115 
116     /**
117      * IP provisioning via IPv4 DHCP, when joining a group as a group client.
118      */
119     public static final int GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP = 0;
120 
121     /**
122      * IP provisioning via IPv6 link-local, when joining a group as a group client.
123      */
124     public static final int GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL = 1;
125 
126     /**
127      * Allow the system to pick the operating frequency from all supported bands.
128      */
129     public static final int GROUP_OWNER_BAND_AUTO = 0;
130     /**
131      * Allow the system to pick the operating frequency from the 2.4 GHz band.
132      */
133     public static final int GROUP_OWNER_BAND_2GHZ = 1;
134     /**
135      * Allow the system to pick the operating frequency from the 5 GHz band.
136      */
137     public static final int GROUP_OWNER_BAND_5GHZ = 2;
138     /**
139      * Allow the system to pick the operating frequency from the 6 GHz band.
140      */
141     @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
142     public static final int GROUP_OWNER_BAND_6GHZ = 3;
143 
144     /**
145      * The least inclination to be a group owner, to be filled in the field
146      * {@link #groupOwnerIntent}.
147      */
148     public static final int GROUP_OWNER_INTENT_MIN = 0;
149 
150     /**
151      * The most inclination to be a group owner, to be filled in the field
152      * {@link #groupOwnerIntent}.
153      */
154     public static final int GROUP_OWNER_INTENT_MAX = 15;
155 
156     /**
157      * The system can choose an appropriate owner intent value, to be filled in the field
158      * {@link #groupOwnerIntent}.
159      */
160     public static final int GROUP_OWNER_INTENT_AUTO = -1;
161 
162     /**
163      * This is an integer value between {@link #GROUP_OWNER_INTENT_MIN} and
164      * {@link #GROUP_OWNER_INTENT_MAX} where
165      * {@link #GROUP_OWNER_INTENT_MIN} indicates the least inclination to be a group owner and
166      * {@link #GROUP_OWNER_INTENT_MAX} indicates the highest inclination to be a group owner.
167      *
168      * A value of {@link #GROUP_OWNER_INTENT_AUTO} indicates the system can choose an appropriate
169      * value.
170      *
171      * By default this field is set to {@link #GROUP_OWNER_INTENT_AUTO}.
172      */
173     @IntRange(from = 0, to = 15)
174     public int groupOwnerIntent = GROUP_OWNER_INTENT_AUTO;
175 
176     /** @hide */
177     @IntDef(prefix = { "GROUP_CLIENT_IP_PROVISIONING_MODE_" }, value = {
178             GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP,
179             GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL
180     })
181     @Retention(RetentionPolicy.SOURCE)
182     public @interface GroupClientIpProvisioningMode {}
183 
184     @GroupClientIpProvisioningMode
185     private int mGroupClientIpProvisioningMode = GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP;
186 
187     /**
188      * Query whether or not join existing group is enabled/disabled.
189      * @see #setJoinExistingGroup(boolean)
190      *
191      * @return true if configured to trigger the join existing group logic. False otherwise.
192      * @hide
193      */
194     @SystemApi
isJoinExistingGroup()195     public boolean isJoinExistingGroup() {
196         return mJoinExistingGroup;
197     }
198 
199     /**
200      * Join an existing group as a client.
201      */
202     private boolean mJoinExistingGroup = false;
203 
204     /** @hide */
205     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
206     public int netId = WifiP2pGroup.NETWORK_ID_PERSISTENT;
207 
208     /**
209      * Get the network ID of this P2P configuration.
210      * @return either a non-negative network ID, or one of
211      * {@link WifiP2pGroup#NETWORK_ID_PERSISTENT} or {@link WifiP2pGroup#NETWORK_ID_TEMPORARY}.
212      */
getNetworkId()213     public int getNetworkId() {
214         return netId;
215     }
216 
217     /** List of {@link OuiKeyedData} providing vendor-specific configuration data. */
218     private @NonNull List<OuiKeyedData> mVendorData = Collections.emptyList();
219 
220     /**
221      * Set additional vendor-provided configuration data.
222      *
223      * @param vendorData List of {@link android.net.wifi.OuiKeyedData} containing the
224      *                   vendor-provided configuration data. Note that multiple elements with
225      *                   the same OUI are allowed.
226      * @hide
227      */
228     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
229     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
230     @SystemApi
setVendorData(@onNull List<OuiKeyedData> vendorData)231     public void setVendorData(@NonNull List<OuiKeyedData> vendorData) {
232         if (!SdkLevel.isAtLeastV()) {
233             throw new UnsupportedOperationException();
234         }
235         if (vendorData == null) {
236             throw new IllegalArgumentException("setVendorData received a null value");
237         }
238         mVendorData = new ArrayList<>(vendorData);
239     }
240 
241     /**
242      * Return the vendor-provided configuration data, if it exists. See also {@link
243      * #setVendorData(List)}
244      *
245      * @return Vendor configuration data, or empty list if it does not exist.
246      * @hide
247      */
248     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
249     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
250     @SystemApi
251     @NonNull
getVendorData()252     public List<OuiKeyedData> getVendorData() {
253         if (!SdkLevel.isAtLeastV()) {
254             throw new UnsupportedOperationException();
255         }
256         return mVendorData;
257     }
258 
259     /**
260      * Default connection type used internally by the P2P service.
261      *
262      * @hide
263      */
264     public static final int PCC_MODE_DEFAULT_CONNECTION_TYPE_LEGACY_ONLY = 0;
265 
266     /**
267      * Legacy connection type.
268      * <p>Group Owner: Configured to support WPA2-Personal connections.
269      * <p>Group Client: Configured to connect to Group Owner using WPA2-Personal.
270      */
271     @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
272     public static final int PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY =
273             PCC_MODE_DEFAULT_CONNECTION_TYPE_LEGACY_ONLY;
274 
275     /**
276      * Wi-Fi Direct R1/R2 compatible mode connection type.
277      * <p>Group Owner: Configured in WPA3-Personal Compatibility Mode to support WPA3-Personal and
278      *              WPA2-Personal connections simultaneously.
279      * <p>Group Client: Configured to connect to Group Owner using WPA3-Personal or WPA2-Personal.
280      *               The system will choose WPA3-Personal if Group Owner support WPA3-Personal.
281      */
282     @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
283     public static final int PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2 = 1;
284 
285     /**
286      * This configuration allows only Wi-Fi Direct R2 supported devices to establish connection.
287      * <p>Group Owner: Configured to support WPA3-Personal connections.
288      * <p>Group Client: Configured to connect to Group Owner using WPA3-Personal.
289      */
290     @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
291     public static final int PCC_MODE_CONNECTION_TYPE_R2_ONLY = 2;
292 
293     /** @hide */
294     @Retention(RetentionPolicy.SOURCE)
295     @IntDef(prefix = { "PCC_MODE_CONNECTION_TYPE_" }, value = {
296             PCC_MODE_DEFAULT_CONNECTION_TYPE_LEGACY_ONLY,
297             PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY,
298             PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2,
299             PCC_MODE_CONNECTION_TYPE_R2_ONLY,
300     })
301     public @interface PccModeConnectionType {}
302 
303     @PccModeConnectionType
304     private int mPccModeConnectionType = PCC_MODE_DEFAULT_CONNECTION_TYPE_LEGACY_ONLY;
305 
306     /**
307      * Get the PCC Mode connection type.
308      *
309      * @return One of the {@code PCC_MODE_CONNECTION_TYPE_*}.
310      */
311     @RequiresApi(Build.VERSION_CODES.BAKLAVA)
312     @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
getPccModeConnectionType()313     public @PccModeConnectionType int getPccModeConnectionType() {
314         if (!Environment.isSdkAtLeastB()) {
315             throw new UnsupportedOperationException();
316         }
317         return mPccModeConnectionType;
318     }
319 
320     /**
321      * Default P2P version used internally by the P2P service.
322      *
323      * @hide
324      */
325     public static final int P2P_DEFAULT_VERSION_1 = 0;
326 
327     /**
328      * P2P Protocol version 1
329      */
330     @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
331     public static final int P2P_VERSION_1 = P2P_DEFAULT_VERSION_1;
332 
333     /**
334      * P2P Protocol version 2
335      */
336     @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
337     public static final int P2P_VERSION_2 = 1;
338 
339 
340     /** @hide */
341     @Retention(RetentionPolicy.SOURCE)
342     @IntDef(flag = false, prefix = { "P2P_VERSION_" }, value = {
343             P2P_DEFAULT_VERSION_1,
344             P2P_VERSION_1,
345             P2P_VERSION_2,
346     })
347     public @interface P2pVersion {}
348 
349     @P2pVersion
350     private int mGroupOwnerVersion = P2P_DEFAULT_VERSION_1;
351 
352     /**
353      * Get the P2P Group Owner version.
354      * See also {@link #setGroupOwnerVersion(int)}.
355      *
356      * @return The P2P Group Owner protocol version.
357      */
358     @RequiresApi(Build.VERSION_CODES.BAKLAVA)
359     @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
getGroupOwnerVersion()360     public @P2pVersion int getGroupOwnerVersion() {
361         if (!Environment.isSdkAtLeastB()) {
362             throw new UnsupportedOperationException();
363         }
364         return mGroupOwnerVersion;
365     }
366 
367     /**
368      * Set the P2P Group Owner version.
369      *
370      * @param version The P2P Group Owner protocol version.
371      */
372     @RequiresApi(Build.VERSION_CODES.BAKLAVA)
373     @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
setGroupOwnerVersion( @2pVersion int version)374     public void setGroupOwnerVersion(
375             @P2pVersion int version) {
376         if (!Environment.isSdkAtLeastB()) {
377             throw new UnsupportedOperationException();
378         }
379         mGroupOwnerVersion = version;
380     }
381 
382     private @Nullable WifiP2pPairingBootstrappingConfig mPairingBootstrappingConfig;
383 
384     /**
385      * Get the pairing bootstrapping configuration , or null if unset.
386      */
387     @RequiresApi(Build.VERSION_CODES.BAKLAVA)
388     @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
389     @Nullable
getPairingBootstrappingConfig()390     public WifiP2pPairingBootstrappingConfig getPairingBootstrappingConfig() {
391         if (!Environment.isSdkAtLeastB()) {
392             throw new UnsupportedOperationException();
393         }
394         return mPairingBootstrappingConfig;
395     }
396 
397     /**
398      * Used to authorize a connection request from the peer device.
399      */
400     private boolean mIsAuthorizeConnectionFromPeerEnabled = false;
401 
402     /**
403      * Query to check if the configuration is for authorizing a connection request
404      * from the peer device. @see {@link Builder#setAuthorizeConnectionFromPeerEnabled(boolean)}
405      *
406      * @return true if configured to authorize a connection request from the Peer device,
407      * False otherwise.
408      */
409     @RequiresApi(Build.VERSION_CODES.BAKLAVA)
410     @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
isAuthorizeConnectionFromPeerEnabled()411     public boolean isAuthorizeConnectionFromPeerEnabled() {
412         if (!Environment.isSdkAtLeastB()) {
413             throw new UnsupportedOperationException();
414         }
415         return mIsAuthorizeConnectionFromPeerEnabled;
416     }
417 
WifiP2pConfig()418     public WifiP2pConfig() {
419         //set defaults
420         wps = new WpsInfo();
421         wps.setup = WpsInfo.PBC;
422     }
423 
424     /** @hide */
invalidate()425     public void invalidate() {
426         deviceAddress = "";
427     }
428 
429     /** P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 {@hide}*/
430     @UnsupportedAppUsage
WifiP2pConfig(String supplicantEvent)431     public WifiP2pConfig(String supplicantEvent) throws IllegalArgumentException {
432         String[] tokens = supplicantEvent.split(" ");
433 
434         if (tokens.length < 2 || !tokens[0].equals("P2P-GO-NEG-REQUEST")) {
435             throw new IllegalArgumentException("Malformed supplicant event");
436         }
437 
438         deviceAddress = tokens[1];
439         wps = new WpsInfo();
440 
441         if (tokens.length > 2) {
442             String[] nameVal = tokens[2].split("=");
443             int devPasswdId;
444             try {
445                 devPasswdId = Integer.parseInt(nameVal[1]);
446             } catch (NumberFormatException e) {
447                 devPasswdId = 0;
448             }
449             //Based on definitions in wps/wps_defs.h
450             switch (devPasswdId) {
451                 //DEV_PW_USER_SPECIFIED = 0x0001,
452                 case 0x01:
453                     wps.setup = WpsInfo.DISPLAY;
454                     break;
455                 //DEV_PW_PUSHBUTTON = 0x0004,
456                 case 0x04:
457                     wps.setup = WpsInfo.PBC;
458                     break;
459                 //DEV_PW_REGISTRAR_SPECIFIED = 0x0005
460                 case 0x05:
461                     wps.setup = WpsInfo.KEYPAD;
462                     break;
463                 default:
464                     wps.setup = WpsInfo.PBC;
465                     break;
466             }
467         }
468     }
469 
470     /**
471      * Get the IP provisioning mode when joining a group as a group client.
472      * The result will be one of the following:
473      * {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP},
474      * {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL}
475      */
476     @GroupClientIpProvisioningMode
getGroupClientIpProvisioningMode()477     public int getGroupClientIpProvisioningMode() {
478         return mGroupClientIpProvisioningMode;
479     }
480 
toString()481     public String toString() {
482         StringBuffer sbuf = new StringBuffer();
483         sbuf.append("\n address: ").append(deviceAddress);
484         sbuf.append("\n wps: ").append(wps);
485         sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent);
486         sbuf.append("\n persist: ").append(netId);
487         sbuf.append("\n networkName: ").append(networkName);
488         sbuf.append("\n passphrase: ").append(
489                 TextUtils.isEmpty(passphrase) ? "<empty>" : "<non-empty>");
490         sbuf.append("\n pccModeConnectionType: ").append(mPccModeConnectionType);
491         sbuf.append("\n groupOwnerBand: ").append(groupOwnerBand);
492         sbuf.append("\n groupClientIpProvisioningMode: ").append(mGroupClientIpProvisioningMode);
493         sbuf.append("\n joinExistingGroup: ").append(mJoinExistingGroup);
494         sbuf.append("\n vendorData: ").append(mVendorData);
495         sbuf.append("\n Group Owner Version: ").append(mGroupOwnerVersion);
496         if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) {
497             sbuf.append("\n Pairing bootstrapping config : ")
498                     .append((mPairingBootstrappingConfig == null)
499                             ? "<null>" : mPairingBootstrappingConfig.toString());
500         }
501         sbuf.append("\n authorizeConnectionFromPeerEnabled: ")
502                 .append(mIsAuthorizeConnectionFromPeerEnabled);
503         return sbuf.toString();
504     }
505 
506     /** Implement the Parcelable interface */
describeContents()507     public int describeContents() {
508         return 0;
509     }
510 
511     /** copy constructor */
WifiP2pConfig(WifiP2pConfig source)512     public WifiP2pConfig(WifiP2pConfig source) {
513         if (source != null) {
514             deviceAddress = source.deviceAddress;
515             wps = new WpsInfo(source.wps);
516             groupOwnerIntent = source.groupOwnerIntent;
517             netId = source.netId;
518             networkName = source.networkName;
519             passphrase = source.passphrase;
520             mPccModeConnectionType = source.mPccModeConnectionType;
521             groupOwnerBand = source.groupOwnerBand;
522             mGroupClientIpProvisioningMode = source.mGroupClientIpProvisioningMode;
523             mJoinExistingGroup = source.mJoinExistingGroup;
524             mVendorData = new ArrayList<>(source.mVendorData);
525             mGroupOwnerVersion = source.mGroupOwnerVersion;
526             mPairingBootstrappingConfig = source.mPairingBootstrappingConfig;
527             mIsAuthorizeConnectionFromPeerEnabled = source.mIsAuthorizeConnectionFromPeerEnabled;
528         }
529     }
530 
531     /** Implement the Parcelable interface */
writeToParcel(Parcel dest, int flags)532     public void writeToParcel(Parcel dest, int flags) {
533         dest.writeString(deviceAddress);
534         dest.writeParcelable(wps, flags);
535         dest.writeInt(groupOwnerIntent);
536         dest.writeInt(netId);
537         dest.writeString(networkName);
538         dest.writeString(passphrase);
539         dest.writeInt(mPccModeConnectionType);
540         dest.writeInt(groupOwnerBand);
541         dest.writeInt(mGroupClientIpProvisioningMode);
542         dest.writeBoolean(mJoinExistingGroup);
543         dest.writeList(mVendorData);
544         dest.writeInt(mGroupOwnerVersion);
545         if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) {
546             dest.writeParcelable(mPairingBootstrappingConfig, flags);
547         }
548         dest.writeBoolean(mIsAuthorizeConnectionFromPeerEnabled);
549     }
550 
551     /** Implement the Parcelable interface */
552     @NonNull
553     public static final Creator<WifiP2pConfig> CREATOR =
554         new Creator<WifiP2pConfig>() {
555             public WifiP2pConfig createFromParcel(Parcel in) {
556                     WifiP2pConfig config = new WifiP2pConfig();
557                     config.deviceAddress = in.readString();
558                     config.wps = (WpsInfo) in.readParcelable(WpsInfo.class.getClassLoader());
559                     config.groupOwnerIntent = in.readInt();
560                     config.netId = in.readInt();
561                     config.networkName = in.readString();
562                     config.passphrase = in.readString();
563                     config.mPccModeConnectionType = in.readInt();
564                     config.groupOwnerBand = in.readInt();
565                     config.mGroupClientIpProvisioningMode = in.readInt();
566                     config.mJoinExistingGroup = in.readBoolean();
567                     config.mVendorData = ParcelUtil.readOuiKeyedDataList(in);
568                     config.mGroupOwnerVersion = in.readInt();
569                     if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) {
570                         config.mPairingBootstrappingConfig = in.readParcelable(
571                                 WifiP2pPairingBootstrappingConfig.class.getClassLoader());
572                     }
573                     config.mIsAuthorizeConnectionFromPeerEnabled = in.readBoolean();
574                     return config;
575             }
576 
577             public WifiP2pConfig[] newArray(int size) {
578                 return new WifiP2pConfig[size];
579             }
580         };
581 
582     /**
583      * Builder used to build {@link WifiP2pConfig} objects for
584      * creating or joining a group.
585      *
586      * The WifiP2pConfig can be constructed for two use-cases:
587      * <ul>
588      * <li>SSID + Passphrase are known: use {@link #setNetworkName(String)} and
589      *   {@link #setPassphrase(String)}.</li>
590      * <li>SSID or Passphrase is unknown, in such a case the MAC address must be known and
591      *   specified using {@link #setDeviceAddress(MacAddress)}.</li>
592      * </ul>
593      */
594     public static final class Builder {
595 
596         private static final MacAddress MAC_ANY_ADDRESS =
597                 MacAddress.fromString("02:00:00:00:00:00");
598         /**
599          * Maximum number of bytes allowed for a SSID.
600          */
601         private static final int MAX_SSID_BYTES = 32;
602 
603         private MacAddress mDeviceAddress = MAC_ANY_ADDRESS;
604         private String mNetworkName = "";
605         private String mPassphrase = "";
606         private int mGroupOperatingBand = GROUP_OWNER_BAND_AUTO;
607         private int mGroupOperatingFrequency = GROUP_OWNER_BAND_AUTO;
608         private int mNetId = WifiP2pGroup.NETWORK_ID_TEMPORARY;
609         private int mGroupClientIpProvisioningMode = GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP;
610         private boolean mJoinExistingGroup = false;
611         @PccModeConnectionType
612         private int mPccModeConnectionType = PCC_MODE_DEFAULT_CONNECTION_TYPE_LEGACY_ONLY;
613         private @Nullable WifiP2pPairingBootstrappingConfig mPairingBootstrappingConfig;
614         private boolean mIsAuthorizeConnectionFromPeerEnabled = false;
615 
616         /**
617          * Specify the peer's MAC address. If not set, the device will
618          * try to find a peer whose SSID matches the network name as
619          * specified by {@link #setNetworkName(String)}. Specifying null will
620          * reset the peer's MAC address to "02:00:00:00:00:00".
621          * <p>
622          *     Optional. "02:00:00:00:00:00" by default.
623          *
624          * <p> If the network name is not set, the peer's MAC address is mandatory.
625          *
626          * @param deviceAddress the peer's MAC address.
627          * @return The builder to facilitate chaining
628          *         {@code builder.setXXX(..).setXXX(..)}.
629          */
630         @NonNull
setDeviceAddress(@ullable MacAddress deviceAddress)631         public Builder setDeviceAddress(@Nullable MacAddress deviceAddress) {
632             if (deviceAddress == null) {
633                 mDeviceAddress = MAC_ANY_ADDRESS;
634             } else {
635                 mDeviceAddress = deviceAddress;
636             }
637             return this;
638         }
639 
640         /**
641          * Specify the network name, a.k.a. group name,
642          * for creating or joining a group.
643          * <p>
644          * A network name shall begin with "DIRECT-xy". x and y are selected
645          * from the following character set: upper case letters, lower case
646          * letters and numbers. Any byte values allowed for an SSID according to
647          * IEEE802.11-2012 [1] may be included after the string "DIRECT-xy"
648          * (including none).
649          * <p>
650          *     Must be called - an empty network name or an network name
651          *     not conforming to the P2P Group ID naming rule is not valid.
652          *
653          * @param networkName network name of a group.
654          * @return The builder to facilitate chaining
655          *         {@code builder.setXXX(..).setXXX(..)}.
656          */
657         @NonNull
setNetworkName(@onNull String networkName)658         public Builder setNetworkName(@NonNull String networkName) {
659             if (TextUtils.isEmpty(networkName)) {
660                 throw new IllegalArgumentException(
661                         "network name must be non-empty.");
662             }
663             if (networkName.getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) {
664                 throw new IllegalArgumentException(
665                         "network name exceeds " + MAX_SSID_BYTES + " bytes.");
666             }
667             try {
668                 if (!networkName.matches("^DIRECT-[a-zA-Z0-9]{2}.*")) {
669                     throw new IllegalArgumentException(
670                             "network name must starts with the prefix DIRECT-xy.");
671                 }
672             } catch (PatternSyntaxException e) {
673                 // can never happen (fixed pattern)
674             }
675             mNetworkName = networkName;
676             return this;
677         }
678 
679         /**
680          * Specify the passphrase for creating or joining a group.
681          * <p>
682          * The passphrase must be an ASCII string whose length is,
683          * 1. Between 8 and 63 for {@link #PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY} and
684          *    {@link #PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2}.
685          * 2. Less than 128 for {@link #PCC_MODE_CONNECTION_TYPE_R2_ONLY}.
686          *
687          * <p>
688          *     Must be called - an empty passphrase is not valid.
689          *
690          * @param passphrase the passphrase of a group.
691          * @return The builder to facilitate chaining
692          *         {@code builder.setXXX(..).setXXX(..)}.
693          */
694         @NonNull
setPassphrase(@onNull String passphrase)695         public Builder setPassphrase(@NonNull String passphrase) {
696             Objects.requireNonNull(passphrase, "passphrase cannot be null");
697             if (TextUtils.isEmpty(passphrase)) {
698                 throw new IllegalArgumentException(
699                         "passphrase must be non-empty.");
700             }
701             if (passphrase.length() > SAE_PASSWORD_MAX_LEN) {
702                 throw new IllegalArgumentException(
703                         "The length of a passphrase must be less than 128");
704             }
705             mPassphrase = passphrase;
706             return this;
707         }
708 
709         /**
710          * Specifies the PCC Mode connection type.
711          *
712          * @param connectionType One of the {@code PCC_MODE_CONNECTION_TYPE_*}.
713          * @return Builder for chaining.
714          *
715          * @throws IllegalArgumentException when the connectionType is invalid.
716          */
717         @RequiresApi(Build.VERSION_CODES.BAKLAVA)
718         @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
719         @NonNull
setPccModeConnectionType( @ccModeConnectionType int connectionType)720         public Builder setPccModeConnectionType(
721                 @PccModeConnectionType int connectionType) {
722             if (!Environment.isSdkAtLeastB()) {
723                 throw new UnsupportedOperationException();
724             }
725             switch (connectionType) {
726                 case PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY:
727                 case PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2:
728                 case PCC_MODE_CONNECTION_TYPE_R2_ONLY:
729                     mPccModeConnectionType = connectionType;
730                     break;
731                 default:
732                     throw new IllegalArgumentException(
733                             "Invalid constant for the PCC Mode connection type!");
734             }
735             return this;
736         }
737 
738         /**
739          * Specify the band to use for creating the group or joining the group. The band should
740          * be {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ} or
741          * {@link #GROUP_OWNER_BAND_6GHZ} or {@link #GROUP_OWNER_BAND_AUTO}.
742          * <p>
743          * When creating a group as Group Owner using {@link
744          * WifiP2pManager#createGroup(WifiP2pManager.Channel,
745          * WifiP2pConfig, WifiP2pManager.ActionListener)},
746          * specifying {@link #GROUP_OWNER_BAND_AUTO} allows the system to pick the operating
747          * frequency from all supported bands.
748          * Specifying {@link #GROUP_OWNER_BAND_2GHZ} or {@link #GROUP_OWNER_BAND_5GHZ} or
749          * {@link #GROUP_OWNER_BAND_6GHZ}
750          * only allows the system to pick the operating frequency in the specified band.
751          * If the Group Owner cannot create a group in the specified band, the operation will fail.
752          * <p>
753          * When joining a group as Group Client using {@link
754          * WifiP2pManager#connect(WifiP2pManager.Channel, WifiP2pConfig,
755          * WifiP2pManager.ActionListener)},
756          * specifying {@link #GROUP_OWNER_BAND_AUTO} allows the system to scan all supported
757          * frequencies to find the desired group. Specifying {@link #GROUP_OWNER_BAND_2GHZ} or
758          * {@link #GROUP_OWNER_BAND_5GHZ} or {@link #GROUP_OWNER_BAND_6GHZ} only allows the
759          * system to scan the specified band.
760          * <p>
761          *     {@link #setGroupOperatingBand(int)} and {@link #setGroupOperatingFrequency(int)} are
762          *     mutually exclusive. Setting operating band and frequency both is invalid.
763          * <p>
764          *     Optional. {@link #GROUP_OWNER_BAND_AUTO} by default.
765          *
766          * @param band the operating band of the group.
767          *             This should be one of {@link #GROUP_OWNER_BAND_AUTO},
768          *             {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ},
769          *             {@link #GROUP_OWNER_BAND_6GHZ}.
770          * @return The builder to facilitate chaining
771          *         {@code builder.setXXX(..).setXXX(..)}.
772          */
773         @NonNull
setGroupOperatingBand(@roupOperatingBandType int band)774         public Builder setGroupOperatingBand(@GroupOperatingBandType int band) {
775             if (GROUP_OWNER_BAND_AUTO == band
776                     || GROUP_OWNER_BAND_2GHZ == band
777                     || GROUP_OWNER_BAND_5GHZ == band
778                     || (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()
779                     && GROUP_OWNER_BAND_6GHZ == band)) {
780                 mGroupOperatingBand = band;
781             } else {
782                 throw new IllegalArgumentException(
783                         "Invalid constant for the group operating band!");
784             }
785             return this;
786         }
787 
788         /**
789          * Specify the frequency, in MHz, to use for creating the group or joining the group.
790          * <p>
791          * When creating a group as Group Owner using {@link WifiP2pManager#createGroup(
792          * WifiP2pManager.Channel, WifiP2pConfig, WifiP2pManager.ActionListener)},
793          * specifying a frequency only allows the system to pick the specified frequency.
794          * If the Group Owner cannot create a group at the specified frequency,
795          * the operation will fail.
796          * When not specifying a frequency, it allows the system to pick operating frequency
797          * from all supported bands.
798          * <p>
799          * When joining a group as Group Client using {@link WifiP2pManager#connect(
800          * WifiP2pManager.Channel, WifiP2pConfig, WifiP2pManager.ActionListener)},
801          * specifying a frequency only allows the system to scan the specified frequency.
802          * If the frequency is not supported or invalid, the operation will fail.
803          * When not specifying a frequency, it allows the system to scan all supported
804          * frequencies to find the desired group.
805          * <p>
806          *     {@link #setGroupOperatingBand(int)} and {@link #setGroupOperatingFrequency(int)} are
807          *     mutually exclusive. Setting operating band and frequency both is invalid.
808          * <p>
809          *     Optional. 0 by default.
810          *
811          * @param frequency the operating frequency of the group.
812          * @return The builder to facilitate chaining
813          *         {@code builder.setXXX(..).setXXX(..)}.
814          */
815         @NonNull
setGroupOperatingFrequency(int frequency)816         public Builder setGroupOperatingFrequency(int frequency) {
817             if (frequency < 0) {
818                 throw new IllegalArgumentException(
819                     "Invalid group operating frequency!");
820             }
821             mGroupOperatingFrequency = frequency;
822             return this;
823         }
824 
825         /**
826          * Specify that the group configuration be persisted (i.e. saved).
827          * By default the group configuration will not be saved.
828          * <p>
829          *     Optional. false by default.
830          *
831          * @param persistent is this group persistent group.
832          * @return The builder to facilitate chaining
833          *         {@code builder.setXXX(..).setXXX(..)}.
834          */
835         @NonNull
enablePersistentMode(boolean persistent)836         public Builder enablePersistentMode(boolean persistent) {
837             if (persistent) {
838                 mNetId = WifiP2pGroup.NETWORK_ID_PERSISTENT;
839             } else {
840                 mNetId = WifiP2pGroup.NETWORK_ID_TEMPORARY;
841             }
842             return this;
843         }
844 
845         /**
846          * Specify the IP provisioning mode when joining a group as a group client. The IP
847          * provisioning mode should be {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP} or
848          * {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL}.
849          * <p>
850          * When joining a group as group client using {@link
851          * WifiP2pManager#connect(WifiP2pManager.Channel, WifiP2pConfig,
852          * WifiP2pManager.ActionListener)},
853          * specifying {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP} directs the system to
854          * assign a IPv4 to the group client using DHCP. Specifying
855          * {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL} directs the system to assign
856          * a link-local IPv6 to the group client.
857          * <p>
858          *     Optional. {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP} by default.
859          * <p>
860          *
861          * If {@link WifiP2pManager#isGroupOwnerIPv6LinkLocalAddressProvided()} is {@code true} and
862          * {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL} is used then the system will
863          * discover the group owner's IPv6 link-local address and broadcast it using the
864          * {@link WifiP2pManager#EXTRA_WIFI_P2P_INFO} extra of the
865          * {@link WifiP2pManager#WIFI_P2P_CONNECTION_CHANGED_ACTION} broadcast. Otherwise, if
866          * {@link WifiP2pManager#isGroupOwnerIPv6LinkLocalAddressProvided()} is
867          * {@code false} then the group owner's IPv6 link-local address is not discovered and it is
868          * the responsibility of the caller to obtain it in some other way, e.g. via out-of-band
869          * communication.
870          *
871          * @param groupClientIpProvisioningMode the IP provisioning mode of the group client.
872          *             This should be one of {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP},
873          *             {@link #GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL}.
874          * @return The builder to facilitate chaining
875          *         {@code builder.setXXX(..).setXXX(..)}.
876          * @see WifiP2pManager#isGroupOwnerIPv6LinkLocalAddressProvided()
877          */
878         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
879         @NonNull
setGroupClientIpProvisioningMode( @roupClientIpProvisioningMode int groupClientIpProvisioningMode)880         public Builder setGroupClientIpProvisioningMode(
881                 @GroupClientIpProvisioningMode int groupClientIpProvisioningMode) {
882             // Since group client IP provisioning modes use NetworkStack functionalities introduced
883             // in T, hence we need at least T sdk for this to be supported.
884             if (!SdkLevel.isAtLeastT()) {
885                 throw new UnsupportedOperationException(
886                         "IPv6 link-local provisioning not supported");
887             }
888             switch (groupClientIpProvisioningMode) {
889                 case GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP:
890                 case GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL:
891                     mGroupClientIpProvisioningMode = groupClientIpProvisioningMode;
892                     break;
893                 default:
894                     throw new IllegalArgumentException(
895                             "Invalid constant for the group client IP provisioning mode!");
896             }
897             return this;
898         }
899 
900         /**
901          * Specify that the device wants to join an existing group as client.
902          * Usually group owner sets the group owner capability bit in beacons/probe responses. But
903          * there are deployed devices which don't set the group owner capability bit.
904          * This API is for applications which can get the peer group owner capability via OOB
905          * (out of band) mechanisms and forcefully trigger the join existing group logic.
906          * <p>
907          *     Optional. false by default.
908          *
909          * @param join true to forcefully trigger the join existing group logic, false to let
910          *             device decide whether to join a group or form a group.
911          * @return The builder to facilitate chaining
912          *         {@code builder.setXXX(..).setXXX(..)}.
913          * @hide
914          */
915         @SystemApi
916         @NonNull
setJoinExistingGroup(boolean join)917         public Builder setJoinExistingGroup(boolean join) {
918             mJoinExistingGroup = join;
919             return this;
920         }
921 
922         /**
923          * Set the pairing bootstrapping configuration for connecting using P2P pairing
924          * Protocol.
925          *
926          * @param config See {@link WifiP2pPairingBootstrappingConfig }
927          * @return The builder to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
928          */
929         @RequiresApi(Build.VERSION_CODES.BAKLAVA)
930         @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
931         @NonNull
setPairingBootstrappingConfig( @onNull WifiP2pPairingBootstrappingConfig config)932         public Builder setPairingBootstrappingConfig(
933                 @NonNull WifiP2pPairingBootstrappingConfig config) {
934             if (!Environment.isSdkAtLeastB()) {
935                 throw new UnsupportedOperationException();
936             }
937             Objects.requireNonNull(config, "config cannot be null");
938             mPairingBootstrappingConfig = config;
939             return this;
940         }
941 
942         /**
943          * Specify that the configuration is to authorize a connection request from a peer device.
944          * The MAC address of the peer device is specified using
945          * {@link WifiP2pConfig.Builder#setDeviceAddress(MacAddress)}.
946          * <p>
947          * Optional. false by default. The default configuration is to join a group or to initiate
948          * a group formation.
949          * <p>
950          * This configuration is typically used in Bluetooth LE assisted P2P pairing protocol
951          * defined in Wi-Fi Direct R2 specification, section 3.9. The collocated Bluetooth Provider
952          * sends the pairing password to the peer device (Seeker) and direct the system to
953          * authorize the connection request from the peer device using {@link
954          * WifiP2pManager#connect(WifiP2pManager.Channel, WifiP2pConfig,
955          * WifiP2pManager.ActionListener)}. The device will then wait for the connection request
956          * from the peer device.
957          *
958          * @param enabled true to authorize a connection request from the peer device, false to
959          *                  let the device join a group or form a group.
960          * @return The builder to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
961          */
962         @RequiresApi(Build.VERSION_CODES.BAKLAVA)
963         @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2)
964         @NonNull
setAuthorizeConnectionFromPeerEnabled(boolean enabled)965         public Builder setAuthorizeConnectionFromPeerEnabled(boolean enabled) {
966             mIsAuthorizeConnectionFromPeerEnabled = enabled;
967             return this;
968         }
969 
970         /**
971          * Build {@link WifiP2pConfig} given the current requests made on the builder.
972          * @return {@link WifiP2pConfig} constructed based on builder method calls.
973          */
974         @NonNull
build()975         public WifiP2pConfig build() {
976             if ((TextUtils.isEmpty(mNetworkName) && !TextUtils.isEmpty(mPassphrase))
977                     || (!TextUtils.isEmpty(mNetworkName) && TextUtils.isEmpty(mPassphrase))) {
978                 throw new IllegalStateException(
979                         "network name and passphrase must be non-empty or empty both.");
980             }
981             if (TextUtils.isEmpty(mNetworkName)
982                     && mDeviceAddress.equals(MAC_ANY_ADDRESS)) {
983                 throw new IllegalStateException(
984                         "peer address must be set if network name and passphrase are not set.");
985             }
986 
987             if (!TextUtils.isEmpty(mNetworkName)
988                     && !TextUtils.isEmpty(mPassphrase)) {
989                 if (mPccModeConnectionType == PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY
990                         || mPccModeConnectionType == PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2) {
991                     if (mPassphrase.length() < PSK_PASSWORD_MIN_LEN
992                             || mPassphrase.length() > PSK_PASSWORD_MAX_LEN) {
993                         throw new IllegalArgumentException(
994                                 "The length of a passphrase must be between "
995                                         + PSK_PASSWORD_MIN_LEN + " and "
996                                         + PSK_PASSWORD_MAX_LEN + " for legacy connection type");
997                     }
998                 }
999             }
1000 
1001             if (mGroupOperatingFrequency > 0 && mGroupOperatingBand > 0) {
1002                 throw new IllegalStateException(
1003                         "Preferred frequency and band are mutually exclusive.");
1004             }
1005 
1006             WifiP2pConfig config = new WifiP2pConfig();
1007             config.deviceAddress = mDeviceAddress.toString();
1008             config.networkName = mNetworkName;
1009             config.passphrase = mPassphrase;
1010             config.mPccModeConnectionType = mPccModeConnectionType;
1011             config.groupOwnerBand = GROUP_OWNER_BAND_AUTO;
1012             if (mGroupOperatingFrequency > 0) {
1013                 config.groupOwnerBand = mGroupOperatingFrequency;
1014             } else if (mGroupOperatingBand > 0) {
1015                 config.groupOwnerBand = mGroupOperatingBand;
1016             }
1017             config.netId = mNetId;
1018             config.mGroupClientIpProvisioningMode = mGroupClientIpProvisioningMode;
1019             config.mJoinExistingGroup = mJoinExistingGroup;
1020             if (mPairingBootstrappingConfig != null) {
1021                 config.mPairingBootstrappingConfig = mPairingBootstrappingConfig;
1022                 config.mGroupClientIpProvisioningMode =
1023                         GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL;
1024                 config.wps.setup = WpsInfo.INVALID;
1025             }
1026             config.mIsAuthorizeConnectionFromPeerEnabled = mIsAuthorizeConnectionFromPeerEnabled;
1027             return config;
1028         }
1029     }
1030 }
1031