1 /* 2 * Copyright (C) 2019 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.shared; 18 19 import static android.net.shared.ParcelableUtil.fromParcelableArray; 20 import static android.net.shared.ParcelableUtil.toParcelableArray; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.net.INetd; 25 import android.net.InformationElementParcelable; 26 import android.net.Network; 27 import android.net.ProvisioningConfigurationParcelable; 28 import android.net.ScanResultInfoParcelable; 29 import android.net.StaticIpConfiguration; 30 import android.net.apf.ApfCapabilities; 31 import android.net.ip.IIpClient; 32 import android.util.Log; 33 34 import java.nio.BufferUnderflowException; 35 import java.nio.ByteBuffer; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.Collections; 39 import java.util.List; 40 import java.util.Objects; 41 import java.util.StringJoiner; 42 43 /** 44 * This class encapsulates parameters to be passed to 45 * IpClient#startProvisioning(). A defensive copy is made by IpClient 46 * and the values specified herein are in force until IpClient#stop() 47 * is called. 48 * 49 * Example use: 50 * 51 * final ProvisioningConfiguration config = 52 * new ProvisioningConfiguration.Builder() 53 * .withPreDhcpAction() 54 * .withProvisioningTimeoutMs(36 * 1000) 55 * .build(); 56 * mIpClient.startProvisioning(config.toStableParcelable()); 57 * ... 58 * mIpClient.stop(); 59 * 60 * The specified provisioning configuration will only be active until 61 * IIpClient#stop() is called. Future calls to IIpClient#startProvisioning() 62 * must specify the configuration again. 63 * @hide 64 */ 65 public class ProvisioningConfiguration { 66 private static final String TAG = "ProvisioningConfiguration"; 67 68 // TODO: Delete this default timeout once those callers that care are 69 // fixed to pass in their preferred timeout. 70 // 71 // We pick 18 seconds so we can send DHCP requests at 72 // 73 // t=0, t=1, t=3, t=7, t=16 74 // 75 // allowing for 10% jitter. 76 private static final int DEFAULT_TIMEOUT_MS = 18 * 1000; 77 78 /** 79 * Builder to create a {@link ProvisioningConfiguration}. 80 */ 81 public static class Builder { 82 protected ProvisioningConfiguration mConfig = new ProvisioningConfiguration(); 83 84 /** 85 * Specify that the configuration should not enable IPv4. It is enabled by default. 86 */ withoutIPv4()87 public Builder withoutIPv4() { 88 mConfig.mEnableIPv4 = false; 89 return this; 90 } 91 92 /** 93 * Specify that the configuration should not enable IPv6. It is enabled by default. 94 */ withoutIPv6()95 public Builder withoutIPv6() { 96 mConfig.mEnableIPv6 = false; 97 return this; 98 } 99 100 /** 101 * Specify that the configuration should not use a MultinetworkPolicyTracker. It is used 102 * by default. 103 */ withoutMultinetworkPolicyTracker()104 public Builder withoutMultinetworkPolicyTracker() { 105 mConfig.mUsingMultinetworkPolicyTracker = false; 106 return this; 107 } 108 109 /** 110 * Specify that the configuration should not use a IpReachabilityMonitor. It is used by 111 * default. 112 */ withoutIpReachabilityMonitor()113 public Builder withoutIpReachabilityMonitor() { 114 mConfig.mUsingIpReachabilityMonitor = false; 115 return this; 116 } 117 118 /** 119 * Identical to {@link #withPreDhcpAction(int)}, using a default timeout. 120 * @see #withPreDhcpAction(int) 121 */ withPreDhcpAction()122 public Builder withPreDhcpAction() { 123 mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS; 124 return this; 125 } 126 127 /** 128 * Specify that {@link IpClientCallbacks#onPreDhcpAction()} should be called. Clients must 129 * call {@link IIpClient#completedPreDhcpAction()} when the callback called. This behavior 130 * is disabled by default. 131 * @param dhcpActionTimeoutMs Timeout for clients to call completedPreDhcpAction(). 132 */ withPreDhcpAction(int dhcpActionTimeoutMs)133 public Builder withPreDhcpAction(int dhcpActionTimeoutMs) { 134 mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs; 135 return this; 136 } 137 138 /** 139 * Specify that preconnection feature would be enabled. It's not used by default. 140 */ withPreconnection()141 public Builder withPreconnection() { 142 mConfig.mEnablePreconnection = true; 143 return this; 144 } 145 146 /** 147 * Specify the initial provisioning configuration. 148 */ withInitialConfiguration(InitialConfiguration initialConfig)149 public Builder withInitialConfiguration(InitialConfiguration initialConfig) { 150 mConfig.mInitialConfig = initialConfig; 151 return this; 152 } 153 154 /** 155 * Specify a static configuration for provisioning. 156 */ withStaticConfiguration(StaticIpConfiguration staticConfig)157 public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) { 158 mConfig.mStaticIpConfig = staticConfig; 159 return this; 160 } 161 162 /** 163 * Specify ApfCapabilities. 164 */ withApfCapabilities(ApfCapabilities apfCapabilities)165 public Builder withApfCapabilities(ApfCapabilities apfCapabilities) { 166 mConfig.mApfCapabilities = apfCapabilities; 167 return this; 168 } 169 170 /** 171 * Specify the timeout to use for provisioning. 172 */ withProvisioningTimeoutMs(int timeoutMs)173 public Builder withProvisioningTimeoutMs(int timeoutMs) { 174 mConfig.mProvisioningTimeoutMs = timeoutMs; 175 return this; 176 } 177 178 /** 179 * Specify that IPv6 address generation should use a random MAC address. 180 */ withRandomMacAddress()181 public Builder withRandomMacAddress() { 182 mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_EUI64; 183 return this; 184 } 185 186 /** 187 * Specify that IPv6 address generation should use a stable MAC address. 188 */ withStableMacAddress()189 public Builder withStableMacAddress() { 190 mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; 191 return this; 192 } 193 194 /** 195 * Specify the network to use for provisioning. 196 */ withNetwork(Network network)197 public Builder withNetwork(Network network) { 198 mConfig.mNetwork = network; 199 return this; 200 } 201 202 /** 203 * Specify the display name that the IpClient should use. 204 */ withDisplayName(String displayName)205 public Builder withDisplayName(String displayName) { 206 mConfig.mDisplayName = displayName; 207 return this; 208 } 209 210 /** 211 * Specify the information elements included in wifi scan result that was obtained 212 * prior to connecting to the access point, if this is a WiFi network. 213 * 214 * <p>The scan result can be used to infer whether the network is metered. 215 */ withScanResultInfo(ScanResultInfo scanResultInfo)216 public Builder withScanResultInfo(ScanResultInfo scanResultInfo) { 217 mConfig.mScanResultInfo = scanResultInfo; 218 return this; 219 } 220 221 /** 222 * Specify the L2 information(bssid, l2key and cluster) that the IpClient should use. 223 */ withLayer2Information(Layer2Information layer2Info)224 public Builder withLayer2Information(Layer2Information layer2Info) { 225 mConfig.mLayer2Info = layer2Info; 226 return this; 227 } 228 229 /** 230 * Build the configuration using previously specified parameters. 231 */ build()232 public ProvisioningConfiguration build() { 233 return new ProvisioningConfiguration(mConfig); 234 } 235 } 236 237 /** 238 * Class wrapper of {@link android.net.wifi.ScanResult} to encapsulate the SSID and 239 * InformationElements fields of ScanResult. 240 */ 241 public static class ScanResultInfo { 242 @NonNull 243 private final String mSsid; 244 @NonNull 245 private final String mBssid; 246 @NonNull 247 private final List<InformationElement> mInformationElements; 248 249 /** 250 * Class wrapper of {@link android.net.wifi.ScanResult.InformationElement} to encapsulate 251 * the specific IE id and payload fields. 252 */ 253 public static class InformationElement { 254 private final int mId; 255 @NonNull 256 private final byte[] mPayload; 257 InformationElement(int id, @NonNull ByteBuffer payload)258 public InformationElement(int id, @NonNull ByteBuffer payload) { 259 mId = id; 260 mPayload = convertToByteArray(payload.asReadOnlyBuffer()); 261 } 262 263 /** 264 * Get the element ID of the information element. 265 */ getId()266 public int getId() { 267 return mId; 268 } 269 270 /** 271 * Get the specific content of the information element. 272 */ 273 @NonNull getPayload()274 public ByteBuffer getPayload() { 275 return ByteBuffer.wrap(mPayload).asReadOnlyBuffer(); 276 } 277 278 @Override equals(Object o)279 public boolean equals(Object o) { 280 if (o == this) return true; 281 if (!(o instanceof InformationElement)) return false; 282 InformationElement other = (InformationElement) o; 283 return mId == other.mId && Arrays.equals(mPayload, other.mPayload); 284 } 285 286 @Override hashCode()287 public int hashCode() { 288 return Objects.hash(mId, mPayload); 289 } 290 291 @Override toString()292 public String toString() { 293 return "ID: " + mId + ", " + Arrays.toString(mPayload); 294 } 295 296 /** 297 * Convert this InformationElement to a {@link InformationElementParcelable}. 298 */ toStableParcelable()299 public InformationElementParcelable toStableParcelable() { 300 final InformationElementParcelable p = new InformationElementParcelable(); 301 p.id = mId; 302 p.payload = mPayload != null ? mPayload.clone() : null; 303 return p; 304 } 305 306 /** 307 * Create an instance of {@link InformationElement} based on the contents of the 308 * specified {@link InformationElementParcelable}. 309 */ 310 @Nullable fromStableParcelable(InformationElementParcelable p)311 public static InformationElement fromStableParcelable(InformationElementParcelable p) { 312 if (p == null) return null; 313 return new InformationElement(p.id, 314 ByteBuffer.wrap(p.payload.clone()).asReadOnlyBuffer()); 315 } 316 } 317 ScanResultInfo(@onNull String ssid, @NonNull String bssid, @NonNull List<InformationElement> informationElements)318 public ScanResultInfo(@NonNull String ssid, @NonNull String bssid, 319 @NonNull List<InformationElement> informationElements) { 320 Objects.requireNonNull(ssid, "ssid must not be null."); 321 Objects.requireNonNull(bssid, "bssid must not be null."); 322 mSsid = ssid; 323 mBssid = bssid; 324 mInformationElements = 325 Collections.unmodifiableList(new ArrayList<>(informationElements)); 326 } 327 328 /** 329 * Get the scanned network name. 330 */ 331 @NonNull getSsid()332 public String getSsid() { 333 return mSsid; 334 } 335 336 /** 337 * Get the address of the access point. 338 */ 339 @NonNull getBssid()340 public String getBssid() { 341 return mBssid; 342 } 343 344 /** 345 * Get all information elements found in the beacon. 346 */ 347 @NonNull getInformationElements()348 public List<InformationElement> getInformationElements() { 349 return mInformationElements; 350 } 351 352 @Override toString()353 public String toString() { 354 StringBuffer str = new StringBuffer(); 355 str.append("SSID: ").append(mSsid); 356 str.append(", BSSID: ").append(mBssid); 357 str.append(", Information Elements: {"); 358 for (InformationElement ie : mInformationElements) { 359 str.append("[").append(ie.toString()).append("]"); 360 } 361 str.append("}"); 362 return str.toString(); 363 } 364 365 @Override equals(Object o)366 public boolean equals(Object o) { 367 if (o == this) return true; 368 if (!(o instanceof ScanResultInfo)) return false; 369 ScanResultInfo other = (ScanResultInfo) o; 370 return Objects.equals(mSsid, other.mSsid) 371 && Objects.equals(mBssid, other.mBssid) 372 && mInformationElements.equals(other.mInformationElements); 373 } 374 375 @Override hashCode()376 public int hashCode() { 377 return Objects.hash(mSsid, mBssid, mInformationElements); 378 } 379 380 /** 381 * Convert this ScanResultInfo to a {@link ScanResultInfoParcelable}. 382 */ toStableParcelable()383 public ScanResultInfoParcelable toStableParcelable() { 384 final ScanResultInfoParcelable p = new ScanResultInfoParcelable(); 385 p.ssid = mSsid; 386 p.bssid = mBssid; 387 p.informationElements = toParcelableArray(mInformationElements, 388 InformationElement::toStableParcelable, InformationElementParcelable.class); 389 return p; 390 } 391 392 /** 393 * Create an instance of {@link ScanResultInfo} based on the contents of the specified 394 * {@link ScanResultInfoParcelable}. 395 */ fromStableParcelable(ScanResultInfoParcelable p)396 public static ScanResultInfo fromStableParcelable(ScanResultInfoParcelable p) { 397 if (p == null) return null; 398 final List<InformationElement> ies = new ArrayList<InformationElement>(); 399 ies.addAll(fromParcelableArray(p.informationElements, 400 InformationElement::fromStableParcelable)); 401 return new ScanResultInfo(p.ssid, p.bssid, ies); 402 } 403 convertToByteArray(@onNull final ByteBuffer buffer)404 private static byte[] convertToByteArray(@NonNull final ByteBuffer buffer) { 405 final byte[] bytes = new byte[buffer.limit()]; 406 final ByteBuffer copy = buffer.asReadOnlyBuffer(); 407 try { 408 copy.position(0); 409 copy.get(bytes); 410 } catch (BufferUnderflowException e) { 411 Log.wtf(TAG, "Buffer under flow exception should never happen."); 412 } finally { 413 return bytes; 414 } 415 } 416 } 417 418 public boolean mEnableIPv4 = true; 419 public boolean mEnableIPv6 = true; 420 public boolean mEnablePreconnection = false; 421 public boolean mUsingMultinetworkPolicyTracker = true; 422 public boolean mUsingIpReachabilityMonitor = true; 423 public int mRequestedPreDhcpActionMs; 424 public InitialConfiguration mInitialConfig; 425 public StaticIpConfiguration mStaticIpConfig; 426 public ApfCapabilities mApfCapabilities; 427 public int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS; 428 public int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; 429 public Network mNetwork = null; 430 public String mDisplayName = null; 431 public ScanResultInfo mScanResultInfo; 432 public Layer2Information mLayer2Info; 433 ProvisioningConfiguration()434 public ProvisioningConfiguration() {} // used by Builder 435 ProvisioningConfiguration(ProvisioningConfiguration other)436 public ProvisioningConfiguration(ProvisioningConfiguration other) { 437 mEnableIPv4 = other.mEnableIPv4; 438 mEnableIPv6 = other.mEnableIPv6; 439 mEnablePreconnection = other.mEnablePreconnection; 440 mUsingMultinetworkPolicyTracker = other.mUsingMultinetworkPolicyTracker; 441 mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor; 442 mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs; 443 mInitialConfig = InitialConfiguration.copy(other.mInitialConfig); 444 mStaticIpConfig = other.mStaticIpConfig == null 445 ? null 446 : new StaticIpConfiguration(other.mStaticIpConfig); 447 mApfCapabilities = other.mApfCapabilities; 448 mProvisioningTimeoutMs = other.mProvisioningTimeoutMs; 449 mIPv6AddrGenMode = other.mIPv6AddrGenMode; 450 mNetwork = other.mNetwork; 451 mDisplayName = other.mDisplayName; 452 mScanResultInfo = other.mScanResultInfo; 453 mLayer2Info = other.mLayer2Info; 454 } 455 456 /** 457 * Create a ProvisioningConfigurationParcelable from this ProvisioningConfiguration. 458 */ toStableParcelable()459 public ProvisioningConfigurationParcelable toStableParcelable() { 460 final ProvisioningConfigurationParcelable p = new ProvisioningConfigurationParcelable(); 461 p.enableIPv4 = mEnableIPv4; 462 p.enableIPv6 = mEnableIPv6; 463 p.enablePreconnection = mEnablePreconnection; 464 p.usingMultinetworkPolicyTracker = mUsingMultinetworkPolicyTracker; 465 p.usingIpReachabilityMonitor = mUsingIpReachabilityMonitor; 466 p.requestedPreDhcpActionMs = mRequestedPreDhcpActionMs; 467 p.initialConfig = mInitialConfig == null ? null : mInitialConfig.toStableParcelable(); 468 p.staticIpConfig = mStaticIpConfig == null 469 ? null 470 : new StaticIpConfiguration(mStaticIpConfig); 471 p.apfCapabilities = mApfCapabilities; // ApfCapabilities is immutable 472 p.provisioningTimeoutMs = mProvisioningTimeoutMs; 473 p.ipv6AddrGenMode = mIPv6AddrGenMode; 474 p.network = mNetwork; 475 p.displayName = mDisplayName; 476 p.scanResultInfo = mScanResultInfo == null ? null : mScanResultInfo.toStableParcelable(); 477 p.layer2Info = mLayer2Info == null ? null : mLayer2Info.toStableParcelable(); 478 return p; 479 } 480 481 /** 482 * Create a ProvisioningConfiguration from a ProvisioningConfigurationParcelable. 483 */ fromStableParcelable( @ullable ProvisioningConfigurationParcelable p)484 public static ProvisioningConfiguration fromStableParcelable( 485 @Nullable ProvisioningConfigurationParcelable p) { 486 if (p == null) return null; 487 final ProvisioningConfiguration config = new ProvisioningConfiguration(); 488 config.mEnableIPv4 = p.enableIPv4; 489 config.mEnableIPv6 = p.enableIPv6; 490 config.mEnablePreconnection = p.enablePreconnection; 491 config.mUsingMultinetworkPolicyTracker = p.usingMultinetworkPolicyTracker; 492 config.mUsingIpReachabilityMonitor = p.usingIpReachabilityMonitor; 493 config.mRequestedPreDhcpActionMs = p.requestedPreDhcpActionMs; 494 config.mInitialConfig = InitialConfiguration.fromStableParcelable(p.initialConfig); 495 config.mStaticIpConfig = p.staticIpConfig == null 496 ? null 497 : new StaticIpConfiguration(p.staticIpConfig); 498 config.mApfCapabilities = p.apfCapabilities; // ApfCapabilities is immutable 499 config.mProvisioningTimeoutMs = p.provisioningTimeoutMs; 500 config.mIPv6AddrGenMode = p.ipv6AddrGenMode; 501 config.mNetwork = p.network; 502 config.mDisplayName = p.displayName; 503 config.mScanResultInfo = ScanResultInfo.fromStableParcelable(p.scanResultInfo); 504 config.mLayer2Info = Layer2Information.fromStableParcelable(p.layer2Info); 505 return config; 506 } 507 508 @Override toString()509 public String toString() { 510 return new StringJoiner(", ", getClass().getSimpleName() + "{", "}") 511 .add("mEnableIPv4: " + mEnableIPv4) 512 .add("mEnableIPv6: " + mEnableIPv6) 513 .add("mEnablePreconnection: " + mEnablePreconnection) 514 .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker) 515 .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor) 516 .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs) 517 .add("mInitialConfig: " + mInitialConfig) 518 .add("mStaticIpConfig: " + mStaticIpConfig) 519 .add("mApfCapabilities: " + mApfCapabilities) 520 .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs) 521 .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode) 522 .add("mNetwork: " + mNetwork) 523 .add("mDisplayName: " + mDisplayName) 524 .add("mScanResultInfo: " + mScanResultInfo) 525 .add("mLayer2Info: " + mLayer2Info) 526 .toString(); 527 } 528 529 @Override equals(Object obj)530 public boolean equals(Object obj) { 531 if (!(obj instanceof ProvisioningConfiguration)) return false; 532 final ProvisioningConfiguration other = (ProvisioningConfiguration) obj; 533 return mEnableIPv4 == other.mEnableIPv4 534 && mEnableIPv6 == other.mEnableIPv6 535 && mEnablePreconnection == other.mEnablePreconnection 536 && mUsingMultinetworkPolicyTracker == other.mUsingMultinetworkPolicyTracker 537 && mUsingIpReachabilityMonitor == other.mUsingIpReachabilityMonitor 538 && mRequestedPreDhcpActionMs == other.mRequestedPreDhcpActionMs 539 && Objects.equals(mInitialConfig, other.mInitialConfig) 540 && Objects.equals(mStaticIpConfig, other.mStaticIpConfig) 541 && Objects.equals(mApfCapabilities, other.mApfCapabilities) 542 && mProvisioningTimeoutMs == other.mProvisioningTimeoutMs 543 && mIPv6AddrGenMode == other.mIPv6AddrGenMode 544 && Objects.equals(mNetwork, other.mNetwork) 545 && Objects.equals(mDisplayName, other.mDisplayName) 546 && Objects.equals(mScanResultInfo, other.mScanResultInfo) 547 && Objects.equals(mLayer2Info, other.mLayer2Info); 548 } 549 isValid()550 public boolean isValid() { 551 return (mInitialConfig == null) || mInitialConfig.isValid(); 552 } 553 } 554