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.compat.annotation.UnsupportedAppUsage;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.regex.Matcher;
28 import java.util.regex.Pattern;
29 
30 /**
31  * A class representing a Wi-Fi P2p group. A p2p group consists of a single group
32  * owner and one or more clients. In the case of a group with only two devices, one
33  * will be the group owner and the other will be a group client.
34  *
35  * {@see WifiP2pManager}
36  */
37 public class WifiP2pGroup implements Parcelable {
38 
39     /** The temporary network id.
40      * {@hide} */
41     @UnsupportedAppUsage
42     public static final int TEMPORARY_NET_ID = -1;
43 
44     /** The persistent network id.
45      * If a matching persistent profile is found, use it.
46      * Otherwise, create a new persistent profile.
47      * {@hide} */
48     public static final int PERSISTENT_NET_ID = -2;
49 
50     /** The network name */
51     private String mNetworkName;
52 
53     /** Group owner */
54     private WifiP2pDevice mOwner;
55 
56     /** Device is group owner */
57     private boolean mIsGroupOwner;
58 
59     /** Group clients */
60     private List<WifiP2pDevice> mClients = new ArrayList<WifiP2pDevice>();
61 
62     /** The passphrase used for WPA2-PSK */
63     private String mPassphrase;
64 
65     private String mInterface;
66 
67     /** The network id in the wpa_supplicant */
68     private int mNetId;
69 
70     /** The frequency (in MHz) used by this group */
71     private int mFrequency;
72 
73     /** P2P group started string pattern */
74     private static final Pattern groupStartedPattern = Pattern.compile(
75         "ssid=\"(.+)\" " +
76         "freq=(\\d+) " +
77         "(?:psk=)?([0-9a-fA-F]{64})?" +
78         "(?:passphrase=)?(?:\"(.{0,63})\")? " +
79         "go_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2})" +
80         " ?(\\[PERSISTENT\\])?"
81     );
82 
WifiP2pGroup()83     public WifiP2pGroup() {
84     }
85 
86     /**
87      * @param supplicantEvent formats supported include
88      *
89      *  P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437
90      *  [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|
91      *  passphrase="fKG4jMe3"] go_dev_addr=fa:7b:7a:42:02:13 [PERSISTENT]
92      *
93      *  P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED
94      *
95      *  P2P-INVITATION-RECEIVED sa=fa:7b:7a:42:02:13 go_dev_addr=f8:7b:7a:42:02:13
96      *  bssid=fa:7b:7a:42:82:13 unknown-network
97      *
98      *  P2P-INVITATION-RECEIVED sa=b8:f9:34:2a:c7:9d persistent=0
99      *
100      *  Note: The events formats can be looked up in the wpa_supplicant code
101      *  @hide
102      */
103     @UnsupportedAppUsage
WifiP2pGroup(String supplicantEvent)104     public WifiP2pGroup(String supplicantEvent) throws IllegalArgumentException {
105 
106         String[] tokens = supplicantEvent.split(" ");
107 
108         if (tokens.length < 3) {
109             throw new IllegalArgumentException("Malformed supplicant event");
110         }
111 
112         if (tokens[0].startsWith("P2P-GROUP")) {
113             mInterface = tokens[1];
114             mIsGroupOwner = tokens[2].equals("GO");
115 
116             Matcher match = groupStartedPattern.matcher(supplicantEvent);
117             if (!match.find()) {
118                 return;
119             }
120 
121             mNetworkName = match.group(1);
122             // It throws NumberFormatException if the string cannot be parsed as an integer.
123             mFrequency = Integer.parseInt(match.group(2));
124             // psk is unused right now
125             //String psk = match.group(3);
126             mPassphrase = match.group(4);
127             mOwner = new WifiP2pDevice(match.group(5));
128             if (match.group(6) != null) {
129                 mNetId = PERSISTENT_NET_ID;
130             } else {
131                 mNetId = TEMPORARY_NET_ID;
132             }
133         } else if (tokens[0].equals("P2P-INVITATION-RECEIVED")) {
134             String sa = null;
135             mNetId = PERSISTENT_NET_ID;
136             for (String token : tokens) {
137                 String[] nameValue = token.split("=");
138                 if (nameValue.length != 2) continue;
139 
140                 if (nameValue[0].equals("sa")) {
141                     sa = nameValue[1];
142 
143                     // set source address into the client list.
144                     WifiP2pDevice dev = new WifiP2pDevice();
145                     dev.deviceAddress = nameValue[1];
146                     mClients.add(dev);
147                     continue;
148                 }
149 
150                 if (nameValue[0].equals("go_dev_addr")) {
151                     mOwner = new WifiP2pDevice(nameValue[1]);
152                     continue;
153                 }
154 
155                 if (nameValue[0].equals("persistent")) {
156                     mNetId = Integer.parseInt(nameValue[1]);
157                     continue;
158                 }
159             }
160         } else {
161             throw new IllegalArgumentException("Malformed supplicant event");
162         }
163     }
164 
165     /** @hide */
setNetworkName(String networkName)166     public void setNetworkName(String networkName) {
167         mNetworkName = networkName;
168     }
169 
170     /**
171      * Get the network name (SSID) of the group. Legacy Wi-Fi clients will discover
172      * the p2p group using the network name.
173      */
getNetworkName()174     public String getNetworkName() {
175         return mNetworkName;
176     }
177 
178     /** @hide */
179     @UnsupportedAppUsage
setIsGroupOwner(boolean isGo)180     public void setIsGroupOwner(boolean isGo) {
181         mIsGroupOwner = isGo;
182     }
183 
184     /** Check whether this device is the group owner of the created p2p group */
isGroupOwner()185     public boolean isGroupOwner() {
186         return mIsGroupOwner;
187     }
188 
189     /** @hide */
setOwner(WifiP2pDevice device)190     public void setOwner(WifiP2pDevice device) {
191         mOwner = device;
192     }
193 
194     /** Get the details of the group owner as a {@link WifiP2pDevice} object */
getOwner()195     public WifiP2pDevice getOwner() {
196         return mOwner;
197     }
198 
199     /** @hide */
addClient(String address)200     public void addClient(String address) {
201         addClient(new WifiP2pDevice(address));
202     }
203 
204     /** @hide */
addClient(WifiP2pDevice device)205     public void addClient(WifiP2pDevice device) {
206         for (WifiP2pDevice client : mClients) {
207             if (client.equals(device)) return;
208         }
209         mClients.add(device);
210     }
211 
212     /** @hide */
removeClient(String address)213     public boolean removeClient(String address) {
214         return mClients.remove(new WifiP2pDevice(address));
215     }
216 
217     /** @hide */
removeClient(WifiP2pDevice device)218     public boolean removeClient(WifiP2pDevice device) {
219         return mClients.remove(device);
220     }
221 
222     /** @hide */
223     @UnsupportedAppUsage
isClientListEmpty()224     public boolean isClientListEmpty() {
225         return mClients.size() == 0;
226     }
227 
228     /** @hide Returns {@code true} if the device is part of the group */
contains(WifiP2pDevice device)229     public boolean contains(WifiP2pDevice device) {
230         if (mOwner.equals(device) || mClients.contains(device)) return true;
231         return false;
232     }
233 
234     /** Get the list of clients currently part of the p2p group */
getClientList()235     public Collection<WifiP2pDevice> getClientList() {
236         return Collections.unmodifiableCollection(mClients);
237     }
238 
239     /** @hide */
setPassphrase(String passphrase)240     public void setPassphrase(String passphrase) {
241         mPassphrase = passphrase;
242     }
243 
244     /**
245      * Get the passphrase of the group. This function will return a valid passphrase only
246      * at the group owner. Legacy Wi-Fi clients will need this passphrase alongside
247      * network name obtained from {@link #getNetworkName()} to join the group
248      */
getPassphrase()249     public String getPassphrase() {
250         return mPassphrase;
251     }
252 
253     /** @hide */
254     @UnsupportedAppUsage
setInterface(String intf)255     public void setInterface(String intf) {
256         mInterface = intf;
257     }
258 
259     /** Get the interface name on which the group is created */
getInterface()260     public String getInterface() {
261         return mInterface;
262     }
263 
264     /** @hide */
265     @UnsupportedAppUsage
getNetworkId()266     public int getNetworkId() {
267         return mNetId;
268     }
269 
270     /** @hide */
271     @UnsupportedAppUsage
setNetworkId(int netId)272     public void setNetworkId(int netId) {
273         this.mNetId = netId;
274     }
275 
276     /** Get the operating frequency (in MHz) of the p2p group */
getFrequency()277     public int getFrequency() {
278         return mFrequency;
279     }
280 
281     /** @hide */
setFrequency(int freq)282     public void setFrequency(int freq) {
283         this.mFrequency = freq;
284     }
285 
toString()286     public String toString() {
287         StringBuffer sbuf = new StringBuffer();
288         sbuf.append("network: ").append(mNetworkName);
289         sbuf.append("\n isGO: ").append(mIsGroupOwner);
290         sbuf.append("\n GO: ").append(mOwner);
291         for (WifiP2pDevice client : mClients) {
292             sbuf.append("\n Client: ").append(client);
293         }
294         sbuf.append("\n interface: ").append(mInterface);
295         sbuf.append("\n networkId: ").append(mNetId);
296         sbuf.append("\n frequency: ").append(mFrequency);
297         return sbuf.toString();
298     }
299 
300     /** Implement the Parcelable interface */
describeContents()301     public int describeContents() {
302         return 0;
303     }
304 
305     /** copy constructor */
WifiP2pGroup(WifiP2pGroup source)306     public WifiP2pGroup(WifiP2pGroup source) {
307         if (source != null) {
308             mNetworkName = source.getNetworkName();
309             mOwner = new WifiP2pDevice(source.getOwner());
310             mIsGroupOwner = source.mIsGroupOwner;
311             for (WifiP2pDevice d : source.getClientList()) mClients.add(d);
312             mPassphrase = source.getPassphrase();
313             mInterface = source.getInterface();
314             mNetId = source.getNetworkId();
315             mFrequency = source.getFrequency();
316         }
317     }
318 
319     /** Implement the Parcelable interface */
writeToParcel(Parcel dest, int flags)320     public void writeToParcel(Parcel dest, int flags) {
321         dest.writeString(mNetworkName);
322         dest.writeParcelable(mOwner, flags);
323         dest.writeByte(mIsGroupOwner ? (byte) 1: (byte) 0);
324         dest.writeInt(mClients.size());
325         for (WifiP2pDevice client : mClients) {
326             dest.writeParcelable(client, flags);
327         }
328         dest.writeString(mPassphrase);
329         dest.writeString(mInterface);
330         dest.writeInt(mNetId);
331         dest.writeInt(mFrequency);
332     }
333 
334     /** Implement the Parcelable interface */
335     public static final @android.annotation.NonNull Creator<WifiP2pGroup> CREATOR =
336         new Creator<WifiP2pGroup>() {
337             public WifiP2pGroup createFromParcel(Parcel in) {
338                 WifiP2pGroup group = new WifiP2pGroup();
339                 group.setNetworkName(in.readString());
340                 group.setOwner((WifiP2pDevice)in.readParcelable(null));
341                 group.setIsGroupOwner(in.readByte() == (byte)1);
342                 int clientCount = in.readInt();
343                 for (int i=0; i<clientCount; i++) {
344                     group.addClient((WifiP2pDevice) in.readParcelable(null));
345                 }
346                 group.setPassphrase(in.readString());
347                 group.setInterface(in.readString());
348                 group.setNetworkId(in.readInt());
349                 group.setFrequency(in.readInt());
350                 return group;
351             }
352 
353             public WifiP2pGroup[] newArray(int size) {
354                 return new WifiP2pGroup[size];
355             }
356         };
357 }
358