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.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SystemApi; 24 import android.compat.annotation.UnsupportedAppUsage; 25 import android.net.MacAddress; 26 import android.net.wifi.OuiKeyedData; 27 import android.net.wifi.ParcelUtil; 28 import android.net.wifi.util.Environment; 29 import android.os.Build; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 import android.util.Log; 33 34 import androidx.annotation.RequiresApi; 35 36 import com.android.modules.utils.build.SdkLevel; 37 import com.android.wifi.flags.Flags; 38 39 import java.lang.annotation.Retention; 40 import java.lang.annotation.RetentionPolicy; 41 import java.net.Inet4Address; 42 import java.net.InetAddress; 43 import java.util.ArrayList; 44 import java.util.Collection; 45 import java.util.Collections; 46 import java.util.List; 47 import java.util.regex.Matcher; 48 import java.util.regex.Pattern; 49 50 /** 51 * A class representing a Wi-Fi P2p group. A p2p group consists of a single group 52 * owner and one or more clients. In the case of a group with only two devices, one 53 * will be the group owner and the other will be a group client. 54 * 55 * {@see WifiP2pManager} 56 */ 57 public class WifiP2pGroup implements Parcelable { 58 59 /** 60 * The temporary network id. 61 * @see #getNetworkId() 62 */ 63 public static final int NETWORK_ID_TEMPORARY = -1; 64 65 /** 66 * The temporary network id. 67 * 68 * @hide 69 */ 70 @UnsupportedAppUsage 71 public static final int TEMPORARY_NET_ID = NETWORK_ID_TEMPORARY; 72 73 /** 74 * The persistent network id. 75 * If a matching persistent profile is found, use it. 76 * Otherwise, create a new persistent profile. 77 * @see #getNetworkId() 78 */ 79 public static final int NETWORK_ID_PERSISTENT = -2; 80 81 /** 82 * The definition of security type unknown. It is set when framework fails to derive the 83 * security type from the authentication key management provided by wpa_supplicant. 84 */ 85 @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) 86 public static final int SECURITY_TYPE_UNKNOWN = -1; 87 88 /** 89 * The definition of security type WPA2-PSK. 90 */ 91 @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) 92 public static final int SECURITY_TYPE_WPA2_PSK = 0; 93 94 /** 95 * The definition of security type WPA3-Compatibility Mode. 96 */ 97 @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) 98 public static final int SECURITY_TYPE_WPA3_COMPATIBILITY = 1; 99 100 /** 101 * The definition of security type WPA3-SAE. 102 */ 103 @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) 104 public static final int SECURITY_TYPE_WPA3_SAE = 2; 105 106 /** @hide */ 107 @Retention(RetentionPolicy.SOURCE) 108 @IntDef(prefix = { "SECURITY_TYPE_" }, value = { 109 SECURITY_TYPE_UNKNOWN, 110 SECURITY_TYPE_WPA2_PSK, 111 SECURITY_TYPE_WPA3_COMPATIBILITY, 112 SECURITY_TYPE_WPA3_SAE, 113 }) 114 public @interface SecurityType {} 115 116 /** 117 * Group owner P2P interface MAC address. 118 * @hide 119 */ 120 @UnsupportedAppUsage 121 public byte[] interfaceAddress; 122 123 /** The network name */ 124 private String mNetworkName; 125 126 /** Group owner */ 127 private WifiP2pDevice mOwner; 128 129 /** Device is group owner */ 130 private boolean mIsGroupOwner; 131 132 /** Group clients */ 133 private List<WifiP2pDevice> mClients = new ArrayList<WifiP2pDevice>(); 134 135 /** The passphrase used for WPA2-PSK */ 136 private String mPassphrase; 137 138 /** The security type of the group */ 139 @SecurityType 140 private int mSecurityType; 141 142 private String mInterface; 143 144 /** The network ID in wpa_supplicant */ 145 private int mNetId; 146 147 /** The frequency (in MHz) used by this group */ 148 private int mFrequency; 149 150 /** List of {@link OuiKeyedData} providing vendor-specific configuration data. */ 151 private @NonNull List<OuiKeyedData> mVendorData = Collections.emptyList(); 152 153 /** 154 * P2P Client IPV4 address allocated via EAPOL-Key exchange. 155 * @hide 156 */ 157 public static class P2pGroupClientEapolIpAddressData { 158 /* 159 * The P2P Client IP address. 160 */ 161 public final Inet4Address mIpAddressClient; 162 /* 163 * The P2P Group Owner IP address. 164 */ 165 public final Inet4Address mIpAddressGo; 166 /* 167 * The subnet that the P2P Group Owner is using. 168 */ 169 public final Inet4Address mIpAddressMask; 170 171 /* 172 * Set P2pClientEapolIpAddressData 173 */ P2pGroupClientEapolIpAddressData(Inet4Address ipAddressClient, Inet4Address ipAddressGo, Inet4Address ipAddressMask)174 public P2pGroupClientEapolIpAddressData(Inet4Address ipAddressClient, 175 Inet4Address ipAddressGo, Inet4Address ipAddressMask) { 176 this.mIpAddressClient = ipAddressClient; 177 this.mIpAddressGo = ipAddressGo; 178 this.mIpAddressMask = ipAddressMask; 179 } 180 } 181 182 /** 183 * P2P Client IP address information obtained via EAPOL Handshake. 184 * @hide 185 */ 186 public P2pGroupClientEapolIpAddressData p2pClientEapolIpInfo; 187 188 /** P2P group started string pattern */ 189 private static final Pattern groupStartedPattern = Pattern.compile( 190 "ssid=\"(.+)\" " + 191 "freq=(\\d+) " + 192 "(?:psk=)?([0-9a-fA-F]{64})?" + 193 "(?:passphrase=)?(?:\"(.{0,63})\")? " + 194 "go_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2})" + 195 " ?(\\[PERSISTENT\\])?" 196 ); 197 WifiP2pGroup()198 public WifiP2pGroup() { 199 } 200 201 /** 202 * @param supplicantEvent formats supported include 203 * 204 * P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437 205 * [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc| 206 * passphrase="fKG4jMe3"] go_dev_addr=fa:7b:7a:42:02:13 [PERSISTENT] 207 * 208 * P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED 209 * 210 * P2P-INVITATION-RECEIVED sa=fa:7b:7a:42:02:13 go_dev_addr=f8:7b:7a:42:02:13 211 * bssid=fa:7b:7a:42:82:13 unknown-network 212 * 213 * P2P-INVITATION-RECEIVED sa=b8:f9:34:2a:c7:9d persistent=0 214 * 215 * Note: The events formats can be looked up in the wpa_supplicant code 216 * @hide 217 */ 218 @UnsupportedAppUsage WifiP2pGroup(String supplicantEvent)219 public WifiP2pGroup(String supplicantEvent) throws IllegalArgumentException { 220 221 String[] tokens = supplicantEvent.split(" "); 222 223 if (tokens.length < 3) { 224 throw new IllegalArgumentException("Malformed supplicant event"); 225 } 226 227 if (tokens[0].startsWith("P2P-GROUP")) { 228 mInterface = tokens[1]; 229 mIsGroupOwner = tokens[2].equals("GO"); 230 231 Matcher match = groupStartedPattern.matcher(supplicantEvent); 232 if (!match.find()) { 233 return; 234 } 235 236 mNetworkName = match.group(1); 237 // It throws NumberFormatException if the string cannot be parsed as an integer. 238 mFrequency = Integer.parseInt(match.group(2)); 239 // psk is unused right now 240 //String psk = match.group(3); 241 mPassphrase = match.group(4); 242 mOwner = new WifiP2pDevice(match.group(5)); 243 if (match.group(6) != null) { 244 mNetId = NETWORK_ID_PERSISTENT; 245 } else { 246 mNetId = NETWORK_ID_TEMPORARY; 247 } 248 } else if (tokens[0].equals("P2P-INVITATION-RECEIVED")) { 249 String sa = null; 250 mNetId = NETWORK_ID_PERSISTENT; 251 for (String token : tokens) { 252 String[] nameValue = token.split("="); 253 if (nameValue.length != 2) continue; 254 255 if (nameValue[0].equals("sa")) { 256 sa = nameValue[1]; 257 258 // set source address into the client list. 259 WifiP2pDevice dev = new WifiP2pDevice(); 260 dev.deviceAddress = nameValue[1]; 261 mClients.add(dev); 262 continue; 263 } 264 265 if (nameValue[0].equals("go_dev_addr")) { 266 mOwner = new WifiP2pDevice(nameValue[1]); 267 continue; 268 } 269 270 if (nameValue[0].equals("persistent")) { 271 mNetId = Integer.parseInt(nameValue[1]); 272 continue; 273 } 274 } 275 } else { 276 throw new IllegalArgumentException("Malformed supplicant event"); 277 } 278 } 279 280 /** @hide */ setNetworkName(String networkName)281 public void setNetworkName(String networkName) { 282 mNetworkName = networkName; 283 } 284 285 /** 286 * Get the network name (SSID) of the group. Legacy Wi-Fi clients will discover 287 * the p2p group using the network name. 288 */ getNetworkName()289 public String getNetworkName() { 290 return mNetworkName; 291 } 292 293 /** @hide */ 294 @UnsupportedAppUsage setIsGroupOwner(boolean isGo)295 public void setIsGroupOwner(boolean isGo) { 296 mIsGroupOwner = isGo; 297 } 298 299 /** Check whether this device is the group owner of the created p2p group */ isGroupOwner()300 public boolean isGroupOwner() { 301 return mIsGroupOwner; 302 } 303 304 /** @hide */ setOwner(WifiP2pDevice device)305 public void setOwner(WifiP2pDevice device) { 306 mOwner = device; 307 } 308 309 /** Get the details of the group owner as a {@link WifiP2pDevice} object */ getOwner()310 public WifiP2pDevice getOwner() { 311 return mOwner; 312 } 313 314 /** @hide */ addClient(String address)315 public void addClient(String address) { 316 addClient(new WifiP2pDevice(address)); 317 } 318 319 /** @hide */ addClient(WifiP2pDevice device)320 public void addClient(WifiP2pDevice device) { 321 for (WifiP2pDevice client : mClients) { 322 if (client.equals(device)) return; 323 } 324 mClients.add(new WifiP2pDevice(device)); 325 } 326 327 /** @hide */ setClientInterfaceMacAddress(@onNull String deviceAddress, @NonNull final MacAddress interfaceMacAddress)328 public void setClientInterfaceMacAddress(@NonNull String deviceAddress, 329 @NonNull final MacAddress interfaceMacAddress) { 330 if (null == interfaceMacAddress) { 331 Log.e("setClientInterfaceMacAddress", "cannot set null interface mac address"); 332 return; 333 } 334 for (WifiP2pDevice client : mClients) { 335 if (client.deviceAddress.equals(deviceAddress)) { 336 Log.i("setClientInterfaceMacAddress", "device: " + deviceAddress 337 + " interfaceAddress: " + interfaceMacAddress.toString()); 338 client.setInterfaceMacAddress(interfaceMacAddress); 339 break; 340 } 341 } 342 } 343 /** @hide */ setClientIpAddress(@onNull final MacAddress interfaceMacAddress, @NonNull final InetAddress ipAddress)344 public void setClientIpAddress(@NonNull final MacAddress interfaceMacAddress, 345 @NonNull final InetAddress ipAddress) { 346 if (null == interfaceMacAddress) { 347 Log.e("setClientIpAddress", "cannot set IP address with null interface mac address"); 348 return; 349 } 350 if (null == ipAddress) { 351 Log.e("setClientIpAddress", "Null IP - Failed to set IP address in WifiP2pDevice"); 352 return; 353 } 354 for (WifiP2pDevice client : mClients) { 355 if (interfaceMacAddress.equals(client.getInterfaceMacAddress())) { 356 Log.i("setClientIpAddress", "Update the IP address" 357 + " device: " + client.deviceAddress + " interfaceAddress: " 358 + interfaceMacAddress.toString() + " IP: " + ipAddress.getHostAddress()); 359 client.setIpAddress(ipAddress); 360 break; 361 } 362 } 363 } 364 365 /** @hide */ removeClient(String address)366 public boolean removeClient(String address) { 367 return mClients.remove(new WifiP2pDevice(address)); 368 } 369 370 /** @hide */ removeClient(WifiP2pDevice device)371 public boolean removeClient(WifiP2pDevice device) { 372 return mClients.remove(device); 373 } 374 375 /** @hide */ 376 @UnsupportedAppUsage isClientListEmpty()377 public boolean isClientListEmpty() { 378 return mClients.size() == 0; 379 } 380 381 /** 382 * Returns {@code true} if the device is part of the group, {@code false} otherwise. 383 * 384 * @hide 385 */ contains(@ullable WifiP2pDevice device)386 public boolean contains(@Nullable WifiP2pDevice device) { 387 return mOwner.equals(device) || mClients.contains(device); 388 } 389 390 /** Get the list of clients currently part of the p2p group */ getClientList()391 public Collection<WifiP2pDevice> getClientList() { 392 return Collections.unmodifiableCollection(mClients); 393 } 394 395 /** @hide */ setPassphrase(String passphrase)396 public void setPassphrase(String passphrase) { 397 mPassphrase = passphrase; 398 } 399 400 /** 401 * Get the passphrase of the group. This function will return a valid passphrase only 402 * at the group owner. Legacy Wi-Fi clients will need this passphrase alongside 403 * network name obtained from {@link #getNetworkName()} to join the group 404 */ getPassphrase()405 public String getPassphrase() { 406 return mPassphrase; 407 } 408 409 /** 410 * Set the security type of the group. 411 * 412 * @param securityType One of the {@code SECURITY_TYPE_*}. 413 * @hide 414 */ setSecurityType(@ecurityType int securityType)415 public void setSecurityType(@SecurityType int securityType) { 416 mSecurityType = securityType; 417 } 418 419 /** 420 * Get the security type of the group. 421 * 422 * @return One of the {@code SECURITY_TYPE_*}. 423 */ 424 @RequiresApi(Build.VERSION_CODES.BAKLAVA) 425 @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) getSecurityType()426 public @SecurityType int getSecurityType() { 427 if (!Environment.isSdkAtLeastB()) { 428 throw new UnsupportedOperationException(); 429 } 430 return mSecurityType; 431 } 432 433 /** @hide */ 434 @UnsupportedAppUsage setInterface(String intf)435 public void setInterface(String intf) { 436 mInterface = intf; 437 } 438 439 /** Get the interface name on which the group is created */ getInterface()440 public String getInterface() { 441 return mInterface; 442 } 443 444 /** The network ID of the P2P group in wpa_supplicant. */ getNetworkId()445 public int getNetworkId() { 446 return mNetId; 447 } 448 449 /** @hide */ 450 @UnsupportedAppUsage setNetworkId(int netId)451 public void setNetworkId(int netId) { 452 this.mNetId = netId; 453 } 454 455 /** Get the operating frequency (in MHz) of the p2p group */ getFrequency()456 public int getFrequency() { 457 return mFrequency; 458 } 459 460 /** @hide */ setFrequency(int freq)461 public void setFrequency(int freq) { 462 this.mFrequency = freq; 463 } 464 465 /** 466 * Return the vendor-provided configuration data, if it exists. See also {@link 467 * #setVendorData(List)} 468 * 469 * @return Vendor configuration data, or empty list if it does not exist. 470 * @hide 471 */ 472 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) 473 @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) 474 @NonNull 475 @SystemApi getVendorData()476 public List<OuiKeyedData> getVendorData() { 477 if (!SdkLevel.isAtLeastV()) { 478 throw new UnsupportedOperationException(); 479 } 480 return mVendorData; 481 } 482 483 /** 484 * Set additional vendor-provided configuration data. 485 * 486 * @param vendorData List of {@link OuiKeyedData} containing the vendor-provided 487 * configuration data. Note that multiple elements with the same OUI are allowed. 488 * @hide 489 */ 490 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) 491 @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) 492 @SystemApi setVendorData(@onNull List<OuiKeyedData> vendorData)493 public void setVendorData(@NonNull List<OuiKeyedData> vendorData) { 494 if (!SdkLevel.isAtLeastV()) { 495 throw new UnsupportedOperationException(); 496 } 497 if (vendorData == null) { 498 throw new IllegalArgumentException("setVendorData received a null value"); 499 } 500 mVendorData = new ArrayList<>(vendorData); 501 } 502 503 /** 504 * Returns the BSSID, if this device is the group owner of the P2P group supporting Wi-Fi 505 * Direct R2 protocol. 506 * <p> 507 * The interface address of a Wi-Fi Direct R2 supported device is randomized. So for every 508 * group owner session a randomized interface address will be returned. 509 * <p> 510 * The BSSID returned will be {@code null}, if this device is a client device or a group owner 511 * which doesn't support Wi-Fi Direct R2 protocol. 512 * @return the BSSID. 513 */ 514 @RequiresApi(Build.VERSION_CODES.BAKLAVA) 515 @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) 516 @Nullable getGroupOwnerBssid()517 public MacAddress getGroupOwnerBssid() { 518 if (!Environment.isSdkAtLeastB()) { 519 throw new UnsupportedOperationException(); 520 } 521 if (isGroupOwner() && getSecurityType() == SECURITY_TYPE_WPA3_SAE 522 && interfaceAddress != null) { 523 return MacAddress.fromBytes(interfaceAddress); 524 } 525 return null; 526 } 527 toString()528 public String toString() { 529 StringBuffer sbuf = new StringBuffer(); 530 sbuf.append("network: ").append(mNetworkName); 531 sbuf.append("\n isGO: ").append(mIsGroupOwner); 532 sbuf.append("\n GO: ").append(mOwner); 533 for (WifiP2pDevice client : mClients) { 534 sbuf.append("\n Client: ").append(client); 535 } 536 sbuf.append("\n interface: ").append(mInterface); 537 sbuf.append("\n networkId: ").append(mNetId); 538 sbuf.append("\n securityType: ").append(mSecurityType); 539 540 sbuf.append("\n frequency: ").append(mFrequency); 541 sbuf.append("\n vendorData: ").append(mVendorData); 542 return sbuf.toString(); 543 } 544 545 /** Implement the Parcelable interface */ describeContents()546 public int describeContents() { 547 return 0; 548 } 549 550 /** copy constructor */ WifiP2pGroup(WifiP2pGroup source)551 public WifiP2pGroup(WifiP2pGroup source) { 552 if (source != null) { 553 mNetworkName = source.getNetworkName(); 554 mOwner = new WifiP2pDevice(source.getOwner()); 555 mIsGroupOwner = source.mIsGroupOwner; 556 for (WifiP2pDevice d : source.getClientList()) mClients.add(d); 557 mPassphrase = source.getPassphrase(); 558 mInterface = source.getInterface(); 559 mNetId = source.getNetworkId(); 560 if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { 561 mSecurityType = source.getSecurityType(); 562 } 563 mFrequency = source.getFrequency(); 564 if (SdkLevel.isAtLeastV()) { 565 mVendorData = new ArrayList<>(source.getVendorData()); 566 } 567 } 568 } 569 570 /** Implement the Parcelable interface */ writeToParcel(Parcel dest, int flags)571 public void writeToParcel(Parcel dest, int flags) { 572 dest.writeString(mNetworkName); 573 dest.writeParcelable(mOwner, flags); 574 dest.writeByte(mIsGroupOwner ? (byte) 1: (byte) 0); 575 dest.writeInt(mClients.size()); 576 for (WifiP2pDevice client : mClients) { 577 dest.writeParcelable(client, flags); 578 } 579 dest.writeString(mPassphrase); 580 dest.writeString(mInterface); 581 dest.writeInt(mNetId); 582 if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { 583 dest.writeInt(mSecurityType); 584 } 585 dest.writeInt(mFrequency); 586 if (SdkLevel.isAtLeastV()) { 587 dest.writeList(mVendorData); 588 } 589 } 590 591 /** Implement the Parcelable interface */ 592 public static final @android.annotation.NonNull Creator<WifiP2pGroup> CREATOR = 593 new Creator<WifiP2pGroup>() { 594 public WifiP2pGroup createFromParcel(Parcel in) { 595 WifiP2pGroup group = new WifiP2pGroup(); 596 group.setNetworkName(in.readString()); 597 group.setOwner((WifiP2pDevice) in.readParcelable( 598 WifiP2pDevice.class.getClassLoader())); 599 group.setIsGroupOwner(in.readByte() == (byte) 1); 600 int clientCount = in.readInt(); 601 for (int i = 0; i < clientCount; i++) { 602 group.addClient((WifiP2pDevice) in.readParcelable( 603 WifiP2pDevice.class.getClassLoader())); 604 } 605 group.setPassphrase(in.readString()); 606 group.setInterface(in.readString()); 607 group.setNetworkId(in.readInt()); 608 if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { 609 group.setSecurityType(in.readInt()); 610 } 611 group.setFrequency(in.readInt()); 612 if (SdkLevel.isAtLeastV()) { 613 group.setVendorData(ParcelUtil.readOuiKeyedDataList(in)); 614 } 615 return group; 616 } 617 618 public WifiP2pGroup[] newArray(int size) { 619 return new WifiP2pGroup[size]; 620 } 621 }; 622 } 623