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