From 819e1a749c815f765b81949643fc52852470a855 Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Wed, 10 Oct 2018 09:40:09 -0700 Subject: [PATCH 1/5] wifi(API): New API surface for connection via NetworkRequest Adds a new builder class build a NetworkSpecifier that can be used along with a standard ConnectivityManager.requestNetwork() to request connection to a specific wifi network. To avoid referencing any params from WifiConfiguration class (which is going to be deprecated), copy over all public non-deprecated constants to the new builder class. Note: Most of these fields will be needed for |NetworkSuggestion| as well, hence moving it to this common builder. Since all the internal modules of wifi stack use WifiConfiguration, WifiNetworkSpecifier embeds a standard WifiConfiguration field to hold the relevant network credentials. There are 2 fields added in WifiNetworkSpecifier to allow apps to specify a SSID/BSSID pattern to match. Bug: 113878056 Test: Unit tests Test: `make api-stubs-docs-update-current-api` Change-Id: I9c8919deb23024123f2b69c6fd705092976a1acf --- api/current.txt | 12 + .../net/wifi/WifiNetworkConfigBuilder.java | 372 ++++++++++++++++++ .../net/wifi/WifiNetworkSpecifier.java | 178 +++++++++ .../wifi/WifiNetworkConfigBuilderTest.java | 300 ++++++++++++++ .../net/wifi/WifiNetworkSpecifierTest.java | 212 ++++++++++ 5 files changed, 1074 insertions(+) create mode 100644 wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java create mode 100644 wifi/java/android/net/wifi/WifiNetworkSpecifier.java create mode 100644 wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java create mode 100644 wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java diff --git a/api/current.txt b/api/current.txt index f27e83cc90335..953859fbc2dd3 100755 --- a/api/current.txt +++ b/api/current.txt @@ -28863,6 +28863,18 @@ package android.net.wifi { method public abstract deprecated void onSucceeded(); } + public class WifiNetworkConfigBuilder { + ctor public WifiNetworkConfigBuilder(); + method public android.net.NetworkSpecifier buildNetworkSpecifier(); + method public android.net.wifi.WifiNetworkConfigBuilder setBssid(android.net.MacAddress); + method public android.net.wifi.WifiNetworkConfigBuilder setBssidPattern(android.net.MacAddress, android.net.MacAddress); + method public android.net.wifi.WifiNetworkConfigBuilder setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig); + method public android.net.wifi.WifiNetworkConfigBuilder setIsHiddenSsid(); + method public android.net.wifi.WifiNetworkConfigBuilder setPskPassphrase(java.lang.String); + method public android.net.wifi.WifiNetworkConfigBuilder setSsid(java.lang.String); + method public android.net.wifi.WifiNetworkConfigBuilder setSsidPattern(android.os.PatternMatcher); + } + public deprecated class WpsInfo implements android.os.Parcelable { ctor public deprecated WpsInfo(); ctor public deprecated WpsInfo(android.net.wifi.WpsInfo); diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java new file mode 100644 index 0000000000000..ed15e07c7a811 --- /dev/null +++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2018 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.wifi; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.MacAddress; +import android.net.NetworkRequest; +import android.net.NetworkSpecifier; +import android.os.PatternMatcher; +import android.os.Process; +import android.text.TextUtils; +import android.util.Pair; + +import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; + +/** + * WifiNetworkConfigBuilder to use for creating Wi-Fi network configuration. + *
  • See {@link #buildNetworkSpecifier()} for creating a network specifier to use in + * {@link NetworkRequest}.
  • + */ +public class WifiNetworkConfigBuilder { + private static final String MATCH_ALL_SSID_PATTERN_PATH = ".*"; + private static final String MATCH_EMPTY_SSID_PATTERN_PATH = ""; + private static final Pair MATCH_NO_BSSID_PATTERN = + new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS); + private static final Pair MATCH_ALL_BSSID_PATTERN = + new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS); + private static final MacAddress MATCH_EXACT_BSSID_PATTERN_MASK = + MacAddress.BROADCAST_ADDRESS; + + /** + * SSID pattern match specified by the app. + */ + private @Nullable PatternMatcher mSsidPatternMatcher; + /** + * BSSID pattern match specified by the app. + * Pair of . + */ + private @Nullable Pair mBssidPatternMatcher; + /** + * Pre-shared key for use with WPA-PSK networks. + */ + private @Nullable String mPskPassphrase; + /** + * The enterprise configuration details specifying the EAP method, + * certificates and other settings associated with the EAP. + */ + private @Nullable WifiEnterpriseConfig mEnterpriseConfig; + /** + * This is a network that does not broadcast its SSID, so an + * SSID-specific probe request must be used for scans. + */ + private boolean mIsHiddenSSID; + + public WifiNetworkConfigBuilder() { + mSsidPatternMatcher = null; + mBssidPatternMatcher = null; + mPskPassphrase = null; + mEnterpriseConfig = null; + mIsHiddenSSID = false; + } + + /** + * Set the unicode SSID match pattern to use for filtering networks from scan results. + *

    + *

  • Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}.
  • + *
  • Overrides any previous value set using {@link #setSsid(String)} or + * {@link #setSsidPattern(PatternMatcher)}.
  • + * + * @param ssidPattern Instance of {@link PatternMatcher} containing the UTF-8 encoded + * string pattern to use for matching the network's SSID. + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + */ + public WifiNetworkConfigBuilder setSsidPattern(@NonNull PatternMatcher ssidPattern) { + checkNotNull(ssidPattern); + mSsidPatternMatcher = ssidPattern; + return this; + } + + /** + * Set the unicode SSID for the network. + *

    + *

  • For network requests ({@link NetworkSpecifier}), built using + * {@link #buildNetworkSpecifier}, sets the SSID to use for filtering networks from scan + * results. Will only match networks whose SSID is identical to the UTF-8 encoding of the + * specified value.
  • + *
  • Overrides any previous value set using {@link #setSsid(String)} or + * {@link #setSsidPattern(PatternMatcher)}.
  • + * + * @param ssid The SSID of the network. It must be valid Unicode. + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + * @throws IllegalArgumentException if the SSID is not valid unicode. + */ + public WifiNetworkConfigBuilder setSsid(@NonNull String ssid) { + checkNotNull(ssid); + final CharsetEncoder unicodeEncoder = StandardCharsets.UTF_8.newEncoder(); + if (!unicodeEncoder.canEncode(ssid)) { + throw new IllegalArgumentException("SSID is not a valid unicode string"); + } + mSsidPatternMatcher = new PatternMatcher(ssid, PatternMatcher.PATTERN_LITERAL); + return this; + } + + /** + * Set the BSSID match pattern to use for filtering networks from scan results. + * Will match all networks with BSSID which satisfies the following: + * {@code BSSID & mask == baseAddress}. + *

    + *

  • Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}.
  • + *
  • Overrides any previous value set using {@link #setBssid(MacAddress)} or + * {@link #setBssidPattern(MacAddress, MacAddress)}.
  • + * + * @param baseAddress Base address for BSSID pattern. + * @param mask Mask for BSSID pattern. + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + */ + public WifiNetworkConfigBuilder setBssidPattern( + @NonNull MacAddress baseAddress, @NonNull MacAddress mask) { + checkNotNull(baseAddress, mask); + mBssidPatternMatcher = Pair.create(baseAddress, mask); + return this; + } + + /** + * Set the BSSID to use for filtering networks from scan results. Will only match network whose + * BSSID is identical to the specified value. + *

    + *

  • Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}.
  • + *
  • Overrides any previous value set using {@link #setBssid(MacAddress)} or + * {@link #setBssidPattern(MacAddress, MacAddress)}.
  • + * + * @param bssid BSSID of the network. + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + */ + public WifiNetworkConfigBuilder setBssid(@NonNull MacAddress bssid) { + checkNotNull(bssid); + mBssidPatternMatcher = Pair.create(bssid, MATCH_EXACT_BSSID_PATTERN_MASK); + return this; + } + + /** + * Set the ASCII PSK passphrase for this network. Needed for authenticating to + * WPA_PSK networks. + * + * @param pskPassphrase PSK passphrase of the network. + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + * @throws IllegalArgumentException if the passphrase is not ASCII encodable. + */ + public WifiNetworkConfigBuilder setPskPassphrase(@NonNull String pskPassphrase) { + checkNotNull(pskPassphrase); + final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); + if (!asciiEncoder.canEncode(pskPassphrase)) { + throw new IllegalArgumentException("passphrase not ASCII encodable"); + } + mPskPassphrase = pskPassphrase; + return this; + } + + /** + * Set the associated enterprise configuration for this network. Needed for authenticating to + * WPA_EAP networks. See {@link WifiEnterpriseConfig} for description. + * + * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}. + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + */ + public WifiNetworkConfigBuilder setEnterpriseConfig( + @NonNull WifiEnterpriseConfig enterpriseConfig) { + checkNotNull(enterpriseConfig); + mEnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig); + return this; + } + + /** + * Whether this represents a hidden network. + *

    + *

  • For network requests (see {@link NetworkSpecifier}), built using + * {@link #buildNetworkSpecifier}, setting this disallows the usage of + * {@link #setSsidPattern(PatternMatcher)} since hidden networks need to be explicitly + * probed for.
  • + *
  • If not set, defaults to false (i.e not a hidden network).
  • + * + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + */ + public WifiNetworkConfigBuilder setIsHiddenSsid() { + mIsHiddenSSID = true; + return this; + } + + /** + * Set defaults for the various low level credential type fields in the newly created + * WifiConfiguration object. + * + * See {@link com.android.server.wifi.WifiConfigManager#setDefaultsInWifiConfiguration( + * WifiConfiguration)}. + * + * @param configuration provided WifiConfiguration object. + */ + private static void setDefaultsInWifiConfiguration(@NonNull WifiConfiguration configuration) { + configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); + configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN); + configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); + } + + private void setKeyMgmtInWifiConfiguration(@NonNull WifiConfiguration configuration) { + if (!TextUtils.isEmpty(mPskPassphrase)) { + // WPA_PSK network. + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + } else if (mEnterpriseConfig != null) { + // WPA_EAP network + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); + } else { + // Open network + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + } + } + + /** + * Helper method to build WifiConfiguration object from the builder. + * @return Instance of {@link WifiConfiguration}. + */ + private WifiConfiguration buildWifiConfiguration() { + final WifiConfiguration wifiConfiguration = new WifiConfiguration(); + setDefaultsInWifiConfiguration(wifiConfiguration); + // WifiConfiguration.SSID needs quotes around unicode SSID. + if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) { + wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\""; + } + setKeyMgmtInWifiConfiguration(wifiConfiguration); + // WifiConfiguration.preSharedKey needs quotes around ASCII password. + if (mPskPassphrase != null) { + wifiConfiguration.preSharedKey = "\"" + mPskPassphrase + "\""; + } + wifiConfiguration.enterpriseConfig = mEnterpriseConfig; + wifiConfiguration.hiddenSSID = mIsHiddenSSID; + return wifiConfiguration; + } + + private boolean hasSetAnyPattern() { + return mSsidPatternMatcher != null || mBssidPatternMatcher != null; + } + + private void setMatchAnyPatternIfUnset() { + if (mSsidPatternMatcher == null) { + mSsidPatternMatcher = new PatternMatcher(MATCH_ALL_SSID_PATTERN_PATH, + PatternMatcher.PATTERN_SIMPLE_GLOB); + } + if (mBssidPatternMatcher == null) { + mBssidPatternMatcher = MATCH_ALL_BSSID_PATTERN; + } + } + + private boolean hasSetMatchNonePattern() { + if (mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_PREFIX + && mSsidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) { + return true; + } + if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN)) { + return true; + } + return false; + } + + private boolean hasSetMatchAllPattern() { + if ((mSsidPatternMatcher.match(MATCH_EMPTY_SSID_PATTERN_PATH)) + && mBssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN)) { + return true; + } + return false; + } + + /** + * Create a specifier object used to request a Wi-Fi network. The generated + * {@link NetworkSpecifier} should be used in + * {@link NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} when building + * the {@link NetworkRequest}. + *

    + * Note: Apps can set a combination of network match params: + *

  • SSID Pattern using {@link #setSsidPattern(PatternMatcher)} OR Specific SSID using + * {@link #setSsid(String)}.
  • + * AND/OR + *
  • BSSID Pattern using {@link #setBssidPattern(MacAddress, MacAddress)} OR Specific BSSID + * using {@link #setBssid(MacAddress)}
  • + * to trigger connection to a network that matches the set params. + * The system will find the set of networks matching the request and present the user + * with a system dialog which will allow the user to select a specific Wi-Fi network to connect + * to or to deny the request. + *

    + * + * For example: + * To connect to an open network with a SSID prefix of "test" and a BSSID OUI of "10:03:23": + * {@code + * final NetworkSpecifier specifier = + * new WifiNetworkConfigBuilder() + * .setSsidPattern(new PatternMatcher("test", PatterMatcher.PATTERN_PREFIX)) + * .setBssidPattern(MacAddress.fromString("10:03:23:00:00:00"), + * MacAddress.fromString("ff:ff:ff:00:00:00")) + * .buildNetworkSpecifier() + * final NetworkRequest request = + * new NetworkRequest.Builder() + * .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + * .setNetworkSpecifier(specifier) + * .build(); + * final ConnectivityManager connectivityManager = + * context.getSystemService(Context.CONNECTIVITY_SERVICE); + * final NetworkCallback networkCallback = new NetworkCallback() { + * ... + * @Override + * void onAvailable(...) {} + * // etc. + * }; + * connectivityManager.requestNetwork(request, networkCallback); + * } + * + * @return Instance of {@link NetworkSpecifier}. + * @throws IllegalStateException on invalid params set. + */ + public NetworkSpecifier buildNetworkSpecifier() { + if (!hasSetAnyPattern()) { + throw new IllegalStateException("one of setSsidPattern/setSsid/setBssidPattern/setBssid" + + " should be invoked for specifier"); + } + setMatchAnyPatternIfUnset(); + if (hasSetMatchNonePattern()) { + throw new IllegalStateException("cannot set match-none pattern for specifier"); + } + if (hasSetMatchAllPattern()) { + throw new IllegalStateException("cannot set match-all pattern for specifier"); + } + if (mIsHiddenSSID && mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_LITERAL) { + throw new IllegalStateException("setSsid should also be invoked when " + + "setIsHiddenSsid is invoked for network specifier"); + } + if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) { + throw new IllegalStateException("only one of setPskPassphrase or setEnterpriseConfig " + + "can be invoked for network specifier"); + } + + return new WifiNetworkSpecifier( + mSsidPatternMatcher, + mBssidPatternMatcher, + buildWifiConfiguration(), + Process.myUid()); + } +} diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java new file mode 100644 index 0000000000000..d3c6330e84603 --- /dev/null +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2018 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.wifi; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.annotation.NonNull; +import android.net.MacAddress; +import android.net.MatchAllNetworkSpecifier; +import android.net.NetworkSpecifier; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PatternMatcher; +import android.util.Pair; + +import java.util.Objects; + +/** + * Network specifier object used to request a Wi-Fi network. Apps should use the + * {@link WifiNetworkConfigBuilder} class to create an instance. + * @hide + */ +public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parcelable { + /** + * SSID pattern match specified by the app. + */ + public final PatternMatcher ssidPatternMatcher; + + /** + * BSSID pattern match specified by the app. + * Pair of . + */ + public final Pair bssidPatternMatcher; + + /** + * Security credentials for the network. + *

    + * Note: {@link WifiConfiguration#SSID} & {@link WifiConfiguration#BSSID} fields from + * WifiConfiguration are not used. Instead we use the {@link #ssidPatternMatcher} & + * {@link #bssidPatternMatcher} fields embedded directly + * within {@link WifiNetworkSpecifier}. + */ + public final WifiConfiguration wifiConfiguration; + + /** + * The UID of the process initializing this network specifier. Validated by receiver using + * checkUidIfNecessary() and is used by satisfiedBy() to determine whether the specifier + * matches the offered network. + */ + public final int requestorUid; + + public WifiNetworkSpecifier(@NonNull PatternMatcher ssidPatternMatcher, + @NonNull Pair bssidPatternMatcher, + @NonNull WifiConfiguration wifiConfiguration, + int requestorUid) { + checkNotNull(ssidPatternMatcher); + checkNotNull(bssidPatternMatcher); + checkNotNull(wifiConfiguration); + + this.ssidPatternMatcher = ssidPatternMatcher; + this.bssidPatternMatcher = bssidPatternMatcher; + this.wifiConfiguration = wifiConfiguration; + this.requestorUid = requestorUid; + } + + public static final Creator CREATOR = + new Creator() { + @Override + public WifiNetworkSpecifier createFromParcel(Parcel in) { + PatternMatcher ssidPatternMatcher = in.readParcelable(/* classLoader */null); + MacAddress baseAddress = in.readParcelable(null); + MacAddress mask = in.readParcelable(null); + Pair bssidPatternMatcher = + Pair.create(baseAddress, mask); + WifiConfiguration wifiConfiguration = in.readParcelable(null); + int requestorUid = in.readInt(); + return new WifiNetworkSpecifier(ssidPatternMatcher, bssidPatternMatcher, + wifiConfiguration, requestorUid); + } + + @Override + public WifiNetworkSpecifier[] newArray(int size) { + return new WifiNetworkSpecifier[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(ssidPatternMatcher, flags); + dest.writeParcelable(bssidPatternMatcher.first, flags); + dest.writeParcelable(bssidPatternMatcher.second, flags); + dest.writeParcelable(wifiConfiguration, flags); + dest.writeInt(requestorUid); + } + + @Override + public boolean satisfiedBy(NetworkSpecifier other) { + if (this == other) { + return true; + } + // Any generic requests should be satisifed by a specific wifi network. + if (other == null || other instanceof MatchAllNetworkSpecifier) { + return true; + } + // Specific requests are checked for equality although testing for equality of 2 patterns do + // not make much sense! + return equals(other); + } + + @Override + public int hashCode() { + return Objects.hash( + ssidPatternMatcher.getPath(), + ssidPatternMatcher.getType(), + bssidPatternMatcher, + wifiConfiguration.allowedKeyManagement, + requestorUid); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof WifiNetworkSpecifier)) { + return false; + } + WifiNetworkSpecifier lhs = (WifiNetworkSpecifier) obj; + return Objects.equals(this.ssidPatternMatcher.getPath(), + lhs.ssidPatternMatcher.getPath()) + && Objects.equals(this.ssidPatternMatcher.getType(), + lhs.ssidPatternMatcher.getType()) + && Objects.equals(this.bssidPatternMatcher, + lhs.bssidPatternMatcher) + && Objects.equals(this.wifiConfiguration.allowedKeyManagement, + lhs.wifiConfiguration.allowedKeyManagement) + && requestorUid == lhs.requestorUid; + } + + @Override + public String toString() { + return new StringBuilder() + .append("WifiNetworkSpecifierWifiNetworkSpecifier [") + .append(", SSID Match pattern=").append(ssidPatternMatcher) + .append(", BSSID Match pattern=").append(bssidPatternMatcher) + .append(", WifiConfiguration=").append( + wifiConfiguration == null ? null : wifiConfiguration.configKey()) + .append(", requestorUid=").append(requestorUid) + .append("]") + .toString(); + } + + @Override + public void assertValidFromUid(int requestorUid) { + if (this.requestorUid != requestorUid) { + throw new SecurityException("mismatched UIDs"); + } + } +} diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java new file mode 100644 index 0000000000000..6615727f533ec --- /dev/null +++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2018 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.wifi; + +import static android.os.PatternMatcher.PATTERN_LITERAL; +import static android.os.PatternMatcher.PATTERN_PREFIX; +import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.net.MacAddress; +import android.net.NetworkSpecifier; +import android.os.PatternMatcher; +import android.os.Process; +import android.support.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Unit tests for {@link android.net.wifi.WifiNetworkConfigBuilder}. + */ +@SmallTest +public class WifiNetworkConfigBuilderTest { + private static final String TEST_SSID = "Test123"; + private static final String TEST_BSSID_OUI_BASE_ADDRESS = "12:12:12:00:00:00"; + private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00"; + private static final String TEST_BSSID = "12:12:12:12:12:12"; + private static final String TEST_PRESHARED_KEY = "Test123"; + + /** + * Validate correctness of WifiNetworkSpecifier object created by + * {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for open network with SSID pattern. + */ + @Test + public void testWifiNetworkSpecifierBuilderForOpenNetworkWithSsidPattern() { + NetworkSpecifier specifier = new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_PREFIX)) + .buildNetworkSpecifier(); + + assertTrue(specifier instanceof WifiNetworkSpecifier); + WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier; + + assertEquals(Process.myUid(), wifiNetworkSpecifier.requestorUid); + assertEquals(TEST_SSID, wifiNetworkSpecifier.ssidPatternMatcher.getPath()); + assertEquals(PATTERN_PREFIX, wifiNetworkSpecifier.ssidPatternMatcher.getType()); + assertEquals(MacAddress.ALL_ZEROS_ADDRESS, wifiNetworkSpecifier.bssidPatternMatcher.first); + assertEquals(MacAddress.ALL_ZEROS_ADDRESS, wifiNetworkSpecifier.bssidPatternMatcher.second); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.NONE)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols + .get(WifiConfiguration.Protocol.RSN)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms + .get(WifiConfiguration.AuthAlgorithm.OPEN)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers + .get(WifiConfiguration.PairwiseCipher.CCMP)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers + .get(WifiConfiguration.GroupCipher.CCMP)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers + .get(WifiConfiguration.GroupCipher.TKIP)); + } + + /** + * Validate correctness of WifiNetworkSpecifier object created by + * {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for WPA_PSK network with BSSID + * pattern. + */ + @Test + public void testWifiNetworkSpecifierBuilderForWpaPskNetworkWithBssidPattern() { + NetworkSpecifier specifier = new WifiNetworkConfigBuilder() + .setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)) + .setPskPassphrase(TEST_PRESHARED_KEY) + .buildNetworkSpecifier(); + + assertTrue(specifier instanceof WifiNetworkSpecifier); + WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier; + + assertEquals(".*", wifiNetworkSpecifier.ssidPatternMatcher.getPath()); + assertEquals(PATTERN_SIMPLE_GLOB, wifiNetworkSpecifier.ssidPatternMatcher.getType()); + assertEquals(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + wifiNetworkSpecifier.bssidPatternMatcher.first); + assertEquals(MacAddress.fromString(TEST_BSSID_OUI_MASK), + wifiNetworkSpecifier.bssidPatternMatcher.second); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.WPA_PSK)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols + .get(WifiConfiguration.Protocol.RSN)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms + .get(WifiConfiguration.AuthAlgorithm.OPEN)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers + .get(WifiConfiguration.PairwiseCipher.CCMP)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers + .get(WifiConfiguration.GroupCipher.CCMP)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers + .get(WifiConfiguration.GroupCipher.TKIP)); + assertEquals("\"" + TEST_PRESHARED_KEY + "\"", + wifiNetworkSpecifier.wifiConfiguration.preSharedKey); + } + + /** + * Validate correctness of WifiNetworkSpecifier object created by + * {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for WPA_EAP network with + * SSID and BSSID pattern. + */ + @Test + public void testWifiNetworkSpecifierBuilderForEnterpriseHiddenNetworkWithSsidAndBssid() { + WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); + enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS); + enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC); + + NetworkSpecifier specifier = new WifiNetworkConfigBuilder() + .setSsid(TEST_SSID) + .setBssid(MacAddress.fromString(TEST_BSSID)) + .setEnterpriseConfig(enterpriseConfig) + .setIsHiddenSsid() + .buildNetworkSpecifier(); + + assertTrue(specifier instanceof WifiNetworkSpecifier); + WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier; + + assertEquals(TEST_SSID, wifiNetworkSpecifier.ssidPatternMatcher.getPath()); + assertEquals(PATTERN_LITERAL, wifiNetworkSpecifier.ssidPatternMatcher.getType()); + assertEquals(MacAddress.fromString(TEST_BSSID), + wifiNetworkSpecifier.bssidPatternMatcher.first); + assertEquals(MacAddress.BROADCAST_ADDRESS, + wifiNetworkSpecifier.bssidPatternMatcher.second); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.WPA_EAP)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.IEEE8021X)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols + .get(WifiConfiguration.Protocol.RSN)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms + .get(WifiConfiguration.AuthAlgorithm.OPEN)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers + .get(WifiConfiguration.PairwiseCipher.CCMP)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers + .get(WifiConfiguration.GroupCipher.CCMP)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers + .get(WifiConfiguration.GroupCipher.TKIP)); + assertTrue(wifiNetworkSpecifier.wifiConfiguration.hiddenSSID); + assertEquals(enterpriseConfig.getEapMethod(), + wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig.getEapMethod()); + assertEquals(enterpriseConfig.getPhase2Method(), + wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig.getPhase2Method()); + } + + + /** + * Ensure {@link WifiNetworkConfigBuilder#setSsid(String)} throws an exception + * when the string is not Unicode. + */ + @Test(expected = IllegalArgumentException.class) + public void testSetSsidWithNonUnicodeString() { + new WifiNetworkConfigBuilder() + .setSsid("\ud800") + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} throws an exception + * when the string is not ASCII encodable. + */ + @Test(expected = IllegalArgumentException.class) + public void testSetPskPassphraseWithNonAsciiString() { + new WifiNetworkConfigBuilder() + .setSsid(TEST_SSID) + .setPskPassphrase("salvē") + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when neither SSID nor BSSID patterns were set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithNoSsidAndBssidPattern() { + new WifiNetworkConfigBuilder().buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when match-all SSID pattern is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithMatchAllSsidPattern1() { + new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB)) + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when match-all SSID pattern is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithMatchAllSsidPattern2() { + new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher(".*", PatternMatcher.PATTERN_ADVANCED_GLOB)) + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when match-all SSID pattern is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithMatchAllSsidPattern3() { + new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher("", PatternMatcher.PATTERN_PREFIX)) + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when match-all BSSID pattern is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithMatchAllBssidPattern() { + new WifiNetworkConfigBuilder() + .setBssidPattern(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS) + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when match-none SSID pattern is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern() { + new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher("", PatternMatcher.PATTERN_LITERAL)) + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when match-none BSSID pattern is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern() { + new WifiNetworkConfigBuilder() + .setBssidPattern(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS) + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when both {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} and + * {@link WifiNetworkConfigBuilder#setEnterpriseConfig(WifiEnterpriseConfig)} are invoked. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithBothPskPassphraseAndEnterpriseConfig() { + new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL)) + .setPskPassphrase(TEST_PRESHARED_KEY) + .setEnterpriseConfig(new WifiEnterpriseConfig()) + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when SSID pattern is set for hidden network. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithSsidMatchPatternForHiddenNetwork() { + new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_PREFIX)) + .setIsHiddenSsid() + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when SSID pattern is set for hidden network. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithBssidMatchPatternForHiddenNetwork() { + new WifiNetworkConfigBuilder() + .setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)) + .setIsHiddenSsid() + .buildNetworkSpecifier(); + } +} diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java new file mode 100644 index 0000000000000..856f0c79a2e7f --- /dev/null +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2018 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.wifi; + +import static android.os.PatternMatcher.PATTERN_LITERAL; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.net.MacAddress; +import android.net.MatchAllNetworkSpecifier; +import android.os.Parcel; +import android.os.PatternMatcher; +import android.support.test.filters.SmallTest; +import android.util.Pair; + +import org.junit.Test; + +/** + * Unit tests for {@link android.net.wifi.WifiNetworkSpecifier}. + */ +@SmallTest +public class WifiNetworkSpecifierTest { + private static final int TEST_UID = 5; + private static final String TEST_SSID = "Test123"; + private static final String TEST_BSSID_OUI_BASE_ADDRESS = "12:12:12:00:00:00"; + private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00"; + private static final String TEST_PRESHARED_KEY = "\"Test123\""; + + /** + * Validate that parcel marshalling/unmarshalling works + */ + @Test + public void testWifiNetworkSpecifierParcel() { + WifiConfiguration wifiConfiguration = new WifiConfiguration(); + wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY; + WifiNetworkSpecifier specifier = + new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)), + wifiConfiguration, + TEST_UID); + + Parcel parcelW = Parcel.obtain(); + specifier.writeToParcel(parcelW, 0); + byte[] bytes = parcelW.marshall(); + parcelW.recycle(); + + Parcel parcelR = Parcel.obtain(); + parcelR.unmarshall(bytes, 0, bytes.length); + parcelR.setDataPosition(0); + WifiNetworkSpecifier parcelSpecifier = + WifiNetworkSpecifier.CREATOR.createFromParcel(parcelR); + + assertEquals(specifier, parcelSpecifier); + } + + /** + * Validate NetworkSpecifier matching. + * a) Create a network specifier for WPA_PSK network + * b) Ensure that the specifier matches {@code null} and {@link MatchAllNetworkSpecifier} + * specifiers. + */ + @Test + public void testWifiNetworkSpecifierSatisfiesNullAndAllMatch() { + WifiConfiguration wifiConfiguration = new WifiConfiguration(); + wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY; + WifiNetworkSpecifier specifier = + new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)), + wifiConfiguration, + TEST_UID); + + assertTrue(specifier.satisfiedBy(null)); + assertTrue(specifier.satisfiedBy(new MatchAllNetworkSpecifier())); + } + + /** + * Validate NetworkSpecifier matching. + * a) Create network specifier 1 for WPA_PSK network + * b) Create network specifier 2 with the same params as specifier 1. + * c) Ensure that the specifier 2 is satisfied by specifier 1. + */ + @Test + public void testWifiNetworkSpecifierSatisfiesSame() { + WifiConfiguration wifiConfiguration = new WifiConfiguration(); + wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY; + + WifiNetworkSpecifier specifier1 = + new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)), + wifiConfiguration, + TEST_UID); + + WifiNetworkSpecifier specifier2 = + new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)), + wifiConfiguration, + TEST_UID); + + assertTrue(specifier2.satisfiedBy(specifier1)); + } + + /** + * Validate NetworkSpecifier matching. + * a) Create network specifier 1 for WPA_PSK network + * b) Create network specifier 2 with different key mgmt params. + * c) Ensure that the specifier 2 is not satisfied by specifier 1. + */ + @Test + public void testWifiNetworkSpecifierDoesNotSatisfyWhenKeyMgmtDifferent() { + WifiConfiguration wifiConfiguration1 = new WifiConfiguration(); + wifiConfiguration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + wifiConfiguration1.preSharedKey = TEST_PRESHARED_KEY; + + WifiNetworkSpecifier specifier1 = + new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)), + wifiConfiguration1, + TEST_UID); + + WifiConfiguration wifiConfiguration2 = new WifiConfiguration(); + wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + WifiNetworkSpecifier specifier2 = + new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)), + wifiConfiguration2, + TEST_UID); + + assertFalse(specifier2.satisfiedBy(specifier1)); + } + + /** + * Validate NetworkSpecifier matching. + * a) Create network specifier 1 for WPA_PSK network + * b) Create network specifier 2 with different SSID pattern. + * c) Ensure that the specifier 2 is not satisfied by specifier 1. + */ + @Test + public void testWifiNetworkSpecifierDoesNotSatisfyWhenSsidDifferent() { + WifiConfiguration wifiConfiguration = new WifiConfiguration(); + wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY; + + WifiNetworkSpecifier specifier1 = + new WifiNetworkSpecifier(new PatternMatcher("", PATTERN_LITERAL), + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)), + wifiConfiguration, + TEST_UID); + + WifiNetworkSpecifier specifier2 = + new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)), + wifiConfiguration, + TEST_UID); + + assertFalse(specifier2.satisfiedBy(specifier1)); + } + + /** + * Validate NetworkSpecifier matching. + * a) Create network specifier 1 for WPA_PSK network + * b) Create network specifier 2 with different BSSID pattern. + * c) Ensure that the specifier 2 is not satisfied by specifier 1. + */ + @Test + public void testWifiNetworkSpecifierDoesNotSatisfyWhenBssidDifferent() { + WifiConfiguration wifiConfiguration = new WifiConfiguration(); + wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY; + + WifiNetworkSpecifier specifier1 = + new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)), + wifiConfiguration, + TEST_UID); + + WifiNetworkSpecifier specifier2 = + new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), + Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS), + wifiConfiguration, + TEST_UID); + + assertFalse(specifier2.satisfiedBy(specifier1)); + } +} From bc662b184492480936d510a543ee6a04259d7112 Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Wed, 10 Oct 2018 09:45:06 -0700 Subject: [PATCH 2/5] wifi(API): New API surface for network suggestion Allows apps to provide the platform a list of wifi network credentials that the device is allowed to connect. Each NetworkSuggestion instance will hold a standard WifiConfiguration object to represent network credentials along with some other meta info that will help the platform make good network selection decisions. NetworkConfigBuilder will be reused for building the NetworkSuggestion objects as well. Apps also have a mechanism to register to be awakened via PendingIntent when the platform connects to one of their suggestions. This mechanism will require the app to a) Hold location permission, and b) Registered PendingIntent should hold a foreground service (to prevent abuse of this mechanism to bypass platform background limit checks). Bug: 115504887 Test: Unit tests Test: `make api-stubs-docs-update-current-api` Change-Id: I9f5223fa45d49c22ce8f81d0ba56e5d12565381d --- api/current.txt | 13 ++ wifi/java/android/net/wifi/WifiManager.java | 60 ++++++ .../net/wifi/WifiNetworkConfigBuilder.java | 145 ++++++++++++- .../net/wifi/WifiNetworkSuggestion.java | 143 +++++++++++++ .../wifi/WifiNetworkConfigBuilderTest.java | 191 +++++++++++++++++- .../net/wifi/WifiNetworkSuggestionTest.java | 146 +++++++++++++ 6 files changed, 690 insertions(+), 8 deletions(-) create mode 100644 wifi/java/android/net/wifi/WifiNetworkSuggestion.java create mode 100644 wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java diff --git a/api/current.txt b/api/current.txt index 953859fbc2dd3..6a2740f8b6064 100755 --- a/api/current.txt +++ b/api/current.txt @@ -28753,6 +28753,7 @@ package android.net.wifi { public class WifiManager { method public int addNetwork(android.net.wifi.WifiConfiguration); + method public boolean addNetworkSuggestions(java.util.List, android.app.PendingIntent); method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); method public static int calculateSignalLevel(int, int); method public deprecated void cancelWps(android.net.wifi.WifiManager.WpsCallback); @@ -28781,6 +28782,7 @@ package android.net.wifi { method public boolean reassociate(); method public boolean reconnect(); method public boolean removeNetwork(int); + method public boolean removeNetworkSuggestions(java.util.List); method public void removePasspointConfiguration(java.lang.String); method public deprecated boolean saveConfiguration(); method public void setTdlsEnabled(java.net.InetAddress, boolean); @@ -28866,15 +28868,26 @@ package android.net.wifi { public class WifiNetworkConfigBuilder { ctor public WifiNetworkConfigBuilder(); method public android.net.NetworkSpecifier buildNetworkSpecifier(); + method public android.net.wifi.WifiNetworkSuggestion buildNetworkSuggestion(); method public android.net.wifi.WifiNetworkConfigBuilder setBssid(android.net.MacAddress); method public android.net.wifi.WifiNetworkConfigBuilder setBssidPattern(android.net.MacAddress, android.net.MacAddress); method public android.net.wifi.WifiNetworkConfigBuilder setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig); + method public android.net.wifi.WifiNetworkConfigBuilder setIsAppInteractionRequired(); method public android.net.wifi.WifiNetworkConfigBuilder setIsHiddenSsid(); + method public android.net.wifi.WifiNetworkConfigBuilder setIsMetered(); + method public android.net.wifi.WifiNetworkConfigBuilder setIsUserInteractionRequired(); + method public android.net.wifi.WifiNetworkConfigBuilder setPriority(int); method public android.net.wifi.WifiNetworkConfigBuilder setPskPassphrase(java.lang.String); method public android.net.wifi.WifiNetworkConfigBuilder setSsid(java.lang.String); method public android.net.wifi.WifiNetworkConfigBuilder setSsidPattern(android.os.PatternMatcher); } + public final class WifiNetworkSuggestion implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + public deprecated class WpsInfo implements android.os.Parcelable { ctor public deprecated WpsInfo(); ctor public deprecated WpsInfo(android.net.wifi.WpsInfo); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 9adbe67c15532..d4b98879f752a 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -25,6 +25,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; +import android.app.PendingIntent; import android.content.Context; import android.content.pm.ParceledListSlice; import android.net.ConnectivityManager; @@ -1184,6 +1185,65 @@ public class WifiManager { } } + /** + * Provide a list of network suggestions to the device. See {@link WifiNetworkSuggestion} + * for a detailed explanation of the parameters. + *

    + * When the device decides to connect to one of the provided network suggestions, platform fires + * the associated {@code pendingIntent} if + * {@link WifiNetworkSuggestion#isAppInteractionRequired} is {@code true} and the + * provided {@code pendingIntent} is non-null. + *

    + * Registration of a non-null pending intent {@code pendingIntent} requires + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission. + *

    + * NOTE: + *

  • These networks are just a suggestion to the platform. The platform will ultimately + * decide on which network the device connects to.
  • + *
  • When an app is uninstalled, all its suggested networks are discarded. If the device is + * currently connected to a suggested network which is being removed then the device will + * disconnect from that network.
  • + *
  • No in-place modification of existing suggestions are allowed. Apps are expected to + * remove suggestions using {@link #removeNetworkSuggestions(List)} and then add the modified + * suggestion back using this API.
  • + * + * @param networkSuggestions List of network suggestions provided by the app. + * @param pendingIntent Pending intent to be fired post connection for networks. These will be + * fired only when connecting to a network which has the + * {@link WifiNetworkSuggestion#isAppInteractionRequired} flag set. + * Pending intent must hold a foreground service, else will be rejected. + * (i.e {@link PendingIntent#isForegroundService()} should return true) + * @return true on success, false if any of the suggestions match (See + * {@link WifiNetworkSuggestion#equals(Object)} any previously provided suggestions by the app. + * @throws {@link SecurityException} if the caller is missing required permissions. + */ + public boolean addNetworkSuggestions( + @NonNull List networkSuggestions, + @Nullable PendingIntent pendingIntent) { + // TODO(b/115504887): Implementation + return false; + } + + + /** + * Remove a subset of or all of networks from previously provided suggestions by the app to the + * device. + * See {@link WifiNetworkSuggestion} for a detailed explanation of the parameters. + * See {@link WifiNetworkSuggestion#equals(Object)} for the equivalence evaluation used. + * + * @param networkSuggestions List of network suggestions to be removed. Pass an empty list + * to remove all the previous suggestions provided by the app. + * @return true on success, false if any of the suggestions do not match any suggestions + * previously provided by the app. Any matching suggestions are removed from the device and + * will not be considered for any further connection attempts. + */ + public boolean removeNetworkSuggestions( + @NonNull List networkSuggestions) { + // TODO(b/115504887): Implementation + return false; + } + /** * Add or update a Passpoint configuration. The configuration provides a credential * for connecting to Passpoint networks that are operated by the Passpoint diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java index ed15e07c7a811..ae4f40511b586 100644 --- a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java +++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java @@ -20,6 +20,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.PendingIntent; import android.net.MacAddress; import android.net.NetworkRequest; import android.net.NetworkSpecifier; @@ -30,11 +31,14 @@ import android.util.Pair; import java.nio.charset.CharsetEncoder; import java.nio.charset.StandardCharsets; +import java.util.List; /** * WifiNetworkConfigBuilder to use for creating Wi-Fi network configuration. *
  • See {@link #buildNetworkSpecifier()} for creating a network specifier to use in * {@link NetworkRequest}.
  • + *
  • See {@link #buildNetworkSuggestion()} for creating a network suggestion to use in + * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)}.
  • */ public class WifiNetworkConfigBuilder { private static final String MATCH_ALL_SSID_PATTERN_PATH = ".*"; @@ -45,6 +49,7 @@ public class WifiNetworkConfigBuilder { new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS); private static final MacAddress MATCH_EXACT_BSSID_PATTERN_MASK = MacAddress.BROADCAST_ADDRESS; + private static final int UNASSIGNED_PRIORITY = -1; /** * SSID pattern match specified by the app. @@ -69,6 +74,23 @@ public class WifiNetworkConfigBuilder { * SSID-specific probe request must be used for scans. */ private boolean mIsHiddenSSID; + /** + * Whether app needs to log in to captive portal to obtain Internet access. + */ + private boolean mIsAppInteractionRequired; + /** + * Whether user needs to log in to captive portal to obtain Internet access. + */ + private boolean mIsUserInteractionRequired; + /** + * Whether this network is metered or not. + */ + private boolean mIsMetered; + /** + * Priority of this network among other network suggestions provided by the app. + * The lower the number, the higher the priority (i.e value of 0 = highest priority). + */ + private int mPriority; public WifiNetworkConfigBuilder() { mSsidPatternMatcher = null; @@ -76,6 +98,10 @@ public class WifiNetworkConfigBuilder { mPskPassphrase = null; mEnterpriseConfig = null; mIsHiddenSSID = false; + mIsAppInteractionRequired = false; + mIsUserInteractionRequired = false; + mIsMetered = false; + mPriority = UNASSIGNED_PRIORITY; } /** @@ -103,6 +129,8 @@ public class WifiNetworkConfigBuilder { * {@link #buildNetworkSpecifier}, sets the SSID to use for filtering networks from scan * results. Will only match networks whose SSID is identical to the UTF-8 encoding of the * specified value. + *
  • For network suggestions ({@link WifiNetworkSuggestion}), built using + * {@link #buildNetworkSuggestion()}, sets the SSID for the network.
  • *
  • Overrides any previous value set using {@link #setSsid(String)} or * {@link #setSsidPattern(PatternMatcher)}.
  • * @@ -195,7 +223,7 @@ public class WifiNetworkConfigBuilder { } /** - * Whether this represents a hidden network. + * Specifies whether this represents a hidden network. *

    *

  • For network requests (see {@link NetworkSpecifier}), built using * {@link #buildNetworkSpecifier}, setting this disallows the usage of @@ -211,6 +239,77 @@ public class WifiNetworkConfigBuilder { return this; } + /** + * Specifies whether the app needs to log in to a captive portal to obtain Internet access. + *

    + * This will dictate if the associated pending intent in + * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)} will be sent after + * successfully connecting to the network. + * Use this for captive portal type networks where the app needs to authenticate the user + * before the device can access the network. + * This setting will be ignored if the {@code PendingIntent} used to add this network + * suggestion is null. + *

    + *

  • Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.
  • + *
  • If not set, defaults to false (i.e no app interaction required).
  • + * + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + */ + public WifiNetworkConfigBuilder setIsAppInteractionRequired() { + mIsAppInteractionRequired = true; + return this; + } + + /** + * Specifies whether the user needs to log in to a captive portal to obtain Internet access. + *

    + *

  • Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.
  • + *
  • If not set, defaults to false (i.e no user interaction required).
  • + * + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + */ + public WifiNetworkConfigBuilder setIsUserInteractionRequired() { + mIsUserInteractionRequired = true; + return this; + } + + /** + * Specify the priority of this network among other network suggestions provided by the same app + * (priorities have no impact on suggestions by different apps). The lower the number, the + * higher the priority (i.e value of 0 = highest priority). + *

    + *

  • Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.
  • + *
  • If not set, defaults to -1 (i.e unassigned priority).
  • + * + * @param priority Integer number representing the priority among suggestions by the app. + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + * @throws IllegalArgumentException if the priority value is negative. + */ + public WifiNetworkConfigBuilder setPriority(int priority) { + if (priority < 0) { + throw new IllegalArgumentException("Invalid priority value " + priority); + } + mPriority = priority; + return this; + } + + /** + * Specifies whether this network is metered. + *

    + *

  • Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.
  • + *
  • If not set, defaults to false (i.e not metered).
  • + * + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + */ + public WifiNetworkConfigBuilder setIsMetered() { + mIsMetered = true; + return this; + } + /** * Set defaults for the various low level credential type fields in the newly created * WifiConfiguration object. @@ -260,6 +359,10 @@ public class WifiNetworkConfigBuilder { } wifiConfiguration.enterpriseConfig = mEnterpriseConfig; wifiConfiguration.hiddenSSID = mIsHiddenSSID; + wifiConfiguration.priority = mPriority; + wifiConfiguration.meteredOverride = + mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED + : WifiConfiguration.METERED_OVERRIDE_NONE; return wifiConfiguration; } @@ -358,9 +461,15 @@ public class WifiNetworkConfigBuilder { throw new IllegalStateException("setSsid should also be invoked when " + "setIsHiddenSsid is invoked for network specifier"); } + if (mIsAppInteractionRequired || mIsUserInteractionRequired + || mPriority != -1 || mIsMetered) { + throw new IllegalStateException("none of setIsAppInteractionRequired/" + + "setIsUserInteractionRequired/setPriority/setIsMetered are allowed for " + + "specifier"); + } if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) { - throw new IllegalStateException("only one of setPskPassphrase or setEnterpriseConfig " - + "can be invoked for network specifier"); + throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can" + + " be invoked for network specifier"); } return new WifiNetworkSpecifier( @@ -369,4 +478,34 @@ public class WifiNetworkConfigBuilder { buildWifiConfiguration(), Process.myUid()); } + + /** + * Create a network suggestion object use in + * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)}. + * See {@link WifiNetworkSuggestion}. + * + * @return Instance of {@link WifiNetworkSuggestion}. + * @throws IllegalStateException on invalid params set. + */ + public WifiNetworkSuggestion buildNetworkSuggestion() { + if (mSsidPatternMatcher == null) { + throw new IllegalStateException("setSsid should be invoked for suggestion"); + } + if (mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_LITERAL + || mBssidPatternMatcher != null) { + throw new IllegalStateException("none of setSsidPattern/setBssidPattern/setBssid are" + + " allowed for suggestion"); + } + if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) { + throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can" + + "be invoked for suggestion"); + } + + return new WifiNetworkSuggestion( + buildWifiConfiguration(), + mIsAppInteractionRequired, + mIsUserInteractionRequired, + Process.myUid()); + + } } diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java new file mode 100644 index 0000000000000..04b9cb5dfb8de --- /dev/null +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2018 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.wifi; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.app.PendingIntent; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; +import java.util.Objects; + +/** + * The Network Suggestion object is used to provide a Wi-Fi network for consideration when + * auto-connecting to networks. Apps cannot directly create this object, they must use + * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} to obtain an instance + * of this object. + *

    + * Apps can provide a list of such networks to the platform using + * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)}. + */ +public final class WifiNetworkSuggestion implements Parcelable { + /** + * Network configuration for the provided network. + * @hide + */ + public final WifiConfiguration wifiConfiguration; + + /** + * Whether app needs to log in to captive portal to obtain Internet access. + * This will dictate if the associated pending intent in + * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)} needs to be sent after + * successfully connecting to the network. + * @hide + */ + public final boolean isAppInteractionRequired; + + /** + * Whether user needs to log in to captive portal to obtain Internet access. + * @hide + */ + public final boolean isUserInteractionRequired; + + /** + * The UID of the process initializing this network suggestion. + * @hide + */ + public final int suggestorUid; + + /** @hide */ + public WifiNetworkSuggestion(WifiConfiguration wifiConfiguration, + boolean isAppInteractionRequired, + boolean isUserInteractionRequired, + int suggestorUid) { + checkNotNull(wifiConfiguration); + + this.wifiConfiguration = wifiConfiguration; + this.isAppInteractionRequired = isAppInteractionRequired; + this.isUserInteractionRequired = isUserInteractionRequired; + this.suggestorUid = suggestorUid; + } + + public static final Creator CREATOR = + new Creator() { + @Override + public WifiNetworkSuggestion createFromParcel(Parcel in) { + return new WifiNetworkSuggestion( + in.readParcelable(null), // wifiConfiguration + in.readBoolean(), // isAppInteractionRequired + in.readBoolean(), // isUserInteractionRequired + in.readInt() // suggestorUid + ); + } + + @Override + public WifiNetworkSuggestion[] newArray(int size) { + return new WifiNetworkSuggestion[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(wifiConfiguration, flags); + dest.writeBoolean(isAppInteractionRequired); + dest.writeBoolean(isUserInteractionRequired); + dest.writeInt(suggestorUid); + } + + @Override + public int hashCode() { + return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.allowedKeyManagement, + suggestorUid); + } + + /** + * Equals for network suggestions. + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof WifiNetworkSuggestion)) { + return false; + } + WifiNetworkSuggestion lhs = (WifiNetworkSuggestion) obj; + return Objects.equals(this.wifiConfiguration.SSID, lhs.wifiConfiguration.SSID) + && Objects.equals(this.wifiConfiguration.allowedKeyManagement, + lhs.wifiConfiguration.allowedKeyManagement) + && suggestorUid == lhs.suggestorUid; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("WifiNetworkSuggestion [") + .append(", WifiConfiguration=").append(wifiConfiguration) + .append(", isAppInteractionRequired=").append(isAppInteractionRequired) + .append(", isUserInteractionRequired=").append(isUserInteractionRequired) + .append(", suggestorUid=").append(suggestorUid) + .append("]"); + return sb.toString(); + } +} diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java index 6615727f533ec..8980ddba238b8 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java @@ -21,6 +21,7 @@ import static android.os.PatternMatcher.PATTERN_PREFIX; import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.net.MacAddress; @@ -259,6 +260,19 @@ public class WifiNetworkConfigBuilderTest { .buildNetworkSpecifier(); } + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when SSID pattern is set for hidden network. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithBssidMatchPatternForHiddenNetwork() { + new WifiNetworkConfigBuilder() + .setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)) + .setIsHiddenSsid() + .buildNetworkSpecifier(); + } + /** * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception * when both {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} and @@ -287,14 +301,181 @@ public class WifiNetworkConfigBuilderTest { /** * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception - * when SSID pattern is set for hidden network. + * when {@link WifiNetworkConfigBuilder#setIsAppInteractionRequired()} is set. */ @Test(expected = IllegalStateException.class) - public void testWifiNetworkSpecifierBuilderWithBssidMatchPatternForHiddenNetwork() { + public void testWifiNetworkSpecifierBuilderWithRequiredAppInteraction() { new WifiNetworkConfigBuilder() - .setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), - MacAddress.fromString(TEST_BSSID_OUI_MASK)) - .setIsHiddenSsid() + .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL)) + .setIsAppInteractionRequired() .buildNetworkSpecifier(); } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when {@link WifiNetworkConfigBuilder#setIsUserInteractionRequired()} is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithRequiredUserInteraction() { + new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL)) + .setIsUserInteractionRequired() + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when {@link WifiNetworkConfigBuilder#setPriority(int)} is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithSetPriority() { + new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL)) + .setPriority(4) + .buildNetworkSpecifier(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception + * when {@link WifiNetworkConfigBuilder#setIsMetered()} is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithMetered() { + new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL)) + .setIsMetered() + .buildNetworkSpecifier(); + } + + /** + * Validate correctness of WifiNetworkSuggestion object created by + * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for Open network which requires + * app interaction. + */ + @Test + public void testWifiNetworkSuggestionBuilderForOpenNetworkWithReqAppInteraction() { + WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder() + .setSsid(TEST_SSID) + .setIsAppInteractionRequired() + .buildNetworkSuggestion(); + + assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); + assertTrue(suggestion.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.NONE)); + assertTrue(suggestion.isAppInteractionRequired); + assertFalse(suggestion.isUserInteractionRequired); + assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE, + suggestion.wifiConfiguration.meteredOverride); + assertEquals(-1, suggestion.wifiConfiguration.priority); + } + + /** + * Validate correctness of WifiNetworkSuggestion object created by + * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for WPA_EAP network which requires + * app interaction and has a priority of zero set. + */ + @Test + public void testWifiNetworkSuggestionBuilderForWpaEapNetworkWithPriorityAndReqAppInteraction() { + WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder() + .setSsid(TEST_SSID) + .setPskPassphrase(TEST_PRESHARED_KEY) + .setIsAppInteractionRequired() + .setPriority(0) + .buildNetworkSuggestion(); + + assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); + assertTrue(suggestion.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.WPA_PSK)); + assertEquals("\"" + TEST_PRESHARED_KEY + "\"", + suggestion.wifiConfiguration.preSharedKey); + assertTrue(suggestion.isAppInteractionRequired); + assertFalse(suggestion.isUserInteractionRequired); + assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE, + suggestion.wifiConfiguration.meteredOverride); + assertEquals(0, suggestion.wifiConfiguration.priority); + } + + /** + * Validate correctness of WifiNetworkSuggestion object created by + * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for WPA_PSK network which requires + * user interaction and is metered. + */ + @Test + public void testWifiNetworkSuggestionBuilderForWpaPskNetworkWithMeteredAndReqUserInteraction() { + WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder() + .setSsid(TEST_SSID) + .setPskPassphrase(TEST_PRESHARED_KEY) + .setIsUserInteractionRequired() + .setIsMetered() + .buildNetworkSuggestion(); + + assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); + assertTrue(suggestion.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.WPA_PSK)); + assertEquals("\"" + TEST_PRESHARED_KEY + "\"", + suggestion.wifiConfiguration.preSharedKey); + assertFalse(suggestion.isAppInteractionRequired); + assertTrue(suggestion.isUserInteractionRequired); + assertEquals(WifiConfiguration.METERED_OVERRIDE_METERED, + suggestion.wifiConfiguration.meteredOverride); + assertEquals(-1, suggestion.wifiConfiguration.priority); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception + * when {@link WifiNetworkConfigBuilder#setSsidPattern(PatternMatcher)} is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSuggestionBuilderWithSsidPattern() { + new WifiNetworkConfigBuilder() + .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_PREFIX)) + .buildNetworkSuggestion(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception + * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSuggestionBuilderWithBssidPattern() { + new WifiNetworkConfigBuilder() + .setSsid(TEST_SSID) + .setBssidPattern(MacAddress.fromString(TEST_BSSID), + MacAddress.fromString(TEST_BSSID)) + .buildNetworkSuggestion(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception + * when {@link WifiNetworkConfigBuilder#setBssidPattern(MacAddress, MacAddress)} is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSuggestionBuilderWithBssid() { + new WifiNetworkConfigBuilder() + .setSsid(TEST_SSID) + .setBssid(MacAddress.fromString(TEST_BSSID)) + .buildNetworkSuggestion(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception + * when {@link WifiNetworkConfigBuilder#setSsid(String)} is not set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSuggestionBuilderWithNoSsid() { + new WifiNetworkConfigBuilder() + .buildNetworkSuggestion(); + } + + /** + * Ensure {@link WifiNetworkConfigBuilder#setPriority(int)} throws an exception + * when the value is negative. + */ + @Test(expected = IllegalArgumentException.class) + public void testWifiNetworkSuggestionBuilderWithInvalidPriority() { + new WifiNetworkConfigBuilder() + .setSsid(TEST_SSID) + .setPriority(-1) + .buildNetworkSuggestion(); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java new file mode 100644 index 0000000000000..6bab60dd480bd --- /dev/null +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2018 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.wifi; + +import static org.junit.Assert.*; + +import android.os.Parcel; +import android.support.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Unit tests for {@link android.net.wifi.WifiNetworkSuggestion}. + */ +@SmallTest +public class WifiNetworkSuggestionTest { + private static final String TEST_SSID = "\"Test123\""; + private static final String TEST_SSID_1 = "\"Test1234\""; + + /** + * Check that parcel marshalling/unmarshalling works + */ + @Test + public void testWifiNetworkSuggestionParcel() { + WifiConfiguration configuration = new WifiConfiguration(); + configuration.SSID = TEST_SSID; + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + WifiNetworkSuggestion suggestion = + new WifiNetworkSuggestion(configuration, false, true, 0); + + Parcel parcelW = Parcel.obtain(); + suggestion.writeToParcel(parcelW, 0); + byte[] bytes = parcelW.marshall(); + parcelW.recycle(); + + Parcel parcelR = Parcel.obtain(); + parcelR.unmarshall(bytes, 0, bytes.length); + parcelR.setDataPosition(0); + WifiNetworkSuggestion parcelSuggestion = + WifiNetworkSuggestion.CREATOR.createFromParcel(parcelR); + + // Two suggestion objects are considered equal if they point to the same network (i.e same + // SSID + keyMgmt + same UID). |isAppInteractionRequired| & |isUserInteractionRequired| are + // not considered for equality and hence needs to be checked for explicitly below. + assertEquals(suggestion, parcelSuggestion); + assertEquals(suggestion.isAppInteractionRequired, + parcelSuggestion.isAppInteractionRequired); + assertEquals(suggestion.isUserInteractionRequired, + parcelSuggestion.isUserInteractionRequired); + } + + /** + * Check NetworkSuggestion equals returns {@code true} for 2 network suggestions with the same + * SSID, key mgmt and UID. + */ + @Test + public void testWifiNetworkSuggestionEqualsSame() { + WifiConfiguration configuration = new WifiConfiguration(); + configuration.SSID = TEST_SSID; + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + WifiNetworkSuggestion suggestion = + new WifiNetworkSuggestion(configuration, true, false, 0); + + WifiConfiguration configuration1 = new WifiConfiguration(); + configuration1.SSID = TEST_SSID; + configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + WifiNetworkSuggestion suggestion1 = + new WifiNetworkSuggestion(configuration1, false, true, 0); + + assertEquals(suggestion, suggestion1); + } + + /** + * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same + * key mgmt and UID, but different SSID. + */ + @Test + public void testWifiNetworkSuggestionEqualsFailsWhenSsidIsDifferent() { + WifiConfiguration configuration = new WifiConfiguration(); + configuration.SSID = TEST_SSID; + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + WifiNetworkSuggestion suggestion = + new WifiNetworkSuggestion(configuration, false, false, 0); + + WifiConfiguration configuration1 = new WifiConfiguration(); + configuration1.SSID = TEST_SSID_1; + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + WifiNetworkSuggestion suggestion1 = + new WifiNetworkSuggestion(configuration1, false, false, 0); + + assertNotEquals(suggestion, suggestion1); + } + + /** + * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same + * SSID and UID, but different key mgmt. + */ + @Test + public void testWifiNetworkSuggestionEqualsFailsWhenKeyMgmtIsDifferent() { + WifiConfiguration configuration = new WifiConfiguration(); + configuration.SSID = TEST_SSID; + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + WifiNetworkSuggestion suggestion = + new WifiNetworkSuggestion(configuration, false, false, 0); + + WifiConfiguration configuration1 = new WifiConfiguration(); + configuration1.SSID = TEST_SSID; + configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + WifiNetworkSuggestion suggestion1 = + new WifiNetworkSuggestion(configuration1, false, false, 0); + + assertNotEquals(suggestion, suggestion1); + } + + /** + * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same + * SSID and key mgmt, but different UID. + */ + @Test + public void testWifiNetworkSuggestionEqualsFailsWhenUidIsDifferent() { + WifiConfiguration configuration = new WifiConfiguration(); + configuration.SSID = TEST_SSID; + configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + WifiNetworkSuggestion suggestion = + new WifiNetworkSuggestion(configuration, false, false, 0); + + WifiNetworkSuggestion suggestion1 = + new WifiNetworkSuggestion(configuration, false, false, 1); + + assertNotEquals(suggestion, suggestion1); + } +} From 914a64ecd259402b937896d0e5a75fb5eba5d61b Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Wed, 10 Oct 2018 09:46:13 -0700 Subject: [PATCH 3/5] wifi(API): Mark old API's deprecated List of API's that are obsoleted by the new connection API proposed for Q. These API's will continue to work with some restrictions for apps targeting older SDK. Also, removes the ability for 3p apps to toggle wifi state regardless of target SDK. This will be limited to 1p apps like settings/setup wizard. Bug: 115504728 Test: Compiles Change-Id: Ifec5ab721b37bc334ff7448b7829df4be2dcab37 --- api/current.txt | 22 ++-- api/system-current.txt | 2 +- .../android/net/wifi/WifiConfiguration.java | 4 + wifi/java/android/net/wifi/WifiManager.java | 120 ++++++++++++++---- 4 files changed, 113 insertions(+), 35 deletions(-) diff --git a/api/current.txt b/api/current.txt index 6a2740f8b6064..71970537abdb3 100755 --- a/api/current.txt +++ b/api/current.txt @@ -28598,7 +28598,7 @@ package android.net.wifi { enum_constant public static final android.net.wifi.SupplicantState UNINITIALIZED; } - public class WifiConfiguration implements android.os.Parcelable { + public deprecated class WifiConfiguration implements android.os.Parcelable { ctor public WifiConfiguration(); method public int describeContents(); method public android.net.ProxyInfo getHttpProxy(); @@ -28752,7 +28752,7 @@ package android.net.wifi { } public class WifiManager { - method public int addNetwork(android.net.wifi.WifiConfiguration); + method public deprecated int addNetwork(android.net.wifi.WifiConfiguration); method public boolean addNetworkSuggestions(java.util.List, android.app.PendingIntent); method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); method public static int calculateSignalLevel(int, int); @@ -28761,10 +28761,10 @@ package android.net.wifi { method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(java.lang.String); method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, java.lang.String); method public android.net.wifi.WifiManager.WifiLock createWifiLock(java.lang.String); - method public boolean disableNetwork(int); - method public boolean disconnect(); - method public boolean enableNetwork(int, boolean); - method public java.util.List getConfiguredNetworks(); + method public deprecated boolean disableNetwork(int); + method public deprecated boolean disconnect(); + method public deprecated boolean enableNetwork(int, boolean); + method public deprecated java.util.List getConfiguredNetworks(); method public android.net.wifi.WifiInfo getConnectionInfo(); method public android.net.DhcpInfo getDhcpInfo(); method public java.util.List getPasspointConfigurations(); @@ -28779,19 +28779,19 @@ package android.net.wifi { method public boolean isTdlsSupported(); method public boolean isWifiEnabled(); method public deprecated boolean pingSupplicant(); - method public boolean reassociate(); - method public boolean reconnect(); - method public boolean removeNetwork(int); + method public deprecated boolean reassociate(); + method public deprecated boolean reconnect(); + method public deprecated boolean removeNetwork(int); method public boolean removeNetworkSuggestions(java.util.List); method public void removePasspointConfiguration(java.lang.String); method public deprecated boolean saveConfiguration(); method public void setTdlsEnabled(java.net.InetAddress, boolean); method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean); - method public boolean setWifiEnabled(boolean); + method public deprecated boolean setWifiEnabled(boolean); method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler); method public deprecated boolean startScan(); method public deprecated void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback); - method public int updateNetwork(android.net.wifi.WifiConfiguration); + method public deprecated int updateNetwork(android.net.wifi.WifiConfiguration); field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"; field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE"; field public static final deprecated int ERROR_AUTHENTICATING = 1; // 0x1 diff --git a/api/system-current.txt b/api/system-current.txt index b1cbfa64a870e..9897db2f034be 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3565,7 +3565,7 @@ package android.net.wifi { field public byte id; } - public class WifiConfiguration implements android.os.Parcelable { + public deprecated class WifiConfiguration implements android.os.Parcelable { method public boolean hasNoInternetAccess(); method public boolean isEphemeral(); method public boolean isNoInternetAccessExpected(); diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 74722787f4411..b51b7dd2fa74b 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -47,7 +47,11 @@ import java.util.HashMap; /** * A class representing a configured Wi-Fi network, including the * security configuration. + * + * @deprecated Use {@link WifiNetworkConfigBuilder} to create {@link NetworkSpecifier} and + * {@link WifiNetworkSuggestion}. This will become a system use only object in the future. */ +@Deprecated public class WifiConfiguration implements Parcelable { private static final String TAG = "WifiConfiguration"; /** diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index d4b98879f752a..f72705aaafae3 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -31,14 +31,11 @@ import android.content.pm.ParceledListSlice; import android.net.ConnectivityManager; import android.net.DhcpInfo; import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.ProvisioningCallback; import android.os.Binder; -import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -53,7 +50,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; -import com.android.server.net.NetworkPinner; import dalvik.system.CloseGuard; @@ -1036,7 +1032,17 @@ public class WifiManager { * * @return a list of network configurations in the form of a list * of {@link WifiConfiguration} objects. + * + * @deprecated + * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new + * mechanism to trigger connection to a Wi-Fi network. + * b) See {@link #addNetworkSuggestions(List, PendingIntent)}, + * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration + * when auto-connecting to wifi. + * Compatibility Note: For applications targeting + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return an empty list. */ + @Deprecated public List getConfiguredNetworks() { try { ParceledListSlice parceledList = @@ -1136,7 +1142,17 @@ public class WifiManager { * @return the ID of the newly created network description. This is used in * other operations to specified the network to be acted upon. * Returns {@code -1} on failure. + * + * @deprecated + * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new + * mechanism to trigger connection to a Wi-Fi network. + * b) See {@link #addNetworkSuggestions(List, PendingIntent)}, + * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration + * when auto-connecting to wifi. + * Compatibility Note: For applications targeting + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return {@code -1}. */ + @Deprecated public int addNetwork(WifiConfiguration config) { if (config == null) { return -1; @@ -1161,7 +1177,17 @@ public class WifiManager { * Returns {@code -1} on failure, including when the {@code networkId} * field of the {@code WifiConfiguration} does not refer to an * existing network. + * + * @deprecated + * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new + * mechanism to trigger connection to a Wi-Fi network. + * b) See {@link #addNetworkSuggestions(List, PendingIntent)}, + * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration + * when auto-connecting to wifi. + * Compatibility Note: For applications targeting + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return {@code -1}. */ + @Deprecated public int updateNetwork(WifiConfiguration config) { if (config == null || config.networkId < 0) { return -1; @@ -1359,7 +1385,17 @@ public class WifiManager { * @param netId the ID of the network as returned by {@link #addNetwork} or {@link * #getConfiguredNetworks}. * @return {@code true} if the operation succeeded + * + * @deprecated + * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new + * mechanism to trigger connection to a Wi-Fi network. + * b) See {@link #addNetworkSuggestions(List, PendingIntent)}, + * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration + * when auto-connecting to wifi. + * Compatibility Note: For applications targeting + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false. */ + @Deprecated public boolean removeNetwork(int netId) { try { return mService.removeNetwork(netId, mContext.getOpPackageName()); @@ -1374,10 +1410,8 @@ public class WifiManager { * network is initiated. This may result in the asynchronous delivery * of state change events. *

    - * Note: If an application's target SDK version is - * {@link android.os.Build.VERSION_CODES#LOLLIPOP} or newer, network - * communication may not use Wi-Fi even if Wi-Fi is connected; traffic may - * instead be sent through another network, such as cellular data, + * Note: Network communication may not use Wi-Fi even if Wi-Fi is connected; + * traffic may instead be sent through another network, such as cellular data, * Bluetooth tethering, or Ethernet. For example, traffic will never use a * Wi-Fi network that does not provide Internet access (e.g. a wireless * printer), if another network that does offer Internet access (e.g. @@ -1395,29 +1429,24 @@ public class WifiManager { * @param attemptConnect The way to select a particular network to connect to is specify * {@code true} for this parameter. * @return {@code true} if the operation succeeded + * + * @deprecated + * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new + * mechanism to trigger connection to a Wi-Fi network. + * b) See {@link #addNetworkSuggestions(List, PendingIntent)}, + * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration + * when auto-connecting to wifi. + * Compatibility Note: For applications targeting + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false. */ + @Deprecated public boolean enableNetwork(int netId, boolean attemptConnect) { - final boolean pin = attemptConnect && mTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP; - if (pin) { - NetworkRequest request = new NetworkRequest.Builder() - .clearCapabilities() - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .build(); - NetworkPinner.pin(mContext, request); - } - boolean success; try { success = mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - - if (pin && !success) { - NetworkPinner.unpin(); - } - return success; } @@ -1432,7 +1461,17 @@ public class WifiManager { * @param netId the ID of the network as returned by {@link #addNetwork} or {@link * #getConfiguredNetworks}. * @return {@code true} if the operation succeeded + * + * @deprecated + * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new + * mechanism to trigger connection to a Wi-Fi network. + * b) See {@link #addNetworkSuggestions(List, PendingIntent)}, + * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration + * when auto-connecting to wifi. + * Compatibility Note: For applications targeting + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false. */ + @Deprecated public boolean disableNetwork(int netId) { try { return mService.disableNetwork(netId, mContext.getOpPackageName()); @@ -1445,7 +1484,17 @@ public class WifiManager { * Disassociate from the currently active access point. This may result * in the asynchronous delivery of state change events. * @return {@code true} if the operation succeeded + * + * @deprecated + * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new + * mechanism to trigger connection to a Wi-Fi network. + * b) See {@link #addNetworkSuggestions(List, PendingIntent)}, + * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration + * when auto-connecting to wifi. + * Compatibility Note: For applications targeting + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false. */ + @Deprecated public boolean disconnect() { try { mService.disconnect(mContext.getOpPackageName()); @@ -1460,7 +1509,17 @@ public class WifiManager { * disconnected. This may result in the asynchronous delivery of state * change events. * @return {@code true} if the operation succeeded + * + * @deprecated + * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new + * mechanism to trigger connection to a Wi-Fi network. + * b) See {@link #addNetworkSuggestions(List, PendingIntent)}, + * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration + * when auto-connecting to wifi. + * Compatibility Note: For applications targeting + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false. */ + @Deprecated public boolean reconnect() { try { mService.reconnect(mContext.getOpPackageName()); @@ -1475,7 +1534,17 @@ public class WifiManager { * connected. This may result in the asynchronous delivery of state * change events. * @return {@code true} if the operation succeeded + * + * @deprecated + * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new + * mechanism to trigger connection to a Wi-Fi network. + * b) See {@link #addNetworkSuggestions(List, PendingIntent)}, + * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration + * when auto-connecting to wifi. + * Compatibility Note: For applications targeting + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false. */ + @Deprecated public boolean reassociate() { try { mService.reassociate(mContext.getOpPackageName()); @@ -1881,7 +1950,12 @@ public class WifiManager { * @return {@code false} if the request cannot be satisfied; {@code true} indicates that wifi is * either already in the requested state, or in progress toward the requested state. * @throws {@link java.lang.SecurityException} if the caller is missing required permissions. + * + * @deprecated Starting with Build.VERSION_CODES#Q, applications are not allowed to + * enable/disable Wi-Fi regardless of application's target SDK. This API will have no effect + * and will always return false. */ + @Deprecated public boolean setWifiEnabled(boolean enabled) { try { return mService.setWifiEnabled(mContext.getOpPackageName(), enabled); From 99cfe09fa20a955f47fcabc18f1f693ca266a1da Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Fri, 5 Oct 2018 09:42:19 -0700 Subject: [PATCH 4/5] wifi(API): NetworkSpecifier for Wifi NetworkAgent Create an @hide NetworkSpecifier to use by the Wifi NetworkAgent. This will be used by connectivity service to match the incoming NetworkRequest (with WifiNetworkSpecifier) with the NetworkAgent we created to serve that request. The WifiNetworkAgentSpecifier will hold the current connected wifi network configuration which will be used to pattern match the WifiNetworkSpecifier from NetworkRequest's. Also, added a @hide helper method in MacAddress to help with matching bssid pattern. Bug: 113878056 Test: Unit tests Change-Id: I9a643f0b914d48ff64104c798ec2869db40cb24b --- core/java/android/net/MacAddress.java | 15 + .../net/java/android/net/MacAddressTest.java | 35 +- .../net/wifi/WifiNetworkAgentSpecifier.java | 187 ++++++++ .../net/wifi/WifiNetworkSpecifier.java | 3 + .../wifi/WifiNetworkAgentSpecifierTest.java | 453 ++++++++++++++++++ 5 files changed, 692 insertions(+), 1 deletion(-) create mode 100644 wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java create mode 100644 wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java index 98f356722bf37..4cd000113b7eb 100644 --- a/core/java/android/net/MacAddress.java +++ b/core/java/android/net/MacAddress.java @@ -393,4 +393,19 @@ public final class MacAddress implements Parcelable { } return out; } + + /** + * Checks if this MAC Address matches the provided range. + * + * @param baseAddress MacAddress representing the base address to compare with. + * @param mask MacAddress representing the mask to use during comparison. + * @return true if this MAC Address matches the given range. + * + * @hide + */ + public boolean matches(@NonNull MacAddress baseAddress, @NonNull MacAddress mask) { + Preconditions.checkNotNull(baseAddress); + Preconditions.checkNotNull(mask); + return (mAddr & mask.mAddr) == (baseAddress.mAddr & mask.mAddr); + } } diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java index 04266c5b3a0f8..b9222a86a092c 100644 --- a/tests/net/java/android/net/MacAddressTest.java +++ b/tests/net/java/android/net/MacAddressTest.java @@ -17,8 +17,8 @@ package android.net; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.support.test.filters.SmallTest; @@ -252,6 +252,39 @@ public class MacAddressTest { } } + @Test + public void testMatches() { + // match 4 bytes prefix + assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( + MacAddress.fromString("aa:bb:cc:dd:00:00"), + MacAddress.fromString("ff:ff:ff:ff:00:00"))); + + // match bytes 0,1,2 and 5 + assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( + MacAddress.fromString("aa:bb:cc:00:00:11"), + MacAddress.fromString("ff:ff:ff:00:00:ff"))); + + // match 34 bit prefix + assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( + MacAddress.fromString("aa:bb:cc:dd:c0:00"), + MacAddress.fromString("ff:ff:ff:ff:c0:00"))); + + // fail to match 36 bit prefix + assertFalse(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( + MacAddress.fromString("aa:bb:cc:dd:40:00"), + MacAddress.fromString("ff:ff:ff:ff:f0:00"))); + + // match all 6 bytes + assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( + MacAddress.fromString("aa:bb:cc:dd:ee:11"), + MacAddress.fromString("ff:ff:ff:ff:ff:ff"))); + + // match none of 6 bytes + assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( + MacAddress.fromString("00:00:00:00:00:00"), + MacAddress.fromString("00:00:00:00:00:00"))); + } + static byte[] toByteArray(int... in) { byte[] out = new byte[in.length]; for (int i = 0; i < in.length; i++) { diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java new file mode 100644 index 0000000000000..55fde4ca335ee --- /dev/null +++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2018 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.wifi; + +import static com.android.internal.util.Preconditions.checkNotNull; +import static com.android.internal.util.Preconditions.checkState; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.MacAddress; +import android.net.MatchAllNetworkSpecifier; +import android.net.NetworkAgent; +import android.net.NetworkRequest; +import android.net.NetworkSpecifier; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Network specifier object used by wifi's {@link android.net.NetworkAgent}. + * @hide + */ +public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements Parcelable { + /** + * Security credentials for the currently connected network. + */ + private final WifiConfiguration mWifiConfiguration; + + /** + * The UID of the app that requested a specific wifi network using {@link WifiNetworkSpecifier}. + * + * Will only be filled when the device connects to a wifi network as a result of a + * {@link NetworkRequest} with {@link WifiNetworkSpecifier}. Will be set to -1 if the device + * auto-connected to a wifi network. + */ + private final int mOriginalRequestorUid; + + public WifiNetworkAgentSpecifier(@NonNull WifiConfiguration wifiConfiguration, + int originalRequestorUid) { + checkNotNull(wifiConfiguration); + + mWifiConfiguration = wifiConfiguration; + mOriginalRequestorUid = originalRequestorUid; + } + + /** + * @hide + */ + public static final Creator CREATOR = + new Creator() { + @Override + public WifiNetworkAgentSpecifier createFromParcel(@NonNull Parcel in) { + WifiConfiguration wifiConfiguration = in.readParcelable(null); + int originalRequestorUid = in.readInt(); + return new WifiNetworkAgentSpecifier(wifiConfiguration, originalRequestorUid); + } + + @Override + public WifiNetworkAgentSpecifier[] newArray(int size) { + return new WifiNetworkAgentSpecifier[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelable(mWifiConfiguration, flags); + dest.writeInt(mOriginalRequestorUid); + } + + @Override + public boolean satisfiedBy(@Nullable NetworkSpecifier other) { + if (this == other) { + return true; + } + // Any generic requests should be satisifed by a specific wifi network. + if (other == null || other instanceof MatchAllNetworkSpecifier) { + return true; + } + if (other instanceof WifiNetworkSpecifier) { + return satisfiesNetworkSpecifier((WifiNetworkSpecifier) other); + } + if (other instanceof WifiNetworkAgentSpecifier) { + throw new IllegalStateException("WifiNetworkAgentSpecifier instances should never be " + + "compared"); + } + return false; + } + + /** + * Match {@link WifiNetworkSpecifier} in app's {@link NetworkRequest} with the + * {@link WifiNetworkAgentSpecifier} in wifi platform's {@link NetworkAgent}. + */ + public boolean satisfiesNetworkSpecifier(@NonNull WifiNetworkSpecifier ns) { + // None of these should be null by construction. + // {@link WifiNetworkConfigBuilder} enforces non-null in {@link WifiNetworkSpecifier}. + // {@link WifiNetworkFactory} ensures non-null in {@link WifiNetworkAgentSpecifier}. + checkNotNull(ns); + checkNotNull(ns.ssidPatternMatcher); + checkNotNull(ns.bssidPatternMatcher); + checkNotNull(ns.wifiConfiguration.allowedKeyManagement); + checkNotNull(this.mWifiConfiguration.SSID); + checkNotNull(this.mWifiConfiguration.BSSID); + checkNotNull(this.mWifiConfiguration.allowedKeyManagement); + + final String ssidWithQuotes = this.mWifiConfiguration.SSID; + checkState(ssidWithQuotes.startsWith("\"") && ssidWithQuotes.endsWith("\"")); + final String ssidWithoutQuotes = ssidWithQuotes.substring(1, ssidWithQuotes.length() - 1); + if (!ns.ssidPatternMatcher.match(ssidWithoutQuotes)) { + return false; + } + final MacAddress bssid = MacAddress.fromString(this.mWifiConfiguration.BSSID); + final MacAddress matchBaseAddress = ns.bssidPatternMatcher.first; + final MacAddress matchMask = ns.bssidPatternMatcher.second; + if (!bssid.matches(matchBaseAddress, matchMask)) { + return false; + } + if (!ns.wifiConfiguration.allowedKeyManagement.equals( + this.mWifiConfiguration.allowedKeyManagement)) { + return false; + } + if (ns.requestorUid != this.mOriginalRequestorUid) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return Objects.hash( + mWifiConfiguration.SSID, + mWifiConfiguration.BSSID, + mWifiConfiguration.allowedKeyManagement, + mOriginalRequestorUid); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof WifiNetworkAgentSpecifier)) { + return false; + } + WifiNetworkAgentSpecifier lhs = (WifiNetworkAgentSpecifier) obj; + return Objects.equals(this.mWifiConfiguration.SSID, lhs.mWifiConfiguration.SSID) + && Objects.equals(this.mWifiConfiguration.BSSID, lhs.mWifiConfiguration.BSSID) + && Objects.equals(this.mWifiConfiguration.allowedKeyManagement, + lhs.mWifiConfiguration.allowedKeyManagement) + && mOriginalRequestorUid == lhs.mOriginalRequestorUid; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("WifiNetworkAgentSpecifier ["); + sb.append(", WifiConfiguration=").append( + mWifiConfiguration == null ? null : mWifiConfiguration.configKey()) + .append(", mOriginalRequestorUid=").append(mOriginalRequestorUid) + .append("]"); + return sb.toString(); + } + + @Override + public void assertValidFromUid(int requestorUid) { + throw new IllegalStateException("WifiNetworkAgentSpecifier should never be used " + + "for requests."); + } +} diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java index d3c6330e84603..4348399b404b4 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -121,6 +121,9 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc if (other == null || other instanceof MatchAllNetworkSpecifier) { return true; } + if (other instanceof WifiNetworkAgentSpecifier) { + return ((WifiNetworkAgentSpecifier) other).satisfiesNetworkSpecifier(this); + } // Specific requests are checked for equality although testing for equality of 2 patterns do // not make much sense! return equals(other); diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java new file mode 100644 index 0000000000000..1b0007c9e7320 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2018 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.wifi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.net.MacAddress; +import android.net.MatchAllNetworkSpecifier; +import android.net.NetworkRequest; +import android.net.NetworkSpecifier; +import android.os.Parcel; +import android.os.PatternMatcher; +import android.support.test.filters.SmallTest; +import android.util.Pair; + +import org.junit.Test; + +/** + * Unit tests for {@link android.net.wifi.WifiNetworkAgentSpecifier}. + */ +@SmallTest +public class WifiNetworkAgentSpecifierTest { + private static final int TEST_UID = 5; + private static final int TEST_UID_1 = 8; + private static final String TEST_SSID = "Test123"; + private static final String TEST_SSID_PATTERN = "Test"; + private static final String TEST_SSID_1 = "456test"; + private static final String TEST_BSSID = "12:12:12:aa:0b:c0"; + private static final String TEST_BSSID_OUI_BASE_ADDRESS = "12:12:12:00:00:00"; + private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00"; + private static final String TEST_BSSID_1 = "aa:cc:12:aa:0b:c0"; + private static final String TEST_PRESHARED_KEY = "\"Test123\""; + + /** + * Validate that parcel marshalling/unmarshalling works + */ + @Test + public void testWifiNetworkAgentSpecifierParcel() { + WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier(); + + Parcel parcelW = Parcel.obtain(); + specifier.writeToParcel(parcelW, 0); + byte[] bytes = parcelW.marshall(); + parcelW.recycle(); + + Parcel parcelR = Parcel.obtain(); + parcelR.unmarshall(bytes, 0, bytes.length); + parcelR.setDataPosition(0); + WifiNetworkAgentSpecifier parcelSpecifier = + WifiNetworkAgentSpecifier.CREATOR.createFromParcel(parcelR); + + assertEquals(specifier, parcelSpecifier); + } + + /** + * Validate that the NetworkAgentSpecifier cannot be used in a {@link NetworkRequest} by apps. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkAgentSpecifierNotUsedInNetworkRequest() { + WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier(); + + specifier.assertValidFromUid(TEST_UID); + } + + /** + * Validate NetworkAgentSpecifier equals with itself. + * a) Create network agent specifier 1 for WPA_PSK network + * b) Create network agent specifier 2 with the same params as specifier 1. + * c) Ensure that the specifier 2 equals specifier 1. + */ + @Test + public void testWifiNetworkAgentSpecifierEqualsSame() { + WifiNetworkAgentSpecifier specifier1 = createDefaultNetworkAgentSpecifier(); + WifiNetworkAgentSpecifier specifier2 = createDefaultNetworkAgentSpecifier(); + + assertTrue(specifier2.equals(specifier1)); + } + + /** + * Validate NetworkAgentSpecifier equals between instances of {@link WifiNetworkAgentSpecifier}. + * a) Create network agent specifier 1 for WPA_PSK network + * b) Create network agent specifier 2 with different key mgmt params. + * c) Ensure that the specifier 2 does not equal specifier 1. + */ + @Test + public void testWifiNetworkAgentSpecifierDoesNotEqualsWhenKeyMgmtDifferent() { + WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration(); + WifiNetworkAgentSpecifier specifier1 = + new WifiNetworkAgentSpecifier( + wifiConfiguration1, + TEST_UID); + + WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1); + wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + WifiNetworkAgentSpecifier specifier2 = + new WifiNetworkAgentSpecifier( + wifiConfiguration2, + TEST_UID); + + assertFalse(specifier2.equals(specifier1)); + } + + /** + * Validate NetworkAgentSpecifier equals between instances of {@link WifiNetworkAgentSpecifier}. + * a) Create network agent specifier 1 for WPA_PSK network + * b) Create network agent specifier 2 with different SSID. + * c) Ensure that the specifier 2 does not equal specifier 1. + */ + @Test + public void testWifiNetworkAgentSpecifierDoesNotSatisifyWhenSsidDifferent() { + WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration(); + WifiNetworkAgentSpecifier specifier1 = + new WifiNetworkAgentSpecifier( + wifiConfiguration1, + TEST_UID); + + WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1); + wifiConfiguration2.SSID = TEST_SSID_1; + WifiNetworkAgentSpecifier specifier2 = + new WifiNetworkAgentSpecifier( + wifiConfiguration2, + TEST_UID); + + assertFalse(specifier2.equals(specifier1)); + } + + /** + * Validate NetworkAgentSpecifier equals between instances of {@link WifiNetworkAgentSpecifier}. + * a) Create network agent specifier 1 for WPA_PSK network + * b) Create network agent specifier 2 with different BSSID. + * c) Ensure that the specifier 2 does not equal specifier 1. + */ + @Test + public void testWifiNetworkAgentSpecifierDoesNotSatisifyWhenBssidDifferent() { + WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration(); + WifiNetworkAgentSpecifier specifier1 = + new WifiNetworkAgentSpecifier( + wifiConfiguration1, + TEST_UID); + + WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1); + wifiConfiguration2.BSSID = TEST_BSSID_1; + WifiNetworkAgentSpecifier specifier2 = + new WifiNetworkAgentSpecifier( + wifiConfiguration2, + TEST_UID); + + assertFalse(specifier2.equals(specifier1)); + } + + /** + * Validate NetworkAgentSpecifier matching. + * a) Create a network agent specifier for WPA_PSK network + * b) Ensure that the specifier matches {@code null} and {@link MatchAllNetworkSpecifier} + * specifiers. + */ + @Test + public void testWifiNetworkAgentSpecifierSatisifiesNullAndAllMatch() { + WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier(); + + assertTrue(specifier.satisfiedBy(null)); + assertTrue(specifier.satisfiedBy(new MatchAllNetworkSpecifier())); + } + + /** + * Validate NetworkAgentSpecifier matching with itself. + * a) Create network agent specifier 1 for WPA_PSK network + * b) Create network agent specifier 2 with the same params as specifier 1. + * c) Ensure that invoking {@link NetworkSpecifier#satisfiedBy(NetworkSpecifier)} on 2 + * {@link WifiNetworkAgentSpecifier} throws an exception. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkAgentSpecifierDoesNotSatisifySame() { + WifiNetworkAgentSpecifier specifier1 = createDefaultNetworkAgentSpecifier(); + WifiNetworkAgentSpecifier specifier2 = createDefaultNetworkAgentSpecifier(); + + assertTrue(specifier2.satisfiedBy(specifier1)); + } + + /** + * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching. + * a) Create network agent specifier for WPA_PSK network + * b) Create network specifier with matching SSID pattern. + * c) Ensure that the agent specifier is satisfied by specifier. + */ + @Test + public void + testWifiNetworkAgentSpecifierSatisfiesNetworkSpecifierWithSsidPattern() { + WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier(); + + PatternMatcher ssidPattern = + new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX); + Pair bssidPattern = + Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS); + WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration(); + wificonfigurationNetworkSpecifier.allowedKeyManagement + .set(WifiConfiguration.KeyMgmt.WPA_PSK); + WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( + ssidPattern, + bssidPattern, + wificonfigurationNetworkSpecifier, + TEST_UID); + + assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); + assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); + } + + /** + * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching. + * a) Create network agent specifier for WPA_PSK network + * b) Create network specifier with matching BSSID pattern. + * c) Ensure that the agent specifier is satisfied by specifier. + */ + @Test + public void + testWifiNetworkAgentSpecifierSatisfiesNetworkSpecifierWithBssidPattern() { + WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier(); + + PatternMatcher ssidPattern = + new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB); + Pair bssidPattern = + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)); + WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration(); + wificonfigurationNetworkSpecifier.allowedKeyManagement + .set(WifiConfiguration.KeyMgmt.WPA_PSK); + WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( + ssidPattern, + bssidPattern, + wificonfigurationNetworkSpecifier, + TEST_UID); + + assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); + assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); + } + + /** + * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching. + * a) Create network agent specifier for WPA_PSK network + * b) Create network specifier with matching SSID & BSSID pattern. + * c) Ensure that the agent specifier is satisfied by specifier. + */ + @Test + public void + testWifiNetworkAgentSpecifierSatisfiesNetworkSpecifierWithSsidAndBssidPattern() { + WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier(); + + PatternMatcher ssidPattern = + new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX); + Pair bssidPattern = + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)); + WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration(); + wificonfigurationNetworkSpecifier.allowedKeyManagement + .set(WifiConfiguration.KeyMgmt.WPA_PSK); + WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( + ssidPattern, + bssidPattern, + wificonfigurationNetworkSpecifier, + TEST_UID); + + assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); + assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); + } + + /** + * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching. + * a) Create network agent specifier for WPA_PSK network + * b) Create network specifier with non-matching SSID pattern. + * c) Ensure that the agent specifier is not satisfied by specifier. + */ + @Test + public void + testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithSsidPattern() { + WifiConfiguration wifiConfigurationNetworkAgent = createDefaultWifiConfiguration(); + wifiConfigurationNetworkAgent.SSID = "\"" + TEST_SSID_1 + "\""; + WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = + new WifiNetworkAgentSpecifier( + wifiConfigurationNetworkAgent, + TEST_UID); + + PatternMatcher ssidPattern = + new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX); + Pair bssidPattern = + Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS); + WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration(); + wificonfigurationNetworkSpecifier.allowedKeyManagement + .set(WifiConfiguration.KeyMgmt.WPA_PSK); + WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( + ssidPattern, + bssidPattern, + wificonfigurationNetworkSpecifier, + TEST_UID); + + assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); + assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); + } + + /** + * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching. + * a) Create network agent specifier for WPA_PSK network + * b) Create network specifier with non-matching BSSID pattern. + * c) Ensure that the agent specifier is not satisfied by specifier. + */ + @Test + public void + testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithBssidPattern() { + WifiConfiguration wifiConfigurationNetworkAgent = createDefaultWifiConfiguration(); + wifiConfigurationNetworkAgent.BSSID = TEST_BSSID_1; + WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = + new WifiNetworkAgentSpecifier( + wifiConfigurationNetworkAgent, + TEST_UID); + + PatternMatcher ssidPattern = + new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB); + Pair bssidPattern = + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)); + WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration(); + wificonfigurationNetworkSpecifier.allowedKeyManagement + .set(WifiConfiguration.KeyMgmt.WPA_PSK); + WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( + ssidPattern, + bssidPattern, + wificonfigurationNetworkSpecifier, + TEST_UID); + + assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); + assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); + } + + /** + * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching. + * a) Create network agent specifier for WPA_PSK network + * b) Create network specifier with non-matching SSID and BSSID pattern. + * c) Ensure that the agent specifier is not satisfied by specifier. + */ + @Test + public void + testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithSsidAndBssidPattern() { + WifiConfiguration wifiConfigurationNetworkAgent = createDefaultWifiConfiguration(); + wifiConfigurationNetworkAgent.BSSID = TEST_BSSID_1; + WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = + new WifiNetworkAgentSpecifier( + wifiConfigurationNetworkAgent, + TEST_UID); + + PatternMatcher ssidPattern = + new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX); + Pair bssidPattern = + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)); + WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration(); + wificonfigurationNetworkSpecifier.allowedKeyManagement + .set(WifiConfiguration.KeyMgmt.WPA_PSK); + WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( + ssidPattern, + bssidPattern, + wificonfigurationNetworkSpecifier, + TEST_UID); + + assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); + assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); + } + + /** + * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching. + * a) Create network agent specifier for WPA_PSK network + * b) Create network specifier with matching SSID and BSSID pattern, but different key mgmt. + * c) Ensure that the agent specifier is not satisfied by specifier. + */ + @Test + public void + testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithDifferentKeyMgmt() { + WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier(); + + PatternMatcher ssidPattern = + new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX); + Pair bssidPattern = + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)); + WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration(); + wificonfigurationNetworkSpecifier.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( + ssidPattern, + bssidPattern, + wificonfigurationNetworkSpecifier, + TEST_UID); + + assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); + assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); + } + + /** + * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching. + * a) Create network agent specifier for WPA_PSK network + * b) Create network specifier with matching SSID and BSSID pattern, but different UID. + * c) Ensure that the agent specifier is not satisfied by specifier. + */ + @Test + public void + testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithDifferentUid() { + WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier(); + + PatternMatcher ssidPattern = + new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX); + Pair bssidPattern = + Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), + MacAddress.fromString(TEST_BSSID_OUI_MASK)); + WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration(); + wificonfigurationNetworkSpecifier.allowedKeyManagement + .set(WifiConfiguration.KeyMgmt.WPA_PSK); + WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( + ssidPattern, + bssidPattern, + wificonfigurationNetworkSpecifier, + TEST_UID_1); + + assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); + assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); + } + + private WifiConfiguration createDefaultWifiConfiguration() { + WifiConfiguration wifiConfiguration = new WifiConfiguration(); + wifiConfiguration.SSID = "\"" + TEST_SSID + "\""; + wifiConfiguration.BSSID = TEST_BSSID; + wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY; + return wifiConfiguration; + } + + private WifiNetworkAgentSpecifier createDefaultNetworkAgentSpecifier() { + return new WifiNetworkAgentSpecifier(createDefaultWifiConfiguration(), TEST_UID); + } + +} From 3c38ee435a8e564d8c9dbb1c9f7ba3619532ff6f Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Tue, 9 Oct 2018 10:06:33 -0700 Subject: [PATCH 5/5] WifiManager: Network request match callback registration Add and Implement the new network request match callback register/unregister @hide methods to be used by settings UI. Note: This uses the same pattern used for other callbacks to settings like traffic state change, softap. Bug: 113878056 Test: ./frameworks/base/wifi/tests/runtests.sh Change-Id: Ia4964759c6b10790dda0fc61a41c4d95dab904c9 --- Android.bp | 2 + api/system-current.txt | 14 ++ .../wifi/INetworkRequestMatchCallback.aidl | 36 +++ .../INetworkRequestUserSelectionCallback.aidl | 31 +++ wifi/java/android/net/wifi/IWifiManager.aidl | 5 + wifi/java/android/net/wifi/WifiManager.java | 237 ++++++++++++++++++ .../src/android/net/wifi/WifiManagerTest.java | 89 ++++++- 7 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl create mode 100644 wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl diff --git a/Android.bp b/Android.bp index 56d6557ee6e1f..940e3d53819bf 100644 --- a/Android.bp +++ b/Android.bp @@ -592,6 +592,8 @@ java_defaults { "telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl", "telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl", "telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl", + "wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl", + "wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl", "wifi/java/android/net/wifi/ISoftApCallback.aidl", "wifi/java/android/net/wifi/ITrafficStateCallback.aidl", "wifi/java/android/net/wifi/IWifiManager.aidl", diff --git a/api/system-current.txt b/api/system-current.txt index 9897db2f034be..22f19dc19a881 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3594,8 +3594,10 @@ package android.net.wifi { method public boolean isPortableHotspotSupported(); method public boolean isWifiApEnabled(); method public boolean isWifiScannerSupported(); + method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler); method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration); method public boolean startScan(android.os.WorkSource); + method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback); field public static final int CHANGE_REASON_ADDED = 0; // 0x0 field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2 field public static final int CHANGE_REASON_REMOVED = 1; // 0x1 @@ -3623,6 +3625,18 @@ package android.net.wifi { method public abstract void onSuccess(); } + public static abstract interface WifiManager.NetworkRequestMatchCallback { + method public abstract void onMatch(java.util.List); + method public abstract void onUserSelectionCallbackRegistration(android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback); + method public abstract void onUserSelectionConnectFailure(android.net.wifi.WifiConfiguration); + method public abstract void onUserSelectionConnectSuccess(android.net.wifi.WifiConfiguration); + } + + public static abstract interface WifiManager.NetworkRequestUserSelectionCallback { + method public abstract void reject(); + method public abstract void select(android.net.wifi.WifiConfiguration); + } + public class WifiNetworkConnectionStatistics implements android.os.Parcelable { ctor public WifiNetworkConnectionStatistics(int, int); ctor public WifiNetworkConnectionStatistics(); diff --git a/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl b/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl new file mode 100644 index 0000000000000..f472a021b0312 --- /dev/null +++ b/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 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.wifi; + +import android.net.wifi.INetworkRequestUserSelectionCallback; +import android.net.wifi.WifiConfiguration; + +/** + * Interface for network request match callback. + * + * @hide + */ +oneway interface INetworkRequestMatchCallback +{ + void onUserSelectionCallbackRegistration(in INetworkRequestUserSelectionCallback userSelectionCallback); + + void onMatch(in List wificonfigurations); + + void onUserSelectionConnectSuccess(in WifiConfiguration wificonfiguration); + + void onUserSelectionConnectFailure(in WifiConfiguration wificonfiguration); +} diff --git a/wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl b/wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl new file mode 100644 index 0000000000000..524cefbb295fb --- /dev/null +++ b/wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2018 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.wifi; + +import android.net.wifi.WifiConfiguration; + +/** + * Interface for providing user selection in response to + * network request match callback. + * @hide + */ +oneway interface INetworkRequestUserSelectionCallback +{ + void select(in WifiConfiguration wificonfiguration); + + void reject(); +} diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 12f50c8af8a95..1fd68ec1df70c 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -25,6 +25,7 @@ import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.DhcpInfo; import android.net.Network; +import android.net.wifi.INetworkRequestMatchCallback; import android.net.wifi.ISoftApCallback; import android.net.wifi.ITrafficStateCallback; import android.net.wifi.PasspointManagementObjectDefinition; @@ -185,5 +186,9 @@ interface IWifiManager void registerTrafficStateCallback(in IBinder binder, in ITrafficStateCallback callback, int callbackIdentifier); void unregisterTrafficStateCallback(int callbackIdentifier); + + void registerNetworkRequestMatchCallback(in IBinder binder, in INetworkRequestMatchCallback callback, int callbackIdentifier); + + void unregisterNetworkRequestMatchCallback(int callbackIdentifier); } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index f72705aaafae3..9ce548601f23e 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1211,6 +1211,243 @@ public class WifiManager { } } + /** + * Interface for indicating user selection from the list of networks presented in the + * {@link NetworkRequestMatchCallback#onMatch(List)}. + * + * The platform will implement this callback and pass it along with the + * {@link NetworkRequestMatchCallback#onUserSelectionCallbackRegistration( + * NetworkRequestUserSelectionCallback)}. The UI component handling + * {@link NetworkRequestMatchCallback} will invoke {@link #select(WifiConfiguration)} or + * {@link #reject()} to return the user's selection back to the platform via this callback. + * @hide + */ + @SystemApi + public interface NetworkRequestUserSelectionCallback { + /** + * User selected this network to connect to. + * @param wifiConfiguration WifiConfiguration object corresponding to the network + * user selected. + */ + void select(@NonNull WifiConfiguration wifiConfiguration); + + /** + * User rejected the app's request. + */ + void reject(); + } + + /** + * Interface for network request callback. Should be implemented by applications and passed when + * calling {@link #registerNetworkRequestMatchCallback(NetworkRequestMatchCallback, Handler)}. + * + * This is meant to be implemented by a UI component to present the user with a list of networks + * matching the app's request. The user is allowed to pick one of these networks to connect to + * or reject the request by the app. + * @hide + */ + @SystemApi + public interface NetworkRequestMatchCallback { + /** + * Invoked to register a callback to be invoked to convey user selection. The callback + * object paased in this method is to be invoked by the UI component after the service sends + * a list of matching scan networks using {@link #onMatch(List)} and user picks a network + * from that list. + * + * @param userSelectionCallback Callback object to send back the user selection. + */ + void onUserSelectionCallbackRegistration( + @NonNull NetworkRequestUserSelectionCallback userSelectionCallback); + + /** + * Invoked when a network request initiated by an app matches some networks in scan results. + * This may be invoked multiple times for a single network request as the platform finds new + * networks in scan results. + * + * @param wifiConfigurations List of {@link WifiConfiguration} objects corresponding to the + * networks matching the request. + */ + void onMatch(@NonNull List wifiConfigurations); + + /** + * Invoked on a successful connection with the network that the user selected + * via {@link NetworkRequestUserSelectionCallback}. + * + * @param wifiConfiguration WifiConfiguration object corresponding to the network that the + * user selected. + */ + void onUserSelectionConnectSuccess(@NonNull WifiConfiguration wifiConfiguration); + + /** + * Invoked on failure to establish connection with the network that the user selected + * via {@link NetworkRequestUserSelectionCallback}. + * + * @param wifiConfiguration WifiConfiguration object corresponding to the network + * user selected. + */ + void onUserSelectionConnectFailure(@NonNull WifiConfiguration wifiConfiguration); + } + + /** + * Callback proxy for NetworkRequestUserSelectionCallback objects. + * @hide + */ + private class NetworkRequestUserSelectionCallbackProxy implements + NetworkRequestUserSelectionCallback { + private final INetworkRequestUserSelectionCallback mCallback; + + NetworkRequestUserSelectionCallbackProxy( + INetworkRequestUserSelectionCallback callback) { + mCallback = callback; + } + + @Override + public void select(@NonNull WifiConfiguration wifiConfiguration) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "NetworkRequestUserSelectionCallbackProxy: select " + + "wificonfiguration: " + wifiConfiguration); + } + try { + mCallback.select(wifiConfiguration); + } catch (RemoteException e) { + Log.e(TAG, "Failed to invoke onSelected", e); + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void reject() { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "NetworkRequestUserSelectionCallbackProxy: reject"); + } + try { + mCallback.reject(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to invoke onRejected", e); + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Callback proxy for NetworkRequestMatchCallback objects. + * @hide + */ + private class NetworkRequestMatchCallbackProxy extends INetworkRequestMatchCallback.Stub { + private final Handler mHandler; + private final NetworkRequestMatchCallback mCallback; + + NetworkRequestMatchCallbackProxy(Looper looper, NetworkRequestMatchCallback callback) { + mHandler = new Handler(looper); + mCallback = callback; + } + + @Override + public void onUserSelectionCallbackRegistration( + INetworkRequestUserSelectionCallback userSelectionCallback) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "NetworkRequestMatchCallbackProxy: " + + "onUserSelectionCallbackRegistration callback: " + userSelectionCallback); + } + mHandler.post(() -> { + mCallback.onUserSelectionCallbackRegistration( + new NetworkRequestUserSelectionCallbackProxy(userSelectionCallback)); + }); + } + + @Override + public void onMatch(List wifiConfigurations) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "NetworkRequestMatchCallbackProxy: onMatch wificonfigurations: " + + wifiConfigurations); + } + mHandler.post(() -> { + mCallback.onMatch(wifiConfigurations); + }); + } + + @Override + public void onUserSelectionConnectSuccess(WifiConfiguration wifiConfiguration) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "NetworkRequestMatchCallbackProxy: onUserSelectionConnectSuccess " + + " wificonfiguration: " + wifiConfiguration); + } + mHandler.post(() -> { + mCallback.onUserSelectionConnectSuccess(wifiConfiguration); + }); + } + + @Override + public void onUserSelectionConnectFailure(WifiConfiguration wifiConfiguration) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "NetworkRequestMatchCallbackProxy: onUserSelectionConnectFailure" + + " wificonfiguration: " + wifiConfiguration); + } + mHandler.post(() -> { + mCallback.onUserSelectionConnectFailure(wifiConfiguration); + }); + } + } + + /** + * Registers a callback for NetworkRequest matches. See {@link NetworkRequestMatchCallback}. + * Caller can unregister a previously registered callback using + * {@link #unregisterNetworkRequestMatchCallback(NetworkRequestMatchCallback)} + *

    + * Applications should have the + * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. Callers + * without the permission will trigger a {@link java.lang.SecurityException}. + *

    + * + * @param callback Callback for network match events + * @param handler The Handler on whose thread to execute the callbacks of the {@code callback} + * object. If null, then the application's main thread will be used. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void registerNetworkRequestMatchCallback(@NonNull NetworkRequestMatchCallback callback, + @Nullable Handler handler) { + if (callback == null) throw new IllegalArgumentException("callback cannot be null"); + Log.v(TAG, "registerNetworkRequestMatchCallback: callback=" + callback + + ", handler=" + handler); + + Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper(); + Binder binder = new Binder(); + try { + mService.registerNetworkRequestMatchCallback( + binder, new NetworkRequestMatchCallbackProxy(looper, callback), + callback.hashCode()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Unregisters a callback for NetworkRequest matches. See {@link NetworkRequestMatchCallback}. + *

    + * Applications should have the + * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. Callers + * without the permission will trigger a {@link java.lang.SecurityException}. + *

    + * + * @param callback Callback for network match events + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void unregisterNetworkRequestMatchCallback( + @NonNull NetworkRequestMatchCallback callback) { + if (callback == null) throw new IllegalArgumentException("callback cannot be null"); + Log.v(TAG, "unregisterNetworkRequestMatchCallback: callback=" + callback); + + try { + mService.unregisterNetworkRequestMatchCallback(callback.hashCode()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Provide a list of network suggestions to the device. See {@link WifiNetworkSuggestion} * for a detailed explanation of the parameters. diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index e40b657aded05..ea41bb39c4d5b 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -43,6 +43,8 @@ import android.net.wifi.WifiManager.LocalOnlyHotspotCallback; import android.net.wifi.WifiManager.LocalOnlyHotspotObserver; import android.net.wifi.WifiManager.LocalOnlyHotspotReservation; import android.net.wifi.WifiManager.LocalOnlyHotspotSubscription; +import android.net.wifi.WifiManager.NetworkRequestMatchCallback; +import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback; import android.net.wifi.WifiManager.SoftApCallback; import android.net.wifi.WifiManager.TrafficStateCallback; import android.os.Handler; @@ -59,6 +61,8 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; + /** * Unit tests for {@link android.net.wifi.WifiManager}. */ @@ -67,16 +71,19 @@ public class WifiManagerTest { private static final int ERROR_NOT_SET = -1; private static final int ERROR_TEST_REASON = 5; + private static final int TEST_UID = 14553; private static final String TEST_PACKAGE_NAME = "TestPackage"; private static final String TEST_COUNTRY_CODE = "US"; @Mock Context mContext; - @Mock IWifiManager mWifiService; + @Mock + android.net.wifi.IWifiManager mWifiService; @Mock ApplicationInfo mApplicationInfo; @Mock WifiConfiguration mApConfig; @Mock IBinder mAppBinder; @Mock SoftApCallback mSoftApCallback; @Mock TrafficStateCallback mTrafficStateCallback; + @Mock NetworkRequestMatchCallback mNetworkRequestMatchCallback; private Handler mHandler; private TestLooper mLooper; @@ -1163,4 +1170,84 @@ i * Verify that a call to cancel WPS immediately returns a failure. assertEquals(1, altLooper.dispatchAll()); verify(mTrafficStateCallback).onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT); } + + /** + * Verify the call to registerNetworkRequestMatchCallback goes to WifiServiceImpl. + */ + @Test + public void registerNetworkRequestMatchCallbackCallGoesToWifiServiceImpl() + throws Exception { + when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(INetworkRequestMatchCallback.Stub.class); + mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback, null); + verify(mWifiService).registerNetworkRequestMatchCallback( + any(IBinder.class), callbackCaptor.capture(), anyInt()); + + INetworkRequestUserSelectionCallback iUserSelectionCallback = + mock(INetworkRequestUserSelectionCallback.class); + + assertEquals(0, mLooper.dispatchAll()); + callbackCaptor.getValue().onMatch(new ArrayList()); + assertEquals(1, mLooper.dispatchAll()); + verify(mNetworkRequestMatchCallback).onMatch(anyList()); + + callbackCaptor.getValue().onUserSelectionConnectSuccess(new WifiConfiguration()); + assertEquals(1, mLooper.dispatchAll()); + verify(mNetworkRequestMatchCallback).onUserSelectionConnectSuccess( + any(WifiConfiguration.class)); + + callbackCaptor.getValue().onUserSelectionConnectFailure(new WifiConfiguration()); + assertEquals(1, mLooper.dispatchAll()); + verify(mNetworkRequestMatchCallback).onUserSelectionConnectFailure( + any(WifiConfiguration.class)); + } + + /** + * Verify the call to unregisterNetworkRequestMatchCallback goes to WifiServiceImpl. + */ + @Test + public void unregisterNetworkRequestMatchCallbackCallGoesToWifiServiceImpl() throws Exception { + ArgumentCaptor callbackIdentifier = ArgumentCaptor.forClass(Integer.class); + mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback, mHandler); + verify(mWifiService).registerNetworkRequestMatchCallback( + any(IBinder.class), any(INetworkRequestMatchCallback.class), + callbackIdentifier.capture()); + + mWifiManager.unregisterNetworkRequestMatchCallback(mNetworkRequestMatchCallback); + verify(mWifiService).unregisterNetworkRequestMatchCallback( + eq((int) callbackIdentifier.getValue())); + } + + /** + * Verify the call to NetworkRequestUserSelectionCallback goes to + * WifiServiceImpl. + */ + @Test + public void networkRequestUserSelectionCallbackCallGoesToWifiServiceImpl() + throws Exception { + when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(INetworkRequestMatchCallback.Stub.class); + mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback, null); + verify(mWifiService).registerNetworkRequestMatchCallback( + any(IBinder.class), callbackCaptor.capture(), anyInt()); + + INetworkRequestUserSelectionCallback iUserSelectionCallback = + mock(INetworkRequestUserSelectionCallback.class); + ArgumentCaptor userSelectionCallbackCaptor = + ArgumentCaptor.forClass(NetworkRequestUserSelectionCallback.class); + callbackCaptor.getValue().onUserSelectionCallbackRegistration( + iUserSelectionCallback); + assertEquals(1, mLooper.dispatchAll()); + verify(mNetworkRequestMatchCallback).onUserSelectionCallbackRegistration( + userSelectionCallbackCaptor.capture()); + + WifiConfiguration selected = new WifiConfiguration(); + userSelectionCallbackCaptor.getValue().select(selected); + verify(iUserSelectionCallback).select(selected); + + userSelectionCallbackCaptor.getValue().reject(); + verify(iUserSelectionCallback).reject(); + } }