diff options
6 files changed, 257 insertions, 3 deletions
diff --git a/common/moduleutils/src/android/net/shared/ProvisioningConfiguration.java b/common/moduleutils/src/android/net/shared/ProvisioningConfiguration.java index 00b4e19..7de376a 100644 --- a/common/moduleutils/src/android/net/shared/ProvisioningConfiguration.java +++ b/common/moduleutils/src/android/net/shared/ProvisioningConfiguration.java @@ -16,14 +16,25 @@ package android.net.shared; +import static android.net.shared.ParcelableUtil.fromParcelableArray; +import static android.net.shared.ParcelableUtil.toParcelableArray; + +import android.annotation.NonNull; import android.annotation.Nullable; import android.net.INetd; +import android.net.InformationElementParcelable; import android.net.Network; import android.net.ProvisioningConfigurationParcelable; +import android.net.ScanResultInfoParcelable; import android.net.StaticIpConfiguration; import android.net.apf.ApfCapabilities; import android.net.ip.IIpClient; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.StringJoiner; @@ -193,6 +204,17 @@ public class ProvisioningConfiguration { } /** + * Specify the information elements included in wifi scan result that was obtained + * prior to connecting to the access point, if this is a WiFi network. + * + * <p>The scan result can be used to infer whether the network is metered. + */ + public Builder withScanResultInfo(ScanResultInfo scanResultInfo) { + mConfig.mScanResultInfo = scanResultInfo; + return this; + } + + /** * Build the configuration using previously specified parameters. */ public ProvisioningConfiguration build() { @@ -200,6 +222,158 @@ public class ProvisioningConfiguration { } } + /** + * Class wrapper of {@link android.net.wifi.ScanResult} to encapsulate the SSID and + * InformationElements fields of ScanResult. + */ + public static class ScanResultInfo { + private final String mSsid; + private final List<InformationElement> mInformationElements; + + /** + * Class wrapper of {@link android.net.wifi.ScanResult.InformationElement} to encapsulate + * the specific IE id and payload fields. + */ + public static class InformationElement { + private final int mId; + private final byte[] mPayload; + + public InformationElement(int id, @NonNull ByteBuffer payload) { + mId = id; + mPayload = convertToByteArray(payload.asReadOnlyBuffer()); + } + + /** + * Get the element ID of the information element. + */ + public int getId() { + return mId; + } + + /** + * Get the specific content of the information element. + */ + public ByteBuffer getPayload() { + return ByteBuffer.wrap(mPayload).asReadOnlyBuffer(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof InformationElement)) return false; + InformationElement other = (InformationElement) o; + return mId == other.mId && Arrays.equals(mPayload, other.mPayload); + } + + @Override + public int hashCode() { + return Objects.hash(mId, mPayload); + } + + @Override + public String toString() { + return "ID: " + mId + ", " + Arrays.toString(mPayload); + } + + /** + * Convert this InformationElement to a {@link InformationElementParcelable}. + */ + public InformationElementParcelable toStableParcelable() { + final InformationElementParcelable p = new InformationElementParcelable(); + p.id = mId; + p.payload = mPayload.clone(); + return p; + } + + /** + * Create an instance of {@link InformationElement} based on the contents of the + * specified {@link InformationElementParcelable}. + */ + public static InformationElement fromStableParcelable(InformationElementParcelable p) { + if (p == null) return null; + return new InformationElement(p.id, + ByteBuffer.wrap(p.payload.clone()).asReadOnlyBuffer()); + } + } + + public ScanResultInfo(String ssid, @NonNull List<InformationElement> informationElements) { + mSsid = ssid; + mInformationElements = + Collections.unmodifiableList(new ArrayList<>(informationElements)); + } + + /** + * Get the scanned network name. + */ + public String getSsid() { + return mSsid; + } + + /** + * Get all information elements found in the beacon. + */ + public List<InformationElement> getInformationElements() { + return mInformationElements; + } + + @Override + public String toString() { + StringBuffer str = new StringBuffer(); + str.append("SSID: ").append(mSsid); + str.append(", Information Elements: {"); + for (InformationElement ie : mInformationElements) { + str.append("[").append(ie.toString()).append("]"); + } + str.append("}"); + return str.toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof ScanResultInfo)) return false; + ScanResultInfo other = (ScanResultInfo) o; + return Objects.equals(mSsid, other.mSsid) + && mInformationElements.equals(other.mInformationElements); + } + + @Override + public int hashCode() { + return Objects.hash(mSsid, mInformationElements); + } + + /** + * Convert this ScanResultInfo to a {@link ScanResultInfoParcelable}. + */ + public ScanResultInfoParcelable toStableParcelable() { + final ScanResultInfoParcelable p = new ScanResultInfoParcelable(); + p.ssid = mSsid; + p.informationElements = toParcelableArray(mInformationElements, + InformationElement::toStableParcelable, InformationElementParcelable.class); + return p; + } + + /** + * Create an instance of {@link ScanResultInfo} based on the contents of the specified + * {@link ScanResultInfoParcelable}. + */ + public static ScanResultInfo fromStableParcelable(ScanResultInfoParcelable p) { + if (p == null) return null; + final List<InformationElement> ies = new ArrayList<InformationElement>(); + ies.addAll(fromParcelableArray(p.informationElements, + InformationElement::fromStableParcelable)); + return new ScanResultInfo(p.ssid, ies); + } + + private static byte[] convertToByteArray(final ByteBuffer buffer) { + if (buffer == null) return null; + byte[] bytes = new byte[buffer.limit()]; + final ByteBuffer copy = buffer.asReadOnlyBuffer(); + copy.get(bytes); + return bytes; + } + } + public boolean mEnableIPv4 = true; public boolean mEnableIPv6 = true; public boolean mEnablePreconnection = false; @@ -213,6 +387,7 @@ public class ProvisioningConfiguration { public int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; public Network mNetwork = null; public String mDisplayName = null; + public ScanResultInfo mScanResultInfo; public ProvisioningConfiguration() {} // used by Builder @@ -232,6 +407,7 @@ public class ProvisioningConfiguration { mIPv6AddrGenMode = other.mIPv6AddrGenMode; mNetwork = other.mNetwork; mDisplayName = other.mDisplayName; + mScanResultInfo = other.mScanResultInfo; } /** @@ -254,6 +430,7 @@ public class ProvisioningConfiguration { p.ipv6AddrGenMode = mIPv6AddrGenMode; p.network = mNetwork; p.displayName = mDisplayName; + p.scanResultInfo = mScanResultInfo == null ? null : mScanResultInfo.toStableParcelable(); return p; } @@ -279,6 +456,7 @@ public class ProvisioningConfiguration { config.mIPv6AddrGenMode = p.ipv6AddrGenMode; config.mNetwork = p.network; config.mDisplayName = p.displayName; + config.mScanResultInfo = ScanResultInfo.fromStableParcelable(p.scanResultInfo); return config; } @@ -298,6 +476,7 @@ public class ProvisioningConfiguration { .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode) .add("mNetwork: " + mNetwork) .add("mDisplayName: " + mDisplayName) + .add("mScanResultInfo: " + mScanResultInfo) .toString(); } @@ -317,7 +496,8 @@ public class ProvisioningConfiguration { && mProvisioningTimeoutMs == other.mProvisioningTimeoutMs && mIPv6AddrGenMode == other.mIPv6AddrGenMode && Objects.equals(mNetwork, other.mNetwork) - && Objects.equals(mDisplayName, other.mDisplayName); + && Objects.equals(mDisplayName, other.mDisplayName) + && Objects.equals(mScanResultInfo, other.mScanResultInfo); } public boolean isValid() { diff --git a/common/networkstackclient/Android.bp b/common/networkstackclient/Android.bp index 31f3384..dbe8ff0 100644 --- a/common/networkstackclient/Android.bp +++ b/common/networkstackclient/Android.bp @@ -45,6 +45,7 @@ aidl_interface { include_dirs: [ "frameworks/base/core/java", // For framework parcelables. "frameworks/native/aidl/binder/android/os", // For PersistableBundle.aidl + "frameworks/base/wifi/aidl-export", // For wifi parcelables. ], srcs: [ "src/android/net/DhcpResultsParcelable.aidl", @@ -53,10 +54,12 @@ aidl_interface { "src/android/net/INetworkStackConnector.aidl", "src/android/net/INetworkStackStatusCallback.aidl", "src/android/net/InitialConfigurationParcelable.aidl", + "src/android/net/InformationElementParcelable.aidl", "src/android/net/Layer2PacketParcelable.aidl", "src/android/net/NattKeepalivePacketDataParcelable.aidl", "src/android/net/PrivateDnsConfigParcel.aidl", "src/android/net/ProvisioningConfigurationParcelable.aidl", + "src/android/net/ScanResultInfoParcelable.aidl", "src/android/net/TcpKeepalivePacketDataParcelable.aidl", "src/android/net/dhcp/DhcpServingParamsParcel.aidl", "src/android/net/dhcp/IDhcpServer.aidl", diff --git a/common/networkstackclient/src/android/net/InformationElementParcelable.aidl b/common/networkstackclient/src/android/net/InformationElementParcelable.aidl new file mode 100644 index 0000000..c70bf6f --- /dev/null +++ b/common/networkstackclient/src/android/net/InformationElementParcelable.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +parcelable InformationElementParcelable { + int id; + byte[] payload; +} diff --git a/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl b/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl index 0b6d7d5..9fcb036 100644 --- a/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl +++ b/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl @@ -19,6 +19,7 @@ package android.net; import android.net.InitialConfigurationParcelable; import android.net.Network; +import android.net.ScanResultInfoParcelable; import android.net.StaticIpConfiguration; import android.net.apf.ApfCapabilities; @@ -36,4 +37,5 @@ parcelable ProvisioningConfigurationParcelable { Network network; String displayName; boolean enablePreconnection; + ScanResultInfoParcelable scanResultInfo; } diff --git a/common/networkstackclient/src/android/net/ScanResultInfoParcelable.aidl b/common/networkstackclient/src/android/net/ScanResultInfoParcelable.aidl new file mode 100644 index 0000000..f5f101d --- /dev/null +++ b/common/networkstackclient/src/android/net/ScanResultInfoParcelable.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.net.InformationElementParcelable; + +parcelable ScanResultInfoParcelable { + String ssid; + InformationElementParcelable[] informationElements; +} diff --git a/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java b/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java index e645a2c..e9384c8 100644 --- a/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java +++ b/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java @@ -28,6 +28,7 @@ import android.net.LinkAddress; import android.net.Network; import android.net.StaticIpConfiguration; import android.net.apf.ApfCapabilities; +import android.net.shared.ProvisioningConfiguration.ScanResultInfo; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -36,6 +37,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.nio.ByteBuffer; +import java.util.Collections; import java.util.function.Consumer; /** @@ -46,6 +49,17 @@ import java.util.function.Consumer; public class ProvisioningConfigurationTest { private ProvisioningConfiguration mConfig; + private ScanResultInfo makeScanResultInfo(final String ssid) { + final byte[] payload = new byte[] { + (byte) 0x00, (byte) 0x17, (byte) 0xF2, (byte) 0x06, (byte) 0x01, + (byte) 0x01, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x00, + }; + final ScanResultInfo.InformationElement ie = + new ScanResultInfo.InformationElement(0xdd /* vendor specific IE id */, + ByteBuffer.wrap(payload)); + return new ScanResultInfo(ssid, Collections.singletonList(ie)); + } + @Before public void setUp() { mConfig = new ProvisioningConfiguration(); @@ -67,8 +81,9 @@ public class ProvisioningConfigurationTest { mConfig.mNetwork = new Network(321); mConfig.mDisplayName = "test_config"; mConfig.mEnablePreconnection = false; + mConfig.mScanResultInfo = makeScanResultInfo("ssid"); // Any added field must be included in equals() to be tested properly - assertFieldCountEquals(13, ProvisioningConfiguration.class); + assertFieldCountEquals(14, ProvisioningConfiguration.class); } @Test @@ -101,6 +116,12 @@ public class ProvisioningConfigurationTest { } @Test + public void testParcelUnparcel_NullScanResultInfo() { + mConfig.mScanResultInfo = null; + doParcelUnparcelTest(); + } + + @Test public void testParcelUnparcel_WithPreDhcpConnection() { mConfig.mEnablePreconnection = true; doParcelUnparcelTest(); @@ -136,7 +157,9 @@ public class ProvisioningConfigurationTest { assertNotEqualsAfterChange(c -> c.mDisplayName = "other_test"); assertNotEqualsAfterChange(c -> c.mDisplayName = null); assertNotEqualsAfterChange(c -> c.mEnablePreconnection = true); - assertFieldCountEquals(13, ProvisioningConfiguration.class); + assertNotEqualsAfterChange(c -> c.mScanResultInfo = null); + assertNotEqualsAfterChange(c -> c.mScanResultInfo = makeScanResultInfo("another ssid")); + assertFieldCountEquals(14, ProvisioningConfiguration.class); } private void assertNotEqualsAfterChange(Consumer<ProvisioningConfiguration> mutator) { |