• 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.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