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.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.UnsupportedAppUsage;
23 import android.net.MacAddress;
24 import android.net.wifi.WpsInfo;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.text.TextUtils;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.util.regex.PatternSyntaxException;
32 
33 /**
34  * A class representing a Wi-Fi P2p configuration for setting up a connection
35  *
36  * {@see WifiP2pManager}
37  */
38 public class WifiP2pConfig implements Parcelable {
39 
40     /**
41      * The device MAC address uniquely identifies a Wi-Fi p2p device
42      */
43     public String deviceAddress = "";
44 
45     /**
46      * Wi-Fi Protected Setup information
47      */
48     public WpsInfo wps;
49 
50     /**
51      * The network name of a group, should be configured by helper method
52      */
53     /** @hide */
54     public String networkName = "";
55 
56     /**
57      * The passphrase of a group, should be configured by helper method
58      */
59     /** @hide */
60     public String passphrase = "";
61 
62     /**
63      * The required band for Group Owner
64      */
65     /** @hide */
66     public int groupOwnerBand = GROUP_OWNER_BAND_AUTO;
67 
68     /** @hide */
69     public static final int MAX_GROUP_OWNER_INTENT   =   15;
70     /** @hide */
71     @UnsupportedAppUsage
72     public static final int MIN_GROUP_OWNER_INTENT   =   0;
73 
74     /** @hide */
75     @IntDef(flag = false, prefix = { "GROUP_OWNER_BAND_" }, value = {
76         GROUP_OWNER_BAND_AUTO,
77         GROUP_OWNER_BAND_2GHZ,
78         GROUP_OWNER_BAND_5GHZ
79     })
80     @Retention(RetentionPolicy.SOURCE)
81     public @interface GroupOperatingBandType {}
82 
83     /**
84      * Allow the system to pick the operating frequency from all supported bands.
85      */
86     public static final int GROUP_OWNER_BAND_AUTO = 0;
87     /**
88      * Allow the system to pick the operating frequency from the 2.4 GHz band.
89      */
90     public static final int GROUP_OWNER_BAND_2GHZ = 1;
91     /**
92      * Allow the system to pick the operating frequency from the 5 GHz band.
93      */
94     public static final int GROUP_OWNER_BAND_5GHZ = 2;
95 
96     /**
97      * This is an integer value between 0 and 15 where 0 indicates the least
98      * inclination to be a group owner and 15 indicates the highest inclination
99      * to be a group owner.
100      *
101      * A value of -1 indicates the system can choose an appropriate value.
102      */
103     public int groupOwnerIntent = -1;
104 
105     /** @hide */
106     @UnsupportedAppUsage
107     public int netId = WifiP2pGroup.PERSISTENT_NET_ID;
108 
WifiP2pConfig()109     public WifiP2pConfig() {
110         //set defaults
111         wps = new WpsInfo();
112         wps.setup = WpsInfo.PBC;
113     }
114 
115     /** @hide */
invalidate()116     public void invalidate() {
117         deviceAddress = "";
118     }
119 
120     /** P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 {@hide}*/
121     @UnsupportedAppUsage
WifiP2pConfig(String supplicantEvent)122     public WifiP2pConfig(String supplicantEvent) throws IllegalArgumentException {
123         String[] tokens = supplicantEvent.split(" ");
124 
125         if (tokens.length < 2 || !tokens[0].equals("P2P-GO-NEG-REQUEST")) {
126             throw new IllegalArgumentException("Malformed supplicant event");
127         }
128 
129         deviceAddress = tokens[1];
130         wps = new WpsInfo();
131 
132         if (tokens.length > 2) {
133             String[] nameVal = tokens[2].split("=");
134             int devPasswdId;
135             try {
136                 devPasswdId = Integer.parseInt(nameVal[1]);
137             } catch (NumberFormatException e) {
138                 devPasswdId = 0;
139             }
140             //Based on definitions in wps/wps_defs.h
141             switch (devPasswdId) {
142                 //DEV_PW_USER_SPECIFIED = 0x0001,
143                 case 0x01:
144                     wps.setup = WpsInfo.DISPLAY;
145                     break;
146                 //DEV_PW_PUSHBUTTON = 0x0004,
147                 case 0x04:
148                     wps.setup = WpsInfo.PBC;
149                     break;
150                 //DEV_PW_REGISTRAR_SPECIFIED = 0x0005
151                 case 0x05:
152                     wps.setup = WpsInfo.KEYPAD;
153                     break;
154                 default:
155                     wps.setup = WpsInfo.PBC;
156                     break;
157             }
158         }
159     }
160 
toString()161     public String toString() {
162         StringBuffer sbuf = new StringBuffer();
163         sbuf.append("\n address: ").append(deviceAddress);
164         sbuf.append("\n wps: ").append(wps);
165         sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent);
166         sbuf.append("\n persist: ").append(netId);
167         sbuf.append("\n networkName: ").append(networkName);
168         sbuf.append("\n passphrase: ").append(
169                 TextUtils.isEmpty(passphrase) ? "<empty>" : "<non-empty>");
170         sbuf.append("\n groupOwnerBand: ").append(groupOwnerBand);
171         return sbuf.toString();
172     }
173 
174     /** Implement the Parcelable interface */
describeContents()175     public int describeContents() {
176         return 0;
177     }
178 
179     /** copy constructor */
WifiP2pConfig(WifiP2pConfig source)180     public WifiP2pConfig(WifiP2pConfig source) {
181         if (source != null) {
182             deviceAddress = source.deviceAddress;
183             wps = new WpsInfo(source.wps);
184             groupOwnerIntent = source.groupOwnerIntent;
185             netId = source.netId;
186             networkName = source.networkName;
187             passphrase = source.passphrase;
188             groupOwnerBand = source.groupOwnerBand;
189         }
190     }
191 
192     /** Implement the Parcelable interface */
writeToParcel(Parcel dest, int flags)193     public void writeToParcel(Parcel dest, int flags) {
194         dest.writeString(deviceAddress);
195         dest.writeParcelable(wps, flags);
196         dest.writeInt(groupOwnerIntent);
197         dest.writeInt(netId);
198         dest.writeString(networkName);
199         dest.writeString(passphrase);
200         dest.writeInt(groupOwnerBand);
201     }
202 
203     /** Implement the Parcelable interface */
204     public static final @android.annotation.NonNull Creator<WifiP2pConfig> CREATOR =
205         new Creator<WifiP2pConfig>() {
206             public WifiP2pConfig createFromParcel(Parcel in) {
207                 WifiP2pConfig config = new WifiP2pConfig();
208                 config.deviceAddress = in.readString();
209                 config.wps = (WpsInfo) in.readParcelable(null);
210                 config.groupOwnerIntent = in.readInt();
211                 config.netId = in.readInt();
212                 config.networkName = in.readString();
213                 config.passphrase = in.readString();
214                 config.groupOwnerBand = in.readInt();
215                 return config;
216             }
217 
218             public WifiP2pConfig[] newArray(int size) {
219                 return new WifiP2pConfig[size];
220             }
221         };
222 
223     /**
224      * Builder used to build {@link WifiP2pConfig} objects for
225      * creating or joining a group.
226      */
227     public static final class Builder {
228 
229         private static final MacAddress MAC_ANY_ADDRESS =
230                 MacAddress.fromString("02:00:00:00:00:00");
231 
232         private MacAddress mDeviceAddress = MAC_ANY_ADDRESS;
233         private String mNetworkName = "";
234         private String mPassphrase = "";
235         private int mGroupOperatingBand = GROUP_OWNER_BAND_AUTO;
236         private int mGroupOperatingFrequency = GROUP_OWNER_BAND_AUTO;
237         private int mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
238 
239         /**
240          * Specify the peer's MAC address. If not set, the device will
241          * try to find a peer whose SSID matches the network name as
242          * specified by {@link #setNetworkName(String)}. Specifying null will
243          * reset the peer's MAC address to "02:00:00:00:00:00".
244          * <p>
245          *     Optional. "02:00:00:00:00:00" by default.
246          *
247          * @param deviceAddress the peer's MAC address.
248          * @return The builder to facilitate chaining
249          *         {@code builder.setXXX(..).setXXX(..)}.
250          */
setDeviceAddress(@ullable MacAddress deviceAddress)251         public @NonNull Builder setDeviceAddress(@Nullable MacAddress deviceAddress) {
252             if (deviceAddress == null) {
253                 mDeviceAddress = MAC_ANY_ADDRESS;
254             } else {
255                 mDeviceAddress = deviceAddress;
256             }
257             return this;
258         }
259 
260         /**
261          * Specify the network name, a.k.a. group name,
262          * for creating or joining a group.
263          * <p>
264          * A network name shall begin with "DIRECT-xy". x and y are selected
265          * from the following character set: upper case letters, lower case
266          * letters and numbers. Any byte values allowed for an SSID according to
267          * IEEE802.11-2012 [1] may be included after the string "DIRECT-xy"
268          * (including none).
269          * <p>
270          *     Must be called - an empty network name or an network name
271          *     not conforming to the P2P Group ID naming rule is not valid.
272          *
273          * @param networkName network name of a group.
274          * @return The builder to facilitate chaining
275          *         {@code builder.setXXX(..).setXXX(..)}.
276          */
setNetworkName(@onNull String networkName)277         public @NonNull Builder setNetworkName(@NonNull String networkName) {
278             if (TextUtils.isEmpty(networkName)) {
279                 throw new IllegalArgumentException(
280                         "network name must be non-empty.");
281             }
282             try {
283                 if (!networkName.matches("^DIRECT-[a-zA-Z0-9]{2}.*")) {
284                     throw new IllegalArgumentException(
285                             "network name must starts with the prefix DIRECT-xy.");
286                 }
287             } catch (PatternSyntaxException e) {
288                 // can never happen (fixed pattern)
289             }
290             mNetworkName = networkName;
291             return this;
292         }
293 
294         /**
295          * Specify the passphrase for creating or joining a group.
296          * <p>
297          * The passphrase must be an ASCII string whose length is between 8
298          * and 63.
299          * <p>
300          *     Must be called - an empty passphrase is not valid.
301          *
302          * @param passphrase the passphrase of a group.
303          * @return The builder to facilitate chaining
304          *         {@code builder.setXXX(..).setXXX(..)}.
305          */
setPassphrase(@onNull String passphrase)306         public @NonNull Builder setPassphrase(@NonNull String passphrase) {
307             if (TextUtils.isEmpty(passphrase)) {
308                 throw new IllegalArgumentException(
309                         "passphrase must be non-empty.");
310             }
311             if (passphrase.length() < 8 || passphrase.length() > 63) {
312                 throw new IllegalArgumentException(
313                         "The length of a passphrase must be between 8 and 63.");
314             }
315             mPassphrase = passphrase;
316             return this;
317         }
318 
319         /**
320          * Specify the band to use for creating the group or joining the group. The band should
321          * be {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ} or
322          * {@link #GROUP_OWNER_BAND_AUTO}.
323          * <p>
324          * When creating a group as Group Owner using {@link
325          * WifiP2pManager#createGroup(WifiP2pManager.Channel,
326          * WifiP2pConfig, WifiP2pManager.ActionListener)},
327          * specifying {@link #GROUP_OWNER_BAND_AUTO} allows the system to pick the operating
328          * frequency from all supported bands.
329          * Specifying {@link #GROUP_OWNER_BAND_2GHZ} or {@link #GROUP_OWNER_BAND_5GHZ}
330          * only allows the system to pick the operating frequency in the specified band.
331          * If the Group Owner cannot create a group in the specified band, the operation will fail.
332          * <p>
333          * When joining a group as Group Client using {@link
334          * WifiP2pManager#connect(WifiP2pManager.Channel, WifiP2pConfig,
335          * WifiP2pManager.ActionListener)},
336          * specifying {@link #GROUP_OWNER_BAND_AUTO} allows the system to scan all supported
337          * frequencies to find the desired group. Specifying {@link #GROUP_OWNER_BAND_2GHZ} or
338          * {@link #GROUP_OWNER_BAND_5GHZ} only allows the system to scan the specified band.
339          * <p>
340          *     {@link #setGroupOperatingBand(int)} and {@link #setGroupOperatingFrequency(int)} are
341          *     mutually exclusive. Setting operating band and frequency both is invalid.
342          * <p>
343          *     Optional. {@link #GROUP_OWNER_BAND_AUTO} by default.
344          *
345          * @param band the operating band of the group.
346          *             This should be one of {@link #GROUP_OWNER_BAND_AUTO},
347          *             {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ}.
348          * @return The builder to facilitate chaining
349          *         {@code builder.setXXX(..).setXXX(..)}.
350          */
setGroupOperatingBand(@roupOperatingBandType int band)351         public @NonNull Builder setGroupOperatingBand(@GroupOperatingBandType int band) {
352             switch (band) {
353                 case GROUP_OWNER_BAND_AUTO:
354                 case GROUP_OWNER_BAND_2GHZ:
355                 case GROUP_OWNER_BAND_5GHZ:
356                     mGroupOperatingBand = band;
357                     break;
358                 default:
359                     throw new IllegalArgumentException(
360                         "Invalid constant for the group operating band!");
361             }
362             return this;
363         }
364 
365         /**
366          * Specify the frequency, in MHz, to use for creating the group or joining the group.
367          * <p>
368          * When creating a group as Group Owner using {@link WifiP2pManager#createGroup(
369          * WifiP2pManager.Channel, WifiP2pConfig, WifiP2pManager.ActionListener)},
370          * specifying a frequency only allows the system to pick the specified frequency.
371          * If the Group Owner cannot create a group at the specified frequency,
372          * the operation will fail.
373          * When not specifying a frequency, it allows the system to pick operating frequency
374          * from all supported bands.
375          * <p>
376          * When joining a group as Group Client using {@link WifiP2pManager#connect(
377          * WifiP2pManager.Channel, WifiP2pConfig, WifiP2pManager.ActionListener)},
378          * specifying a frequency only allows the system to scan the specified frequency.
379          * If the frequency is not supported or invalid, the operation will fail.
380          * When not specifying a frequency, it allows the system to scan all supported
381          * frequencies to find the desired group.
382          * <p>
383          *     {@link #setGroupOperatingBand(int)} and {@link #setGroupOperatingFrequency(int)} are
384          *     mutually exclusive. Setting operating band and frequency both is invalid.
385          * <p>
386          *     Optional. 0 by default.
387          *
388          * @param frequency the operating frequency of the group.
389          * @return The builder to facilitate chaining
390          *         {@code builder.setXXX(..).setXXX(..)}.
391          */
setGroupOperatingFrequency(int frequency)392         public @NonNull Builder setGroupOperatingFrequency(int frequency) {
393             if (frequency < 0) {
394                 throw new IllegalArgumentException(
395                     "Invalid group operating frequency!");
396             }
397             mGroupOperatingFrequency = frequency;
398             return this;
399         }
400 
401         /**
402          * Specify that the group configuration be persisted (i.e. saved).
403          * By default the group configuration will not be saved.
404          * <p>
405          *     Optional. false by default.
406          *
407          * @param persistent is this group persistent group.
408          * @return The builder to facilitate chaining
409          *         {@code builder.setXXX(..).setXXX(..)}.
410          */
enablePersistentMode(boolean persistent)411         public @NonNull Builder enablePersistentMode(boolean persistent) {
412             if (persistent) {
413                 mNetId = WifiP2pGroup.PERSISTENT_NET_ID;
414             } else {
415                 mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
416             }
417             return this;
418         }
419 
420         /**
421          * Build {@link WifiP2pConfig} given the current requests made on the builder.
422          * @return {@link WifiP2pConfig} constructed based on builder method calls.
423          */
build()424         public @NonNull WifiP2pConfig build() {
425             if (TextUtils.isEmpty(mNetworkName)) {
426                 throw new IllegalStateException(
427                         "network name must be non-empty.");
428             }
429             if (TextUtils.isEmpty(mPassphrase)) {
430                 throw new IllegalStateException(
431                         "passphrase must be non-empty.");
432             }
433 
434             if (mGroupOperatingFrequency > 0 && mGroupOperatingBand > 0) {
435                 throw new IllegalStateException(
436                         "Preferred frequency and band are mutually exclusive.");
437             }
438 
439             WifiP2pConfig config = new WifiP2pConfig();
440             config.deviceAddress = mDeviceAddress.toString();
441             config.networkName = mNetworkName;
442             config.passphrase = mPassphrase;
443             config.groupOwnerBand = GROUP_OWNER_BAND_AUTO;
444             if (mGroupOperatingFrequency > 0) {
445                 config.groupOwnerBand = mGroupOperatingFrequency;
446             } else if (mGroupOperatingBand > 0) {
447                 config.groupOwnerBand = mGroupOperatingBand;
448             }
449             config.netId = mNetId;
450             return config;
451         }
452     }
453 }
454