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