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
17package android.net.wifi.p2p;
18
19import android.os.Parcelable;
20import android.os.Parcel;
21
22import java.util.ArrayList;
23import java.util.List;
24import java.util.Collection;
25import java.util.Collections;
26import java.util.regex.Pattern;
27import 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 */
36public 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
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     */
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                    mOwner = new WifiP2pDevice(sa);
150                    mNetId = Integer.parseInt(nameValue[1]);
151                    continue;
152                }
153            }
154        } else {
155            throw new IllegalArgumentException("Malformed supplicant event");
156        }
157    }
158
159    /** @hide */
160    public void setNetworkName(String networkName) {
161        mNetworkName = networkName;
162    }
163
164    /**
165     * Get the network name (SSID) of the group. Legacy Wi-Fi clients will discover
166     * the p2p group using the network name.
167     */
168    public String getNetworkName() {
169        return mNetworkName;
170    }
171
172    /** @hide */
173    public void setIsGroupOwner(boolean isGo) {
174        mIsGroupOwner = isGo;
175    }
176
177    /** Check whether this device is the group owner of the created p2p group */
178    public boolean isGroupOwner() {
179        return mIsGroupOwner;
180    }
181
182    /** @hide */
183    public void setOwner(WifiP2pDevice device) {
184        mOwner = device;
185    }
186
187    /** Get the details of the group owner as a {@link WifiP2pDevice} object */
188    public WifiP2pDevice getOwner() {
189        return mOwner;
190    }
191
192    /** @hide */
193    public void addClient(String address) {
194        addClient(new WifiP2pDevice(address));
195    }
196
197    /** @hide */
198    public void addClient(WifiP2pDevice device) {
199        for (WifiP2pDevice client : mClients) {
200            if (client.equals(device)) return;
201        }
202        mClients.add(device);
203    }
204
205    /** @hide */
206    public boolean removeClient(String address) {
207        return mClients.remove(new WifiP2pDevice(address));
208    }
209
210    /** @hide */
211    public boolean removeClient(WifiP2pDevice device) {
212        return mClients.remove(device);
213    }
214
215    /** @hide */
216    public boolean isClientListEmpty() {
217        return mClients.size() == 0;
218    }
219
220    /** @hide Returns {@code true} if the device is part of the group */
221    public boolean contains(WifiP2pDevice device) {
222        if (mOwner.equals(device) || mClients.contains(device)) return true;
223        return false;
224    }
225
226    /** Get the list of clients currently part of the p2p group */
227    public Collection<WifiP2pDevice> getClientList() {
228        return Collections.unmodifiableCollection(mClients);
229    }
230
231    /** @hide */
232    public void setPassphrase(String passphrase) {
233        mPassphrase = passphrase;
234    }
235
236    /**
237     * Get the passphrase of the group. This function will return a valid passphrase only
238     * at the group owner. Legacy Wi-Fi clients will need this passphrase alongside
239     * network name obtained from {@link #getNetworkName()} to join the group
240     */
241    public String getPassphrase() {
242        return mPassphrase;
243    }
244
245    /** @hide */
246    public void setInterface(String intf) {
247        mInterface = intf;
248    }
249
250    /** Get the interface name on which the group is created */
251    public String getInterface() {
252        return mInterface;
253    }
254
255    /** @hide */
256    public int getNetworkId() {
257        return mNetId;
258    }
259
260    /** @hide */
261    public void setNetworkId(int netId) {
262        this.mNetId = netId;
263    }
264
265    public String toString() {
266        StringBuffer sbuf = new StringBuffer();
267        sbuf.append("network: ").append(mNetworkName);
268        sbuf.append("\n isGO: ").append(mIsGroupOwner);
269        sbuf.append("\n GO: ").append(mOwner);
270        for (WifiP2pDevice client : mClients) {
271            sbuf.append("\n Client: ").append(client);
272        }
273        sbuf.append("\n interface: ").append(mInterface);
274        sbuf.append("\n networkId: ").append(mNetId);
275        return sbuf.toString();
276    }
277
278    /** Implement the Parcelable interface */
279    public int describeContents() {
280        return 0;
281    }
282
283    /** copy constructor */
284    public WifiP2pGroup(WifiP2pGroup source) {
285        if (source != null) {
286            mNetworkName = source.getNetworkName();
287            mOwner = new WifiP2pDevice(source.getOwner());
288            mIsGroupOwner = source.mIsGroupOwner;
289            for (WifiP2pDevice d : source.getClientList()) mClients.add(d);
290            mPassphrase = source.getPassphrase();
291            mInterface = source.getInterface();
292            mNetId = source.getNetworkId();
293        }
294    }
295
296    /** Implement the Parcelable interface */
297    public void writeToParcel(Parcel dest, int flags) {
298        dest.writeString(mNetworkName);
299        dest.writeParcelable(mOwner, flags);
300        dest.writeByte(mIsGroupOwner ? (byte) 1: (byte) 0);
301        dest.writeInt(mClients.size());
302        for (WifiP2pDevice client : mClients) {
303            dest.writeParcelable(client, flags);
304        }
305        dest.writeString(mPassphrase);
306        dest.writeString(mInterface);
307        dest.writeInt(mNetId);
308    }
309
310    /** Implement the Parcelable interface */
311    public static final Creator<WifiP2pGroup> CREATOR =
312        new Creator<WifiP2pGroup>() {
313            public WifiP2pGroup createFromParcel(Parcel in) {
314                WifiP2pGroup group = new WifiP2pGroup();
315                group.setNetworkName(in.readString());
316                group.setOwner((WifiP2pDevice)in.readParcelable(null));
317                group.setIsGroupOwner(in.readByte() == (byte)1);
318                int clientCount = in.readInt();
319                for (int i=0; i<clientCount; i++) {
320                    group.addClient((WifiP2pDevice) in.readParcelable(null));
321                }
322                group.setPassphrase(in.readString());
323                group.setInterface(in.readString());
324                group.setNetworkId(in.readInt());
325                return group;
326            }
327
328            public WifiP2pGroup[] newArray(int size) {
329                return new WifiP2pGroup[size];
330            }
331        };
332}
333