diff options
author | 2018-11-06 17:42:58 +0000 | |
---|---|---|
committer | 2018-11-06 17:42:58 +0000 | |
commit | d84d668d45de133c4191cd21baf50bb4f3d219a6 (patch) | |
tree | 6c2bf40883d50ac82b5b3f82f9acab1d18102090 | |
parent | 1cc9e5a9f05bac51fb8bac54b01861ca3551a5d0 (diff) | |
parent | 3c38ee435a8e564d8c9dbb1c9f7ba3619532ff6f (diff) |
Merge changes from topic "network_request_match_callback"
* changes:
WifiManager: Network request match callback registration
wifi(API): NetworkSpecifier for Wifi NetworkAgent
wifi(API): Mark old API's deprecated
wifi(API): New API surface for network suggestion
wifi(API): New API surface for connection via NetworkRequest
19 files changed, 2974 insertions, 37 deletions
diff --git a/Android.bp b/Android.bp index 44f7edd98e0a..65f232d85614 100644 --- a/Android.bp +++ b/Android.bp @@ -595,6 +595,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/current.txt b/api/current.txt index f387c5d7e11b..dd4c781b6e7b 100755 --- a/api/current.txt +++ b/api/current.txt @@ -28690,7 +28690,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(); @@ -28844,7 +28844,8 @@ 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.net.wifi.WifiNetworkSuggestion>, 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); @@ -28852,10 +28853,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<android.net.wifi.WifiConfiguration> 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<android.net.wifi.WifiConfiguration> getConfiguredNetworks(); method public android.net.wifi.WifiInfo getConnectionInfo(); method public android.net.DhcpInfo getDhcpInfo(); method public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations(); @@ -28870,18 +28871,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<android.net.wifi.WifiNetworkSuggestion>); 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 @@ -28955,6 +28957,29 @@ 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.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<android.net.wifi.WifiNetworkSuggestion> CREATOR; + } + public deprecated class WpsInfo implements android.os.Parcelable { ctor public deprecated WpsInfo(); ctor public deprecated WpsInfo(android.net.wifi.WpsInfo); diff --git a/api/system-current.txt b/api/system-current.txt index 3074efdc5567..d8da475e3eec 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3615,7 +3615,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(); @@ -3644,8 +3644,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 @@ -3673,6 +3675,18 @@ package android.net.wifi { method public abstract void onSuccess(); } + public static abstract interface WifiManager.NetworkRequestMatchCallback { + method public abstract void onMatch(java.util.List<android.net.wifi.WifiConfiguration>); + 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/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java index 98f356722bf3..4cd000113b7e 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 04266c5b3a0f..b9222a86a092 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/INetworkRequestMatchCallback.aidl b/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl new file mode 100644 index 000000000000..f472a021b031 --- /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<WifiConfiguration> 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 000000000000..524cefbb295f --- /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 12f50c8af8a9..1fd68ec1df70 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/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 03306143872b..05747163550e 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 9adbe67c1553..9ce548601f23 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -25,19 +25,17 @@ 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; 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; @@ -52,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; @@ -1035,7 +1032,17 @@ public class WifiManager { * </ul> * @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. + * <b>Compatibility Note:</b> For applications targeting + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return an empty list. */ + @Deprecated public List<WifiConfiguration> getConfiguredNetworks() { try { ParceledListSlice<WifiConfiguration> parceledList = @@ -1135,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. + * <b>Compatibility Note:</b> 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; @@ -1160,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. + * <b>Compatibility Note:</b> 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; @@ -1185,6 +1212,302 @@ 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<WifiConfiguration> 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<WifiConfiguration> 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)} + * <p> + * Applications should have the + * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. Callers + * without the permission will trigger a {@link java.lang.SecurityException}. + * <p> + * + * @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}. + * <p> + * Applications should have the + * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. Callers + * without the permission will trigger a {@link java.lang.SecurityException}. + * <p> + * + * @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. + *<p> + * 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. + *<p> + * 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. + *<p> + * NOTE: + * <li> These networks are just a suggestion to the platform. The platform will ultimately + * decide on which network the device connects to. </li> + * <li> 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.</li> + * <li> 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.</li> + * + * @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<WifiNetworkSuggestion> 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<WifiNetworkSuggestion> 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 * service provider specified in the configuration. @@ -1299,7 +1622,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. + * <b>Compatibility Note:</b> 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()); @@ -1314,10 +1647,8 @@ public class WifiManager { * network is initiated. This may result in the asynchronous delivery * of state change events. * <p> - * <b>Note:</b> 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, + * <b>Note:</b> 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. @@ -1335,29 +1666,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. + * <b>Compatibility Note:</b> 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; } @@ -1372,7 +1698,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. + * <b>Compatibility Note:</b> 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()); @@ -1385,7 +1721,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. + * <b>Compatibility Note:</b> 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()); @@ -1400,7 +1746,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. + * <b>Compatibility Note:</b> 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()); @@ -1415,7 +1771,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. + * <b>Compatibility Note:</b> 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()); @@ -1821,7 +2187,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); diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java new file mode 100644 index 000000000000..55fde4ca335e --- /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<WifiNetworkAgentSpecifier> CREATOR = + new Creator<WifiNetworkAgentSpecifier>() { + @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/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java new file mode 100644 index 000000000000..ae4f40511b58 --- /dev/null +++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java @@ -0,0 +1,511 @@ +/* + * 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.app.PendingIntent; +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; +import java.util.List; + +/** + * WifiNetworkConfigBuilder to use for creating Wi-Fi network configuration. + * <li>See {@link #buildNetworkSpecifier()} for creating a network specifier to use in + * {@link NetworkRequest}.</li> + * <li>See {@link #buildNetworkSuggestion()} for creating a network suggestion to use in + * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)}.</li> + */ +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<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN = + new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS); + private static final Pair<MacAddress, MacAddress> 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; + private static final int UNASSIGNED_PRIORITY = -1; + + /** + * SSID pattern match specified by the app. + */ + private @Nullable PatternMatcher mSsidPatternMatcher; + /** + * BSSID pattern match specified by the app. + * Pair of <BaseAddress, Mask>. + */ + private @Nullable Pair<MacAddress, MacAddress> 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; + /** + * 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; + mBssidPatternMatcher = null; + mPskPassphrase = null; + mEnterpriseConfig = null; + mIsHiddenSSID = false; + mIsAppInteractionRequired = false; + mIsUserInteractionRequired = false; + mIsMetered = false; + mPriority = UNASSIGNED_PRIORITY; + } + + /** + * Set the unicode SSID match pattern to use for filtering networks from scan results. + * <p> + * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li> + * <li>Overrides any previous value set using {@link #setSsid(String)} or + * {@link #setSsidPattern(PatternMatcher)}.</li> + * + * @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. + * <p> + * <li>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.</li> + * <li>For network suggestions ({@link WifiNetworkSuggestion}), built using + * {@link #buildNetworkSuggestion()}, sets the SSID for the network.</li> + * <li>Overrides any previous value set using {@link #setSsid(String)} or + * {@link #setSsidPattern(PatternMatcher)}.</li> + * + * @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}. + * <p> + * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li> + * <li>Overrides any previous value set using {@link #setBssid(MacAddress)} or + * {@link #setBssidPattern(MacAddress, MacAddress)}.</li> + * + * @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. + * <p> + * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li> + * <li>Overrides any previous value set using {@link #setBssid(MacAddress)} or + * {@link #setBssidPattern(MacAddress, MacAddress)}.</li> + * + * @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; + } + + /** + * Specifies whether this represents a hidden network. + * <p> + * <li>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.</li> + * <li>If not set, defaults to false (i.e not a hidden network).</li> + * + * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder + * method. + */ + public WifiNetworkConfigBuilder setIsHiddenSsid() { + mIsHiddenSSID = true; + return this; + } + + /** + * Specifies whether the app needs to log in to a captive portal to obtain Internet access. + * <p> + * 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. + * <p> + * <li>Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.</li> + * <li>If not set, defaults to false (i.e no app interaction required).</li> + * + * @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. + * <p> + * <li>Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.</li> + * <li>If not set, defaults to false (i.e no user interaction required).</li> + * + * @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). + * <p> + * <li>Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.</li> + * <li>If not set, defaults to -1 (i.e unassigned priority).</li> + * + * @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. + * <p> + * <li>Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.</li> + * <li>If not set, defaults to false (i.e not metered).</li> + * + * @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. + * + * 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; + wifiConfiguration.priority = mPriority; + wifiConfiguration.meteredOverride = + mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED + : WifiConfiguration.METERED_OVERRIDE_NONE; + 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}. + *<p> + * Note: Apps can set a combination of network match params: + * <li> SSID Pattern using {@link #setSsidPattern(PatternMatcher)} OR Specific SSID using + * {@link #setSsid(String)}. </li> + * AND/OR + * <li> BSSID Pattern using {@link #setBssidPattern(MacAddress, MacAddress)} OR Specific BSSID + * using {@link #setBssid(MacAddress)} </li> + * 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. + *</p> + * + * 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 (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 setPreSharedKey or setEnterpriseConfig can" + + " be invoked for network specifier"); + } + + return new WifiNetworkSpecifier( + mSsidPatternMatcher, + mBssidPatternMatcher, + 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/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java new file mode 100644 index 000000000000..4348399b404b --- /dev/null +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -0,0 +1,181 @@ +/* + * 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 <BaseAddress, Mask>. + */ + public final Pair<MacAddress, MacAddress> bssidPatternMatcher; + + /** + * Security credentials for the network. + * <p> + * 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<MacAddress, MacAddress> 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<WifiNetworkSpecifier> CREATOR = + new Creator<WifiNetworkSpecifier>() { + @Override + public WifiNetworkSpecifier createFromParcel(Parcel in) { + PatternMatcher ssidPatternMatcher = in.readParcelable(/* classLoader */null); + MacAddress baseAddress = in.readParcelable(null); + MacAddress mask = in.readParcelable(null); + Pair<MacAddress, MacAddress> 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; + } + 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); + } + + @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/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java new file mode 100644 index 000000000000..04b9cb5dfb8d --- /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. + *<p> + * 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<WifiNetworkSuggestion> CREATOR = + new Creator<WifiNetworkSuggestion>() { + @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/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index e40b657aded0..ea41bb39c4d5 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<INetworkRequestMatchCallback.Stub> 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<WifiConfiguration>()); + 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<Integer> 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<INetworkRequestMatchCallback.Stub> 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<NetworkRequestUserSelectionCallback> 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(); + } } 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 000000000000..1b0007c9e732 --- /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<MacAddress, MacAddress> 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<MacAddress, MacAddress> 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<MacAddress, MacAddress> 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<MacAddress, MacAddress> 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<MacAddress, MacAddress> 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<MacAddress, MacAddress> 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<MacAddress, MacAddress> 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<MacAddress, MacAddress> 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); + } + +} 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 000000000000..8980ddba238b --- /dev/null +++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java @@ -0,0 +1,481 @@ +/* + * 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.assertFalse; +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 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 + * {@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 {@link WifiNetworkConfigBuilder#setIsAppInteractionRequired()} is set. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSpecifierBuilderWithRequiredAppInteraction() { + new WifiNetworkConfigBuilder() + .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/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java new file mode 100644 index 000000000000..856f0c79a2e7 --- /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)); + } +} 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 000000000000..6bab60dd480b --- /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); + } +} |