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