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