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