• 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      * The WifiP2pConfig can be constructed for two use-cases:
273      * <ul>
274      * <li>SSID + Passphrase are known: use {@link #setNetworkName(String)} and
275      *   {@link #setPassphrase(String)}.</li>
276      * <li>SSID or Passphrase is unknown, in such a case the MAC address must be known and
277      *   specified using {@link #setDeviceAddress(MacAddress)}.</li>
278      * </ul>
279      */
280     public static final class Builder {
281 
282         private static final MacAddress MAC_ANY_ADDRESS =
283                 MacAddress.fromString("02:00:00:00:00:00");
284         /**
285          * Maximum number of bytes allowed for a SSID.
286          */
287         private static final int MAX_SSID_BYTES = 32;
288 
289         private MacAddress mDeviceAddress = MAC_ANY_ADDRESS;
290         private String mNetworkName = "";
291         private String mPassphrase = "";
292         private int mGroupOperatingBand = GROUP_OWNER_BAND_AUTO;
293         private int mGroupOperatingFrequency = GROUP_OWNER_BAND_AUTO;
294         private int mNetId = WifiP2pGroup.NETWORK_ID_TEMPORARY;
295 
296         /**
297          * Specify the peer's MAC address. If not set, the device will
298          * try to find a peer whose SSID matches the network name as
299          * specified by {@link #setNetworkName(String)}. Specifying null will
300          * reset the peer's MAC address to "02:00:00:00:00:00".
301          * <p>
302          *     Optional. "02:00:00:00:00:00" by default.
303          *
304          * <p> If the network name is not set, the peer's MAC address is mandatory.
305          *
306          * @param deviceAddress the peer's MAC address.
307          * @return The builder to facilitate chaining
308          *         {@code builder.setXXX(..).setXXX(..)}.
309          */
setDeviceAddress(@ullable MacAddress deviceAddress)310         public @NonNull Builder setDeviceAddress(@Nullable MacAddress deviceAddress) {
311             if (deviceAddress == null) {
312                 mDeviceAddress = MAC_ANY_ADDRESS;
313             } else {
314                 mDeviceAddress = deviceAddress;
315             }
316             return this;
317         }
318 
319         /**
320          * Specify the network name, a.k.a. group name,
321          * for creating or joining a group.
322          * <p>
323          * A network name shall begin with "DIRECT-xy". x and y are selected
324          * from the following character set: upper case letters, lower case
325          * letters and numbers. Any byte values allowed for an SSID according to
326          * IEEE802.11-2012 [1] may be included after the string "DIRECT-xy"
327          * (including none).
328          * <p>
329          *     Must be called - an empty network name or an network name
330          *     not conforming to the P2P Group ID naming rule is not valid.
331          *
332          * @param networkName network name of a group.
333          * @return The builder to facilitate chaining
334          *         {@code builder.setXXX(..).setXXX(..)}.
335          */
setNetworkName(@onNull String networkName)336         public @NonNull Builder setNetworkName(@NonNull String networkName) {
337             if (TextUtils.isEmpty(networkName)) {
338                 throw new IllegalArgumentException(
339                         "network name must be non-empty.");
340             }
341             if (networkName.getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) {
342                 throw new IllegalArgumentException(
343                         "network name exceeds " + MAX_SSID_BYTES + " bytes.");
344             }
345             try {
346                 if (!networkName.matches("^DIRECT-[a-zA-Z0-9]{2}.*")) {
347                     throw new IllegalArgumentException(
348                             "network name must starts with the prefix DIRECT-xy.");
349                 }
350             } catch (PatternSyntaxException e) {
351                 // can never happen (fixed pattern)
352             }
353             mNetworkName = networkName;
354             return this;
355         }
356 
357         /**
358          * Specify the passphrase for creating or joining a group.
359          * <p>
360          * The passphrase must be an ASCII string whose length is between 8
361          * and 63.
362          * <p>
363          *     Must be called - an empty passphrase is not valid.
364          *
365          * @param passphrase the passphrase of a group.
366          * @return The builder to facilitate chaining
367          *         {@code builder.setXXX(..).setXXX(..)}.
368          */
setPassphrase(@onNull String passphrase)369         public @NonNull Builder setPassphrase(@NonNull String passphrase) {
370             if (TextUtils.isEmpty(passphrase)) {
371                 throw new IllegalArgumentException(
372                         "passphrase must be non-empty.");
373             }
374             if (passphrase.length() < 8 || passphrase.length() > 63) {
375                 throw new IllegalArgumentException(
376                         "The length of a passphrase must be between 8 and 63.");
377             }
378             mPassphrase = passphrase;
379             return this;
380         }
381 
382         /**
383          * Specify the band to use for creating the group or joining the group. The band should
384          * be {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ} or
385          * {@link #GROUP_OWNER_BAND_AUTO}.
386          * <p>
387          * When creating a group as Group Owner using {@link
388          * WifiP2pManager#createGroup(WifiP2pManager.Channel,
389          * WifiP2pConfig, WifiP2pManager.ActionListener)},
390          * specifying {@link #GROUP_OWNER_BAND_AUTO} allows the system to pick the operating
391          * frequency from all supported bands.
392          * Specifying {@link #GROUP_OWNER_BAND_2GHZ} or {@link #GROUP_OWNER_BAND_5GHZ}
393          * only allows the system to pick the operating frequency in the specified band.
394          * If the Group Owner cannot create a group in the specified band, the operation will fail.
395          * <p>
396          * When joining a group as Group Client using {@link
397          * WifiP2pManager#connect(WifiP2pManager.Channel, WifiP2pConfig,
398          * WifiP2pManager.ActionListener)},
399          * specifying {@link #GROUP_OWNER_BAND_AUTO} allows the system to scan all supported
400          * frequencies to find the desired group. Specifying {@link #GROUP_OWNER_BAND_2GHZ} or
401          * {@link #GROUP_OWNER_BAND_5GHZ} only allows the system to scan the specified band.
402          * <p>
403          *     {@link #setGroupOperatingBand(int)} and {@link #setGroupOperatingFrequency(int)} are
404          *     mutually exclusive. Setting operating band and frequency both is invalid.
405          * <p>
406          *     Optional. {@link #GROUP_OWNER_BAND_AUTO} by default.
407          *
408          * @param band the operating band of the group.
409          *             This should be one of {@link #GROUP_OWNER_BAND_AUTO},
410          *             {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ}.
411          * @return The builder to facilitate chaining
412          *         {@code builder.setXXX(..).setXXX(..)}.
413          */
setGroupOperatingBand(@roupOperatingBandType int band)414         public @NonNull Builder setGroupOperatingBand(@GroupOperatingBandType int band) {
415             switch (band) {
416                 case GROUP_OWNER_BAND_AUTO:
417                 case GROUP_OWNER_BAND_2GHZ:
418                 case GROUP_OWNER_BAND_5GHZ:
419                     mGroupOperatingBand = band;
420                     break;
421                 default:
422                     throw new IllegalArgumentException(
423                         "Invalid constant for the group operating band!");
424             }
425             return this;
426         }
427 
428         /**
429          * Specify the frequency, in MHz, to use for creating the group or joining the group.
430          * <p>
431          * When creating a group as Group Owner using {@link WifiP2pManager#createGroup(
432          * WifiP2pManager.Channel, WifiP2pConfig, WifiP2pManager.ActionListener)},
433          * specifying a frequency only allows the system to pick the specified frequency.
434          * If the Group Owner cannot create a group at the specified frequency,
435          * the operation will fail.
436          * When not specifying a frequency, it allows the system to pick operating frequency
437          * from all supported bands.
438          * <p>
439          * When joining a group as Group Client using {@link WifiP2pManager#connect(
440          * WifiP2pManager.Channel, WifiP2pConfig, WifiP2pManager.ActionListener)},
441          * specifying a frequency only allows the system to scan the specified frequency.
442          * If the frequency is not supported or invalid, the operation will fail.
443          * When not specifying a frequency, it allows the system to scan all supported
444          * frequencies to find the desired group.
445          * <p>
446          *     {@link #setGroupOperatingBand(int)} and {@link #setGroupOperatingFrequency(int)} are
447          *     mutually exclusive. Setting operating band and frequency both is invalid.
448          * <p>
449          *     Optional. 0 by default.
450          *
451          * @param frequency the operating frequency of the group.
452          * @return The builder to facilitate chaining
453          *         {@code builder.setXXX(..).setXXX(..)}.
454          */
setGroupOperatingFrequency(int frequency)455         public @NonNull Builder setGroupOperatingFrequency(int frequency) {
456             if (frequency < 0) {
457                 throw new IllegalArgumentException(
458                     "Invalid group operating frequency!");
459             }
460             mGroupOperatingFrequency = frequency;
461             return this;
462         }
463 
464         /**
465          * Specify that the group configuration be persisted (i.e. saved).
466          * By default the group configuration will not be saved.
467          * <p>
468          *     Optional. false by default.
469          *
470          * @param persistent is this group persistent group.
471          * @return The builder to facilitate chaining
472          *         {@code builder.setXXX(..).setXXX(..)}.
473          */
enablePersistentMode(boolean persistent)474         public @NonNull Builder enablePersistentMode(boolean persistent) {
475             if (persistent) {
476                 mNetId = WifiP2pGroup.NETWORK_ID_PERSISTENT;
477             } else {
478                 mNetId = WifiP2pGroup.NETWORK_ID_TEMPORARY;
479             }
480             return this;
481         }
482 
483         /**
484          * Build {@link WifiP2pConfig} given the current requests made on the builder.
485          * @return {@link WifiP2pConfig} constructed based on builder method calls.
486          */
build()487         public @NonNull WifiP2pConfig build() {
488             if ((TextUtils.isEmpty(mNetworkName) && !TextUtils.isEmpty(mPassphrase))
489                     || (!TextUtils.isEmpty(mNetworkName) && TextUtils.isEmpty(mPassphrase))) {
490                 throw new IllegalStateException(
491                         "network name and passphrase must be non-empty or empty both.");
492             }
493             if (TextUtils.isEmpty(mNetworkName)
494                     && mDeviceAddress.equals(MAC_ANY_ADDRESS)) {
495                 throw new IllegalStateException(
496                         "peer address must be set if network name and pasphrase are not set.");
497             }
498 
499             if (mGroupOperatingFrequency > 0 && mGroupOperatingBand > 0) {
500                 throw new IllegalStateException(
501                         "Preferred frequency and band are mutually exclusive.");
502             }
503 
504             WifiP2pConfig config = new WifiP2pConfig();
505             config.deviceAddress = mDeviceAddress.toString();
506             config.networkName = mNetworkName;
507             config.passphrase = mPassphrase;
508             config.groupOwnerBand = GROUP_OWNER_BAND_AUTO;
509             if (mGroupOperatingFrequency > 0) {
510                 config.groupOwnerBand = mGroupOperatingFrequency;
511             } else if (mGroupOperatingBand > 0) {
512                 config.groupOwnerBand = mGroupOperatingBand;
513             }
514             config.netId = mNetId;
515             return config;
516         }
517     }
518 }
519