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