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