diff options
author | Xin Li <delphij@google.com> | 2020-08-31 21:21:38 -0700 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2020-08-31 21:21:38 -0700 |
commit | 628590d7ec80e10a3fc24b1c18a1afb55cca10a8 (patch) | |
tree | 4b1c3f52d86d7fb53afbe9e9438468588fa489f8 /wifi | |
parent | b11b8ec3aec8bb42f2c07e1c5ac7942da293baa8 (diff) | |
parent | d2d3a20624d968199353ccf6ddbae6f3ac39c9af (diff) |
Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)
Bug: 166295507
Merged-In: I3d92a6de21a938f6b352ec26dc23420c0fe02b27
Change-Id: Ifdb80563ef042738778ebb8a7581a97c4e3d96e2
Diffstat (limited to 'wifi')
144 files changed, 20366 insertions, 4384 deletions
diff --git a/wifi/Android.bp b/wifi/Android.bp new file mode 100644 index 000000000000..941ff61b3ba5 --- /dev/null +++ b/wifi/Android.bp @@ -0,0 +1,172 @@ +// Copyright (C) 2019 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. + +java_defaults { + name: "wifi-module-sdk-version-defaults", + min_sdk_version: "30", + target_sdk_version: "30", +} + +filegroup { + name: "framework-wifi-updatable-exported-aidl-sources", + srcs: ["aidl-export/**/*.aidl"], + path: "aidl-export", + visibility: ["//visibility:private"], +} + +filegroup { + name: "framework-wifi-updatable-java-sources", + srcs: [ + "java/**/*.java", + "java/**/*.aidl", + ], + exclude_srcs: [ + ":framework-wifi-non-updatable-sources" + ], + path: "java", + visibility: ["//visibility:private"], +} + +filegroup { + name: "framework-wifi-updatable-sources", + srcs: [ + ":framework-wifi-updatable-java-sources", + ":framework-wifi-updatable-exported-aidl-sources", + ], +} + +filegroup { + name: "framework-wifi-non-updatable-sources", + srcs: [ + // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and + // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache + // to a separate package. + "java/android/net/wifi/SoftApConfToXmlMigrationUtil.java", + "java/android/net/wifi/WifiNetworkScoreCache.java", + "java/android/net/wifi/WifiMigration.java", + "java/android/net/wifi/nl80211/*.java", + ":libwificond_ipc_aidl", + ], +} + +filegroup { + name: "framework-wifi-annotations", + srcs: ["java/android/net/wifi/WifiAnnotations.java"], +} + +// list of tests that are allowed to access @hide APIs from framework-wifi +test_access_hidden_api_whitelist = [ + "//frameworks/base/wifi/tests", + "//frameworks/opt/net/wifi/tests/wifitests:__subpackages__", + + "//external/robolectric-shadows:__subpackages__", + "//frameworks/base/packages/SettingsLib/tests/integ", + "//external/sl4a:__subpackages__", +] + +// wifi-service needs pre-jarjared version of framework-wifi so it can reference copied utility +// classes before they are renamed. +java_library { + name: "framework-wifi-pre-jarjar", + defaults: ["wifi-module-sdk-version-defaults"], + sdk_version: "module_current", + static_libs: [ + "framework-wifi-util-lib", + "android.hardware.wifi-V1.0-java-constants", + ], + libs: [ + "framework-annotations-lib", + "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage + ], + srcs: [ + ":framework-wifi-updatable-sources", + ":framework-wifi-util-lib-aidls", + ], + // java_api_finder must accompany `srcs` + plugins: ["java_api_finder"], + installable: false, + visibility: [ + "//frameworks/opt/net/wifi/service", + "//frameworks/opt/net/wifi/tests/wifitests", + ], +} + +// post-jarjar version of framework-wifi +java_sdk_library { + name: "framework-wifi", + defaults: [ + "framework-module-defaults", + "wifi-module-sdk-version-defaults", + ], + static_libs: [ + "framework-wifi-util-lib", + "android.hardware.wifi-V1.0-java-constants", + ], + libs: [ + "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage + ], + srcs: [ + ":framework-wifi-updatable-sources", + ":framework-wifi-util-lib-aidls", + ], + + jarjar_rules: ":wifi-jarjar-rules", + + installable: true, + optimize: { + enabled: false + }, + hostdex: true, // for hiddenapi check + + // Allow access to the stubs from anywhere. + visibility: ["//visibility:public"], + + // Restrict access to implementation library. + impl_library_visibility: [ + "//visibility:override", // Ignore the visibility property. + "//frameworks/opt/net/wifi/service:__subpackages__", + ] + test_access_hidden_api_whitelist, + + apex_available: [ + "com.android.wifi", + "test_com.android.wifi", + ], + permitted_packages: [ + "android.hardware.wifi", + "android.net.wifi", + // Created by jarjar rules. + "com.android.wifi.x", + ], +} + +// defaults for tests that need to build against framework-wifi's @hide APIs +java_defaults { + name: "framework-wifi-test-defaults", + sdk_version: "core_platform", // tests can use @CorePlatformApi's + libs: [ + // order matters: classes in framework-wifi are resolved before framework, meaning + // @hide APIs in framework-wifi are resolved before @SystemApi stubs in framework + "framework-wifi.impl", + "framework", + + // if sdk_version="" this gets automatically included, but here we need to add manually. + "framework-res", + ], + visibility: test_access_hidden_api_whitelist, +} + +filegroup { + name: "wifi-jarjar-rules", + srcs: ["jarjar-rules.txt"], +} diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/wifi/aidl-export/android/net/wifi/SoftApCapability.aidl index 007ec94378fd..bf30709f15d1 100644 --- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl +++ b/wifi/aidl-export/android/net/wifi/SoftApCapability.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2014, The Android Open Source Project + * Copyright (c) 2019, 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. @@ -16,4 +16,4 @@ package android.net.wifi; -parcelable WifiActivityEnergyInfo; +parcelable SoftApCapability; diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.aidl b/wifi/aidl-export/android/net/wifi/SoftApConfiguration.aidl index c81d1f98422d..1d06f458318f 100644 --- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.aidl +++ b/wifi/aidl-export/android/net/wifi/SoftApConfiguration.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2019 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.net.wifi.p2p.servicediscovery; +package android.net.wifi; -parcelable WifiP2pServiceResponse; +parcelable SoftApConfiguration;
\ No newline at end of file diff --git a/wifi/java/android/net/wifi/WpsResult.aidl b/wifi/aidl-export/android/net/wifi/SoftApInfo.aidl index eb4c4f5539ba..d4551cfac044 100644 --- a/wifi/java/android/net/wifi/WpsResult.aidl +++ b/wifi/aidl-export/android/net/wifi/SoftApInfo.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010, The Android Open Source Project + * Copyright (c) 2019, 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. @@ -16,4 +16,5 @@ package android.net.wifi; -parcelable WpsResult; +parcelable SoftApInfo; + diff --git a/wifi/api/current.txt b/wifi/api/current.txt new file mode 100644 index 000000000000..53c3b33d9845 --- /dev/null +++ b/wifi/api/current.txt @@ -0,0 +1,1199 @@ +// Signature format: 2.0 +package android.net.wifi { + + public abstract class EasyConnectStatusCallback { + field public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe + field public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5; // 0xfffffffb + field public static final int EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK = -10; // 0xfffffff6 + field public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc + field public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION = -11; // 0xfffffff5 + field public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION = -12; // 0xfffffff4 + field public static final int EASY_CONNECT_EVENT_FAILURE_GENERIC = -7; // 0xfffffff9 + field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7 + field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff + field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd + field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8 + field public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa + } + + public final class ScanResult implements android.os.Parcelable { + ctor public ScanResult(@NonNull android.net.wifi.ScanResult); + ctor public ScanResult(); + method public int describeContents(); + method @NonNull public java.util.List<android.net.wifi.ScanResult.InformationElement> getInformationElements(); + method public int getWifiStandard(); + method public boolean is80211mcResponder(); + method public boolean isPasspointNetwork(); + method public void writeToParcel(android.os.Parcel, int); + field public String BSSID; + field public static final int CHANNEL_WIDTH_160MHZ = 3; // 0x3 + field public static final int CHANNEL_WIDTH_20MHZ = 0; // 0x0 + field public static final int CHANNEL_WIDTH_40MHZ = 1; // 0x1 + field public static final int CHANNEL_WIDTH_80MHZ = 2; // 0x2 + field public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4; // 0x4 + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.ScanResult> CREATOR; + field public String SSID; + field public static final int WIFI_STANDARD_11AC = 5; // 0x5 + field public static final int WIFI_STANDARD_11AX = 6; // 0x6 + field public static final int WIFI_STANDARD_11N = 4; // 0x4 + field public static final int WIFI_STANDARD_LEGACY = 1; // 0x1 + field public static final int WIFI_STANDARD_UNKNOWN = 0; // 0x0 + field public String capabilities; + field public int centerFreq0; + field public int centerFreq1; + field public int channelWidth; + field public int frequency; + field public int level; + field public CharSequence operatorFriendlyName; + field public long timestamp; + field public CharSequence venueName; + } + + public static class ScanResult.InformationElement { + ctor public ScanResult.InformationElement(@NonNull android.net.wifi.ScanResult.InformationElement); + method @NonNull public java.nio.ByteBuffer getBytes(); + method public int getId(); + method public int getIdExt(); + } + + public final class SoftApConfiguration implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.net.MacAddress getBssid(); + method @Nullable public String getPassphrase(); + method public int getSecurityType(); + method @Nullable public String getSsid(); + method public boolean isHiddenSsid(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApConfiguration> CREATOR; + field public static final int SECURITY_TYPE_OPEN = 0; // 0x0 + field public static final int SECURITY_TYPE_WPA2_PSK = 1; // 0x1 + field public static final int SECURITY_TYPE_WPA3_SAE = 3; // 0x3 + field public static final int SECURITY_TYPE_WPA3_SAE_TRANSITION = 2; // 0x2 + } + + public enum SupplicantState implements android.os.Parcelable { + method public int describeContents(); + method public static boolean isValidState(android.net.wifi.SupplicantState); + method public void writeToParcel(android.os.Parcel, int); + enum_constant public static final android.net.wifi.SupplicantState ASSOCIATED; + enum_constant public static final android.net.wifi.SupplicantState ASSOCIATING; + enum_constant public static final android.net.wifi.SupplicantState AUTHENTICATING; + enum_constant public static final android.net.wifi.SupplicantState COMPLETED; + enum_constant public static final android.net.wifi.SupplicantState DISCONNECTED; + enum_constant public static final android.net.wifi.SupplicantState DORMANT; + enum_constant public static final android.net.wifi.SupplicantState FOUR_WAY_HANDSHAKE; + enum_constant public static final android.net.wifi.SupplicantState GROUP_HANDSHAKE; + enum_constant public static final android.net.wifi.SupplicantState INACTIVE; + enum_constant public static final android.net.wifi.SupplicantState INTERFACE_DISABLED; + enum_constant public static final android.net.wifi.SupplicantState INVALID; + enum_constant public static final android.net.wifi.SupplicantState SCANNING; + enum_constant public static final android.net.wifi.SupplicantState UNINITIALIZED; + } + + @Deprecated public class WifiConfiguration implements android.os.Parcelable { + ctor @Deprecated public WifiConfiguration(); + ctor @Deprecated public WifiConfiguration(@NonNull android.net.wifi.WifiConfiguration); + method public int describeContents(); + method @Deprecated public android.net.ProxyInfo getHttpProxy(); + method @Deprecated @NonNull public String getKey(); + method @Deprecated @NonNull public android.net.MacAddress getRandomizedMacAddress(); + method @Deprecated public boolean isPasspoint(); + method @Deprecated public void setHttpProxy(android.net.ProxyInfo); + method @Deprecated public void setSecurityParams(int); + method public void writeToParcel(android.os.Parcel, int); + field @Deprecated public String BSSID; + field @Deprecated public String FQDN; + field @Deprecated public static final int SECURITY_TYPE_EAP = 3; // 0x3 + field @Deprecated public static final int SECURITY_TYPE_EAP_SUITE_B = 5; // 0x5 + field @Deprecated public static final int SECURITY_TYPE_OPEN = 0; // 0x0 + field @Deprecated public static final int SECURITY_TYPE_OWE = 6; // 0x6 + field @Deprecated public static final int SECURITY_TYPE_PSK = 2; // 0x2 + field @Deprecated public static final int SECURITY_TYPE_SAE = 4; // 0x4 + field @Deprecated public static final int SECURITY_TYPE_WAPI_CERT = 8; // 0x8 + field @Deprecated public static final int SECURITY_TYPE_WAPI_PSK = 7; // 0x7 + field @Deprecated public static final int SECURITY_TYPE_WEP = 1; // 0x1 + field @Deprecated public String SSID; + field @Deprecated @NonNull public java.util.BitSet allowedAuthAlgorithms; + field @Deprecated @NonNull public java.util.BitSet allowedGroupCiphers; + field @Deprecated @NonNull public java.util.BitSet allowedGroupManagementCiphers; + field @Deprecated @NonNull public java.util.BitSet allowedKeyManagement; + field @Deprecated @NonNull public java.util.BitSet allowedPairwiseCiphers; + field @Deprecated @NonNull public java.util.BitSet allowedProtocols; + field @Deprecated @NonNull public java.util.BitSet allowedSuiteBCiphers; + field @Deprecated public android.net.wifi.WifiEnterpriseConfig enterpriseConfig; + field @Deprecated public boolean hiddenSSID; + field @Deprecated public boolean isHomeProviderNetwork; + field @Deprecated public int networkId; + field @Deprecated public String preSharedKey; + field @Deprecated public int priority; + field @Deprecated public String providerFriendlyName; + field @Deprecated public long[] roamingConsortiumIds; + field @Deprecated public int status; + field @Deprecated public String[] wepKeys; + field @Deprecated public int wepTxKeyIndex; + } + + @Deprecated public static class WifiConfiguration.AuthAlgorithm { + field @Deprecated public static final int LEAP = 2; // 0x2 + field @Deprecated public static final int OPEN = 0; // 0x0 + field @Deprecated public static final int SAE = 3; // 0x3 + field @Deprecated public static final int SHARED = 1; // 0x1 + field @Deprecated public static final String[] strings; + field @Deprecated public static final String varName = "auth_alg"; + } + + @Deprecated public static class WifiConfiguration.GroupCipher { + field @Deprecated public static final int CCMP = 3; // 0x3 + field @Deprecated public static final int GCMP_256 = 5; // 0x5 + field @Deprecated public static final int SMS4 = 6; // 0x6 + field @Deprecated public static final int TKIP = 2; // 0x2 + field @Deprecated public static final int WEP104 = 1; // 0x1 + field @Deprecated public static final int WEP40 = 0; // 0x0 + field @Deprecated public static final String[] strings; + field @Deprecated public static final String varName = "group"; + } + + @Deprecated public static class WifiConfiguration.GroupMgmtCipher { + field @Deprecated public static final int BIP_CMAC_256 = 0; // 0x0 + field @Deprecated public static final int BIP_GMAC_128 = 1; // 0x1 + field @Deprecated public static final int BIP_GMAC_256 = 2; // 0x2 + } + + @Deprecated public static class WifiConfiguration.KeyMgmt { + field @Deprecated public static final int IEEE8021X = 3; // 0x3 + field @Deprecated public static final int NONE = 0; // 0x0 + field @Deprecated public static final int OWE = 9; // 0x9 + field @Deprecated public static final int SAE = 8; // 0x8 + field @Deprecated public static final int SUITE_B_192 = 10; // 0xa + field @Deprecated public static final int WPA_EAP = 2; // 0x2 + field @Deprecated public static final int WPA_PSK = 1; // 0x1 + field @Deprecated public static final String[] strings; + field @Deprecated public static final String varName = "key_mgmt"; + } + + @Deprecated public static class WifiConfiguration.PairwiseCipher { + field @Deprecated public static final int CCMP = 2; // 0x2 + field @Deprecated public static final int GCMP_256 = 3; // 0x3 + field @Deprecated public static final int NONE = 0; // 0x0 + field @Deprecated public static final int SMS4 = 4; // 0x4 + field @Deprecated public static final int TKIP = 1; // 0x1 + field @Deprecated public static final String[] strings; + field @Deprecated public static final String varName = "pairwise"; + } + + @Deprecated public static class WifiConfiguration.Protocol { + field @Deprecated public static final int RSN = 1; // 0x1 + field @Deprecated public static final int WAPI = 3; // 0x3 + field @Deprecated public static final int WPA = 0; // 0x0 + field @Deprecated public static final String[] strings; + field @Deprecated public static final String varName = "proto"; + } + + @Deprecated public static class WifiConfiguration.Status { + field @Deprecated public static final int CURRENT = 0; // 0x0 + field @Deprecated public static final int DISABLED = 1; // 0x1 + field @Deprecated public static final int ENABLED = 2; // 0x2 + field @Deprecated public static final String[] strings; + } + + public class WifiEnterpriseConfig implements android.os.Parcelable { + ctor public WifiEnterpriseConfig(); + ctor public WifiEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig); + method public int describeContents(); + method public String getAltSubjectMatch(); + method public String getAnonymousIdentity(); + method @Nullable public java.security.cert.X509Certificate getCaCertificate(); + method @Nullable public java.security.cert.X509Certificate[] getCaCertificates(); + method public java.security.cert.X509Certificate getClientCertificate(); + method @Nullable public java.security.cert.X509Certificate[] getClientCertificateChain(); + method @Nullable public java.security.PrivateKey getClientPrivateKey(); + method public String getDomainSuffixMatch(); + method public int getEapMethod(); + method public String getIdentity(); + method public String getPassword(); + method public int getPhase2Method(); + method public String getPlmn(); + method public String getRealm(); + method @Deprecated public String getSubjectMatch(); + method public boolean isAuthenticationSimBased(); + method public void setAltSubjectMatch(String); + method public void setAnonymousIdentity(String); + method public void setCaCertificate(@Nullable java.security.cert.X509Certificate); + method public void setCaCertificates(@Nullable java.security.cert.X509Certificate[]); + method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate); + method public void setClientKeyEntryWithCertificateChain(java.security.PrivateKey, java.security.cert.X509Certificate[]); + method public void setDomainSuffixMatch(String); + method public void setEapMethod(int); + method public void setIdentity(String); + method public void setPassword(String); + method public void setPhase2Method(int); + method public void setPlmn(String); + method public void setRealm(String); + method @Deprecated public void setSubjectMatch(String); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiEnterpriseConfig> CREATOR; + field public static final String EXTRA_WAPI_AS_CERTIFICATE_DATA = "android.net.wifi.extra.WAPI_AS_CERTIFICATE_DATA"; + field public static final String EXTRA_WAPI_AS_CERTIFICATE_NAME = "android.net.wifi.extra.WAPI_AS_CERTIFICATE_NAME"; + field public static final String EXTRA_WAPI_USER_CERTIFICATE_DATA = "android.net.wifi.extra.WAPI_USER_CERTIFICATE_DATA"; + field public static final String EXTRA_WAPI_USER_CERTIFICATE_NAME = "android.net.wifi.extra.WAPI_USER_CERTIFICATE_NAME"; + field public static final String WAPI_AS_CERTIFICATE = "WAPIAS_"; + field public static final String WAPI_USER_CERTIFICATE = "WAPIUSR_"; + } + + public static final class WifiEnterpriseConfig.Eap { + field public static final int AKA = 5; // 0x5 + field public static final int AKA_PRIME = 6; // 0x6 + field public static final int NONE = -1; // 0xffffffff + field public static final int PEAP = 0; // 0x0 + field public static final int PWD = 3; // 0x3 + field public static final int SIM = 4; // 0x4 + field public static final int TLS = 1; // 0x1 + field public static final int TTLS = 2; // 0x2 + field public static final int UNAUTH_TLS = 7; // 0x7 + field public static final int WAPI_CERT = 8; // 0x8 + } + + public static final class WifiEnterpriseConfig.Phase2 { + field public static final int AKA = 6; // 0x6 + field public static final int AKA_PRIME = 7; // 0x7 + field public static final int GTC = 4; // 0x4 + field public static final int MSCHAP = 2; // 0x2 + field public static final int MSCHAPV2 = 3; // 0x3 + field public static final int NONE = 0; // 0x0 + field public static final int PAP = 1; // 0x1 + field public static final int SIM = 5; // 0x5 + } + + public class WifiInfo implements android.os.Parcelable { + method public int describeContents(); + method public String getBSSID(); + method public static android.net.NetworkInfo.DetailedState getDetailedStateOf(android.net.wifi.SupplicantState); + method public int getFrequency(); + method public boolean getHiddenSSID(); + method public int getIpAddress(); + method public int getLinkSpeed(); + method public String getMacAddress(); + method public int getMaxSupportedRxLinkSpeedMbps(); + method public int getMaxSupportedTxLinkSpeedMbps(); + method public int getNetworkId(); + method @Nullable public String getPasspointFqdn(); + method @Nullable public String getPasspointProviderFriendlyName(); + method public int getRssi(); + method @IntRange(from=0xffffffff) public int getRxLinkSpeedMbps(); + method public String getSSID(); + method public android.net.wifi.SupplicantState getSupplicantState(); + method @IntRange(from=0xffffffff) public int getTxLinkSpeedMbps(); + method public int getWifiStandard(); + method public void writeToParcel(android.os.Parcel, int); + field public static final String FREQUENCY_UNITS = "MHz"; + field public static final String LINK_SPEED_UNITS = "Mbps"; + field public static final int LINK_SPEED_UNKNOWN = -1; // 0xffffffff + } + + public static final class WifiInfo.Builder { + ctor public WifiInfo.Builder(); + method @NonNull public android.net.wifi.WifiInfo build(); + method @NonNull public android.net.wifi.WifiInfo.Builder setBssid(@NonNull String); + method @NonNull public android.net.wifi.WifiInfo.Builder setNetworkId(int); + method @NonNull public android.net.wifi.WifiInfo.Builder setRssi(int); + method @NonNull public android.net.wifi.WifiInfo.Builder setSsid(@NonNull byte[]); + } + + public class WifiManager { + method @Deprecated public int addNetwork(android.net.wifi.WifiConfiguration); + method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public int addNetworkSuggestions(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>); + method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); + method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public void addSuggestionConnectionStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SuggestionConnectionStatusListener); + method @Deprecated public static int calculateSignalLevel(int, int); + method @IntRange(from=0) public int calculateSignalLevel(int); + method @Deprecated public void cancelWps(android.net.wifi.WifiManager.WpsCallback); + method public static int compareSignalLevel(int, int); + method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(String); + method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, String); + method @Deprecated public android.net.wifi.WifiManager.WifiLock createWifiLock(String); + method @Deprecated public boolean disableNetwork(int); + method @Deprecated public boolean disconnect(); + method @Deprecated public boolean enableNetwork(int, boolean); + method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks(); + method public android.net.wifi.WifiInfo getConnectionInfo(); + method public android.net.DhcpInfo getDhcpInfo(); + method public int getMaxNumberOfNetworkSuggestionsPerApp(); + method @IntRange(from=0) public int getMaxSignalLevel(); + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public java.util.List<android.net.wifi.WifiNetworkSuggestion> getNetworkSuggestions(); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations(); + method public java.util.List<android.net.wifi.ScanResult> getScanResults(); + method public int getWifiState(); + method public boolean is5GHzBandSupported(); + method public boolean is6GHzBandSupported(); + method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isAutoWakeupEnabled(); + method @Deprecated public boolean isDeviceToApRttSupported(); + method public boolean isEasyConnectSupported(); + method public boolean isEnhancedOpenSupported(); + method public boolean isEnhancedPowerReportingSupported(); + method public boolean isP2pSupported(); + method public boolean isPreferredNetworkOffloadSupported(); + method @Deprecated public boolean isScanAlwaysAvailable(); + method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isScanThrottleEnabled(); + method public boolean isStaApConcurrencySupported(); + method public boolean isTdlsSupported(); + method public boolean isWapiSupported(); + method public boolean isWifiEnabled(); + method public boolean isWifiStandardSupported(int); + method public boolean isWpa3SaeSupported(); + method public boolean isWpa3SuiteBSupported(); + method @Deprecated public boolean pingSupplicant(); + method @Deprecated public boolean reassociate(); + method @Deprecated public boolean reconnect(); + method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void registerScanResultsCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.ScanResultsCallback); + method @Deprecated public boolean removeNetwork(int); + method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public int removeNetworkSuggestions(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_CARRIER_PROVISIONING}) public void removePasspointConfiguration(String); + method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void removeSuggestionConnectionStatusListener(@NonNull android.net.wifi.WifiManager.SuggestionConnectionStatusListener); + method @Deprecated public boolean saveConfiguration(); + method public void setTdlsEnabled(java.net.InetAddress, boolean); + method public void setTdlsEnabledWithMacAddress(String, boolean); + method @Deprecated public boolean setWifiEnabled(boolean); + method @RequiresPermission(allOf={android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, @Nullable android.os.Handler); + method @Deprecated public boolean startScan(); + method @Deprecated public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback); + method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void unregisterScanResultsCallback(@NonNull android.net.wifi.WifiManager.ScanResultsCallback); + method @Deprecated public int updateNetwork(android.net.wifi.WifiConfiguration); + field public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"; + field public static final String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE"; + field public static final String ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION = "android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION"; + field public static final String ACTION_WIFI_SCAN_AVAILABILITY_CHANGED = "android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED"; + field @Deprecated public static final int ERROR_AUTHENTICATING = 1; // 0x1 + field @Deprecated public static final String EXTRA_BSSID = "bssid"; + field public static final String EXTRA_NETWORK_INFO = "networkInfo"; + field public static final String EXTRA_NETWORK_SUGGESTION = "android.net.wifi.extra.NETWORK_SUGGESTION"; + field public static final String EXTRA_NEW_RSSI = "newRssi"; + field @Deprecated public static final String EXTRA_NEW_STATE = "newState"; + field public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state"; + field public static final String EXTRA_RESULTS_UPDATED = "resultsUpdated"; + field public static final String EXTRA_SCAN_AVAILABLE = "android.net.wifi.extra.SCAN_AVAILABLE"; + field @Deprecated public static final String EXTRA_SUPPLICANT_CONNECTED = "connected"; + field @Deprecated public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError"; + field @Deprecated public static final String EXTRA_WIFI_INFO = "wifiInfo"; + field public static final String EXTRA_WIFI_STATE = "wifi_state"; + field public static final String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED"; + field public static final String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE"; + field public static final String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED"; + field public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS"; + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 3; // 0x3 + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 4; // 0x4 + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID = 7; // 0x7 + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED = 6; // 0x6 + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED = 2; // 0x2 + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; // 0x1 + field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5; // 0x5 + field public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0; // 0x0 + field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_ASSOCIATION = 1; // 0x1 + field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION = 2; // 0x2 + field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_IP_PROVISIONING = 3; // 0x3 + field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_UNKNOWN = 0; // 0x0 + field @Deprecated public static final String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE"; + field @Deprecated public static final String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE"; + field public static final String UNKNOWN_SSID = "<unknown ssid>"; + field @Deprecated public static final int WIFI_MODE_FULL = 1; // 0x1 + field public static final int WIFI_MODE_FULL_HIGH_PERF = 3; // 0x3 + field public static final int WIFI_MODE_FULL_LOW_LATENCY = 4; // 0x4 + field @Deprecated public static final int WIFI_MODE_SCAN_ONLY = 2; // 0x2 + field public static final String WIFI_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_STATE_CHANGED"; + field public static final int WIFI_STATE_DISABLED = 1; // 0x1 + field public static final int WIFI_STATE_DISABLING = 0; // 0x0 + field public static final int WIFI_STATE_ENABLED = 3; // 0x3 + field public static final int WIFI_STATE_ENABLING = 2; // 0x2 + field public static final int WIFI_STATE_UNKNOWN = 4; // 0x4 + field @Deprecated public static final int WPS_AUTH_FAILURE = 6; // 0x6 + field @Deprecated public static final int WPS_OVERLAP_ERROR = 3; // 0x3 + field @Deprecated public static final int WPS_TIMED_OUT = 7; // 0x7 + field @Deprecated public static final int WPS_TKIP_ONLY_PROHIBITED = 5; // 0x5 + field @Deprecated public static final int WPS_WEP_PROHIBITED = 4; // 0x4 + } + + public static class WifiManager.LocalOnlyHotspotCallback { + ctor public WifiManager.LocalOnlyHotspotCallback(); + method public void onFailed(int); + method public void onStarted(android.net.wifi.WifiManager.LocalOnlyHotspotReservation); + method public void onStopped(); + field public static final int ERROR_GENERIC = 2; // 0x2 + field public static final int ERROR_INCOMPATIBLE_MODE = 3; // 0x3 + field public static final int ERROR_NO_CHANNEL = 1; // 0x1 + field public static final int ERROR_TETHERING_DISALLOWED = 4; // 0x4 + } + + public class WifiManager.LocalOnlyHotspotReservation implements java.lang.AutoCloseable { + method public void close(); + method @NonNull public android.net.wifi.SoftApConfiguration getSoftApConfiguration(); + method @Deprecated @Nullable public android.net.wifi.WifiConfiguration getWifiConfiguration(); + } + + public class WifiManager.MulticastLock { + method public void acquire(); + method public boolean isHeld(); + method public void release(); + method public void setReferenceCounted(boolean); + } + + public abstract static class WifiManager.ScanResultsCallback { + ctor public WifiManager.ScanResultsCallback(); + method public abstract void onScanResultsAvailable(); + } + + public static interface WifiManager.SuggestionConnectionStatusListener { + method public void onConnectionStatus(@NonNull android.net.wifi.WifiNetworkSuggestion, int); + } + + public class WifiManager.WifiLock { + method public void acquire(); + method public boolean isHeld(); + method public void release(); + method public void setReferenceCounted(boolean); + method public void setWorkSource(android.os.WorkSource); + } + + @Deprecated public abstract static class WifiManager.WpsCallback { + ctor @Deprecated public WifiManager.WpsCallback(); + method @Deprecated public abstract void onFailed(int); + method @Deprecated public abstract void onStarted(String); + method @Deprecated public abstract void onSucceeded(); + } + + public final class WifiNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiNetworkSpecifier> CREATOR; + } + + public static final class WifiNetworkSpecifier.Builder { + ctor public WifiNetworkSpecifier.Builder(); + method @NonNull public android.net.wifi.WifiNetworkSpecifier build(); + method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setBssid(@NonNull android.net.MacAddress); + method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setBssidPattern(@NonNull android.net.MacAddress, @NonNull android.net.MacAddress); + method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setIsEnhancedOpen(boolean); + method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setIsHiddenSsid(boolean); + method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setSsid(@NonNull String); + method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setSsidPattern(@NonNull android.os.PatternMatcher); + method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa2EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig); + method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa2Passphrase(@NonNull String); + method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig); + method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3Passphrase(@NonNull String); + } + + public final class WifiNetworkSuggestion implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.net.MacAddress getBssid(); + method @Nullable public android.net.wifi.WifiEnterpriseConfig getEnterpriseConfig(); + method @Nullable public String getPassphrase(); + method @Nullable public android.net.wifi.hotspot2.PasspointConfiguration getPasspointConfig(); + method @IntRange(from=0) public int getPriority(); + method @Nullable public String getSsid(); + method public boolean isAppInteractionRequired(); + method public boolean isCredentialSharedWithUser(); + method public boolean isEnhancedOpen(); + method public boolean isHiddenSsid(); + method public boolean isInitialAutojoinEnabled(); + method public boolean isMetered(); + method public boolean isUntrusted(); + method public boolean isUserInteractionRequired(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiNetworkSuggestion> CREATOR; + } + + public static final class WifiNetworkSuggestion.Builder { + ctor public WifiNetworkSuggestion.Builder(); + method @NonNull public android.net.wifi.WifiNetworkSuggestion build(); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setBssid(@NonNull android.net.MacAddress); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setCredentialSharedWithUser(boolean); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsAppInteractionRequired(boolean); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsEnhancedOpen(boolean); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsHiddenSsid(boolean); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsInitialAutojoinEnabled(boolean); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsMetered(boolean); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsUserInteractionRequired(boolean); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPasspointConfig(@NonNull android.net.wifi.hotspot2.PasspointConfiguration); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriority(@IntRange(from=0) int); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setSsid(@NonNull String); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setUntrusted(boolean); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWapiEnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWapiPassphrase(@NonNull String); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa2EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa2Passphrase(@NonNull String); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3Passphrase(@NonNull String); + } + + public class WpsInfo implements android.os.Parcelable { + ctor public WpsInfo(); + ctor public WpsInfo(android.net.wifi.WpsInfo); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public String BSSID; + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WpsInfo> CREATOR; + field public static final int DISPLAY = 1; // 0x1 + field public static final int INVALID = 4; // 0x4 + field public static final int KEYPAD = 2; // 0x2 + field public static final int LABEL = 3; // 0x3 + field public static final int PBC = 0; // 0x0 + field public String pin; + field public int setup; + } + +} + +package android.net.wifi.aware { + + public class AttachCallback { + ctor public AttachCallback(); + method public void onAttachFailed(); + method public void onAttached(android.net.wifi.aware.WifiAwareSession); + } + + public final class Characteristics implements android.os.Parcelable { + method public int describeContents(); + method public int getMaxMatchFilterLength(); + method public int getMaxServiceNameLength(); + method public int getMaxServiceSpecificInfoLength(); + method public int getSupportedCipherSuites(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.Characteristics> CREATOR; + field public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_128 = 1; // 0x1 + field public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_256 = 2; // 0x2 + } + + public class DiscoverySession implements java.lang.AutoCloseable { + method public void close(); + method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierOpen(@NonNull android.net.wifi.aware.PeerHandle); + method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(@NonNull android.net.wifi.aware.PeerHandle, @NonNull String); + method public void sendMessage(@NonNull android.net.wifi.aware.PeerHandle, int, @Nullable byte[]); + } + + public class DiscoverySessionCallback { + ctor public DiscoverySessionCallback(); + method public void onMessageReceived(android.net.wifi.aware.PeerHandle, byte[]); + method public void onMessageSendFailed(int); + method public void onMessageSendSucceeded(int); + method public void onPublishStarted(@NonNull android.net.wifi.aware.PublishDiscoverySession); + method public void onServiceDiscovered(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>); + method public void onServiceDiscoveredWithinRange(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>, int); + method public void onSessionConfigFailed(); + method public void onSessionConfigUpdated(); + method public void onSessionTerminated(); + method public void onSubscribeStarted(@NonNull android.net.wifi.aware.SubscribeDiscoverySession); + } + + public class IdentityChangedListener { + ctor public IdentityChangedListener(); + method public void onIdentityChanged(byte[]); + } + + public final class ParcelablePeerHandle extends android.net.wifi.aware.PeerHandle implements android.os.Parcelable { + ctor public ParcelablePeerHandle(@NonNull android.net.wifi.aware.PeerHandle); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.ParcelablePeerHandle> CREATOR; + } + + public class PeerHandle { + } + + public final class PublishConfig implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.PublishConfig> CREATOR; + field public static final int PUBLISH_TYPE_SOLICITED = 1; // 0x1 + field public static final int PUBLISH_TYPE_UNSOLICITED = 0; // 0x0 + } + + public static final class PublishConfig.Builder { + ctor public PublishConfig.Builder(); + method public android.net.wifi.aware.PublishConfig build(); + method public android.net.wifi.aware.PublishConfig.Builder setMatchFilter(@Nullable java.util.List<byte[]>); + method public android.net.wifi.aware.PublishConfig.Builder setPublishType(int); + method public android.net.wifi.aware.PublishConfig.Builder setRangingEnabled(boolean); + method public android.net.wifi.aware.PublishConfig.Builder setServiceName(@NonNull String); + method public android.net.wifi.aware.PublishConfig.Builder setServiceSpecificInfo(@Nullable byte[]); + method public android.net.wifi.aware.PublishConfig.Builder setTerminateNotificationEnabled(boolean); + method public android.net.wifi.aware.PublishConfig.Builder setTtlSec(int); + } + + public class PublishDiscoverySession extends android.net.wifi.aware.DiscoverySession { + method public void updatePublish(@NonNull android.net.wifi.aware.PublishConfig); + } + + public final class SubscribeConfig implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.SubscribeConfig> CREATOR; + field public static final int SUBSCRIBE_TYPE_ACTIVE = 1; // 0x1 + field public static final int SUBSCRIBE_TYPE_PASSIVE = 0; // 0x0 + } + + public static final class SubscribeConfig.Builder { + ctor public SubscribeConfig.Builder(); + method public android.net.wifi.aware.SubscribeConfig build(); + method public android.net.wifi.aware.SubscribeConfig.Builder setMatchFilter(@Nullable java.util.List<byte[]>); + method public android.net.wifi.aware.SubscribeConfig.Builder setMaxDistanceMm(int); + method public android.net.wifi.aware.SubscribeConfig.Builder setMinDistanceMm(int); + method public android.net.wifi.aware.SubscribeConfig.Builder setServiceName(@NonNull String); + method public android.net.wifi.aware.SubscribeConfig.Builder setServiceSpecificInfo(@Nullable byte[]); + method public android.net.wifi.aware.SubscribeConfig.Builder setSubscribeType(int); + method public android.net.wifi.aware.SubscribeConfig.Builder setTerminateNotificationEnabled(boolean); + method public android.net.wifi.aware.SubscribeConfig.Builder setTtlSec(int); + } + + public class SubscribeDiscoverySession extends android.net.wifi.aware.DiscoverySession { + method public void updateSubscribe(@NonNull android.net.wifi.aware.SubscribeConfig); + } + + public class WifiAwareManager { + method public void attach(@NonNull android.net.wifi.aware.AttachCallback, @Nullable android.os.Handler); + method public void attach(@NonNull android.net.wifi.aware.AttachCallback, @NonNull android.net.wifi.aware.IdentityChangedListener, @Nullable android.os.Handler); + method public android.net.wifi.aware.Characteristics getCharacteristics(); + method public boolean isAvailable(); + field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED"; + field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0 + field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1 + } + + public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo { + method public int describeContents(); + method @Nullable public java.net.Inet6Address getPeerIpv6Addr(); + method public int getPort(); + method public int getTransportProtocol(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.WifiAwareNetworkInfo> CREATOR; + } + + public final class WifiAwareNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.WifiAwareNetworkSpecifier> CREATOR; + } + + public static final class WifiAwareNetworkSpecifier.Builder { + ctor public WifiAwareNetworkSpecifier.Builder(@NonNull android.net.wifi.aware.DiscoverySession, @NonNull android.net.wifi.aware.PeerHandle); + method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier build(); + method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setPmk(@NonNull byte[]); + method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setPort(@IntRange(from=0, to=65535) int); + method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setPskPassphrase(@NonNull String); + method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setTransportProtocol(@IntRange(from=0, to=255) int); + } + + public class WifiAwareSession implements java.lang.AutoCloseable { + method public void close(); + method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, @NonNull byte[]); + method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, @NonNull byte[], @NonNull String); + method public void publish(@NonNull android.net.wifi.aware.PublishConfig, @NonNull android.net.wifi.aware.DiscoverySessionCallback, @Nullable android.os.Handler); + method public void subscribe(@NonNull android.net.wifi.aware.SubscribeConfig, @NonNull android.net.wifi.aware.DiscoverySessionCallback, @Nullable android.os.Handler); + } + +} + +package android.net.wifi.hotspot2 { + + public final class ConfigParser { + method public static android.net.wifi.hotspot2.PasspointConfiguration parsePasspointConfig(String, byte[]); + } + + public final class PasspointConfiguration implements android.os.Parcelable { + ctor public PasspointConfiguration(); + ctor public PasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); + method public int describeContents(); + method public android.net.wifi.hotspot2.pps.Credential getCredential(); + method public android.net.wifi.hotspot2.pps.HomeSp getHomeSp(); + method public long getSubscriptionExpirationTimeMillis(); + method @NonNull public String getUniqueId(); + method public boolean isOsuProvisioned(); + method public void setCredential(android.net.wifi.hotspot2.pps.Credential); + method public void setHomeSp(android.net.wifi.hotspot2.pps.HomeSp); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.PasspointConfiguration> CREATOR; + } + +} + +package android.net.wifi.hotspot2.omadm { + + public final class PpsMoParser { + method public static android.net.wifi.hotspot2.PasspointConfiguration parseMoText(String); + } + +} + +package android.net.wifi.hotspot2.pps { + + public final class Credential implements android.os.Parcelable { + ctor public Credential(); + ctor public Credential(android.net.wifi.hotspot2.pps.Credential); + method public int describeContents(); + method public java.security.cert.X509Certificate getCaCertificate(); + method public android.net.wifi.hotspot2.pps.Credential.CertificateCredential getCertCredential(); + method public java.security.cert.X509Certificate[] getClientCertificateChain(); + method public java.security.PrivateKey getClientPrivateKey(); + method public String getRealm(); + method public android.net.wifi.hotspot2.pps.Credential.SimCredential getSimCredential(); + method public android.net.wifi.hotspot2.pps.Credential.UserCredential getUserCredential(); + method public void setCaCertificate(java.security.cert.X509Certificate); + method public void setCertCredential(android.net.wifi.hotspot2.pps.Credential.CertificateCredential); + method public void setClientCertificateChain(java.security.cert.X509Certificate[]); + method public void setClientPrivateKey(java.security.PrivateKey); + method public void setRealm(String); + method public void setSimCredential(android.net.wifi.hotspot2.pps.Credential.SimCredential); + method public void setUserCredential(android.net.wifi.hotspot2.pps.Credential.UserCredential); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential> CREATOR; + } + + public static final class Credential.CertificateCredential implements android.os.Parcelable { + ctor public Credential.CertificateCredential(); + ctor public Credential.CertificateCredential(android.net.wifi.hotspot2.pps.Credential.CertificateCredential); + method public int describeContents(); + method public byte[] getCertSha256Fingerprint(); + method public String getCertType(); + method public void setCertSha256Fingerprint(byte[]); + method public void setCertType(String); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.CertificateCredential> CREATOR; + } + + public static final class Credential.SimCredential implements android.os.Parcelable { + ctor public Credential.SimCredential(); + ctor public Credential.SimCredential(android.net.wifi.hotspot2.pps.Credential.SimCredential); + method public int describeContents(); + method public int getEapType(); + method public String getImsi(); + method public void setEapType(int); + method public void setImsi(String); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.SimCredential> CREATOR; + } + + public static final class Credential.UserCredential implements android.os.Parcelable { + ctor public Credential.UserCredential(); + ctor public Credential.UserCredential(android.net.wifi.hotspot2.pps.Credential.UserCredential); + method public int describeContents(); + method public int getEapType(); + method public String getNonEapInnerMethod(); + method public String getPassword(); + method public String getUsername(); + method public void setEapType(int); + method public void setNonEapInnerMethod(String); + method public void setPassword(String); + method public void setUsername(String); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.UserCredential> CREATOR; + } + + public final class HomeSp implements android.os.Parcelable { + ctor public HomeSp(); + ctor public HomeSp(android.net.wifi.hotspot2.pps.HomeSp); + method public int describeContents(); + method public String getFqdn(); + method public String getFriendlyName(); + method public long[] getRoamingConsortiumOis(); + method public void setFqdn(String); + method public void setFriendlyName(String); + method public void setRoamingConsortiumOis(long[]); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR; + } + +} + +package android.net.wifi.p2p { + + public class WifiP2pConfig implements android.os.Parcelable { + ctor public WifiP2pConfig(); + ctor public WifiP2pConfig(android.net.wifi.p2p.WifiP2pConfig); + method public int describeContents(); + method public int getGroupOwnerBand(); + method public int getNetworkId(); + method @Nullable public String getNetworkName(); + method @Nullable public String getPassphrase(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pConfig> CREATOR; + field public static final int GROUP_OWNER_BAND_2GHZ = 1; // 0x1 + field public static final int GROUP_OWNER_BAND_5GHZ = 2; // 0x2 + field public static final int GROUP_OWNER_BAND_AUTO = 0; // 0x0 + field public static final int GROUP_OWNER_INTENT_AUTO = -1; // 0xffffffff + field public static final int GROUP_OWNER_INTENT_MAX = 15; // 0xf + field public static final int GROUP_OWNER_INTENT_MIN = 0; // 0x0 + field public String deviceAddress; + field @IntRange(from=0, to=15) public int groupOwnerIntent; + field public android.net.wifi.WpsInfo wps; + } + + public static final class WifiP2pConfig.Builder { + ctor public WifiP2pConfig.Builder(); + method @NonNull public android.net.wifi.p2p.WifiP2pConfig build(); + method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder enablePersistentMode(boolean); + method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setDeviceAddress(@Nullable android.net.MacAddress); + method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOperatingBand(int); + method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOperatingFrequency(int); + method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setNetworkName(@NonNull String); + method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setPassphrase(@NonNull String); + } + + public class WifiP2pDevice implements android.os.Parcelable { + ctor public WifiP2pDevice(); + ctor public WifiP2pDevice(android.net.wifi.p2p.WifiP2pDevice); + method public int describeContents(); + method @Nullable public android.net.wifi.p2p.WifiP2pWfdInfo getWfdInfo(); + method public boolean isGroupOwner(); + method public boolean isServiceDiscoveryCapable(); + method public void update(@NonNull android.net.wifi.p2p.WifiP2pDevice); + method public boolean wpsDisplaySupported(); + method public boolean wpsKeypadSupported(); + method public boolean wpsPbcSupported(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int AVAILABLE = 3; // 0x3 + field public static final int CONNECTED = 0; // 0x0 + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pDevice> CREATOR; + field public static final int FAILED = 2; // 0x2 + field public static final int INVITED = 1; // 0x1 + field public static final int UNAVAILABLE = 4; // 0x4 + field public String deviceAddress; + field public String deviceName; + field public String primaryDeviceType; + field public String secondaryDeviceType; + field public int status; + } + + public class WifiP2pDeviceList implements android.os.Parcelable { + ctor public WifiP2pDeviceList(); + ctor public WifiP2pDeviceList(android.net.wifi.p2p.WifiP2pDeviceList); + method public int describeContents(); + method public android.net.wifi.p2p.WifiP2pDevice get(String); + method public java.util.Collection<android.net.wifi.p2p.WifiP2pDevice> getDeviceList(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pDeviceList> CREATOR; + } + + public class WifiP2pGroup implements android.os.Parcelable { + ctor public WifiP2pGroup(); + ctor public WifiP2pGroup(android.net.wifi.p2p.WifiP2pGroup); + method public int describeContents(); + method public java.util.Collection<android.net.wifi.p2p.WifiP2pDevice> getClientList(); + method public int getFrequency(); + method public String getInterface(); + method public int getNetworkId(); + method public String getNetworkName(); + method public android.net.wifi.p2p.WifiP2pDevice getOwner(); + method public String getPassphrase(); + method public boolean isGroupOwner(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pGroup> CREATOR; + field public static final int NETWORK_ID_PERSISTENT = -2; // 0xfffffffe + field public static final int NETWORK_ID_TEMPORARY = -1; // 0xffffffff + } + + public class WifiP2pInfo implements android.os.Parcelable { + ctor public WifiP2pInfo(); + ctor public WifiP2pInfo(android.net.wifi.p2p.WifiP2pInfo); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pInfo> CREATOR; + field public boolean groupFormed; + field public java.net.InetAddress groupOwnerAddress; + field public boolean isGroupOwner; + } + + public class WifiP2pManager { + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void addLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method public void addServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method public void cancelConnect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method public void clearLocalServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method public void clearServiceRequests(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void connect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void createGroup(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pConfig, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void discoverPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method public android.net.wifi.p2p.WifiP2pManager.Channel initialize(android.content.Context, android.os.Looper, android.net.wifi.p2p.WifiP2pManager.ChannelListener); + method public void removeGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method public void removeLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method public void removeServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method public void requestConnectionInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestDeviceInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.DeviceInfoListener); + method public void requestDiscoveryState(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.DiscoveryStateListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.GroupInfoListener); + method public void requestNetworkInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.NetworkInfoListener); + method public void requestP2pState(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.P2pStateListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PeerListListener); + method public void setDnsSdResponseListeners(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener, android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener); + method public void setServiceResponseListener(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ServiceResponseListener); + method public void setUpnpServiceResponseListener(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.UpnpServiceResponseListener); + method public void stopPeerDiscovery(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); + field public static final int BUSY = 2; // 0x2 + field public static final int ERROR = 0; // 0x0 + field public static final String EXTRA_DISCOVERY_STATE = "discoveryState"; + field public static final String EXTRA_NETWORK_INFO = "networkInfo"; + field public static final String EXTRA_P2P_DEVICE_LIST = "wifiP2pDeviceList"; + field public static final String EXTRA_WIFI_P2P_DEVICE = "wifiP2pDevice"; + field public static final String EXTRA_WIFI_P2P_GROUP = "p2pGroupInfo"; + field public static final String EXTRA_WIFI_P2P_INFO = "wifiP2pInfo"; + field public static final String EXTRA_WIFI_STATE = "wifi_p2p_state"; + field public static final int NO_SERVICE_REQUESTS = 3; // 0x3 + field public static final int P2P_UNSUPPORTED = 1; // 0x1 + field public static final String WIFI_P2P_CONNECTION_CHANGED_ACTION = "android.net.wifi.p2p.CONNECTION_STATE_CHANGE"; + field public static final String WIFI_P2P_DISCOVERY_CHANGED_ACTION = "android.net.wifi.p2p.DISCOVERY_STATE_CHANGE"; + field public static final int WIFI_P2P_DISCOVERY_STARTED = 2; // 0x2 + field public static final int WIFI_P2P_DISCOVERY_STOPPED = 1; // 0x1 + field public static final String WIFI_P2P_PEERS_CHANGED_ACTION = "android.net.wifi.p2p.PEERS_CHANGED"; + field public static final String WIFI_P2P_STATE_CHANGED_ACTION = "android.net.wifi.p2p.STATE_CHANGED"; + field public static final int WIFI_P2P_STATE_DISABLED = 1; // 0x1 + field public static final int WIFI_P2P_STATE_ENABLED = 2; // 0x2 + field public static final String WIFI_P2P_THIS_DEVICE_CHANGED_ACTION = "android.net.wifi.p2p.THIS_DEVICE_CHANGED"; + } + + public static interface WifiP2pManager.ActionListener { + method public void onFailure(int); + method public void onSuccess(); + } + + public static class WifiP2pManager.Channel implements java.lang.AutoCloseable { + method public void close(); + } + + public static interface WifiP2pManager.ChannelListener { + method public void onChannelDisconnected(); + } + + public static interface WifiP2pManager.ConnectionInfoListener { + method public void onConnectionInfoAvailable(android.net.wifi.p2p.WifiP2pInfo); + } + + public static interface WifiP2pManager.DeviceInfoListener { + method public void onDeviceInfoAvailable(@Nullable android.net.wifi.p2p.WifiP2pDevice); + } + + public static interface WifiP2pManager.DiscoveryStateListener { + method public void onDiscoveryStateAvailable(int); + } + + public static interface WifiP2pManager.DnsSdServiceResponseListener { + method public void onDnsSdServiceAvailable(String, String, android.net.wifi.p2p.WifiP2pDevice); + } + + public static interface WifiP2pManager.DnsSdTxtRecordListener { + method public void onDnsSdTxtRecordAvailable(String, java.util.Map<java.lang.String,java.lang.String>, android.net.wifi.p2p.WifiP2pDevice); + } + + public static interface WifiP2pManager.GroupInfoListener { + method public void onGroupInfoAvailable(android.net.wifi.p2p.WifiP2pGroup); + } + + public static interface WifiP2pManager.NetworkInfoListener { + method public void onNetworkInfoAvailable(@NonNull android.net.NetworkInfo); + } + + public static interface WifiP2pManager.P2pStateListener { + method public void onP2pStateAvailable(int); + } + + public static interface WifiP2pManager.PeerListListener { + method public void onPeersAvailable(android.net.wifi.p2p.WifiP2pDeviceList); + } + + public static interface WifiP2pManager.ServiceResponseListener { + method public void onServiceAvailable(int, byte[], android.net.wifi.p2p.WifiP2pDevice); + } + + public static interface WifiP2pManager.UpnpServiceResponseListener { + method public void onUpnpServiceAvailable(java.util.List<java.lang.String>, android.net.wifi.p2p.WifiP2pDevice); + } + + public final class WifiP2pWfdInfo implements android.os.Parcelable { + ctor public WifiP2pWfdInfo(); + ctor public WifiP2pWfdInfo(@Nullable android.net.wifi.p2p.WifiP2pWfdInfo); + method public int describeContents(); + method public int getControlPort(); + method public int getDeviceType(); + method public int getMaxThroughput(); + method public boolean isContentProtectionSupported(); + method public boolean isEnabled(); + method public boolean isSessionAvailable(); + method public void setContentProtectionSupported(boolean); + method public void setControlPort(@IntRange(from=0) int); + method public boolean setDeviceType(int); + method public void setEnabled(boolean); + method public void setMaxThroughput(@IntRange(from=0) int); + method public void setSessionAvailable(boolean); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pWfdInfo> CREATOR; + field public static final int DEVICE_TYPE_PRIMARY_SINK = 1; // 0x1 + field public static final int DEVICE_TYPE_SECONDARY_SINK = 2; // 0x2 + field public static final int DEVICE_TYPE_SOURCE_OR_PRIMARY_SINK = 3; // 0x3 + field public static final int DEVICE_TYPE_WFD_SOURCE = 0; // 0x0 + } + +} + +package android.net.wifi.p2p.nsd { + + public class WifiP2pDnsSdServiceInfo extends android.net.wifi.p2p.nsd.WifiP2pServiceInfo { + method public static android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo newInstance(String, String, java.util.Map<java.lang.String,java.lang.String>); + } + + public class WifiP2pDnsSdServiceRequest extends android.net.wifi.p2p.nsd.WifiP2pServiceRequest { + method public static android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceRequest newInstance(); + method public static android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceRequest newInstance(String); + method public static android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceRequest newInstance(String, String); + } + + public class WifiP2pServiceInfo implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int SERVICE_TYPE_ALL = 0; // 0x0 + field public static final int SERVICE_TYPE_BONJOUR = 1; // 0x1 + field public static final int SERVICE_TYPE_UPNP = 2; // 0x2 + field public static final int SERVICE_TYPE_VENDOR_SPECIFIC = 255; // 0xff + } + + public class WifiP2pServiceRequest implements android.os.Parcelable { + method public int describeContents(); + method public static android.net.wifi.p2p.nsd.WifiP2pServiceRequest newInstance(int, String); + method public static android.net.wifi.p2p.nsd.WifiP2pServiceRequest newInstance(int); + method public void writeToParcel(android.os.Parcel, int); + } + + public class WifiP2pUpnpServiceInfo extends android.net.wifi.p2p.nsd.WifiP2pServiceInfo { + method public static android.net.wifi.p2p.nsd.WifiP2pUpnpServiceInfo newInstance(String, String, java.util.List<java.lang.String>); + } + + public class WifiP2pUpnpServiceRequest extends android.net.wifi.p2p.nsd.WifiP2pServiceRequest { + method public static android.net.wifi.p2p.nsd.WifiP2pUpnpServiceRequest newInstance(); + method public static android.net.wifi.p2p.nsd.WifiP2pUpnpServiceRequest newInstance(String); + } + +} + +package android.net.wifi.rtt { + + public class CivicLocationKeys { + field public static final int ADDITIONAL_CODE = 32; // 0x20 + field public static final int APT = 26; // 0x1a + field public static final int BOROUGH = 4; // 0x4 + field public static final int BRANCH_ROAD_NAME = 36; // 0x24 + field public static final int BUILDING = 25; // 0x19 + field public static final int CITY = 3; // 0x3 + field public static final int COUNTY = 2; // 0x2 + field public static final int DESK = 33; // 0x21 + field public static final int FLOOR = 27; // 0x1b + field public static final int GROUP_OF_STREETS = 6; // 0x6 + field public static final int HNO = 19; // 0x13 + field public static final int HNS = 20; // 0x14 + field public static final int LANGUAGE = 0; // 0x0 + field public static final int LMK = 21; // 0x15 + field public static final int LOC = 22; // 0x16 + field public static final int NAM = 23; // 0x17 + field public static final int NEIGHBORHOOD = 5; // 0x5 + field public static final int PCN = 30; // 0x1e + field public static final int POD = 17; // 0x11 + field public static final int POSTAL_CODE = 24; // 0x18 + field public static final int PO_BOX = 31; // 0x1f + field public static final int PRD = 16; // 0x10 + field public static final int PRIMARY_ROAD_NAME = 34; // 0x22 + field public static final int ROAD_SECTION = 35; // 0x23 + field public static final int ROOM = 28; // 0x1c + field public static final int SCRIPT = 128; // 0x80 + field public static final int STATE = 1; // 0x1 + field public static final int STREET_NAME_POST_MODIFIER = 39; // 0x27 + field public static final int STREET_NAME_PRE_MODIFIER = 38; // 0x26 + field public static final int STS = 18; // 0x12 + field public static final int SUBBRANCH_ROAD_NAME = 37; // 0x25 + field public static final int TYPE_OF_PLACE = 29; // 0x1d + } + + public final class RangingRequest implements android.os.Parcelable { + method public int describeContents(); + method public static int getMaxPeers(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.rtt.RangingRequest> CREATOR; + } + + public static final class RangingRequest.Builder { + ctor public RangingRequest.Builder(); + method public android.net.wifi.rtt.RangingRequest.Builder addAccessPoint(@NonNull android.net.wifi.ScanResult); + method public android.net.wifi.rtt.RangingRequest.Builder addAccessPoints(@NonNull java.util.List<android.net.wifi.ScanResult>); + method public android.net.wifi.rtt.RangingRequest.Builder addWifiAwarePeer(@NonNull android.net.MacAddress); + method public android.net.wifi.rtt.RangingRequest.Builder addWifiAwarePeer(@NonNull android.net.wifi.aware.PeerHandle); + method public android.net.wifi.rtt.RangingRequest build(); + } + + public final class RangingResult implements android.os.Parcelable { + method public int describeContents(); + method public int getDistanceMm(); + method public int getDistanceStdDevMm(); + method @Nullable public android.net.MacAddress getMacAddress(); + method public int getNumAttemptedMeasurements(); + method public int getNumSuccessfulMeasurements(); + method @Nullable public android.net.wifi.aware.PeerHandle getPeerHandle(); + method public long getRangingTimestampMillis(); + method public int getRssi(); + method public int getStatus(); + method @Nullable public android.net.wifi.rtt.ResponderLocation getUnverifiedResponderLocation(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.rtt.RangingResult> CREATOR; + field public static final int STATUS_FAIL = 1; // 0x1 + field public static final int STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2; // 0x2 + field public static final int STATUS_SUCCESS = 0; // 0x0 + } + + public abstract class RangingResultCallback { + ctor public RangingResultCallback(); + method public abstract void onRangingFailure(int); + method public abstract void onRangingResults(@NonNull java.util.List<android.net.wifi.rtt.RangingResult>); + field public static final int STATUS_CODE_FAIL = 1; // 0x1 + field public static final int STATUS_CODE_FAIL_RTT_NOT_AVAILABLE = 2; // 0x2 + } + + public final class ResponderLocation implements android.os.Parcelable { + method public int describeContents(); + method public double getAltitude(); + method public int getAltitudeType(); + method public double getAltitudeUncertainty(); + method public java.util.List<android.net.MacAddress> getColocatedBssids(); + method public int getDatum(); + method public int getExpectedToMove(); + method public double getFloorNumber(); + method public double getHeightAboveFloorMeters(); + method public double getHeightAboveFloorUncertaintyMeters(); + method public double getLatitude(); + method public double getLatitudeUncertainty(); + method public int getLciVersion(); + method public double getLongitude(); + method public double getLongitudeUncertainty(); + method @Nullable public String getMapImageMimeType(); + method @Nullable public android.net.Uri getMapImageUri(); + method public boolean getRegisteredLocationAgreementIndication(); + method public boolean isLciSubelementValid(); + method public boolean isZaxisSubelementValid(); + method @Nullable public android.location.Address toCivicLocationAddress(); + method @Nullable public android.util.SparseArray<java.lang.String> toCivicLocationSparseArray(); + method @NonNull public android.location.Location toLocation(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int ALTITUDE_FLOORS = 2; // 0x2 + field public static final int ALTITUDE_METERS = 1; // 0x1 + field public static final int ALTITUDE_UNDEFINED = 0; // 0x0 + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.rtt.ResponderLocation> CREATOR; + field public static final int DATUM_NAD83_MLLW = 3; // 0x3 + field public static final int DATUM_NAD83_NAV88 = 2; // 0x2 + field public static final int DATUM_UNDEFINED = 0; // 0x0 + field public static final int DATUM_WGS84 = 1; // 0x1 + field public static final int LCI_VERSION_1 = 1; // 0x1 + field public static final int LOCATION_FIXED = 0; // 0x0 + field public static final int LOCATION_MOVEMENT_UNKNOWN = 2; // 0x2 + field public static final int LOCATION_RESERVED = 3; // 0x3 + field public static final int LOCATION_VARIABLE = 1; // 0x1 + } + + public class WifiRttManager { + method public boolean isAvailable(); + method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.ACCESS_WIFI_STATE}) public void startRanging(@NonNull android.net.wifi.rtt.RangingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.rtt.RangingResultCallback); + field public static final String ACTION_WIFI_RTT_STATE_CHANGED = "android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED"; + } + +} + diff --git a/wifi/api/lint-baseline.txt b/wifi/api/lint-baseline.txt new file mode 100644 index 000000000000..892411f8c3a1 --- /dev/null +++ b/wifi/api/lint-baseline.txt @@ -0,0 +1,13 @@ +// Baseline format: 1.0 +GenericException: android.net.wifi.WifiManager.LocalOnlyHotspotReservation#finalize(): + Methods must not throw generic exceptions (`java.lang.Throwable`) +GenericException: android.net.wifi.WifiManager.MulticastLock#finalize(): + Methods must not throw generic exceptions (`java.lang.Throwable`) +GenericException: android.net.wifi.WifiManager.WifiLock#finalize(): + Methods must not throw generic exceptions (`java.lang.Throwable`) + + +VisiblySynchronized: PsiThisExpression:WifiManager.this: + Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.net.wifi.WifiManager.WifiLock.finalize() +VisiblySynchronized: android.net.wifi.WifiManager.WifiLock#finalize(): + Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.net.wifi.WifiManager.WifiLock.finalize() diff --git a/wifi/api/module-lib-current.txt b/wifi/api/module-lib-current.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/wifi/api/module-lib-current.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/wifi/api/module-lib-removed.txt b/wifi/api/module-lib-removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/wifi/api/module-lib-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/wifi/api/removed.txt b/wifi/api/removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/wifi/api/removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt new file mode 100644 index 000000000000..07793c1eb02a --- /dev/null +++ b/wifi/api/system-current.txt @@ -0,0 +1,939 @@ +// Signature format: 2.0 +package android.net.wifi { + + public abstract class EasyConnectStatusCallback { + ctor public EasyConnectStatusCallback(); + method public abstract void onConfiguratorSuccess(int); + method public abstract void onEnrolleeSuccess(int); + method public void onFailure(int); + method public void onFailure(int, @Nullable String, @NonNull android.util.SparseArray<int[]>, @NonNull int[]); + method public abstract void onProgress(int); + field public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0 + field public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_ACCEPTED = 3; // 0x3 + field public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE = 2; // 0x2 + field public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1 + field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED = 1; // 0x1 + field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0 + } + + @Deprecated public class RttManager { + method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void disableResponder(android.net.wifi.RttManager.ResponderCallback); + method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void enableResponder(android.net.wifi.RttManager.ResponderCallback); + method @Deprecated public android.net.wifi.RttManager.Capabilities getCapabilities(); + method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.net.wifi.RttManager.RttCapabilities getRttCapabilities(); + method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startRanging(android.net.wifi.RttManager.RttParams[], android.net.wifi.RttManager.RttListener); + method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopRanging(android.net.wifi.RttManager.RttListener); + field @Deprecated public static final int BASE = 160256; // 0x27200 + field @Deprecated public static final int CMD_OP_ABORTED = 160260; // 0x27204 + field @Deprecated public static final int CMD_OP_DISABLE_RESPONDER = 160262; // 0x27206 + field @Deprecated public static final int CMD_OP_ENABLE_RESPONDER = 160261; // 0x27205 + field @Deprecated public static final int CMD_OP_ENALBE_RESPONDER_FAILED = 160264; // 0x27208 + field @Deprecated public static final int CMD_OP_ENALBE_RESPONDER_SUCCEEDED = 160263; // 0x27207 + field @Deprecated public static final int CMD_OP_FAILED = 160258; // 0x27202 + field @Deprecated public static final int CMD_OP_START_RANGING = 160256; // 0x27200 + field @Deprecated public static final int CMD_OP_STOP_RANGING = 160257; // 0x27201 + field @Deprecated public static final int CMD_OP_SUCCEEDED = 160259; // 0x27203 + field @Deprecated public static final String DESCRIPTION_KEY = "android.net.wifi.RttManager.Description"; + field @Deprecated public static final int PREAMBLE_HT = 2; // 0x2 + field @Deprecated public static final int PREAMBLE_LEGACY = 1; // 0x1 + field @Deprecated public static final int PREAMBLE_VHT = 4; // 0x4 + field @Deprecated public static final int REASON_INITIATOR_NOT_ALLOWED_WHEN_RESPONDER_ON = -6; // 0xfffffffa + field @Deprecated public static final int REASON_INVALID_LISTENER = -3; // 0xfffffffd + field @Deprecated public static final int REASON_INVALID_REQUEST = -4; // 0xfffffffc + field @Deprecated public static final int REASON_NOT_AVAILABLE = -2; // 0xfffffffe + field @Deprecated public static final int REASON_PERMISSION_DENIED = -5; // 0xfffffffb + field @Deprecated public static final int REASON_UNSPECIFIED = -1; // 0xffffffff + field @Deprecated public static final int RTT_BW_10_SUPPORT = 2; // 0x2 + field @Deprecated public static final int RTT_BW_160_SUPPORT = 32; // 0x20 + field @Deprecated public static final int RTT_BW_20_SUPPORT = 4; // 0x4 + field @Deprecated public static final int RTT_BW_40_SUPPORT = 8; // 0x8 + field @Deprecated public static final int RTT_BW_5_SUPPORT = 1; // 0x1 + field @Deprecated public static final int RTT_BW_80_SUPPORT = 16; // 0x10 + field @Deprecated public static final int RTT_CHANNEL_WIDTH_10 = 6; // 0x6 + field @Deprecated public static final int RTT_CHANNEL_WIDTH_160 = 3; // 0x3 + field @Deprecated public static final int RTT_CHANNEL_WIDTH_20 = 0; // 0x0 + field @Deprecated public static final int RTT_CHANNEL_WIDTH_40 = 1; // 0x1 + field @Deprecated public static final int RTT_CHANNEL_WIDTH_5 = 5; // 0x5 + field @Deprecated public static final int RTT_CHANNEL_WIDTH_80 = 2; // 0x2 + field @Deprecated public static final int RTT_CHANNEL_WIDTH_80P80 = 4; // 0x4 + field @Deprecated public static final int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1; // 0xffffffff + field @Deprecated public static final int RTT_PEER_NAN = 5; // 0x5 + field @Deprecated public static final int RTT_PEER_P2P_CLIENT = 4; // 0x4 + field @Deprecated public static final int RTT_PEER_P2P_GO = 3; // 0x3 + field @Deprecated public static final int RTT_PEER_TYPE_AP = 1; // 0x1 + field @Deprecated public static final int RTT_PEER_TYPE_STA = 2; // 0x2 + field @Deprecated public static final int RTT_PEER_TYPE_UNSPECIFIED = 0; // 0x0 + field @Deprecated public static final int RTT_STATUS_ABORTED = 8; // 0x8 + field @Deprecated public static final int RTT_STATUS_FAILURE = 1; // 0x1 + field @Deprecated public static final int RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL = 6; // 0x6 + field @Deprecated public static final int RTT_STATUS_FAIL_BUSY_TRY_LATER = 12; // 0xc + field @Deprecated public static final int RTT_STATUS_FAIL_FTM_PARAM_OVERRIDE = 15; // 0xf + field @Deprecated public static final int RTT_STATUS_FAIL_INVALID_TS = 9; // 0x9 + field @Deprecated public static final int RTT_STATUS_FAIL_NOT_SCHEDULED_YET = 4; // 0x4 + field @Deprecated public static final int RTT_STATUS_FAIL_NO_CAPABILITY = 7; // 0x7 + field @Deprecated public static final int RTT_STATUS_FAIL_NO_RSP = 2; // 0x2 + field @Deprecated public static final int RTT_STATUS_FAIL_PROTOCOL = 10; // 0xa + field @Deprecated public static final int RTT_STATUS_FAIL_REJECTED = 3; // 0x3 + field @Deprecated public static final int RTT_STATUS_FAIL_SCHEDULE = 11; // 0xb + field @Deprecated public static final int RTT_STATUS_FAIL_TM_TIMEOUT = 5; // 0x5 + field @Deprecated public static final int RTT_STATUS_INVALID_REQ = 13; // 0xd + field @Deprecated public static final int RTT_STATUS_NO_WIFI = 14; // 0xe + field @Deprecated public static final int RTT_STATUS_SUCCESS = 0; // 0x0 + field @Deprecated public static final int RTT_TYPE_11_MC = 4; // 0x4 + field @Deprecated public static final int RTT_TYPE_11_V = 2; // 0x2 + field @Deprecated public static final int RTT_TYPE_ONE_SIDED = 1; // 0x1 + field @Deprecated public static final int RTT_TYPE_TWO_SIDED = 2; // 0x2 + field @Deprecated public static final int RTT_TYPE_UNSPECIFIED = 0; // 0x0 + } + + @Deprecated public class RttManager.Capabilities { + ctor @Deprecated public RttManager.Capabilities(); + field @Deprecated public int supportedPeerType; + field @Deprecated public int supportedType; + } + + @Deprecated public static class RttManager.ParcelableRttParams implements android.os.Parcelable { + field @Deprecated @NonNull public android.net.wifi.RttManager.RttParams[] mParams; + } + + @Deprecated public static class RttManager.ParcelableRttResults implements android.os.Parcelable { + ctor @Deprecated public RttManager.ParcelableRttResults(android.net.wifi.RttManager.RttResult[]); + field @Deprecated public android.net.wifi.RttManager.RttResult[] mResults; + } + + @Deprecated public abstract static class RttManager.ResponderCallback { + ctor @Deprecated public RttManager.ResponderCallback(); + method @Deprecated public abstract void onResponderEnableFailure(int); + method @Deprecated public abstract void onResponderEnabled(android.net.wifi.RttManager.ResponderConfig); + } + + @Deprecated public static class RttManager.ResponderConfig implements android.os.Parcelable { + ctor @Deprecated public RttManager.ResponderConfig(); + method @Deprecated public int describeContents(); + method @Deprecated public void writeToParcel(android.os.Parcel, int); + field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.RttManager.ResponderConfig> CREATOR; + field @Deprecated public int centerFreq0; + field @Deprecated public int centerFreq1; + field @Deprecated public int channelWidth; + field @Deprecated public int frequency; + field @Deprecated public String macAddress; + field @Deprecated public int preamble; + } + + @Deprecated public static class RttManager.RttCapabilities implements android.os.Parcelable { + ctor @Deprecated public RttManager.RttCapabilities(); + field @Deprecated public int bwSupported; + field @Deprecated public boolean lciSupported; + field @Deprecated public boolean lcrSupported; + field @Deprecated public int mcVersion; + field @Deprecated public boolean oneSidedRttSupported; + field @Deprecated public int preambleSupported; + field @Deprecated public boolean responderSupported; + field @Deprecated public boolean secureRttSupported; + field @Deprecated public boolean supportedPeerType; + field @Deprecated public boolean supportedType; + field @Deprecated public boolean twoSided11McRttSupported; + } + + @Deprecated public static interface RttManager.RttListener { + method @Deprecated public void onAborted(); + method @Deprecated public void onFailure(int, String); + method @Deprecated public void onSuccess(android.net.wifi.RttManager.RttResult[]); + } + + @Deprecated public static class RttManager.RttParams { + ctor @Deprecated public RttManager.RttParams(); + field @Deprecated public boolean LCIRequest; + field @Deprecated public boolean LCRRequest; + field @Deprecated public int bandwidth; + field @Deprecated public String bssid; + field @Deprecated public int burstTimeout; + field @Deprecated public int centerFreq0; + field @Deprecated public int centerFreq1; + field @Deprecated public int channelWidth; + field @Deprecated public int deviceType; + field @Deprecated public int frequency; + field @Deprecated public int interval; + field @Deprecated public int numRetriesPerFTMR; + field @Deprecated public int numRetriesPerMeasurementFrame; + field @Deprecated public int numSamplesPerBurst; + field @Deprecated public int num_retries; + field @Deprecated public int num_samples; + field @Deprecated public int numberBurst; + field @Deprecated public int preamble; + field @Deprecated public int requestType; + field @Deprecated public boolean secure; + } + + @Deprecated public static class RttManager.RttResult { + ctor @Deprecated public RttManager.RttResult(); + field @Deprecated public android.net.wifi.RttManager.WifiInformationElement LCI; + field @Deprecated public android.net.wifi.RttManager.WifiInformationElement LCR; + field @Deprecated public String bssid; + field @Deprecated public int burstDuration; + field @Deprecated public int burstNumber; + field @Deprecated public int distance; + field @Deprecated public int distanceSpread; + field @Deprecated public int distanceStandardDeviation; + field @Deprecated public int distance_cm; + field @Deprecated public int distance_sd_cm; + field @Deprecated public int distance_spread_cm; + field @Deprecated public int frameNumberPerBurstPeer; + field @Deprecated public int measurementFrameNumber; + field @Deprecated public int measurementType; + field @Deprecated public int negotiatedBurstNum; + field @Deprecated public int requestType; + field @Deprecated public int retryAfterDuration; + field @Deprecated public int rssi; + field @Deprecated public int rssiSpread; + field @Deprecated public int rssi_spread; + field @Deprecated public long rtt; + field @Deprecated public long rttSpread; + field @Deprecated public long rttStandardDeviation; + field @Deprecated public long rtt_ns; + field @Deprecated public long rtt_sd_ns; + field @Deprecated public long rtt_spread_ns; + field @Deprecated public int rxRate; + field @Deprecated public boolean secure; + field @Deprecated public int status; + field @Deprecated public int successMeasurementFrameNumber; + field @Deprecated public long ts; + field @Deprecated public int txRate; + field @Deprecated public int tx_rate; + } + + @Deprecated public static class RttManager.WifiInformationElement { + ctor @Deprecated public RttManager.WifiInformationElement(); + field @Deprecated public byte[] data; + field @Deprecated public byte id; + } + + public final class ScanResult implements android.os.Parcelable { + field public static final int CIPHER_CCMP = 3; // 0x3 + field public static final int CIPHER_GCMP_256 = 4; // 0x4 + field public static final int CIPHER_NONE = 0; // 0x0 + field public static final int CIPHER_NO_GROUP_ADDRESSED = 1; // 0x1 + field public static final int CIPHER_SMS4 = 5; // 0x5 + field public static final int CIPHER_TKIP = 2; // 0x2 + field public static final int KEY_MGMT_EAP = 2; // 0x2 + field public static final int KEY_MGMT_EAP_SHA256 = 6; // 0x6 + field public static final int KEY_MGMT_EAP_SUITE_B_192 = 10; // 0xa + field public static final int KEY_MGMT_FT_EAP = 4; // 0x4 + field public static final int KEY_MGMT_FT_PSK = 3; // 0x3 + field public static final int KEY_MGMT_FT_SAE = 11; // 0xb + field public static final int KEY_MGMT_NONE = 0; // 0x0 + field public static final int KEY_MGMT_OSEN = 7; // 0x7 + field public static final int KEY_MGMT_OWE = 9; // 0x9 + field public static final int KEY_MGMT_OWE_TRANSITION = 12; // 0xc + field public static final int KEY_MGMT_PSK = 1; // 0x1 + field public static final int KEY_MGMT_PSK_SHA256 = 5; // 0x5 + field public static final int KEY_MGMT_SAE = 8; // 0x8 + field public static final int KEY_MGMT_WAPI_CERT = 14; // 0xe + field public static final int KEY_MGMT_WAPI_PSK = 13; // 0xd + field public static final int PROTOCOL_NONE = 0; // 0x0 + field public static final int PROTOCOL_OSEN = 3; // 0x3 + field public static final int PROTOCOL_RSN = 2; // 0x2 + field public static final int PROTOCOL_WAPI = 4; // 0x4 + field public static final int PROTOCOL_WPA = 1; // 0x1 + } + + public final class SoftApCapability implements android.os.Parcelable { + method public boolean areFeaturesSupported(long); + method public int describeContents(); + method public int getMaxSupportedClients(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR; + field public static final long SOFTAP_FEATURE_ACS_OFFLOAD = 1L; // 0x1L + field public static final long SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 2L; // 0x2L + field public static final long SOFTAP_FEATURE_WPA3_SAE = 4L; // 0x4L + } + + public final class SoftApConfiguration implements android.os.Parcelable { + method @NonNull public java.util.List<android.net.MacAddress> getAllowedClientList(); + method public int getBand(); + method @NonNull public java.util.List<android.net.MacAddress> getBlockedClientList(); + method public int getChannel(); + method public int getMaxNumberOfClients(); + method public long getShutdownTimeoutMillis(); + method public boolean isAutoShutdownEnabled(); + method public boolean isClientControlByUserEnabled(); + method @Nullable public android.net.wifi.WifiConfiguration toWifiConfiguration(); + field public static final int BAND_2GHZ = 1; // 0x1 + field public static final int BAND_5GHZ = 2; // 0x2 + field public static final int BAND_6GHZ = 4; // 0x4 + field public static final int BAND_ANY = 7; // 0x7 + } + + public static final class SoftApConfiguration.Builder { + ctor public SoftApConfiguration.Builder(); + ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration); + method @NonNull public android.net.wifi.SoftApConfiguration build(); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAllowedClientList(@NonNull java.util.List<android.net.MacAddress>); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAutoShutdownEnabled(boolean); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBand(int); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBlockedClientList(@NonNull java.util.List<android.net.MacAddress>); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientControlByUserEnabled(boolean); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(@IntRange(from=0) int); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(@IntRange(from=0) long); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String); + } + + public final class SoftApInfo implements android.os.Parcelable { + method public int describeContents(); + method public int getBandwidth(); + method public int getFrequency(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CHANNEL_WIDTH_160MHZ = 6; // 0x6 + field public static final int CHANNEL_WIDTH_20MHZ = 2; // 0x2 + field public static final int CHANNEL_WIDTH_20MHZ_NOHT = 1; // 0x1 + field public static final int CHANNEL_WIDTH_40MHZ = 3; // 0x3 + field public static final int CHANNEL_WIDTH_80MHZ = 4; // 0x4 + field public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 5; // 0x5 + field public static final int CHANNEL_WIDTH_INVALID = 0; // 0x0 + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApInfo> CREATOR; + } + + public final class WifiClient implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.net.MacAddress getMacAddress(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiClient> CREATOR; + } + + @Deprecated public class WifiConfiguration implements android.os.Parcelable { + method @Deprecated public int getAuthType(); + method @Deprecated @NonNull public android.net.IpConfiguration getIpConfiguration(); + method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus getNetworkSelectionStatus(); + method @Deprecated @NonNull public String getPrintableSsid(); + method @Deprecated public int getRecentFailureReason(); + method @Deprecated public boolean hasNoInternetAccess(); + method @Deprecated public boolean isEphemeral(); + method @Deprecated public static boolean isMetered(@Nullable android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiInfo); + method @Deprecated public boolean isNoInternetAccessExpected(); + method @Deprecated public void setIpConfiguration(@Nullable android.net.IpConfiguration); + method @Deprecated public void setNetworkSelectionStatus(@NonNull android.net.wifi.WifiConfiguration.NetworkSelectionStatus); + field @Deprecated public static final int INVALID_NETWORK_ID = -1; // 0xffffffff + field @Deprecated public static final int METERED_OVERRIDE_METERED = 1; // 0x1 + field @Deprecated public static final int METERED_OVERRIDE_NONE = 0; // 0x0 + field @Deprecated public static final int METERED_OVERRIDE_NOT_METERED = 2; // 0x2 + field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0 + field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1 + field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11 + field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0 + field @Deprecated public boolean allowAutojoin; + field @Deprecated public int carrierId; + field @Deprecated public String creatorName; + field @Deprecated public int creatorUid; + field @Deprecated public boolean fromWifiNetworkSpecifier; + field @Deprecated public boolean fromWifiNetworkSuggestion; + field @Deprecated public String lastUpdateName; + field @Deprecated public int lastUpdateUid; + field @Deprecated public int macRandomizationSetting; + field @Deprecated public boolean meteredHint; + field @Deprecated public int meteredOverride; + field @Deprecated public int numAssociation; + field @Deprecated public int numScorerOverride; + field @Deprecated public int numScorerOverrideAndSwitchedNetwork; + field @Deprecated public boolean requirePmf; + field @Deprecated public boolean shared; + field @Deprecated public boolean useExternalScores; + } + + @Deprecated public static class WifiConfiguration.KeyMgmt { + field @Deprecated public static final int WAPI_CERT = 14; // 0xe + field @Deprecated public static final int WAPI_PSK = 13; // 0xd + field @Deprecated public static final int WPA2_PSK = 4; // 0x4 + } + + @Deprecated public static class WifiConfiguration.NetworkSelectionStatus { + method @Deprecated public int getDisableReasonCounter(int); + method @Deprecated public long getDisableTime(); + method @Deprecated public static int getMaxNetworkSelectionDisableReason(); + method @Deprecated public int getNetworkSelectionDisableReason(); + method @Deprecated @Nullable public static String getNetworkSelectionDisableReasonString(int); + method @Deprecated public int getNetworkSelectionStatus(); + method @Deprecated @NonNull public String getNetworkStatusString(); + method @Deprecated public boolean hasEverConnected(); + field @Deprecated public static final int DISABLED_ASSOCIATION_REJECTION = 1; // 0x1 + field @Deprecated public static final int DISABLED_AUTHENTICATION_FAILURE = 2; // 0x2 + field @Deprecated public static final int DISABLED_AUTHENTICATION_NO_CREDENTIALS = 5; // 0x5 + field @Deprecated public static final int DISABLED_AUTHENTICATION_NO_SUBSCRIPTION = 9; // 0x9 + field @Deprecated public static final int DISABLED_BY_WIFI_MANAGER = 7; // 0x7 + field @Deprecated public static final int DISABLED_BY_WRONG_PASSWORD = 8; // 0x8 + field @Deprecated public static final int DISABLED_DHCP_FAILURE = 3; // 0x3 + field @Deprecated public static final int DISABLED_NONE = 0; // 0x0 + field @Deprecated public static final int DISABLED_NO_INTERNET_PERMANENT = 6; // 0x6 + field @Deprecated public static final int DISABLED_NO_INTERNET_TEMPORARY = 4; // 0x4 + field @Deprecated public static final int NETWORK_SELECTION_ENABLED = 0; // 0x0 + field @Deprecated public static final int NETWORK_SELECTION_PERMANENTLY_DISABLED = 2; // 0x2 + field @Deprecated public static final int NETWORK_SELECTION_TEMPORARY_DISABLED = 1; // 0x1 + } + + @Deprecated public static final class WifiConfiguration.NetworkSelectionStatus.Builder { + ctor @Deprecated public WifiConfiguration.NetworkSelectionStatus.Builder(); + method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus build(); + method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus.Builder setNetworkSelectionDisableReason(int); + method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus.Builder setNetworkSelectionStatus(int); + } + + public class WifiEnterpriseConfig implements android.os.Parcelable { + method @Nullable public String[] getCaCertificateAliases(); + method @NonNull public String getCaPath(); + method @NonNull public String getClientCertificateAlias(); + method public int getOcsp(); + method @NonNull public String getWapiCertSuite(); + method public void setCaCertificateAliases(@Nullable String[]); + method public void setCaPath(@NonNull String); + method public void setClientCertificateAlias(@NonNull String); + method public void setOcsp(int); + method public void setWapiCertSuite(@NonNull String); + field public static final int OCSP_NONE = 0; // 0x0 + field public static final int OCSP_REQUEST_CERT_STATUS = 1; // 0x1 + field public static final int OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS = 3; // 0x3 + field public static final int OCSP_REQUIRE_CERT_STATUS = 2; // 0x2 + } + + public class WifiFrameworkInitializer { + method public static void registerServiceWrappers(); + } + + public class WifiInfo implements android.os.Parcelable { + method public double getLostTxPacketsPerSecond(); + method @Nullable public String getRequestingPackageName(); + method public double getRetriedTxPacketsPerSecond(); + method public int getScore(); + method public double getSuccessfulRxPacketsPerSecond(); + method public double getSuccessfulTxPacketsPerSecond(); + method public boolean isEphemeral(); + method public boolean isOsuAp(); + method public boolean isPasspointAp(); + method @Nullable public static String sanitizeSsid(@Nullable String); + field public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00"; + field public static final int INVALID_RSSI = -127; // 0xffffff81 + } + + public class WifiManager { + method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinGlobal(boolean); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinPasspoint(@NonNull String, boolean); + method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void clearWifiConnectedNetworkScorer(); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK}) public void disableEphemeralNetwork(@NonNull String); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>); + method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCountryCode(); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.Network getCurrentNetwork(); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses(); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>); + method @NonNull @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.Map<android.net.wifi.WifiNetworkSuggestion,java.util.List<android.net.wifi.ScanResult>> getMatchingScanResults(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>, @Nullable java.util.List<android.net.wifi.ScanResult>); + method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks(); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public android.net.wifi.SoftApConfiguration getSoftApConfiguration(); + method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void getWifiActivityEnergyInfoAsync(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener); + method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration(); + method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState(); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(@NonNull java.util.List<android.net.wifi.ScanResult>); + method public boolean isApMacRandomizationSupported(); + method public boolean isConnectedMacRandomizationSupported(); + method @Deprecated public boolean isDeviceToDeviceRttSupported(); + method public boolean isPortableHotspotSupported(); + method public boolean isVerboseLoggingEnabled(); + method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled(); + method public boolean isWifiScannerSupported(); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerNetworkRequestMatchCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.TrafficStateCallback); + method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreBackupData(@NonNull byte[]); + method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public android.net.wifi.SoftApConfiguration restoreSoftApBackupData(@NonNull byte[]); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreSupplicantBackupData(@NonNull byte[], @NonNull byte[]); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveBackupData(); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData(); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setAutoWakeupEnabled(boolean); + method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setPasspointMeteredOverride(@NonNull String, int); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanAlwaysAvailable(boolean); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanThrottleEnabled(boolean); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setVerboseLoggingEnabled(boolean); + method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration); + method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public boolean setWifiConnectedNetworkScorer(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiConnectedNetworkScorer); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startLocalOnlyHotspot(@NonNull android.net.wifi.SoftApConfiguration, @Nullable java.util.concurrent.Executor, @Nullable android.net.wifi.WifiManager.LocalOnlyHotspotCallback); + method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startTetheredHotspot(@Nullable android.net.wifi.SoftApConfiguration); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession(); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp(); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterNetworkRequestMatchCallback(@NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterSoftApCallback(@NonNull android.net.wifi.WifiManager.SoftApCallback); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterTrafficStateCallback(@NonNull android.net.wifi.WifiManager.TrafficStateCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateInterfaceIpState(@Nullable String, int); + method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void updateWifiUsabilityScore(int, int, int); + field public static final String ACTION_LINK_CONFIGURATION_CHANGED = "android.net.wifi.LINK_CONFIGURATION_CHANGED"; + field @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public static final String ACTION_NETWORK_SETTINGS_RESET = "android.net.wifi.action.NETWORK_SETTINGS_RESET"; + field public static final String ACTION_PASSPOINT_LAUNCH_OSU_VIEW = "android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW"; + field public static final String ACTION_REQUEST_DISABLE = "android.net.wifi.action.REQUEST_DISABLE"; + field public static final String ACTION_REQUEST_ENABLE = "android.net.wifi.action.REQUEST_ENABLE"; + 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 + field public static final String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE"; + field public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1; // 0x1 + field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2 + field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3 + field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0 + field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1 + field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0 + field public static final String EXTRA_CHANGE_REASON = "changeReason"; + field public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES"; + field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges"; + field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK"; + field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state"; + field public static final String EXTRA_URL = "android.net.wifi.extra.URL"; + field public static final String EXTRA_WIFI_AP_FAILURE_REASON = "android.net.wifi.extra.WIFI_AP_FAILURE_REASON"; + field public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME"; + field public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE"; + field public static final String EXTRA_WIFI_AP_STATE = "wifi_state"; + field public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration"; + field public static final String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et"; + field public static final String EXTRA_WIFI_CREDENTIAL_SSID = "ssid"; + field public static final int IFACE_IP_MODE_CONFIGURATION_ERROR = 0; // 0x0 + field public static final int IFACE_IP_MODE_LOCAL_ONLY = 2; // 0x2 + field public static final int IFACE_IP_MODE_TETHERED = 1; // 0x1 + field public static final int IFACE_IP_MODE_UNSPECIFIED = -1; // 0xffffffff + field public static final int PASSPOINT_HOME_NETWORK = 0; // 0x0 + field public static final int PASSPOINT_ROAMING_NETWORK = 1; // 0x1 + field public static final int SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER = 0; // 0x0 + field public static final int SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS = 1; // 0x1 + field public static final int SAP_START_FAILURE_GENERAL = 0; // 0x0 + field public static final int SAP_START_FAILURE_NO_CHANNEL = 1; // 0x1 + field public static final int SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION = 2; // 0x2 + field public static final String WIFI_AP_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_AP_STATE_CHANGED"; + field public static final int WIFI_AP_STATE_DISABLED = 11; // 0xb + field public static final int WIFI_AP_STATE_DISABLING = 10; // 0xa + field public static final int WIFI_AP_STATE_ENABLED = 13; // 0xd + field public static final int WIFI_AP_STATE_ENABLING = 12; // 0xc + field public static final int WIFI_AP_STATE_FAILED = 14; // 0xe + field public static final String WIFI_CREDENTIAL_CHANGED_ACTION = "android.net.wifi.WIFI_CREDENTIAL_CHANGED"; + field public static final int WIFI_CREDENTIAL_FORGOT = 1; // 0x1 + field public static final int WIFI_CREDENTIAL_SAVED = 0; // 0x0 + } + + public static interface WifiManager.ActionListener { + method public void onFailure(int); + method public void onSuccess(); + } + + public static interface WifiManager.NetworkRequestMatchCallback { + method public default void onAbort(); + method public default void onMatch(@NonNull java.util.List<android.net.wifi.ScanResult>); + method public default void onUserSelectionCallbackRegistration(@NonNull android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback); + method public default void onUserSelectionConnectFailure(@NonNull android.net.wifi.WifiConfiguration); + method public default void onUserSelectionConnectSuccess(@NonNull android.net.wifi.WifiConfiguration); + } + + public static interface WifiManager.NetworkRequestUserSelectionCallback { + method public default void reject(); + method public default void select(@NonNull android.net.wifi.WifiConfiguration); + } + + public static interface WifiManager.OnWifiActivityEnergyInfoListener { + method public void onWifiActivityEnergyInfo(@Nullable android.os.connectivity.WifiActivityEnergyInfo); + } + + public static interface WifiManager.OnWifiUsabilityStatsListener { + method public void onWifiUsabilityStats(int, boolean, @NonNull android.net.wifi.WifiUsabilityStatsEntry); + } + + public static interface WifiManager.ScoreUpdateObserver { + method public void notifyScoreUpdate(int, int); + method public void triggerUpdateOfWifiUsabilityStats(int); + } + + public static interface WifiManager.SoftApCallback { + method public default void onBlockedClientConnecting(@NonNull android.net.wifi.WifiClient, int); + method public default void onCapabilityChanged(@NonNull android.net.wifi.SoftApCapability); + method public default void onConnectedClientsChanged(@NonNull java.util.List<android.net.wifi.WifiClient>); + method public default void onInfoChanged(@NonNull android.net.wifi.SoftApInfo); + method public default void onStateChanged(int, int); + } + + public static interface WifiManager.TrafficStateCallback { + method public void onStateChanged(int); + field public static final int DATA_ACTIVITY_IN = 1; // 0x1 + field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3 + field public static final int DATA_ACTIVITY_NONE = 0; // 0x0 + field public static final int DATA_ACTIVITY_OUT = 2; // 0x2 + } + + public static interface WifiManager.WifiConnectedNetworkScorer { + method public void onSetScoreUpdateObserver(@NonNull android.net.wifi.WifiManager.ScoreUpdateObserver); + method public void onStart(int); + method public void onStop(int); + } + + public class WifiNetworkConnectionStatistics implements android.os.Parcelable { + ctor public WifiNetworkConnectionStatistics(int, int); + ctor public WifiNetworkConnectionStatistics(); + ctor public WifiNetworkConnectionStatistics(android.net.wifi.WifiNetworkConnectionStatistics); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiNetworkConnectionStatistics> CREATOR; + field public int numConnection; + field public int numUsage; + } + + public final class WifiNetworkSuggestion implements android.os.Parcelable { + method @NonNull public android.net.wifi.WifiConfiguration getWifiConfiguration(); + } + + public static final class WifiNetworkSuggestion.Builder { + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int); + } + + public class WifiScanner { + method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]); + method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings); + method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<java.lang.Integer> getAvailableChannels(int); + method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean getScanResults(); + method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<android.net.wifi.ScanResult> getSingleScanResults(); + method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void registerScanListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiScanner.ScanListener); + method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setScanningEnabled(boolean); + method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener); + method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener, android.os.WorkSource); + method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener); + method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener, android.os.WorkSource); + method @Deprecated public void startTrackingBssids(android.net.wifi.WifiScanner.BssidInfo[], int, android.net.wifi.WifiScanner.BssidListener); + method @Deprecated public void startTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener); + method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopBackgroundScan(android.net.wifi.WifiScanner.ScanListener); + method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopScan(android.net.wifi.WifiScanner.ScanListener); + method @Deprecated public void stopTrackingBssids(android.net.wifi.WifiScanner.BssidListener); + method @Deprecated public void stopTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener); + method public void unregisterScanListener(@NonNull android.net.wifi.WifiScanner.ScanListener); + field public static final int MAX_SCAN_PERIOD_MS = 1024000; // 0xfa000 + field public static final int MIN_SCAN_PERIOD_MS = 1000; // 0x3e8 + field public static final int REASON_DUPLICATE_REQEUST = -5; // 0xfffffffb + field public static final int REASON_INVALID_LISTENER = -2; // 0xfffffffe + field public static final int REASON_INVALID_REQUEST = -3; // 0xfffffffd + field public static final int REASON_NOT_AUTHORIZED = -4; // 0xfffffffc + field public static final int REASON_SUCCEEDED = 0; // 0x0 + field public static final int REASON_UNSPECIFIED = -1; // 0xffffffff + field @Deprecated public static final int REPORT_EVENT_AFTER_BUFFER_FULL = 0; // 0x0 + field public static final int REPORT_EVENT_AFTER_EACH_SCAN = 1; // 0x1 + field public static final int REPORT_EVENT_FULL_SCAN_RESULT = 2; // 0x2 + field public static final int REPORT_EVENT_NO_BATCH = 4; // 0x4 + field public static final int SCAN_TYPE_HIGH_ACCURACY = 2; // 0x2 + field public static final int SCAN_TYPE_LOW_LATENCY = 0; // 0x0 + field public static final int SCAN_TYPE_LOW_POWER = 1; // 0x1 + field public static final int WIFI_BAND_24_5_6_GHZ = 11; // 0xb + field public static final int WIFI_BAND_24_5_WITH_DFS_6_GHZ = 15; // 0xf + field public static final int WIFI_BAND_24_GHZ = 1; // 0x1 + field public static final int WIFI_BAND_5_GHZ = 2; // 0x2 + field public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; // 0x4 + field public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; // 0x6 + field public static final int WIFI_BAND_6_GHZ = 8; // 0x8 + field public static final int WIFI_BAND_BOTH = 3; // 0x3 + field public static final int WIFI_BAND_BOTH_WITH_DFS = 7; // 0x7 + field public static final int WIFI_BAND_UNSPECIFIED = 0; // 0x0 + } + + public static interface WifiScanner.ActionListener { + method public void onFailure(int, String); + method public void onSuccess(); + } + + @Deprecated public static class WifiScanner.BssidInfo { + ctor @Deprecated public WifiScanner.BssidInfo(); + field @Deprecated public String bssid; + field @Deprecated public int frequencyHint; + field @Deprecated public int high; + field @Deprecated public int low; + } + + @Deprecated public static interface WifiScanner.BssidListener extends android.net.wifi.WifiScanner.ActionListener { + method @Deprecated public void onFound(android.net.wifi.ScanResult[]); + method @Deprecated public void onLost(android.net.wifi.ScanResult[]); + } + + public static class WifiScanner.ChannelSpec { + ctor public WifiScanner.ChannelSpec(int); + field public int frequency; + } + + @Deprecated public static class WifiScanner.HotlistSettings implements android.os.Parcelable { + ctor @Deprecated public WifiScanner.HotlistSettings(); + field @Deprecated public int apLostThreshold; + field @Deprecated public android.net.wifi.WifiScanner.BssidInfo[] bssidInfos; + } + + public static class WifiScanner.ParcelableScanData implements android.os.Parcelable { + ctor public WifiScanner.ParcelableScanData(android.net.wifi.WifiScanner.ScanData[]); + method public android.net.wifi.WifiScanner.ScanData[] getResults(); + field public android.net.wifi.WifiScanner.ScanData[] mResults; + } + + public static class WifiScanner.ParcelableScanResults implements android.os.Parcelable { + ctor public WifiScanner.ParcelableScanResults(android.net.wifi.ScanResult[]); + method public android.net.wifi.ScanResult[] getResults(); + field public android.net.wifi.ScanResult[] mResults; + } + + public static class WifiScanner.ScanData implements android.os.Parcelable { + ctor public WifiScanner.ScanData(int, int, android.net.wifi.ScanResult[]); + ctor public WifiScanner.ScanData(android.net.wifi.WifiScanner.ScanData); + method public int getFlags(); + method public int getId(); + method public android.net.wifi.ScanResult[] getResults(); + } + + public static interface WifiScanner.ScanListener extends android.net.wifi.WifiScanner.ActionListener { + method public void onFullResult(android.net.wifi.ScanResult); + method @Deprecated public void onPeriodChanged(int); + method public void onResults(android.net.wifi.WifiScanner.ScanData[]); + } + + public static class WifiScanner.ScanSettings implements android.os.Parcelable { + ctor public WifiScanner.ScanSettings(); + field public int band; + field public android.net.wifi.WifiScanner.ChannelSpec[] channels; + field @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public final java.util.List<android.net.wifi.WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks; + field public boolean hideFromAppOps; + field public boolean ignoreLocationSettings; + field @Deprecated public int maxPeriodInMs; + field @Deprecated public int maxScansToCache; + field @Deprecated public int numBssidsPerScan; + field @Deprecated public int periodInMs; + field @Deprecated public int reportEvents; + field @Deprecated public int stepCount; + field @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public int type; + } + + public static class WifiScanner.ScanSettings.HiddenNetwork { + ctor public WifiScanner.ScanSettings.HiddenNetwork(@NonNull String); + field @NonNull public final String ssid; + } + + @Deprecated public static interface WifiScanner.WifiChangeListener extends android.net.wifi.WifiScanner.ActionListener { + method @Deprecated public void onChanging(android.net.wifi.ScanResult[]); + method @Deprecated public void onQuiescence(android.net.wifi.ScanResult[]); + } + + @Deprecated public static class WifiScanner.WifiChangeSettings implements android.os.Parcelable { + ctor @Deprecated public WifiScanner.WifiChangeSettings(); + field @Deprecated public android.net.wifi.WifiScanner.BssidInfo[] bssidInfos; + field @Deprecated public int lostApSampleSize; + field @Deprecated public int minApsBreachingThreshold; + field @Deprecated public int periodInMs; + field @Deprecated public int rssiSampleSize; + field @Deprecated public int unchangedSampleSize; + } + + public final class WifiUsabilityStatsEntry implements android.os.Parcelable { + method public int describeContents(); + method public int getCellularDataNetworkType(); + method public int getCellularSignalStrengthDb(); + method public int getCellularSignalStrengthDbm(); + method public int getLinkSpeedMbps(); + method public int getProbeElapsedTimeSinceLastUpdateMillis(); + method public int getProbeMcsRateSinceLastUpdate(); + method public int getProbeStatusSinceLastUpdate(); + method public int getRssi(); + method public int getRxLinkSpeedMbps(); + method public long getTimeStampMillis(); + method public long getTotalBackgroundScanTimeMillis(); + method public long getTotalBeaconRx(); + method public long getTotalCcaBusyFreqTimeMillis(); + method public long getTotalHotspot2ScanTimeMillis(); + method public long getTotalNanScanTimeMillis(); + method public long getTotalPnoScanTimeMillis(); + method public long getTotalRadioOnFreqTimeMillis(); + method public long getTotalRadioOnTimeMillis(); + method public long getTotalRadioRxTimeMillis(); + method public long getTotalRadioTxTimeMillis(); + method public long getTotalRoamScanTimeMillis(); + method public long getTotalRxSuccess(); + method public long getTotalScanTimeMillis(); + method public long getTotalTxBad(); + method public long getTotalTxRetries(); + method public long getTotalTxSuccess(); + method public boolean isSameRegisteredCell(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiUsabilityStatsEntry> CREATOR; + field public static final int PROBE_STATUS_FAILURE = 3; // 0x3 + field public static final int PROBE_STATUS_NO_PROBE = 1; // 0x1 + field public static final int PROBE_STATUS_SUCCESS = 2; // 0x2 + field public static final int PROBE_STATUS_UNKNOWN = 0; // 0x0 + } + +} + +package android.net.wifi.aware { + + public class DiscoverySession implements java.lang.AutoCloseable { + method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierPmk(@NonNull android.net.wifi.aware.PeerHandle, @NonNull byte[]); + } + + public class WifiAwareSession implements java.lang.AutoCloseable { + method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, @NonNull byte[], @NonNull byte[]); + } + +} + +package android.net.wifi.hotspot2 { + + public final class OsuProvider implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public String getFriendlyName(); + method @Nullable public android.net.Uri getServerUri(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.OsuProvider> CREATOR; + } + + public final class PasspointConfiguration implements android.os.Parcelable { + method public int getMeteredOverride(); + method public boolean isAutojoinEnabled(); + method public boolean isMacRandomizationEnabled(); + } + + public abstract class ProvisioningCallback { + ctor public ProvisioningCallback(); + method public abstract void onProvisioningComplete(); + method public abstract void onProvisioningFailure(int); + method public abstract void onProvisioningStatus(int); + field public static final int OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION = 22; // 0x16 + field public static final int OSU_FAILURE_AP_CONNECTION = 1; // 0x1 + field public static final int OSU_FAILURE_INVALID_URL_FORMAT_FOR_OSU = 8; // 0x8 + field public static final int OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE = 17; // 0x11 + field public static final int OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE = 21; // 0x15 + field public static final int OSU_FAILURE_NO_OSU_ACTIVITY_FOUND = 14; // 0xe + field public static final int OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE = 19; // 0x13 + field public static final int OSU_FAILURE_NO_PPS_MO = 16; // 0x10 + field public static final int OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE = 18; // 0x12 + field public static final int OSU_FAILURE_OSU_PROVIDER_NOT_FOUND = 23; // 0x17 + field public static final int OSU_FAILURE_PROVISIONING_ABORTED = 6; // 0x6 + field public static final int OSU_FAILURE_PROVISIONING_NOT_AVAILABLE = 7; // 0x7 + field public static final int OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES = 20; // 0x14 + field public static final int OSU_FAILURE_SERVER_CONNECTION = 3; // 0x3 + field public static final int OSU_FAILURE_SERVER_URL_INVALID = 2; // 0x2 + field public static final int OSU_FAILURE_SERVER_VALIDATION = 4; // 0x4 + field public static final int OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION = 5; // 0x5 + field public static final int OSU_FAILURE_SOAP_MESSAGE_EXCHANGE = 11; // 0xb + field public static final int OSU_FAILURE_START_REDIRECT_LISTENER = 12; // 0xc + field public static final int OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER = 13; // 0xd + field public static final int OSU_FAILURE_UNEXPECTED_COMMAND_TYPE = 9; // 0x9 + field public static final int OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS = 15; // 0xf + field public static final int OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE = 10; // 0xa + field public static final int OSU_STATUS_AP_CONNECTED = 2; // 0x2 + field public static final int OSU_STATUS_AP_CONNECTING = 1; // 0x1 + field public static final int OSU_STATUS_INIT_SOAP_EXCHANGE = 6; // 0x6 + field public static final int OSU_STATUS_REDIRECT_RESPONSE_RECEIVED = 8; // 0x8 + field public static final int OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS = 11; // 0xb + field public static final int OSU_STATUS_SECOND_SOAP_EXCHANGE = 9; // 0x9 + field public static final int OSU_STATUS_SERVER_CONNECTED = 5; // 0x5 + field public static final int OSU_STATUS_SERVER_CONNECTING = 3; // 0x3 + field public static final int OSU_STATUS_SERVER_VALIDATED = 4; // 0x4 + field public static final int OSU_STATUS_THIRD_SOAP_EXCHANGE = 10; // 0xa + field public static final int OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE = 7; // 0x7 + } + +} + +package android.net.wifi.p2p { + + public final class WifiP2pGroupList implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<android.net.wifi.p2p.WifiP2pGroup> getGroupList(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pGroupList> CREATOR; + } + + public class WifiP2pManager { + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void deletePersistentGroup(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, int, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public void requestPersistentGroupInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.PersistentGroupInfoListener); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setDeviceName(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull String, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); + method @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setMiracastMode(int); + method @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setWfdInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pWfdInfo, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setWifiP2pChannels(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, int, int, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void startListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void stopListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); + field public static final String ACTION_WIFI_P2P_PERSISTENT_GROUPS_CHANGED = "android.net.wifi.p2p.action.WIFI_P2P_PERSISTENT_GROUPS_CHANGED"; + field public static final int MIRACAST_DISABLED = 0; // 0x0 + field public static final int MIRACAST_SINK = 2; // 0x2 + field public static final int MIRACAST_SOURCE = 1; // 0x1 + } + + public static interface WifiP2pManager.PersistentGroupInfoListener { + method public void onPersistentGroupInfoAvailable(@NonNull android.net.wifi.p2p.WifiP2pGroupList); + } + +} + +package android.net.wifi.rtt { + + public static final class RangingRequest.Builder { + method public android.net.wifi.rtt.RangingRequest.Builder addResponder(@NonNull android.net.wifi.rtt.ResponderConfig); + } + + public final class RangingResult implements android.os.Parcelable { + method @NonNull public byte[] getLci(); + method @NonNull public byte[] getLcr(); + } + + public final class ResponderConfig implements android.os.Parcelable { + ctor public ResponderConfig(@NonNull android.net.MacAddress, int, boolean, int, int, int, int, int); + ctor public ResponderConfig(@NonNull android.net.wifi.aware.PeerHandle, int, boolean, int, int, int, int, int); + method public int describeContents(); + method public static android.net.wifi.rtt.ResponderConfig fromScanResult(android.net.wifi.ScanResult); + method public static android.net.wifi.rtt.ResponderConfig fromWifiAwarePeerHandleWithDefaults(android.net.wifi.aware.PeerHandle); + method public static android.net.wifi.rtt.ResponderConfig fromWifiAwarePeerMacAddressWithDefaults(android.net.MacAddress); + method public void writeToParcel(android.os.Parcel, int); + field public static final int CHANNEL_WIDTH_160MHZ = 3; // 0x3 + field public static final int CHANNEL_WIDTH_20MHZ = 0; // 0x0 + field public static final int CHANNEL_WIDTH_40MHZ = 1; // 0x1 + field public static final int CHANNEL_WIDTH_80MHZ = 2; // 0x2 + field public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4; // 0x4 + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.rtt.ResponderConfig> CREATOR; + field public static final int PREAMBLE_HE = 3; // 0x3 + field public static final int PREAMBLE_HT = 1; // 0x1 + field public static final int PREAMBLE_LEGACY = 0; // 0x0 + field public static final int PREAMBLE_VHT = 2; // 0x2 + field public static final int RESPONDER_AP = 0; // 0x0 + field public static final int RESPONDER_AWARE = 4; // 0x4 + field public static final int RESPONDER_P2P_CLIENT = 3; // 0x3 + field public static final int RESPONDER_P2P_GO = 2; // 0x2 + field public static final int RESPONDER_STA = 1; // 0x1 + field public final int centerFreq0; + field public final int centerFreq1; + field public final int channelWidth; + field public final int frequency; + field public final android.net.MacAddress macAddress; + field public final android.net.wifi.aware.PeerHandle peerHandle; + field public final int preamble; + field public final int responderType; + field public final boolean supports80211mc; + } + + public final class ResponderLocation implements android.os.Parcelable { + method public boolean getExtraInfoOnAssociationIndication(); + } + + public class WifiRttManager { + method @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE}) public void cancelRanging(@Nullable android.os.WorkSource); + method @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.ACCESS_WIFI_STATE}) public void startRanging(@Nullable android.os.WorkSource, @NonNull android.net.wifi.rtt.RangingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.rtt.RangingResultCallback); + } + +} + diff --git a/wifi/api/system-lint-baseline.txt b/wifi/api/system-lint-baseline.txt new file mode 100644 index 000000000000..6547ee8a2188 --- /dev/null +++ b/wifi/api/system-lint-baseline.txt @@ -0,0 +1,6 @@ +// Baseline format: 1.0 +MissingGetterMatchingBuilder: android.net.wifi.rtt.RangingRequest.Builder#addResponder(android.net.wifi.rtt.ResponderConfig): + android.net.wifi.rtt.RangingRequest does not declare a `getResponders()` method matching method android.net.wifi.rtt.RangingRequest.Builder.addResponder(android.net.wifi.rtt.ResponderConfig) + +MissingNullability: android.net.wifi.rtt.RangingRequest.Builder#addResponder(android.net.wifi.rtt.ResponderConfig): + diff --git a/wifi/api/system-removed.txt b/wifi/api/system-removed.txt new file mode 100644 index 000000000000..a2d0dff3e712 --- /dev/null +++ b/wifi/api/system-removed.txt @@ -0,0 +1,16 @@ +// Signature format: 2.0 +package android.net.wifi { + + @Deprecated public class BatchedScanResult implements android.os.Parcelable { + ctor public BatchedScanResult(); + ctor public BatchedScanResult(android.net.wifi.BatchedScanResult); + field public final java.util.List<android.net.wifi.ScanResult> scanResults; + field public boolean truncated; + } + + public final class ScanResult implements android.os.Parcelable { + field public boolean untrusted; + } + +} + diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt new file mode 100644 index 000000000000..e253ae25659e --- /dev/null +++ b/wifi/jarjar-rules.txt @@ -0,0 +1,126 @@ +## used by service-wifi ## + +# Network Stack AIDL interface. +rule android.net.DataStallReportParcelable* com.android.wifi.x.@0 +rule android.net.DhcpResultsParcelable* com.android.wifi.x.@0 +rule android.net.IIpMemoryStore* com.android.wifi.x.@0 +rule android.net.IIpMemoryStoreCallbacks* com.android.wifi.x.@0 +rule android.net.INetd* com.android.wifi.x.@0 +rule android.net.INetdUnsolicitedEventListener* com.android.wifi.x.@0 +rule android.net.INetworkStackConnector* com.android.wifi.x.@0 +rule android.net.InformationElementParcelable* com.android.wifi.x.@0 +rule android.net.InitialConfigurationParcelable* com.android.wifi.x.@0 +rule android.net.InterfaceConfigurationParcel* com.android.wifi.x.@0 +rule android.net.Layer2InformationParcelable* com.android.wifi.x.@0 +rule android.net.Layer2PacketParcelable* com.android.wifi.x.@0 +rule android.net.MarkMaskParcel* com.android.wifi.x.@0 +rule android.net.NattKeepalivePacketDataParcelable* com.android.wifi.x.@0 +rule android.net.NetworkTestResultParcelable* com.android.wifi.x.@0 +rule android.net.PrivateDnsConfigParcel* com.android.wifi.x.@0 +rule android.net.ProvisioningConfigurationParcelable* com.android.wifi.x.@0 +rule android.net.ResolverParamsParcel* com.android.wifi.x.@0 +rule android.net.RouteInfoParcel* com.android.wifi.x.@0 +rule android.net.ScanResultInfoParcelable* com.android.wifi.x.@0 +rule android.net.TetherConfigParcel* com.android.wifi.x.@0 +rule android.net.TetherOffloadRuleParcel* com.android.wifi.x.@0 +rule android.net.TetherStatsParcel* com.android.wifi.x.@0 +rule android.net.UidRangeParcel* com.android.wifi.x.@0 +rule android.net.dhcp.DhcpLeaseParcelable* com.android.wifi.x.@0 +rule android.net.dhcp.DhcpServingParamsParcel* com.android.wifi.x.@0 +rule android.net.ip.IIpClient* com.android.wifi.x.@0 +rule android.net.ip.IIpClientCallbacks* com.android.wifi.x.@0 +rule android.net.ipmemorystore.Blob* com.android.wifi.x.@0 +rule android.net.ipmemorystore.IOnBlobRetrievedListener* com.android.wifi.x.@0 +rule android.net.ipmemorystore.IOnStatusAndCountListener* com.android.wifi.x.@0 +rule android.net.ipmemorystore.IOnStatusListener* com.android.wifi.x.@0 +rule android.net.ipmemorystore.NetworkAttributesParcelable* com.android.wifi.x.@0 +rule android.net.ipmemorystore.SameL3NetworkResponseParcelable* com.android.wifi.x.@0 +rule android.net.ipmemorystore.StatusParcelable* com.android.wifi.x.@0 + +# Net utils (includes Network Stack helper classes). +rule android.net.DhcpResults* com.android.wifi.x.@0 +rule android.net.InterfaceConfiguration* com.android.wifi.x.@0 +rule android.net.IpMemoryStore* com.android.wifi.x.@0 +rule android.net.NetworkMonitorManager* com.android.wifi.x.@0 +rule android.net.TcpKeepalivePacketData* com.android.wifi.x.@0 +rule android.net.NetworkFactory* com.android.wifi.x.@0 +rule android.net.ip.IpClientCallbacks* com.android.wifi.x.@0 +rule android.net.ip.IpClientManager* com.android.wifi.x.@0 +rule android.net.ip.IpClientUtil* com.android.wifi.x.@0 +rule android.net.ipmemorystore.NetworkAttributes* com.android.wifi.x.@0 +rule android.net.ipmemorystore.OnBlobRetrievedListener* com.android.wifi.x.@0 +rule android.net.ipmemorystore.OnDeleteStatusListener* com.android.wifi.x.@0 +rule android.net.ipmemorystore.OnStatusListener* com.android.wifi.x.@0 +rule android.net.ipmemorystore.Status* com.android.wifi.x.@0 +rule android.net.networkstack.ModuleNetworkStackClient* com.android.wifi.x.@0 +rule android.net.networkstack.NetworkStackClientBase* com.android.wifi.x.@0 +rule android.net.shared.InetAddressUtils* com.android.wifi.x.@0 +rule android.net.shared.InitialConfiguration* com.android.wifi.x.@0 +rule android.net.shared.IpConfigurationParcelableUtil* com.android.wifi.x.@0 +rule android.net.shared.Layer2Information* com.android.wifi.x.@0 +rule android.net.shared.LinkPropertiesParcelableUtil* com.android.wifi.x.@0 +rule android.net.shared.NetdUtils* com.android.wifi.x.@0 +rule android.net.shared.NetworkMonitorUtils* com.android.wifi.x.@0 +rule android.net.shared.ParcelableUtil* com.android.wifi.x.@0 +rule android.net.shared.PrivateDnsConfig* com.android.wifi.x.@0 +rule android.net.shared.ProvisioningConfiguration* com.android.wifi.x.@0 +rule android.net.shared.RouteUtils* com.android.wifi.x.@0 +rule android.net.util.KeepalivePacketDataUtil* com.android.wifi.x.@0 +rule android.net.util.NetworkConstants* com.android.wifi.x.@0 +rule android.net.util.InterfaceParams* com.android.wifi.x.@0 +rule android.net.util.SharedLog* com.android.wifi.x.@0 +rule android.net.util.NetUtils* com.android.wifi.x.@0 +rule android.net.util.IpUtils* com.android.wifi.x.@0 + +rule androidx.annotation.** com.android.wifi.x.@0 + +# We don't jar-jar the entire package because, we still use some classes (like +# AsyncChannel in com.android.internal.util) from these packages which are not +# inside our jar (currently in framework.jar, but will be in wifisdk.jar in the future). +rule com.android.internal.util.FastXmlSerializer* com.android.wifi.x.@0 +rule com.android.internal.util.HexDump* com.android.wifi.x.@0 +rule com.android.internal.util.IState* com.android.wifi.x.@0 +rule com.android.internal.util.MessageUtils* com.android.wifi.x.@0 +rule com.android.internal.util.State* com.android.wifi.x.@0 +rule com.android.internal.util.StateMachine* com.android.wifi.x.@0 +rule com.android.internal.util.WakeupMessage* com.android.wifi.x.@0 + +rule android.util.BackupUtils* com.android.wifi.x.@0 +rule android.util.LocalLog* com.android.wifi.x.@0 +rule android.util.Rational* com.android.wifi.x.@0 + +rule android.os.BasicShellCommandHandler* com.android.wifi.x.@0 + +# Use our statically linked bouncy castle library +rule org.bouncycastle.** com.android.wifi.x.@0 +# Use our statically linked protobuf library +rule com.google.protobuf.** com.android.wifi.x.@0 +# use statically linked SystemMessageProto +rule com.android.internal.messages.SystemMessageProto* com.android.wifi.x.@0 +# Use our statically linked PlatformProperties library +rule android.sysprop.** com.android.wifi.x.@0 +# Use our statically linked HIDL stubs +# Note: android.hardware.wifi.** is used by various wifi feature flags. This unfortunately is also the namespace +# used by vendor HAL stubs. So, this rule is intentionally weird to try and filter the vendor HAL stubs only. +rule android.hardware.wifi.V** com.android.wifi.x.@0 +rule android.hardware.wifi.supplicant.** com.android.wifi.x.@0 +rule android.hardware.wifi.hostapd.** com.android.wifi.x.@0 +rule android.hidl.** com.android.wifi.x.@0 +# Use our statically linked ksoap2 +rule org.ksoap2.** com.android.wifi.x.@0 +# Use our statically linked nanohttpd +rule fi.iki.elonen.** com.android.wifi.x.@0 + +## used by both framework-wifi and service-wifi ## +rule android.content.pm.BaseParceledListSlice* com.android.wifi.x.@0 +rule android.content.pm.ParceledListSlice* com.android.wifi.x.@0 +rule android.net.util.MacAddressUtils* com.android.wifi.x.@0 +rule android.net.util.nsd.DnsSdTxtRecord* com.android.wifi.x.@0 +rule android.os.HandlerExecutor* com.android.wifi.x.@0 +rule android.telephony.Annotation* com.android.wifi.x.@0 +rule com.android.internal.util.AsyncChannel* com.android.wifi.x.@0 +rule com.android.internal.util.AsyncService* com.android.wifi.x.@0 +rule com.android.internal.util.Preconditions* com.android.wifi.x.@0 +rule com.android.internal.util.Protocol* com.android.wifi.x.@0 + +rule com.android.net.module.util.** com.android.wifi.x.@0 diff --git a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java index b8c82fd9e0ae..6c2e6ddf5dd2 100644 --- a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java +++ b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java @@ -17,32 +17,44 @@ package android.net.wifi; import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; -import android.os.Handler; +import android.util.SparseArray; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; /** * Easy Connect (DPP) Status Callback. Use this callback to get status updates (success, failure, - * progress) from the Easy Connect operation started with - * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, - * int, int, Handler, EasyConnectStatusCallback)} or - * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String, - * Handler, EasyConnectStatusCallback)} - * - * @hide + * progress) from the Easy Connect operations. */ -@SystemApi public abstract class EasyConnectStatusCallback { /** - * Easy Connect Success event: Configuration sent (Configurator mode). + * Easy Connect R1 Success event: Configuration sent (Configurator mode). This is the last + * and final Easy Connect event when either the local device or remote device implement R1. + * If both devices implement R2, this event will never be received, and the + * {@link #EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED} will be received. + * @hide */ + @SystemApi public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0; + /** + * Easy Connect R2 Success event: Configuration applied by Enrollee (Configurator mode). + * This is the last and final Easy Connect event when both the local device and remote device + * implement R2. If either the local device or remote device implement R1, this event will never + * be received, and the {@link #EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT} will be received. + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED = 1; + /** @hide */ @IntDef(prefix = {"EASY_CONNECT_EVENT_SUCCESS_"}, value = { EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT, + EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED, }) @Retention(RetentionPolicy.SOURCE) public @interface EasyConnectSuccessStatusCode { @@ -50,18 +62,38 @@ public abstract class EasyConnectStatusCallback { /** * Easy Connect Progress event: Initial authentication with peer succeeded. + * @hide */ + @SystemApi public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; /** * Easy Connect Progress event: Peer requires more time to process bootstrapping. + * @hide */ + @SystemApi public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1; + /** + * Easy Connect R2 Progress event: Configuration sent to Enrollee, waiting for response + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE = 2; + + /** + * Easy Connect R2 Progress event: Configuration accepted by Enrollee, waiting for response + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_ACCEPTED = 3; + /** @hide */ @IntDef(prefix = {"EASY_CONNECT_EVENT_PROGRESS_"}, value = { EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS, EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING, + EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE, + EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_ACCEPTED, }) @Retention(RetentionPolicy.SOURCE) public @interface EasyConnectProgressStatusCode { @@ -114,6 +146,20 @@ public abstract class EasyConnectStatusCallback { */ public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; + /** + * Easy Connect R2 Failure event: Enrollee cannot find the network. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK = -10; + + /** + * Easy Connect R2 Failure event: Enrollee failed to authenticate with the network. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION = -11; + + /** + * Easy Connect R2 Failure event: Enrollee rejected the configuration. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION = -12; /** @hide */ @IntDef(prefix = {"EASY_CONNECT_EVENT_FAILURE_"}, value = { @@ -126,11 +172,20 @@ public abstract class EasyConnectStatusCallback { EASY_CONNECT_EVENT_FAILURE_GENERIC, EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED, EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK, + EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK, + EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION, + EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION, }) @Retention(RetentionPolicy.SOURCE) public @interface EasyConnectFailureStatusCode { } + /** @hide */ + @SystemApi + public EasyConnectStatusCallback() { + // Fully-static utility classes must not have constructor + } + /** * Called when local Easy Connect Enrollee successfully receives a new Wi-Fi configuration from * the @@ -138,42 +193,75 @@ public abstract class EasyConnectStatusCallback { * current Easy Connect * session, and no further callbacks will be called. This callback is the successful outcome * of a Easy Connect flow starting with - * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String, - * Handler, - * EasyConnectStatusCallback)}. + * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String, Executor, + * EasyConnectStatusCallback)} . * * @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator + * @hide */ + @SystemApi public abstract void onEnrolleeSuccess(int newNetworkId); /** * Called when a Easy Connect success event takes place, except for when configuration is - * received from - * an external Configurator. The callback onSuccessConfigReceived will be used in this case. - * This callback marks the successful end of the current Easy Connect session, and no further - * callbacks will be called. This callback is the successful outcome of a Easy Connect flow - * starting with - * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, int, int, Handler, - * EasyConnectStatusCallback)}. + * received from an external Configurator. The callback onSuccessConfigReceived will be used in + * this case. This callback marks the successful end of the current Easy Connect session, and no + * further callbacks will be called. This callback is the successful outcome of a Easy Connect + * flow starting with {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, int, + * int, Executor,EasyConnectStatusCallback)}. * * @param code Easy Connect success status code. + * @hide */ + @SystemApi public abstract void onConfiguratorSuccess(@EasyConnectSuccessStatusCode int code); /** * Called when a Easy Connect Failure event takes place. This callback marks the unsuccessful - * end of the - * current Easy Connect session, and no further callbacks will be called. + * end of the current Easy Connect session, and no further callbacks will be called. + * + * @param code Easy Connect failure status code. + * @hide + */ + @SystemApi + public void onFailure(@EasyConnectFailureStatusCode int code) {} + + /** + * Called when a Easy Connect Failure event takes place. This callback marks the unsuccessful + * end of the current Easy Connect session, and no further callbacks will be called. + * + * Note: Easy Connect (DPP) R2, provides additional details for the Configurator when the + * remote Enrollee is unable to connect to a network. The ssid, channelList and bandList + * inputs are initialized only for the EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK failure + * code, and the ssid and bandList are initialized for the + * EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION failure code. * * @param code Easy Connect failure status code. + * @param ssid SSID of the network the Enrollee tried to connect to. + * @param channelListArray List of Global Operating classes and channel sets the Enrollee used + * to scan to find the network, see the "DPP Connection Status Object" + * section in the specification for the format, and Table E-4 in + * IEEE Std 802.11-2016 - Global operating classes for more details. + * The sparse array key is the Global Operating class, and the value + * is an integer array of Wi-Fi channels. + * @param operatingClassArray Array of bands the Enrollee supports as expressed as the Global + * Operating Class, see Table E-4 in IEEE Std 802.11-2016 - Global + * operating classes. + * @hide */ - public abstract void onFailure(@EasyConnectFailureStatusCode int code); + @SystemApi + public void onFailure(@EasyConnectFailureStatusCode int code, @Nullable String ssid, + @NonNull SparseArray<int[]> channelListArray, @NonNull int[] operatingClassArray) { + onFailure(code); + } /** * Called when Easy Connect events that indicate progress take place. Can be used by UI elements * to show progress. * * @param code Easy Connect progress status code. + * @hide */ + @SystemApi public abstract void onProgress(@EasyConnectProgressStatusCode int code); } diff --git a/wifi/java/android/net/wifi/IActionListener.aidl b/wifi/java/android/net/wifi/IActionListener.aidl new file mode 100644 index 000000000000..faa0901cb087 --- /dev/null +++ b/wifi/java/android/net/wifi/IActionListener.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 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; + +/** + * Interface for generic wifi callbacks. + * @hide + */ +oneway interface IActionListener +{ + void onSuccess(); + void onFailure(int reason); +} diff --git a/wifi/java/android/net/wifi/IDppCallback.aidl b/wifi/java/android/net/wifi/IDppCallback.aidl index c452c7664c12..d7a958a5b4b1 100644 --- a/wifi/java/android/net/wifi/IDppCallback.aidl +++ b/wifi/java/android/net/wifi/IDppCallback.aidl @@ -38,7 +38,7 @@ oneway interface IDppCallback /** * Called when DPP Failure events take place. */ - void onFailure(int status); + void onFailure(int status, String ssid, String channelList, in int[] bandArray); /** * Called when DPP events that indicate progress take place. Can be used by UI elements diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl b/wifi/java/android/net/wifi/ILocalOnlyHotspotCallback.aidl index e923f1f0fee8..b567f29660a7 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl +++ b/wifi/java/android/net/wifi/ILocalOnlyHotspotCallback.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017, The Android Open Source Project + * Copyright (c) 2019, 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. @@ -14,6 +14,17 @@ * limitations under the License. */ -package android.net.wifi.hotspot2.pps; +package android.net.wifi; -parcelable Policy; +import android.net.wifi.SoftApConfiguration; + +/** + * Communicates LOHS status back to the application process. + * + * @hide + */ +oneway interface ILocalOnlyHotspotCallback { + void onHotspotStarted(in SoftApConfiguration config); + void onHotspotStopped(); + void onHotspotFailed(int reason); +} diff --git a/wifi/java/android/net/wifi/IOnWifiActivityEnergyInfoListener.aidl b/wifi/java/android/net/wifi/IOnWifiActivityEnergyInfoListener.aidl new file mode 100644 index 000000000000..7e25fd8a3be2 --- /dev/null +++ b/wifi/java/android/net/wifi/IOnWifiActivityEnergyInfoListener.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 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.os.connectivity.WifiActivityEnergyInfo; + +/** + * Interface for Wi-Fi activity energy info listener. + * + * @hide + */ +oneway interface IOnWifiActivityEnergyInfoListener +{ + /** + * Service to manager callback providing current Wi-Fi activity energy info. + * @param info the Wi-Fi activity energy info + */ + void onWifiActivityEnergyInfo(in WifiActivityEnergyInfo info); +} diff --git a/wifi/java/android/net/wifi/IScanResultsCallback.aidl b/wifi/java/android/net/wifi/IScanResultsCallback.aidl new file mode 100644 index 000000000000..56f602510fd9 --- /dev/null +++ b/wifi/java/android/net/wifi/IScanResultsCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 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; + +/** + * Interface for Wi-Fi scan result available callback. + * + * @hide + */ +oneway interface IScanResultsCallback +{ + void onScanResultsAvailable(); +} diff --git a/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.aidl b/wifi/java/android/net/wifi/IScanResultsListener.aidl index eb7cc39d5e33..e7eaddd712c9 100644 --- a/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.aidl +++ b/wifi/java/android/net/wifi/IScanResultsListener.aidl @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2008, The Android Open Source Project +/* + * Copyright (C) 2019 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 + * 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, @@ -16,4 +16,9 @@ package android.net.wifi; -parcelable PasspointManagementObjectDefinition; +/** @hide */ + +oneway interface IScanResultsListener +{ + void onScanResultsAvailable(); +} diff --git a/wifi/java/android/net/wifi/IScoreUpdateObserver.aidl b/wifi/java/android/net/wifi/IScoreUpdateObserver.aidl new file mode 100644 index 000000000000..775fed7d47ef --- /dev/null +++ b/wifi/java/android/net/wifi/IScoreUpdateObserver.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi; + +/** + * Interface for Wi-Fi score callback. + * + * @hide + */ +oneway interface IScoreUpdateObserver +{ + void notifyScoreUpdate(int sessionId, int score); + + void triggerUpdateOfWifiUsabilityStats(int sessionId); +} diff --git a/wifi/java/android/net/wifi/ISoftApCallback.aidl b/wifi/java/android/net/wifi/ISoftApCallback.aidl index 8a252dd1e447..f81bcb9e06d7 100644 --- a/wifi/java/android/net/wifi/ISoftApCallback.aidl +++ b/wifi/java/android/net/wifi/ISoftApCallback.aidl @@ -15,6 +15,8 @@ */ package android.net.wifi; +import android.net.wifi.SoftApCapability; +import android.net.wifi.SoftApInfo; import android.net.wifi.WifiClient; @@ -43,4 +45,27 @@ oneway interface ISoftApCallback * @param clients the currently connected clients */ void onConnectedClientsChanged(in List<WifiClient> clients); + + /** + * Service to manager callback providing information of softap. + * + * @param softApInfo is the softap information. {@link SoftApInfo} + */ + void onInfoChanged(in SoftApInfo softApInfo); + + + /** + * Service to manager callback providing capability of softap. + * + * @param capability is the softap capability. {@link SoftApCapability} + */ + void onCapabilityChanged(in SoftApCapability capability); + + /** + * Service to manager callback providing blocked client of softap with specific reason code. + * + * @param client the currently blocked client. + * @param blockedReason one of blocked reason from {@link WifiManager.SapClientBlockedReason} + */ + void onBlockedClientConnecting(in WifiClient client, int blockedReason); } diff --git a/wifi/java/android/net/wifi/ISuggestionConnectionStatusListener.aidl b/wifi/java/android/net/wifi/ISuggestionConnectionStatusListener.aidl new file mode 100644 index 000000000000..b49e49ba0cd5 --- /dev/null +++ b/wifi/java/android/net/wifi/ISuggestionConnectionStatusListener.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 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.WifiNetworkSuggestion; + +/** + * Interface for suggestion network connection listener. + * + * @hide + */ +oneway interface ISuggestionConnectionStatusListener +{ + void onConnectionStatus(in WifiNetworkSuggestion wifiNetworkSuggestion, int failureReason); +} diff --git a/wifi/java/android/net/wifi/ITxPacketCountListener.aidl b/wifi/java/android/net/wifi/ITxPacketCountListener.aidl new file mode 100644 index 000000000000..9105bd0265cb --- /dev/null +++ b/wifi/java/android/net/wifi/ITxPacketCountListener.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 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; + +/** + * Interface for tx packet counter callback. + * @deprecated no longer used, remove once removed from BaseWifiService + * @hide + */ +oneway interface ITxPacketCountListener +{ + void onSuccess(int count); + void onFailure(int reason); +} diff --git a/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl b/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl new file mode 100644 index 000000000000..f96d037cbfea --- /dev/null +++ b/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi; + +import android.net.wifi.IScoreUpdateObserver; + +/** + * Interface for Wi-Fi connected network scorer. + * + * @hide + */ +oneway interface IWifiConnectedNetworkScorer +{ + void onStart(int sessionId); + + void onStop(int sessionId); + + void onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl); +} diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 9010f3c9943e..5063ad66e446 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -24,14 +24,19 @@ import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.DhcpInfo; import android.net.Network; +import android.net.wifi.IActionListener; import android.net.wifi.IDppCallback; +import android.net.wifi.ILocalOnlyHotspotCallback; import android.net.wifi.INetworkRequestMatchCallback; +import android.net.wifi.IOnWifiActivityEnergyInfoListener; +import android.net.wifi.IOnWifiUsabilityStatsListener; +import android.net.wifi.IScanResultsCallback; import android.net.wifi.ISoftApCallback; +import android.net.wifi.ISuggestionConnectionStatusListener; import android.net.wifi.ITrafficStateCallback; -import android.net.wifi.IOnWifiUsabilityStatsListener; -import android.net.wifi.PasspointManagementObjectDefinition; +import android.net.wifi.IWifiConnectedNetworkScorer; import android.net.wifi.ScanResult; -import android.net.wifi.WifiActivityEnergyInfo; +import android.net.wifi.SoftApConfiguration; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiNetworkSuggestion; @@ -49,19 +54,11 @@ interface IWifiManager { long getSupportedFeatures(); - WifiActivityEnergyInfo reportActivityInfo(); - - /** - * Requests the controller activity info asynchronously. - * The implementor is expected to reply with the - * {@link android.net.wifi.WifiActivityEnergyInfo} object placed into the Bundle with the key - * {@link android.os.BatteryStats#RESULT_RECEIVER_CONTROLLER_KEY}. The result code is ignored. - */ - oneway void requestActivityInfo(in ResultReceiver result); + oneway void getWifiActivityEnergyInfoAsync(in IOnWifiActivityEnergyInfoListener listener); - ParceledListSlice getConfiguredNetworks(String packageName); + ParceledListSlice getConfiguredNetworks(String packageName, String featureId); - ParceledListSlice getPrivilegedConfiguredNetworks(String packageName); + ParceledListSlice getPrivilegedConfiguredNetworks(String packageName, String featureId); Map getAllMatchingFqdnsForScanResults(in List<ScanResult> scanResult); @@ -91,9 +88,19 @@ interface IWifiManager boolean disableNetwork(int netId, String packageName); - boolean startScan(String packageName); + void allowAutojoinGlobal(boolean choice); + + void allowAutojoin(int netId, boolean choice); + + void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin); + + void setMacRandomizationSettingPasspointEnabled(String fqdn, boolean enable); + + void setPasspointMeteredOverride(String fqdn, int meteredOverride); + + boolean startScan(String packageName, String featureId); - List<ScanResult> getScanResults(String callingPackage); + List<ScanResult> getScanResults(String callingPackage, String callingFeatureId); boolean disconnect(String packageName); @@ -101,22 +108,24 @@ interface IWifiManager boolean reassociate(String packageName); - WifiInfo getConnectionInfo(String callingPackage); + WifiInfo getConnectionInfo(String callingPackage, String callingFeatureId); boolean setWifiEnabled(String packageName, boolean enable); int getWifiEnabledState(); - void setCountryCode(String country); - String getCountryCode(); - boolean isDualBandSupported(); + boolean is5GHzBandSupported(); + + boolean is6GHzBandSupported(); - boolean needs5GHzToAnyApBandConversion(); + boolean isWifiStandardSupported(int standard); DhcpInfo getDhcpInfo(); + void setScanAlwaysAvailable(boolean isAvailable); + boolean isScanAlwaysAvailable(); boolean acquireWifiLock(IBinder lock, int lockType, String tag, in WorkSource ws); @@ -137,13 +146,16 @@ interface IWifiManager boolean startSoftAp(in WifiConfiguration wifiConfig); + boolean startTetheredHotspot(in SoftApConfiguration softApConfig); + boolean stopSoftAp(); - int startLocalOnlyHotspot(in Messenger messenger, in IBinder binder, String packageName); + int startLocalOnlyHotspot(in ILocalOnlyHotspotCallback callback, String packageName, + String featureId, in SoftApConfiguration customConfig); void stopLocalOnlyHotspot(); - void startWatchLocalOnlyHotspot(in Messenger messenger, in IBinder binder); + void startWatchLocalOnlyHotspot(in ILocalOnlyHotspotCallback callback); void stopWatchLocalOnlyHotspot(); @@ -153,11 +165,13 @@ interface IWifiManager @UnsupportedAppUsage WifiConfiguration getWifiApConfiguration(); + SoftApConfiguration getSoftApConfiguration(); + boolean setWifiApConfiguration(in WifiConfiguration wifiConfig, String packageName); - void notifyUserOfApBandConversion(String packageName); + boolean setSoftApConfiguration(in SoftApConfiguration softApConfig, String packageName); - Messenger getWifiServiceMessenger(String packageName); + void notifyUserOfApBandConversion(String packageName); void enableTdls(String remoteIPAddress, boolean enable); @@ -169,8 +183,6 @@ interface IWifiManager int getVerboseLoggingLevel(); - void enableWifiConnectivityManager(boolean enabled); - void disableEphemeralNetwork(String SSID, String packageName); void factoryReset(String packageName); @@ -182,6 +194,10 @@ interface IWifiManager void restoreBackupData(in byte[] data); + byte[] retrieveSoftApBackupData(); + + SoftApConfiguration restoreSoftApBackupData(in byte[] data); + void restoreSupplicantBackupData(in byte[] supplicantData, in byte[] ipConfigData); void startSubscriptionProvisioning(in OsuProvider provider, in IProvisioningCallback callback); @@ -202,10 +218,13 @@ interface IWifiManager void unregisterNetworkRequestMatchCallback(int callbackIdentifier); - int addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName); + int addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName, + in String featureId); int removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName); + List<WifiNetworkSuggestion> getNetworkSuggestions(in String packageName); + String[] getFactoryMacAddresses(); void setDeviceMobilityState(int state); @@ -219,4 +238,41 @@ interface IWifiManager void stopDppSession(); void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec); + + oneway void connect(in WifiConfiguration config, int netId, in IBinder binder, in IActionListener listener, int callbackIdentifier); + + oneway void save(in WifiConfiguration config, in IBinder binder, in IActionListener listener, int callbackIdentifier); + + oneway void forget(int netId, in IBinder binder, in IActionListener listener, int callbackIdentifier); + + void registerScanResultsCallback(in IScanResultsCallback callback); + + void unregisterScanResultsCallback(in IScanResultsCallback callback); + + void registerSuggestionConnectionStatusListener(in IBinder binder, in ISuggestionConnectionStatusListener listener, int listenerIdentifier, String packageName, String featureId); + + void unregisterSuggestionConnectionStatusListener(int listenerIdentifier, String packageName); + + int calculateSignalLevel(int rssi); + + List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(in List<ScanResult> scanResults); + + boolean setWifiConnectedNetworkScorer(in IBinder binder, in IWifiConnectedNetworkScorer scorer); + + void clearWifiConnectedNetworkScorer(); + + /** + * Return the Map of {@link WifiNetworkSuggestion} and the list of <ScanResult> + */ + Map getMatchingScanResults(in List<WifiNetworkSuggestion> networkSuggestions, in List<ScanResult> scanResults, String callingPackage, String callingFeatureId); + + void setScanThrottleEnabled(boolean enable); + + boolean isScanThrottleEnabled(); + + Map getAllMatchingPasspointProfilesForScanResults(in List<ScanResult> scanResult); + + void setAutoWakeupEnabled(boolean enable); + + boolean isAutoWakeupEnabled(); } diff --git a/wifi/java/android/net/wifi/IWifiScanner.aidl b/wifi/java/android/net/wifi/IWifiScanner.aidl index 398493439bbe..485f5ce5415e 100644 --- a/wifi/java/android/net/wifi/IWifiScanner.aidl +++ b/wifi/java/android/net/wifi/IWifiScanner.aidl @@ -26,5 +26,5 @@ interface IWifiScanner { Messenger getMessenger(); - Bundle getAvailableChannels(int band); + Bundle getAvailableChannels(int band, String packageName, String featureId); } diff --git a/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java b/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java deleted file mode 100644 index 70577b9695e5..000000000000 --- a/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2016 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.os.Parcel; -import android.os.Parcelable; - -/** - * This object describes a partial tree structure in the Hotspot 2.0 release 2 management object. - * The object is used during subscription remediation to modify parts of an existing PPS MO - * tree (Hotspot 2.0 specification section 9.1). - * @hide - */ -public class PasspointManagementObjectDefinition implements Parcelable { - private final String mBaseUri; - private final String mUrn; - private final String mMoTree; - - public PasspointManagementObjectDefinition(String baseUri, String urn, String moTree) { - mBaseUri = baseUri; - mUrn = urn; - mMoTree = moTree; - } - - public String getBaseUri() { - return mBaseUri; - } - - public String getUrn() { - return mUrn; - } - - public String getMoTree() { - return mMoTree; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(mBaseUri); - dest.writeString(mUrn); - dest.writeString(mMoTree); - } - - /** - * Implement the Parcelable interface {@hide} - */ - public static final @android.annotation.NonNull Creator<PasspointManagementObjectDefinition> CREATOR = - new Creator<PasspointManagementObjectDefinition>() { - public PasspointManagementObjectDefinition createFromParcel(Parcel in) { - return new PasspointManagementObjectDefinition( - in.readString(), /* base URI */ - in.readString(), /* URN */ - in.readString() /* Tree XML */ - ); - } - - public PasspointManagementObjectDefinition[] newArray(int size) { - return new PasspointManagementObjectDefinition[size]; - } - }; -} - diff --git a/wifi/java/android/net/wifi/RssiPacketCountInfo.java b/wifi/java/android/net/wifi/RssiPacketCountInfo.java deleted file mode 100644 index 4301165308d1..000000000000 --- a/wifi/java/android/net/wifi/RssiPacketCountInfo.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2012 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.os.Parcel; -import android.os.Parcelable; - -/** - * Bundle of RSSI and packet count information, for WiFi watchdog - * - * @see WifiWatchdogStateMachine - * - * @hide - */ -public class RssiPacketCountInfo implements Parcelable { - - public int rssi; - - public int txgood; - - public int txbad; - - public int rxgood; - - public RssiPacketCountInfo() { - rssi = txgood = txbad = rxgood = 0; - } - - private RssiPacketCountInfo(Parcel in) { - rssi = in.readInt(); - txgood = in.readInt(); - txbad = in.readInt(); - rxgood = in.readInt(); - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(rssi); - out.writeInt(txgood); - out.writeInt(txbad); - out.writeInt(rxgood); - } - - @Override - public int describeContents() { - return 0; - } - - public static final @android.annotation.NonNull Parcelable.Creator<RssiPacketCountInfo> CREATOR = - new Parcelable.Creator<RssiPacketCountInfo>() { - @Override - public RssiPacketCountInfo createFromParcel(Parcel in) { - return new RssiPacketCountInfo(in); - } - - @Override - public RssiPacketCountInfo[] newArray(int size) { - return new RssiPacketCountInfo[size]; - } - }; -} diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java index 6a03c73bc3f9..73c52ab0ab1b 100644 --- a/wifi/java/android/net/wifi/RttManager.java +++ b/wifi/java/android/net/wifi/RttManager.java @@ -1212,7 +1212,7 @@ public class RttManager { * * @hide */ - public RttManager(Context context, WifiRttManager service) { + public RttManager(@NonNull Context context, @NonNull WifiRttManager service) { mNewService = service; mContext = context; diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index 2d0942d3ffa7..aa3a13925894 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -17,8 +17,11 @@ package android.net.wifi; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.net.wifi.WifiAnnotations.ChannelWidth; +import android.net.wifi.WifiAnnotations.WifiStandard; import android.os.Parcel; import android.os.Parcelable; @@ -35,7 +38,7 @@ import java.util.Objects; * {@code quality}, {@code noise}, and {@code maxbitrate} attributes, * but does not currently report them to external clients. */ -public class ScanResult implements Parcelable { +public final class ScanResult implements Parcelable { /** * The network name. */ @@ -81,16 +84,19 @@ public class ScanResult implements Parcelable { * @hide * No security protocol. */ + @SystemApi public static final int PROTOCOL_NONE = 0; /** * @hide * Security protocol type: WPA version 1. */ + @SystemApi public static final int PROTOCOL_WPA = 1; /** * @hide * Security protocol type: RSN, for WPA version 2, and version 3. */ + @SystemApi public static final int PROTOCOL_RSN = 2; /** * @hide @@ -98,99 +104,154 @@ public class ScanResult implements Parcelable { * OSU Server-only authenticated layer 2 Encryption Network. * Used for Hotspot 2.0. */ + @SystemApi public static final int PROTOCOL_OSEN = 3; /** * @hide + * Security protocol type: WAPI. + */ + @SystemApi + public static final int PROTOCOL_WAPI = 4; + + /** + * @hide * No security key management scheme. */ + @SystemApi public static final int KEY_MGMT_NONE = 0; /** * @hide * Security key management scheme: PSK. */ + @SystemApi public static final int KEY_MGMT_PSK = 1; /** * @hide * Security key management scheme: EAP. */ + @SystemApi public static final int KEY_MGMT_EAP = 2; /** * @hide * Security key management scheme: FT_PSK. */ + @SystemApi public static final int KEY_MGMT_FT_PSK = 3; /** * @hide * Security key management scheme: FT_EAP. */ + @SystemApi public static final int KEY_MGMT_FT_EAP = 4; /** * @hide * Security key management scheme: PSK_SHA256 */ + @SystemApi public static final int KEY_MGMT_PSK_SHA256 = 5; /** * @hide * Security key management scheme: EAP_SHA256. */ + @SystemApi public static final int KEY_MGMT_EAP_SHA256 = 6; /** * @hide * Security key management scheme: OSEN. * Used for Hotspot 2.0. */ + @SystemApi public static final int KEY_MGMT_OSEN = 7; /** * @hide * Security key management scheme: SAE. */ + @SystemApi public static final int KEY_MGMT_SAE = 8; /** * @hide * Security key management scheme: OWE. */ + @SystemApi public static final int KEY_MGMT_OWE = 9; /** * @hide * Security key management scheme: SUITE_B_192. */ + @SystemApi public static final int KEY_MGMT_EAP_SUITE_B_192 = 10; /** * @hide * Security key management scheme: FT_SAE. */ + @SystemApi public static final int KEY_MGMT_FT_SAE = 11; /** * @hide * Security key management scheme: OWE in transition mode. */ + @SystemApi public static final int KEY_MGMT_OWE_TRANSITION = 12; /** * @hide + * Security key management scheme: WAPI_PSK. + */ + @SystemApi + public static final int KEY_MGMT_WAPI_PSK = 13; + /** + * @hide + * Security key management scheme: WAPI_CERT. + */ + @SystemApi + public static final int KEY_MGMT_WAPI_CERT = 14; + + /** + * @hide + * Security key management scheme: FILS_SHA256. + */ + public static final int KEY_MGMT_FILS_SHA256 = 15; + /** + * @hide + * Security key management scheme: FILS_SHA384. + */ + public static final int KEY_MGMT_FILS_SHA384 = 16; + /** + * @hide * No cipher suite. */ + @SystemApi public static final int CIPHER_NONE = 0; /** * @hide * No group addressed, only used for group data cipher. */ + @SystemApi public static final int CIPHER_NO_GROUP_ADDRESSED = 1; /** * @hide * Cipher suite: TKIP */ + @SystemApi public static final int CIPHER_TKIP = 2; /** * @hide * Cipher suite: CCMP */ + @SystemApi public static final int CIPHER_CCMP = 3; /** * @hide * Cipher suite: GCMP */ + @SystemApi public static final int CIPHER_GCMP_256 = 4; + /** + * @hide + * Cipher suite: SMS4 + */ + @SystemApi + public static final int CIPHER_SMS4 = 5; /** * The detected signal level in dBm, also known as the RSSI. @@ -226,12 +287,76 @@ public class ScanResult implements Parcelable { */ public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4; - /** - * AP Channel bandwidth; one of {@link #CHANNEL_WIDTH_20MHZ}, {@link #CHANNEL_WIDTH_40MHZ}, - * {@link #CHANNEL_WIDTH_80MHZ}, {@link #CHANNEL_WIDTH_160MHZ} - * or {@link #CHANNEL_WIDTH_80MHZ_PLUS_MHZ}. - */ - public int channelWidth; + /** + * Wi-Fi unknown standard + */ + public static final int WIFI_STANDARD_UNKNOWN = 0; + + /** + * Wi-Fi 802.11a/b/g + */ + public static final int WIFI_STANDARD_LEGACY = 1; + + /** + * Wi-Fi 802.11n + */ + public static final int WIFI_STANDARD_11N = 4; + + /** + * Wi-Fi 802.11ac + */ + public static final int WIFI_STANDARD_11AC = 5; + + /** + * Wi-Fi 802.11ax + */ + public static final int WIFI_STANDARD_11AX = 6; + + /** + * AP wifi standard. + */ + private @WifiStandard int mWifiStandard; + + /** + * return the AP wifi standard. + */ + public @WifiStandard int getWifiStandard() { + return mWifiStandard; + } + + /** + * sets the AP wifi standard. + * @hide + */ + public void setWifiStandard(@WifiStandard int standard) { + mWifiStandard = standard; + } + + /** + * Convert Wi-Fi standard to string + */ + private static @Nullable String wifiStandardToString(@WifiStandard int standard) { + switch(standard) { + case WIFI_STANDARD_LEGACY: + return "legacy"; + case WIFI_STANDARD_11N: + return "11n"; + case WIFI_STANDARD_11AC: + return "11ac"; + case WIFI_STANDARD_11AX: + return "11ax"; + case WIFI_STANDARD_UNKNOWN: + return "unknown"; + } + return null; + } + + /** + * AP Channel bandwidth; one of {@link #CHANNEL_WIDTH_20MHZ}, {@link #CHANNEL_WIDTH_40MHZ}, + * {@link #CHANNEL_WIDTH_80MHZ}, {@link #CHANNEL_WIDTH_160MHZ} + * or {@link #CHANNEL_WIDTH_80MHZ_PLUS_MHZ}. + */ + public @ChannelWidth int channelWidth; /** * Not used if the AP bandwidth is 20 MHz @@ -399,19 +524,166 @@ public class ScanResult implements Parcelable { * {@hide} */ public final static int UNSPECIFIED = -1; + /** + * 2.4 GHz band first channel number * @hide */ - public boolean is24GHz() { - return ScanResult.is24GHz(frequency); + public static final int BAND_24_GHZ_FIRST_CH_NUM = 1; + /** + * 2.4 GHz band last channel number + * @hide + */ + public static final int BAND_24_GHZ_LAST_CH_NUM = 14; + /** + * 2.4 GHz band frequency of first channel in MHz + * @hide + */ + public static final int BAND_24_GHZ_START_FREQ_MHZ = 2412; + /** + * 2.4 GHz band frequency of last channel in MHz + * @hide + */ + public static final int BAND_24_GHZ_END_FREQ_MHZ = 2484; + + /** + * 5 GHz band first channel number + * @hide + */ + public static final int BAND_5_GHZ_FIRST_CH_NUM = 32; + /** + * 5 GHz band last channel number + * @hide + */ + public static final int BAND_5_GHZ_LAST_CH_NUM = 173; + /** + * 5 GHz band frequency of first channel in MHz + * @hide + */ + public static final int BAND_5_GHZ_START_FREQ_MHZ = 5160; + /** + * 5 GHz band frequency of last channel in MHz + * @hide + */ + public static final int BAND_5_GHZ_END_FREQ_MHZ = 5865; + + /** + * 6 GHz band first channel number + * @hide + */ + public static final int BAND_6_GHZ_FIRST_CH_NUM = 1; + /** + * 6 GHz band last channel number + * @hide + */ + public static final int BAND_6_GHZ_LAST_CH_NUM = 233; + /** + * 6 GHz band frequency of first channel in MHz + * @hide + */ + public static final int BAND_6_GHZ_START_FREQ_MHZ = 5945; + /** + * 6 GHz band frequency of last channel in MHz + * @hide + */ + public static final int BAND_6_GHZ_END_FREQ_MHZ = 7105; + + /** + * Utility function to check if a frequency within 2.4 GHz band + * @param freqMhz frequency in MHz + * @return true if within 2.4GHz, false otherwise + * + * @hide + */ + public static boolean is24GHz(int freqMhz) { + return freqMhz >= BAND_24_GHZ_START_FREQ_MHZ && freqMhz <= BAND_24_GHZ_END_FREQ_MHZ; + } + + /** + * Utility function to check if a frequency within 5 GHz band + * @param freqMhz frequency in MHz + * @return true if within 5GHz, false otherwise + * + * @hide + */ + public static boolean is5GHz(int freqMhz) { + return freqMhz >= BAND_5_GHZ_START_FREQ_MHZ && freqMhz <= BAND_5_GHZ_END_FREQ_MHZ; + } + + /** + * Utility function to check if a frequency within 6 GHz band + * @param freqMhz + * @return true if within 6GHz, false otherwise + * + * @hide + */ + public static boolean is6GHz(int freqMhz) { + return freqMhz >= BAND_6_GHZ_START_FREQ_MHZ && freqMhz <= BAND_6_GHZ_END_FREQ_MHZ; + } + + /** + * Utility function to convert channel number/band to frequency in MHz + * @param channel number to convert + * @param band of channel to convert + * @return center frequency in Mhz of the channel, {@link UNSPECIFIED} if no match + * + * @hide + */ + public static int convertChannelToFrequencyMhz(int channel, @WifiScanner.WifiBand int band) { + if (band == WifiScanner.WIFI_BAND_24_GHZ) { + // Special case + if (channel == 14) { + return 2484; + } else if (channel >= BAND_24_GHZ_FIRST_CH_NUM && channel <= BAND_24_GHZ_LAST_CH_NUM) { + return ((channel - BAND_24_GHZ_FIRST_CH_NUM) * 5) + BAND_24_GHZ_START_FREQ_MHZ; + } else { + return UNSPECIFIED; + } + } + if (band == WifiScanner.WIFI_BAND_5_GHZ) { + if (channel >= BAND_5_GHZ_FIRST_CH_NUM && channel <= BAND_5_GHZ_LAST_CH_NUM) { + return ((channel - BAND_5_GHZ_FIRST_CH_NUM) * 5) + BAND_5_GHZ_START_FREQ_MHZ; + } else { + return UNSPECIFIED; + } + } + if (band == WifiScanner.WIFI_BAND_6_GHZ) { + if (channel >= BAND_6_GHZ_FIRST_CH_NUM && channel <= BAND_6_GHZ_LAST_CH_NUM) { + return ((channel - BAND_6_GHZ_FIRST_CH_NUM) * 5) + BAND_6_GHZ_START_FREQ_MHZ; + } else { + return UNSPECIFIED; + } + } + return UNSPECIFIED; + } + + /** + * Utility function to convert frequency in MHz to channel number + * @param freqMhz frequency in MHz + * @return channel number associated with given frequency, {@link UNSPECIFIED} if no match + * + * @hide + */ + public static int convertFrequencyMhzToChannel(int freqMhz) { + // Special case + if (freqMhz == 2484) { + return 14; + } else if (is24GHz(freqMhz)) { + return (freqMhz - BAND_24_GHZ_START_FREQ_MHZ) / 5 + BAND_24_GHZ_FIRST_CH_NUM; + } else if (is5GHz(freqMhz)) { + return ((freqMhz - BAND_5_GHZ_START_FREQ_MHZ) / 5) + BAND_5_GHZ_FIRST_CH_NUM; + } else if (is6GHz(freqMhz)) { + return ((freqMhz - BAND_6_GHZ_START_FREQ_MHZ) / 5) + BAND_6_GHZ_FIRST_CH_NUM; + } + + return UNSPECIFIED; } /** * @hide - * TODO: makes real freq boundaries */ - public static boolean is24GHz(int freq) { - return freq > 2400 && freq < 2500; + public boolean is24GHz() { + return ScanResult.is24GHz(frequency); } /** @@ -423,10 +695,9 @@ public class ScanResult implements Parcelable { /** * @hide - * TODO: makes real freq boundaries */ - public static boolean is5GHz(int freq) { - return freq > 4900 && freq < 5900; + public boolean is6GHz() { + return ScanResult.is6GHz(frequency); } /** @@ -483,11 +754,22 @@ public class ScanResult implements Parcelable { /** @hide */ @UnsupportedAppUsage public static final int EID_VSA = 221; + /** @hide */ + public static final int EID_EXTENSION_PRESENT = 255; + + // Extension IDs + /** @hide */ + public static final int EID_EXT_HE_CAPABILITIES = 35; + /** @hide */ + public static final int EID_EXT_HE_OPERATION = 36; /** @hide */ @UnsupportedAppUsage public int id; /** @hide */ + public int idExt; + + /** @hide */ @UnsupportedAppUsage public byte[] bytes; @@ -497,6 +779,7 @@ public class ScanResult implements Parcelable { public InformationElement(@NonNull InformationElement rhs) { this.id = rhs.id; + this.idExt = rhs.idExt; this.bytes = rhs.bytes.clone(); } @@ -509,6 +792,14 @@ public class ScanResult implements Parcelable { } /** + * The element ID Extension of the information element. Defined in the IEEE 802.11-2016 spec + * Table 9-77. + */ + public int getIdExt() { + return idExt; + } + + /** * Get the specific content of the information element. */ @NonNull @@ -536,33 +827,11 @@ public class ScanResult implements Parcelable { */ public AnqpInformationElement[] anqpElements; - /** - * Flag indicating if this AP is a carrier AP. The determination is based - * on the AP's SSID and if AP is using EAP security. - * - * @hide - */ - public boolean isCarrierAp; - - /** - * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP. - * - * @hide - */ - public int carrierApEapType; - - /** - * The name of the carrier that's associated with this AP if it is a carrier AP. - * - * @hide - */ - public String carrierName; - /** {@hide} */ public ScanResult(WifiSsid wifiSsid, String BSSID, long hessid, int anqpDomainId, byte[] osuProviders, String caps, int level, int frequency, long tsf) { this.wifiSsid = wifiSsid; - this.SSID = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE; + this.SSID = (wifiSsid != null) ? wifiSsid.toString() : WifiManager.UNKNOWN_SSID; this.BSSID = BSSID; this.hessid = hessid; this.anqpDomainId = anqpDomainId; @@ -582,17 +851,15 @@ public class ScanResult implements Parcelable { this.centerFreq0 = UNSPECIFIED; this.centerFreq1 = UNSPECIFIED; this.flags = 0; - this.isCarrierAp = false; - this.carrierApEapType = UNSPECIFIED; - this.carrierName = null; this.radioChainInfos = null; + this.mWifiStandard = WIFI_STANDARD_UNKNOWN; } /** {@hide} */ public ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency, long tsf, int distCm, int distSdCm) { this.wifiSsid = wifiSsid; - this.SSID = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE; + this.SSID = (wifiSsid != null) ? wifiSsid.toString() : WifiManager.UNKNOWN_SSID; this.BSSID = BSSID; this.capabilities = caps; this.level = level; @@ -604,10 +871,8 @@ public class ScanResult implements Parcelable { this.centerFreq0 = UNSPECIFIED; this.centerFreq1 = UNSPECIFIED; this.flags = 0; - this.isCarrierAp = false; - this.carrierApEapType = UNSPECIFIED; - this.carrierName = null; this.radioChainInfos = null; + this.mWifiStandard = WIFI_STANDARD_UNKNOWN; } /** {@hide} */ @@ -633,10 +898,8 @@ public class ScanResult implements Parcelable { } else { this.flags = 0; } - this.isCarrierAp = false; - this.carrierApEapType = UNSPECIFIED; - this.carrierName = null; this.radioChainInfos = null; + this.mWifiStandard = WIFI_STANDARD_UNKNOWN; } /** {@hide} */ @@ -674,17 +937,12 @@ public class ScanResult implements Parcelable { venueName = source.venueName; operatorFriendlyName = source.operatorFriendlyName; flags = source.flags; - isCarrierAp = source.isCarrierAp; - carrierApEapType = source.carrierApEapType; - carrierName = source.carrierName; radioChainInfos = source.radioChainInfos; + this.mWifiStandard = source.mWifiStandard; } } - /** empty scan result - * - * {@hide} - * */ + /** Construct an empty scan result. */ public ScanResult() { } @@ -693,19 +951,18 @@ public class ScanResult implements Parcelable { StringBuffer sb = new StringBuffer(); String none = "<none>"; - sb.append("SSID: "). - append(wifiSsid == null ? WifiSsid.NONE : wifiSsid). - append(", BSSID: "). - append(BSSID == null ? none : BSSID). - append(", capabilities: "). - append(capabilities == null ? none : capabilities). - append(", level: "). - append(level). - append(", frequency: "). - append(frequency). - append(", timestamp: "). - append(timestamp); - + sb.append("SSID: ") + .append(wifiSsid == null ? WifiManager.UNKNOWN_SSID : wifiSsid) + .append(", BSSID: ") + .append(BSSID == null ? none : BSSID) + .append(", capabilities: ") + .append(capabilities == null ? none : capabilities) + .append(", level: ") + .append(level) + .append(", frequency: ") + .append(frequency) + .append(", timestamp: ") + .append(timestamp); sb.append(", distance: ").append((distanceCm != UNSPECIFIED ? distanceCm : "?")). append("(cm)"); sb.append(", distanceSd: ").append((distanceSdCm != UNSPECIFIED ? distanceSdCm : "?")). @@ -716,11 +973,9 @@ public class ScanResult implements Parcelable { sb.append(", ChannelBandwidth: ").append(channelWidth); sb.append(", centerFreq0: ").append(centerFreq0); sb.append(", centerFreq1: ").append(centerFreq1); + sb.append(", standard: ").append(wifiStandardToString(mWifiStandard)); sb.append(", 80211mcResponder: "); sb.append(((flags & FLAG_80211mc_RESPONDER) != 0) ? "is supported" : "is not supported"); - sb.append(", Carrier AP: ").append(isCarrierAp ? "yes" : "no"); - sb.append(", Carrier AP EAP Type: ").append(carrierApEapType); - sb.append(", Carrier name: ").append(carrierName); sb.append(", Radio Chain Infos: ").append(Arrays.toString(radioChainInfos)); return sb.toString(); } @@ -751,6 +1006,7 @@ public class ScanResult implements Parcelable { dest.writeInt(channelWidth); dest.writeInt(centerFreq0); dest.writeInt(centerFreq1); + dest.writeInt(mWifiStandard); dest.writeLong(seen); dest.writeInt(untrusted ? 1 : 0); dest.writeInt(numUsage); @@ -762,6 +1018,7 @@ public class ScanResult implements Parcelable { dest.writeInt(informationElements.length); for (int i = 0; i < informationElements.length; i++) { dest.writeInt(informationElements[i].id); + dest.writeInt(informationElements[i].idExt); dest.writeInt(informationElements[i].bytes.length); dest.writeByteArray(informationElements[i].bytes); } @@ -789,9 +1046,6 @@ public class ScanResult implements Parcelable { } else { dest.writeInt(0); } - dest.writeInt(isCarrierAp ? 1 : 0); - dest.writeInt(carrierApEapType); - dest.writeString(carrierName); if (radioChainInfos != null) { dest.writeInt(radioChainInfos.length); @@ -831,6 +1085,7 @@ public class ScanResult implements Parcelable { fixed with flags below */ ); + sr.mWifiStandard = in.readInt(); sr.seen = in.readLong(); sr.untrusted = in.readInt() != 0; sr.numUsage = in.readInt(); @@ -843,6 +1098,7 @@ public class ScanResult implements Parcelable { for (int i = 0; i < n; i++) { sr.informationElements[i] = new InformationElement(); sr.informationElements[i].id = in.readInt(); + sr.informationElements[i].idExt = in.readInt(); int len = in.readInt(); sr.informationElements[i].bytes = new byte[len]; in.readByteArray(sr.informationElements[i].bytes); @@ -869,9 +1125,6 @@ public class ScanResult implements Parcelable { new AnqpInformationElement(vendorId, elementId, payload); } } - sr.isCarrierAp = in.readInt() != 0; - sr.carrierApEapType = in.readInt(); - sr.carrierName = in.readString(); n = in.readInt(); if (n != 0) { sr.radioChainInfos = new RadioChainInfo[n]; diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java new file mode 100644 index 000000000000..dcb57ecc933f --- /dev/null +++ b/wifi/java/android/net/wifi/SoftApCapability.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2019 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.annotation.LongDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * A class representing capability of the SoftAp. + * {@see WifiManager} + * + * @hide + */ +@SystemApi +public final class SoftApCapability implements Parcelable { + + /** + * Support for automatic channel selection in driver (ACS). + * Driver will auto select best channel based on interference to optimize performance. + * + * flag when {@link R.bool.config_wifi_softap_acs_supported)} is true. + * + * <p> + * Use {@link WifiManager.SoftApCallback#onInfoChanged(SoftApInfo)} and + * {@link SoftApInfo#getFrequency} and {@link SoftApInfo#getBandwidth} to get + * driver channel selection result. + */ + public static final long SOFTAP_FEATURE_ACS_OFFLOAD = 1 << 0; + + /** + * Support for client force disconnect. + * flag when {@link R.bool.config_wifi_sofap_client_force_disconnect_supported)} is true + * + * <p> + * Several Soft AP client control features, e.g. specifying the maximum number of + * Soft AP clients, only work when this feature support is present. + * Check feature support before invoking + * {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)} + */ + public static final long SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 1 << 1; + + + /** + * Support for WPA3 Simultaneous Authentication of Equals (WPA3-SAE). + * + * flag when {@link config_wifi_softap_sae_supported)} is true. + */ + public static final long SOFTAP_FEATURE_WPA3_SAE = 1 << 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @LongDef(flag = true, prefix = { "SOFTAP_FEATURE_" }, value = { + SOFTAP_FEATURE_ACS_OFFLOAD, + SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT, + SOFTAP_FEATURE_WPA3_SAE, + }) + public @interface HotspotFeatures {} + + private @HotspotFeatures long mSupportedFeatures = 0; + + private int mMaximumSupportedClientNumber; + + /** + * Get the maximum supported client numbers which AP resides on. + */ + public int getMaxSupportedClients() { + return mMaximumSupportedClientNumber; + } + + /** + * Set the maximum supported client numbers which AP resides on. + * + * @param maxClient maximum supported client numbers for the softap. + * @hide + */ + public void setMaxSupportedClients(int maxClient) { + mMaximumSupportedClientNumber = maxClient; + } + + /** + * Returns true when all of the queried features are supported, otherwise false. + * + * @param features One or combination of the following features: + * {@link #SOFTAP_FEATURE_ACS_OFFLOAD}, {@link #SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} or + * {@link #SOFTAP_FEATURE_WPA3_SAE}. + */ + public boolean areFeaturesSupported(@HotspotFeatures long features) { + return (mSupportedFeatures & features) == features; + } + + /** + * @hide + */ + public SoftApCapability(@Nullable SoftApCapability source) { + if (source != null) { + mSupportedFeatures = source.mSupportedFeatures; + mMaximumSupportedClientNumber = source.mMaximumSupportedClientNumber; + } + } + + /** + * Constructor with combination of the feature. + * Zero to no supported feature. + * + * @param features One or combination of the following features: + * {@link #SOFTAP_FEATURE_ACS_OFFLOAD}, {@link #SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} or + * {@link #SOFTAP_FEATURE_WPA3_SAE}. + * @hide + */ + public SoftApCapability(@HotspotFeatures long features) { + mSupportedFeatures = features; + } + + @Override + /** Implement the Parcelable interface. */ + public int describeContents() { + return 0; + } + + @Override + /** Implement the Parcelable interface */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeLong(mSupportedFeatures); + dest.writeInt(mMaximumSupportedClientNumber); + } + + @NonNull + /** Implement the Parcelable interface */ + public static final Creator<SoftApCapability> CREATOR = new Creator<SoftApCapability>() { + public SoftApCapability createFromParcel(Parcel in) { + long supportedFeatures = in.readLong(); + SoftApCapability capability = new SoftApCapability(supportedFeatures); + capability.mMaximumSupportedClientNumber = in.readInt(); + return capability; + } + + public SoftApCapability[] newArray(int size) { + return new SoftApCapability[size]; + } + }; + + @NonNull + @Override + public String toString() { + StringBuilder sbuf = new StringBuilder(); + sbuf.append("SupportedFeatures=").append(mSupportedFeatures); + sbuf.append("MaximumSupportedClientNumber=").append(mMaximumSupportedClientNumber); + return sbuf.toString(); + } + + @Override + public boolean equals(@NonNull Object o) { + if (this == o) return true; + if (!(o instanceof SoftApCapability)) return false; + SoftApCapability capability = (SoftApCapability) o; + return mSupportedFeatures == capability.mSupportedFeatures + && mMaximumSupportedClientNumber == capability.mMaximumSupportedClientNumber; + } + + @Override + public int hashCode() { + return Objects.hash(mSupportedFeatures, mMaximumSupportedClientNumber); + } +} diff --git a/wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java b/wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java new file mode 100755 index 000000000000..c5472ce34478 --- /dev/null +++ b/wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi; + +import static android.os.Environment.getDataMiscDirectory; + +import android.annotation.Nullable; +import android.net.MacAddress; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +/** + * Utility class to convert the legacy softap.conf file format to the new XML format. + * Note: + * <li>This should be modified by the OEM if they want to migrate configuration for existing + * devices for new softap features supported by AOSP in Android 11. + * For ex: client allowlist/blocklist feature was already supported by some OEM's before Android 10 + * while AOSP only supported it in Android 11. </li> + * <li>Most of this class was copied over from WifiApConfigStore class in Android 10 and + * SoftApStoreData class in Android 11</li> + * @hide + */ +public final class SoftApConfToXmlMigrationUtil { + private static final String TAG = "SoftApConfToXmlMigrationUtil"; + + /** + * Directory to read the wifi config store files from under. + */ + private static final String LEGACY_WIFI_STORE_DIRECTORY_NAME = "wifi"; + /** + * The legacy Softap config file which contained key/value pairs. + */ + private static final String LEGACY_AP_CONFIG_FILE = "softap.conf"; + + /** + * Pre-apex wifi shared folder. + */ + private static File getLegacyWifiSharedDirectory() { + return new File(getDataMiscDirectory(), LEGACY_WIFI_STORE_DIRECTORY_NAME); + } + + /* @hide constants copied from WifiConfiguration */ + /** + * 2GHz band. + */ + private static final int WIFICONFIG_AP_BAND_2GHZ = 0; + /** + * 5GHz band. + */ + private static final int WIFICONFIG_AP_BAND_5GHZ = 1; + /** + * Device is allowed to choose the optimal band (2Ghz or 5Ghz) based on device capability, + * operating country code and current radio conditions. + */ + private static final int WIFICONFIG_AP_BAND_ANY = -1; + /** + * Convert band from WifiConfiguration into SoftApConfiguration + * + * @param wifiConfigBand band encoded as WIFICONFIG_AP_BAND_xxxx + * @return band as encoded as SoftApConfiguration.BAND_xxx + */ + @VisibleForTesting + public static int convertWifiConfigBandToSoftApConfigBand(int wifiConfigBand) { + switch (wifiConfigBand) { + case WIFICONFIG_AP_BAND_2GHZ: + return SoftApConfiguration.BAND_2GHZ; + case WIFICONFIG_AP_BAND_5GHZ: + return SoftApConfiguration.BAND_5GHZ; + case WIFICONFIG_AP_BAND_ANY: + return SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ; + default: + return SoftApConfiguration.BAND_2GHZ; + } + } + + /** + * Load AP configuration from legacy persistent storage. + * Note: This is deprecated and only used for migrating data once on reboot. + */ + private static SoftApConfiguration loadFromLegacyFile(InputStream fis) { + SoftApConfiguration config = null; + DataInputStream in = null; + try { + SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); + in = new DataInputStream(new BufferedInputStream(fis)); + + int version = in.readInt(); + if (version < 1 || version > 3) { + Log.e(TAG, "Bad version on hotspot configuration file"); + return null; + } + configBuilder.setSsid(in.readUTF()); + + if (version >= 2) { + int band = in.readInt(); + int channel = in.readInt(); + if (channel == 0) { + configBuilder.setBand( + convertWifiConfigBandToSoftApConfigBand(band)); + } else { + configBuilder.setChannel(channel, + convertWifiConfigBandToSoftApConfigBand(band)); + } + } + if (version >= 3) { + configBuilder.setHiddenSsid(in.readBoolean()); + } + int authType = in.readInt(); + if (authType == WifiConfiguration.KeyMgmt.WPA2_PSK) { + configBuilder.setPassphrase(in.readUTF(), + SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); + } + config = configBuilder.build(); + } catch (IOException e) { + Log.e(TAG, "Error reading hotspot configuration ", e); + config = null; + } catch (IllegalArgumentException ie) { + Log.e(TAG, "Invalid hotspot configuration ", ie); + config = null; + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + Log.e(TAG, "Error closing hotspot configuration during read", e); + } + } + } + // NOTE: OEM's should add their customized parsing code here. + return config; + } + + // This is the version that Android 11 released with. + private static final int CONFIG_STORE_DATA_VERSION = 3; + + private static final String XML_TAG_DOCUMENT_HEADER = "WifiConfigStoreData"; + private static final String XML_TAG_VERSION = "Version"; + private static final String XML_TAG_SECTION_HEADER_SOFTAP = "SoftAp"; + private static final String XML_TAG_SSID = "SSID"; + private static final String XML_TAG_BSSID = "Bssid"; + private static final String XML_TAG_CHANNEL = "Channel"; + private static final String XML_TAG_HIDDEN_SSID = "HiddenSSID"; + private static final String XML_TAG_SECURITY_TYPE = "SecurityType"; + private static final String XML_TAG_AP_BAND = "ApBand"; + private static final String XML_TAG_PASSPHRASE = "Passphrase"; + private static final String XML_TAG_MAX_NUMBER_OF_CLIENTS = "MaxNumberOfClients"; + private static final String XML_TAG_AUTO_SHUTDOWN_ENABLED = "AutoShutdownEnabled"; + private static final String XML_TAG_SHUTDOWN_TIMEOUT_MILLIS = "ShutdownTimeoutMillis"; + private static final String XML_TAG_CLIENT_CONTROL_BY_USER = "ClientControlByUser"; + private static final String XML_TAG_BLOCKED_CLIENT_LIST = "BlockedClientList"; + private static final String XML_TAG_ALLOWED_CLIENT_LIST = "AllowedClientList"; + public static final String XML_TAG_CLIENT_MACADDRESS = "ClientMacAddress"; + + private static byte[] convertConfToXml(SoftApConfiguration softApConf) { + try { + final XmlSerializer out = new FastXmlSerializer(); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + out.setOutput(outputStream, StandardCharsets.UTF_8.name()); + + // Header for the XML file. + out.startDocument(null, true); + out.startTag(null, XML_TAG_DOCUMENT_HEADER); + XmlUtils.writeValueXml(CONFIG_STORE_DATA_VERSION, XML_TAG_VERSION, out); + out.startTag(null, XML_TAG_SECTION_HEADER_SOFTAP); + + // SoftAp conf + XmlUtils.writeValueXml(softApConf.getSsid(), XML_TAG_SSID, out); + if (softApConf.getBssid() != null) { + XmlUtils.writeValueXml(softApConf.getBssid().toString(), XML_TAG_BSSID, out); + } + XmlUtils.writeValueXml(softApConf.getBand(), XML_TAG_AP_BAND, out); + XmlUtils.writeValueXml(softApConf.getChannel(), XML_TAG_CHANNEL, out); + XmlUtils.writeValueXml(softApConf.isHiddenSsid(), XML_TAG_HIDDEN_SSID, out); + XmlUtils.writeValueXml(softApConf.getSecurityType(), XML_TAG_SECURITY_TYPE, out); + if (softApConf.getSecurityType() != SoftApConfiguration.SECURITY_TYPE_OPEN) { + XmlUtils.writeValueXml(softApConf.getPassphrase(), XML_TAG_PASSPHRASE, out); + } + XmlUtils.writeValueXml(softApConf.getMaxNumberOfClients(), + XML_TAG_MAX_NUMBER_OF_CLIENTS, out); + XmlUtils.writeValueXml(softApConf.isClientControlByUserEnabled(), + XML_TAG_CLIENT_CONTROL_BY_USER, out); + XmlUtils.writeValueXml(softApConf.isAutoShutdownEnabled(), + XML_TAG_AUTO_SHUTDOWN_ENABLED, out); + XmlUtils.writeValueXml(softApConf.getShutdownTimeoutMillis(), + XML_TAG_SHUTDOWN_TIMEOUT_MILLIS, out); + out.startTag(null, XML_TAG_BLOCKED_CLIENT_LIST); + for (MacAddress mac: softApConf.getBlockedClientList()) { + XmlUtils.writeValueXml(mac.toString(), XML_TAG_CLIENT_MACADDRESS, out); + } + out.endTag(null, XML_TAG_BLOCKED_CLIENT_LIST); + out.startTag(null, XML_TAG_ALLOWED_CLIENT_LIST); + for (MacAddress mac: softApConf.getAllowedClientList()) { + XmlUtils.writeValueXml(mac.toString(), XML_TAG_CLIENT_MACADDRESS, out); + } + out.endTag(null, XML_TAG_ALLOWED_CLIENT_LIST); + + // Footer for the XML file. + out.endTag(null, XML_TAG_SECTION_HEADER_SOFTAP); + out.endTag(null, XML_TAG_DOCUMENT_HEADER); + out.endDocument(); + + return outputStream.toByteArray(); + } catch (IOException | XmlPullParserException e) { + Log.e(TAG, "Failed to convert softap conf to XML", e); + return null; + } + } + + private SoftApConfToXmlMigrationUtil() { } + + /** + * Read the legacy /data/misc/wifi/softap.conf file format and convert to the new XML + * format understood by WifiConfigStore. + * Note: Used for unit testing. + */ + @VisibleForTesting + @Nullable + public static InputStream convert(InputStream fis) { + SoftApConfiguration softApConf = loadFromLegacyFile(fis); + if (softApConf == null) return null; + + byte[] xmlBytes = convertConfToXml(softApConf); + if (xmlBytes == null) return null; + + return new ByteArrayInputStream(xmlBytes); + } + + /** + * Read the legacy /data/misc/wifi/softap.conf file format and convert to the new XML + * format understood by WifiConfigStore. + */ + @Nullable + public static InputStream convert() { + File file = new File(getLegacyWifiSharedDirectory(), LEGACY_AP_CONFIG_FILE); + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + } catch (FileNotFoundException e) { + return null; + } + if (fis == null) return null; + return convert(fis); + } + + /** + * Remove the legacy /data/misc/wifi/softap.conf file. + */ + @Nullable + public static void remove() { + File file = new File(getLegacyWifiSharedDirectory(), LEGACY_AP_CONFIG_FILE); + file.delete(); + } +} diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java new file mode 100644 index 000000000000..a5e76e6c92ee --- /dev/null +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -0,0 +1,972 @@ +/* + * Copyright (C) 2019 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.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.net.MacAddress; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Configuration for a soft access point (a.k.a. Soft AP, SAP, Hotspot). + * + * This is input for the framework provided by a client app, i.e. it exposes knobs to instruct the + * framework how it should configure a hotspot. + * + * System apps can use this to configure a tethered hotspot using + * {@code WifiManager#startTetheredHotspot(SoftApConfiguration)} and + * {@code WifiManager#setSoftApConfiguration(SoftApConfiguration)} + * or local-only hotspot using + * {@code WifiManager#startLocalOnlyHotspot(SoftApConfiguration, Executor, + * WifiManager.LocalOnlyHotspotCallback)}. + * + * Instances of this class are immutable; use {@link SoftApConfiguration.Builder} and its methods to + * create a new instance. + * + */ +public final class SoftApConfiguration implements Parcelable { + + private static final String TAG = "SoftApConfiguration"; + + @VisibleForTesting + static final int PSK_MIN_LEN = 8; + + @VisibleForTesting + static final int PSK_MAX_LEN = 63; + + /** + * 2GHz band. + * @hide + */ + @SystemApi + public static final int BAND_2GHZ = 1 << 0; + + /** + * 5GHz band. + * @hide + */ + @SystemApi + public static final int BAND_5GHZ = 1 << 1; + + /** + * 6GHz band. + * @hide + */ + @SystemApi + public static final int BAND_6GHZ = 1 << 2; + + /** + * Device is allowed to choose the optimal band (2Ghz, 5Ghz, 6Ghz) based on device capability, + * operating country code and current radio conditions. + * @hide + */ + @SystemApi + public static final int BAND_ANY = BAND_2GHZ | BAND_5GHZ | BAND_6GHZ; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "BAND_TYPE_" }, value = { + BAND_2GHZ, + BAND_5GHZ, + BAND_6GHZ, + }) + public @interface BandType {} + + private static boolean isBandValid(@BandType int band) { + return ((band != 0) && ((band & ~BAND_ANY) == 0)); + } + + private static final int MIN_CH_2G_BAND = 1; + private static final int MAX_CH_2G_BAND = 14; + private static final int MIN_CH_5G_BAND = 34; + private static final int MAX_CH_5G_BAND = 196; + private static final int MIN_CH_6G_BAND = 1; + private static final int MAX_CH_6G_BAND = 253; + + + + private static boolean isChannelBandPairValid(int channel, @BandType int band) { + switch (band) { + case BAND_2GHZ: + if (channel < MIN_CH_2G_BAND || channel > MAX_CH_2G_BAND) { + return false; + } + break; + + case BAND_5GHZ: + if (channel < MIN_CH_5G_BAND || channel > MAX_CH_5G_BAND) { + return false; + } + break; + + case BAND_6GHZ: + if (channel < MIN_CH_6G_BAND || channel > MAX_CH_6G_BAND) { + return false; + } + break; + default: + return false; + } + return true; + } + + /** + * SSID for the AP, or null for a framework-determined SSID. + */ + private final @Nullable String mSsid; + + /** + * BSSID for the AP, or null to use a framework-determined BSSID. + */ + private final @Nullable MacAddress mBssid; + + /** + * Pre-shared key for WPA2-PSK or WPA3-SAE-Transition or WPA3-SAE encryption which depends on + * the security type. + */ + private final @Nullable String mPassphrase; + + /** + * This is a network that does not broadcast its SSID, so an + * SSID-specific probe request must be used for scans. + */ + private final boolean mHiddenSsid; + + /** + * The operating band of the AP. + * One or combination of the following band type: + * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}. + */ + private final @BandType int mBand; + + /** + * The operating channel of the AP. + */ + private final int mChannel; + + /** + * The maximim allowed number of clients that can associate to the AP. + */ + private final int mMaxNumberOfClients; + + /** + * The operating security type of the AP. + * One of the following security types: + * {@link #SECURITY_TYPE_OPEN}, + * {@link #SECURITY_TYPE_WPA2_PSK}, + * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION}, + * {@link #SECURITY_TYPE_WPA3_SAE} + */ + private final @SecurityType int mSecurityType; + + /** + * The flag to indicate client need to authorize by user + * when client is connecting to AP. + */ + private final boolean mClientControlByUser; + + /** + * The list of blocked client that can't associate to the AP. + */ + private final List<MacAddress> mBlockedClientList; + + /** + * The list of allowed client that can associate to the AP. + */ + private final List<MacAddress> mAllowedClientList; + + /** + * Whether auto shutdown of soft AP is enabled or not. + */ + private final boolean mAutoShutdownEnabled; + + /** + * Delay in milliseconds before shutting down soft AP when + * there are no connected devices. + */ + private final long mShutdownTimeoutMillis; + + /** + * THe definition of security type OPEN. + */ + public static final int SECURITY_TYPE_OPEN = 0; + + /** + * The definition of security type WPA2-PSK. + */ + public static final int SECURITY_TYPE_WPA2_PSK = 1; + + /** + * The definition of security type WPA3-SAE Transition mode. + */ + public static final int SECURITY_TYPE_WPA3_SAE_TRANSITION = 2; + + /** + * The definition of security type WPA3-SAE. + */ + public static final int SECURITY_TYPE_WPA3_SAE = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "SECURITY_TYPE_" }, value = { + SECURITY_TYPE_OPEN, + SECURITY_TYPE_WPA2_PSK, + SECURITY_TYPE_WPA3_SAE_TRANSITION, + SECURITY_TYPE_WPA3_SAE, + }) + public @interface SecurityType {} + + /** Private constructor for Builder and Parcelable implementation. */ + private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid, + @Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel, + @SecurityType int securityType, int maxNumberOfClients, boolean shutdownTimeoutEnabled, + long shutdownTimeoutMillis, boolean clientControlByUser, + @NonNull List<MacAddress> blockedList, @NonNull List<MacAddress> allowedList) { + mSsid = ssid; + mBssid = bssid; + mPassphrase = passphrase; + mHiddenSsid = hiddenSsid; + mBand = band; + mChannel = channel; + mSecurityType = securityType; + mMaxNumberOfClients = maxNumberOfClients; + mAutoShutdownEnabled = shutdownTimeoutEnabled; + mShutdownTimeoutMillis = shutdownTimeoutMillis; + mClientControlByUser = clientControlByUser; + mBlockedClientList = new ArrayList<>(blockedList); + mAllowedClientList = new ArrayList<>(allowedList); + } + + @Override + public boolean equals(Object otherObj) { + if (this == otherObj) { + return true; + } + if (!(otherObj instanceof SoftApConfiguration)) { + return false; + } + SoftApConfiguration other = (SoftApConfiguration) otherObj; + return Objects.equals(mSsid, other.mSsid) + && Objects.equals(mBssid, other.mBssid) + && Objects.equals(mPassphrase, other.mPassphrase) + && mHiddenSsid == other.mHiddenSsid + && mBand == other.mBand + && mChannel == other.mChannel + && mSecurityType == other.mSecurityType + && mMaxNumberOfClients == other.mMaxNumberOfClients + && mAutoShutdownEnabled == other.mAutoShutdownEnabled + && mShutdownTimeoutMillis == other.mShutdownTimeoutMillis + && mClientControlByUser == other.mClientControlByUser + && Objects.equals(mBlockedClientList, other.mBlockedClientList) + && Objects.equals(mAllowedClientList, other.mAllowedClientList); + } + + @Override + public int hashCode() { + return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid, + mBand, mChannel, mSecurityType, mMaxNumberOfClients, mAutoShutdownEnabled, + mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList, + mAllowedClientList); + } + + @Override + public String toString() { + StringBuilder sbuf = new StringBuilder(); + sbuf.append("ssid=").append(mSsid); + if (mBssid != null) sbuf.append(" \n bssid=").append(mBssid.toString()); + sbuf.append(" \n Passphrase =").append( + TextUtils.isEmpty(mPassphrase) ? "<empty>" : "<non-empty>"); + sbuf.append(" \n HiddenSsid =").append(mHiddenSsid); + sbuf.append(" \n Band =").append(mBand); + sbuf.append(" \n Channel =").append(mChannel); + sbuf.append(" \n SecurityType=").append(getSecurityType()); + sbuf.append(" \n MaxClient=").append(mMaxNumberOfClients); + sbuf.append(" \n AutoShutdownEnabled=").append(mAutoShutdownEnabled); + sbuf.append(" \n ShutdownTimeoutMillis=").append(mShutdownTimeoutMillis); + sbuf.append(" \n ClientControlByUser=").append(mClientControlByUser); + sbuf.append(" \n BlockedClientList=").append(mBlockedClientList); + sbuf.append(" \n AllowedClientList=").append(mAllowedClientList); + return sbuf.toString(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mSsid); + dest.writeParcelable(mBssid, flags); + dest.writeString(mPassphrase); + dest.writeBoolean(mHiddenSsid); + dest.writeInt(mBand); + dest.writeInt(mChannel); + dest.writeInt(mSecurityType); + dest.writeInt(mMaxNumberOfClients); + dest.writeBoolean(mAutoShutdownEnabled); + dest.writeLong(mShutdownTimeoutMillis); + dest.writeBoolean(mClientControlByUser); + dest.writeTypedList(mBlockedClientList); + dest.writeTypedList(mAllowedClientList); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator<SoftApConfiguration> CREATOR = new Creator<SoftApConfiguration>() { + @Override + public SoftApConfiguration createFromParcel(Parcel in) { + return new SoftApConfiguration( + in.readString(), + in.readParcelable(MacAddress.class.getClassLoader()), + in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(), + in.readInt(), in.readBoolean(), in.readLong(), in.readBoolean(), + in.createTypedArrayList(MacAddress.CREATOR), + in.createTypedArrayList(MacAddress.CREATOR)); + } + + @Override + public SoftApConfiguration[] newArray(int size) { + return new SoftApConfiguration[size]; + } + }; + + /** + * Return String set to be the SSID for the AP. + * {@link Builder#setSsid(String)}. + */ + @Nullable + public String getSsid() { + return mSsid; + } + + /** + * Returns MAC address set to be BSSID for the AP. + * {@link Builder#setBssid(MacAddress)}. + */ + @Nullable + public MacAddress getBssid() { + return mBssid; + } + + /** + * Returns String set to be passphrase for current AP. + * {@link Builder#setPassphrase(String, int)}. + */ + @Nullable + public String getPassphrase() { + return mPassphrase; + } + + /** + * Returns Boolean set to be indicate hidden (true: doesn't broadcast its SSID) or + * not (false: broadcasts its SSID) for the AP. + * {@link Builder#setHiddenSsid(boolean)}. + */ + public boolean isHiddenSsid() { + return mHiddenSsid; + } + + /** + * Returns band type set to be the band for the AP. + * + * One or combination of the following band type: + * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}. + * + * {@link Builder#setBand(int)}. + * + * @hide + */ + @SystemApi + public @BandType int getBand() { + return mBand; + } + + /** + * Returns Integer set to be the channel for the AP. + * {@link Builder#setChannel(int)}. + * + * @hide + */ + @SystemApi + public int getChannel() { + return mChannel; + } + + /** + * Get security type params which depends on which security passphrase to set. + * + * @return One of: + * {@link #SECURITY_TYPE_OPEN}, + * {@link #SECURITY_TYPE_WPA2_PSK}, + * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION}, + * {@link #SECURITY_TYPE_WPA3_SAE} + */ + public @SecurityType int getSecurityType() { + return mSecurityType; + } + + /** + * Returns the maximum number of clients that can associate to the AP. + * {@link Builder#setMaxNumberOfClients(int)}. + * + * @hide + */ + @SystemApi + public int getMaxNumberOfClients() { + return mMaxNumberOfClients; + } + + /** + * Returns whether auto shutdown is enabled or not. + * The Soft AP will shutdown when there are no devices associated to it for + * the timeout duration. See {@link Builder#setAutoShutdownEnabled(boolean)}. + * + * @hide + */ + @SystemApi + public boolean isAutoShutdownEnabled() { + return mAutoShutdownEnabled; + } + + /** + * Returns the shutdown timeout in milliseconds. + * The Soft AP will shutdown when there are no devices associated to it for + * the timeout duration. See {@link Builder#setShutdownTimeoutMillis(long)}. + * + * @hide + */ + @SystemApi + public long getShutdownTimeoutMillis() { + return mShutdownTimeoutMillis; + } + + /** + * Returns a flag indicating whether clients need to be pre-approved by the user. + * (true: authorization required) or not (false: not required). + * {@link Builder#setClientControlByUserEnabled(Boolean)}. + * + * @hide + */ + @SystemApi + public boolean isClientControlByUserEnabled() { + return mClientControlByUser; + } + + /** + * Returns List of clients which aren't allowed to associate to the AP. + * + * Clients are configured using {@link Builder#setBlockedClientList(List)} + * + * @hide + */ + @NonNull + @SystemApi + public List<MacAddress> getBlockedClientList() { + return mBlockedClientList; + } + + /** + * List of clients which are allowed to associate to the AP. + * Clients are configured using {@link Builder#setAllowedClientList(List)} + * + * @hide + */ + @NonNull + @SystemApi + public List<MacAddress> getAllowedClientList() { + return mAllowedClientList; + } + + /** + * Returns a {@link WifiConfiguration} representation of this {@link SoftApConfiguration}. + * Note that SoftApConfiguration may contain configuration which is cannot be represented + * by the legacy WifiConfiguration, in such cases a null will be returned. + * + * <li> SoftAp band in {@link WifiConfiguration.apBand} only supports + * 2GHz, 5GHz, 2GHz+5GHz bands, so conversion is limited to these bands. </li> + * + * <li> SoftAp security type in {@link WifiConfiguration.KeyMgmt} only supports + * NONE, WPA2_PSK, so conversion is limited to these security type.</li> + * @hide + */ + @Nullable + @SystemApi + public WifiConfiguration toWifiConfiguration() { + WifiConfiguration wifiConfig = new WifiConfiguration(); + wifiConfig.SSID = mSsid; + wifiConfig.preSharedKey = mPassphrase; + wifiConfig.hiddenSSID = mHiddenSsid; + wifiConfig.apChannel = mChannel; + switch (mSecurityType) { + case SECURITY_TYPE_OPEN: + wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + break; + case SECURITY_TYPE_WPA2_PSK: + wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK); + break; + default: + Log.e(TAG, "Convert fail, unsupported security type :" + mSecurityType); + return null; + } + + switch (mBand) { + case BAND_2GHZ: + wifiConfig.apBand = WifiConfiguration.AP_BAND_2GHZ; + break; + case BAND_5GHZ: + wifiConfig.apBand = WifiConfiguration.AP_BAND_5GHZ; + break; + case BAND_2GHZ | BAND_5GHZ: + wifiConfig.apBand = WifiConfiguration.AP_BAND_ANY; + break; + case BAND_ANY: + wifiConfig.apBand = WifiConfiguration.AP_BAND_ANY; + break; + default: + Log.e(TAG, "Convert fail, unsupported band setting :" + mBand); + return null; + } + return wifiConfig; + } + + /** + * Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a + * Soft AP. + * + * All fields are optional. By default, SSID and BSSID are automatically chosen by the + * framework, and an open network is created. + * + * @hide + */ + @SystemApi + public static final class Builder { + private String mSsid; + private MacAddress mBssid; + private String mPassphrase; + private boolean mHiddenSsid; + private int mBand; + private int mChannel; + private int mMaxNumberOfClients; + private int mSecurityType; + private boolean mAutoShutdownEnabled; + private long mShutdownTimeoutMillis; + private boolean mClientControlByUser; + private List<MacAddress> mBlockedClientList; + private List<MacAddress> mAllowedClientList; + + /** + * Constructs a Builder with default values (see {@link Builder}). + */ + public Builder() { + mSsid = null; + mBssid = null; + mPassphrase = null; + mHiddenSsid = false; + mBand = BAND_2GHZ; + mChannel = 0; + mMaxNumberOfClients = 0; + mSecurityType = SECURITY_TYPE_OPEN; + mAutoShutdownEnabled = true; // enabled by default. + mShutdownTimeoutMillis = 0; + mClientControlByUser = false; + mBlockedClientList = new ArrayList<>(); + mAllowedClientList = new ArrayList<>(); + } + + /** + * Constructs a Builder initialized from an existing {@link SoftApConfiguration} instance. + */ + public Builder(@NonNull SoftApConfiguration other) { + Objects.requireNonNull(other); + + mSsid = other.mSsid; + mBssid = other.mBssid; + mPassphrase = other.mPassphrase; + mHiddenSsid = other.mHiddenSsid; + mBand = other.mBand; + mChannel = other.mChannel; + mMaxNumberOfClients = other.mMaxNumberOfClients; + mSecurityType = other.mSecurityType; + mAutoShutdownEnabled = other.mAutoShutdownEnabled; + mShutdownTimeoutMillis = other.mShutdownTimeoutMillis; + mClientControlByUser = other.mClientControlByUser; + mBlockedClientList = new ArrayList<>(other.mBlockedClientList); + mAllowedClientList = new ArrayList<>(other.mAllowedClientList); + } + + /** + * Builds the {@link SoftApConfiguration}. + * + * @return A new {@link SoftApConfiguration}, as configured by previous method calls. + */ + @NonNull + public SoftApConfiguration build() { + for (MacAddress client : mAllowedClientList) { + if (mBlockedClientList.contains(client)) { + throw new IllegalArgumentException("A MacAddress exist in both client list"); + } + } + return new SoftApConfiguration(mSsid, mBssid, mPassphrase, + mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients, + mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser, + mBlockedClientList, mAllowedClientList); + } + + /** + * Specifies an SSID for the AP. + * <p> + * Null SSID only support when configure a local-only hotspot. + * <p> + * <li>If not set, defaults to null.</li> + * + * @param ssid SSID of valid Unicode characters, or null to have the SSID automatically + * chosen by the framework. + * @return Builder for chaining. + * @throws IllegalArgumentException when the SSID is empty or not valid Unicode. + */ + @NonNull + public Builder setSsid(@Nullable String ssid) { + if (ssid != null) { + Preconditions.checkStringNotEmpty(ssid); + Preconditions.checkArgument(StandardCharsets.UTF_8.newEncoder().canEncode(ssid)); + } + mSsid = ssid; + return this; + } + + /** + * Specifies a BSSID for the AP. + * <p> + * <li>If not set, defaults to null.</li> + * @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is + * responsible for avoiding collisions. + * @return Builder for chaining. + * @throws IllegalArgumentException when the given BSSID is the all-zero or broadcast MAC + * address. + */ + @NonNull + public Builder setBssid(@Nullable MacAddress bssid) { + if (bssid != null) { + Preconditions.checkArgument(!bssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS)); + Preconditions.checkArgument(!bssid.equals(MacAddress.BROADCAST_ADDRESS)); + } + mBssid = bssid; + return this; + } + + /** + * Specifies that this AP should use specific security type with the given ASCII passphrase. + * + * @param securityType One of the following security types: + * {@link #SECURITY_TYPE_OPEN}, + * {@link #SECURITY_TYPE_WPA2_PSK}, + * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION}, + * {@link #SECURITY_TYPE_WPA3_SAE}. + * @param passphrase The passphrase to use for sepcific {@code securityType} configuration + * or null with {@link #SECURITY_TYPE_OPEN}. + * + * @return Builder for chaining. + * @throws IllegalArgumentException when the passphrase length is invalid and + * {@code securityType} is not {@link #SECURITY_TYPE_OPEN} + * or non-null passphrase and {@code securityType} is + * {@link #SECURITY_TYPE_OPEN}. + */ + @NonNull + public Builder setPassphrase(@Nullable String passphrase, @SecurityType int securityType) { + if (securityType == SECURITY_TYPE_OPEN) { + if (passphrase != null) { + throw new IllegalArgumentException( + "passphrase should be null when security type is open"); + } + } else { + Preconditions.checkStringNotEmpty(passphrase); + final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); + if (!asciiEncoder.canEncode(passphrase)) { + throw new IllegalArgumentException("passphrase not ASCII encodable"); + } + if (securityType == SECURITY_TYPE_WPA2_PSK + || securityType == SECURITY_TYPE_WPA3_SAE_TRANSITION) { + if (passphrase.length() < PSK_MIN_LEN || passphrase.length() > PSK_MAX_LEN) { + throw new IllegalArgumentException( + "Password size must be at least " + PSK_MIN_LEN + + " and no more than " + PSK_MAX_LEN + + " for WPA2_PSK and WPA3_SAE_TRANSITION Mode"); + } + } + } + mSecurityType = securityType; + mPassphrase = passphrase; + return this; + } + + /** + * Specifies whether the AP is hidden (doesn't broadcast its SSID) or + * not (broadcasts its SSID). + * <p> + * <li>If not set, defaults to false (i.e not a hidden network).</li> + * + * @param hiddenSsid true for a hidden SSID, false otherwise. + * @return Builder for chaining. + */ + @NonNull + public Builder setHiddenSsid(boolean hiddenSsid) { + mHiddenSsid = hiddenSsid; + return this; + } + + /** + * Specifies the band for the AP. + * <p> + * <li>If not set, defaults to {@link #BAND_2GHZ}.</li> + * + * @param band One or combination of the following band type: + * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}. + * @return Builder for chaining. + */ + @NonNull + public Builder setBand(@BandType int band) { + if (!isBandValid(band)) { + throw new IllegalArgumentException("Invalid band type"); + } + mBand = band; + // Since band preference is specified, no specific channel is selected. + mChannel = 0; + return this; + } + + /** + * Specifies the channel and associated band for the AP. + * + * The channel which AP resides on. Valid channels are country dependent. + * <p> + * The default for the channel is a the special value 0 to have the framework + * auto-select a valid channel from the band configured with + * {@link #setBand(int)}. + * + * The channel auto selection will offload to driver when + * {@link SoftApCapability#areFeaturesSupported( + * SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)} + * return true. Driver will auto select best channel which based on environment + * interference to get best performance. Check {@link SoftApCapability} to get more detail. + * + * Note, since 6GHz band use the same channel numbering of 2.4GHz and 5GHZ bands, + * the caller needs to pass the band containing the selected channel. + * + * <p> + * <li>If not set, defaults to 0.</li> + * @param channel operating channel of the AP. + * @param band containing this channel. + * @return Builder for chaining. + */ + @NonNull + public Builder setChannel(int channel, @BandType int band) { + if (!isChannelBandPairValid(channel, band)) { + throw new IllegalArgumentException("Invalid band type"); + } + mBand = band; + mChannel = channel; + return this; + } + + /** + * Specifies the maximum number of clients that can associate to the AP. + * + * The maximum number of clients (STAs) which can associate to the AP. + * The AP will reject association from any clients above this number. + * Specify a value of 0 to have the framework automatically use the maximum number + * which the device can support (based on hardware and carrier constraints). + * <p> + * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and + * {@link SoftApCapability#getMaxSupportedClients} to get the maximum number of clients + * which the device supports (based on hardware and carrier constraints). + * + * <p> + * <li>If not set, defaults to 0.</li> + * + * This method requires hardware support. If the method is used to set a + * non-zero {@code maxNumberOfClients} value then + * {@link WifiManager#startTetheredHotspot} will report error code + * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}. + * + * <p> + * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and + * {@link SoftApCapability#areFeaturesSupported(int)} + * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} to determine whether + * or not this feature is supported. + * + * @param maxNumberOfClients maximum client number of the AP. + * @return Builder for chaining. + */ + @NonNull + public Builder setMaxNumberOfClients(@IntRange(from = 0) int maxNumberOfClients) { + if (maxNumberOfClients < 0) { + throw new IllegalArgumentException("maxNumberOfClients should be not negative"); + } + mMaxNumberOfClients = maxNumberOfClients; + return this; + } + + /** + * Specifies whether auto shutdown is enabled or not. + * The Soft AP will shut down when there are no devices connected to it for + * the timeout duration. + * + * <p> + * <li>If not set, defaults to true</li> + * + * @param enable true to enable, false to disable. + * @return Builder for chaining. + * + * @see #setShutdownTimeoutMillis(long) + */ + @NonNull + public Builder setAutoShutdownEnabled(boolean enable) { + mAutoShutdownEnabled = enable; + return this; + } + + /** + * Specifies the shutdown timeout in milliseconds. + * The Soft AP will shut down when there are no devices connected to it for + * the timeout duration. + * + * Specify a value of 0 to have the framework automatically use default timeout + * setting which defined in {@link R.integer.config_wifi_framework_soft_ap_timeout_delay} + * + * <p> + * <li>If not set, defaults to 0</li> + * <li>The shut down timeout will apply when {@link #setAutoShutdownEnabled(boolean)} is + * set to true</li> + * + * @param timeoutMillis milliseconds of the timeout delay. + * @return Builder for chaining. + * + * @see #setAutoShutdownEnabled(boolean) + */ + @NonNull + public Builder setShutdownTimeoutMillis(@IntRange(from = 0) long timeoutMillis) { + if (timeoutMillis < 0) { + throw new IllegalArgumentException("Invalid timeout value"); + } + mShutdownTimeoutMillis = timeoutMillis; + return this; + } + + /** + * Configure the Soft AP to require manual user control of client association. + * If disabled (the default) then any client which isn't in the blocked list + * {@link #getBlockedClientList()} can associate to this Soft AP using the + * correct credentials until the Soft AP capacity is reached (capacity is hardware, carrier, + * or user limited - using {@link #setMaxNumberOfClients(int)}). + * + * If manual user control is enabled then clients will be accepted, rejected, or require + * a user approval based on the configuration provided by + * {@link #setBlockedClientList(List)} and {@link #setAllowedClientList(List)}. + * + * <p> + * This method requires hardware support. Hardware support can be determined using + * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and + * {@link SoftApCapability#areFeaturesSupported(int)} + * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} + * + * <p> + * If the method is called on a device without hardware support then starting the soft AP + * using {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will fail with + * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}. + * + * <p> + * <li>If not set, defaults to false (i.e The authoriztion is not required).</li> + * + * @param enabled true for enabling the control by user, false otherwise. + * @return Builder for chaining. + */ + @NonNull + public Builder setClientControlByUserEnabled(boolean enabled) { + mClientControlByUser = enabled; + return this; + } + + + /** + * This method together with {@link setClientControlByUserEnabled(boolean)} control client + * connections to the AP. If client control by user is disabled using the above method then + * this API has no effect and clients are allowed to associate to the AP (within limit of + * max number of clients). + * + * If client control by user is enabled then this API configures the list of clients + * which are explicitly allowed. These are auto-accepted. + * + * All other clients which attempt to associate, whose MAC addresses are on neither list, + * are: + * <ul> + * <li>Rejected</li> + * <li>A callback {@link WifiManager.SoftApCallback#onBlockedClientConnecting(WifiClient)} + * is issued (which allows the user to add them to the allowed client list if desired).<li> + * </ul> + * + * @param allowedClientList list of clients which are allowed to associate to the AP + * without user pre-approval. + * @return Builder for chaining. + */ + @NonNull + public Builder setAllowedClientList(@NonNull List<MacAddress> allowedClientList) { + mAllowedClientList = new ArrayList<>(allowedClientList); + return this; + } + + /** + * This API configures the list of clients which are blocked and cannot associate + * to the Soft AP. + * + * <p> + * This method requires hardware support. Hardware support can be determined using + * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and + * {@link SoftApCapability#areFeaturesSupported(int)} + * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} + * + * <p> + * If the method is called on a device without hardware support then starting the soft AP + * using {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will fail with + * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}. + * + * @param blockedClientList list of clients which are not allowed to associate to the AP. + * @return Builder for chaining. + */ + @NonNull + public Builder setBlockedClientList(@NonNull List<MacAddress> blockedClientList) { + mBlockedClientList = new ArrayList<>(blockedClientList); + return this; + } + } +} diff --git a/wifi/java/android/net/wifi/SoftApInfo.java b/wifi/java/android/net/wifi/SoftApInfo.java new file mode 100644 index 000000000000..24ed8effe471 --- /dev/null +++ b/wifi/java/android/net/wifi/SoftApInfo.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2019 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.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * A class representing information about SoftAp. + * {@see WifiManager} + * + * @hide + */ +@SystemApi +public final class SoftApInfo implements Parcelable { + + /** + * AP Channel bandwidth is invalid. + * + * @see #getBandwidth() + */ + public static final int CHANNEL_WIDTH_INVALID = 0; + + /** + * AP Channel bandwidth is 20 MHZ but no HT. + * + * @see #getBandwidth() + */ + public static final int CHANNEL_WIDTH_20MHZ_NOHT = 1; + + /** + * AP Channel bandwidth is 20 MHZ. + * + * @see #getBandwidth() + */ + public static final int CHANNEL_WIDTH_20MHZ = 2; + + /** + * AP Channel bandwidth is 40 MHZ. + * + * @see #getBandwidth() + */ + public static final int CHANNEL_WIDTH_40MHZ = 3; + + /** + * AP Channel bandwidth is 80 MHZ. + * + * @see #getBandwidth() + */ + public static final int CHANNEL_WIDTH_80MHZ = 4; + + /** + * AP Channel bandwidth is 160 MHZ, but 80MHZ + 80MHZ. + * + * @see #getBandwidth() + */ + public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 5; + + /** + * AP Channel bandwidth is 160 MHZ. + * + * @see #getBandwidth() + */ + public static final int CHANNEL_WIDTH_160MHZ = 6; + + + + /** The frequency which AP resides on. */ + private int mFrequency = 0; + + @WifiAnnotations.Bandwidth + private int mBandwidth = CHANNEL_WIDTH_INVALID; + + /** + * Get the frequency which AP resides on. + */ + public int getFrequency() { + return mFrequency; + } + + /** + * Set the frequency which AP resides on. + * @hide + */ + public void setFrequency(int freq) { + mFrequency = freq; + } + + /** + * Get AP Channel bandwidth. + * + * @return One of {@link #CHANNEL_WIDTH_20MHZ}, {@link #CHANNEL_WIDTH_40MHZ}, + * {@link #CHANNEL_WIDTH_80MHZ}, {@link #CHANNEL_WIDTH_160MHZ}, + * {@link #CHANNEL_WIDTH_80MHZ_PLUS_MHZ} or {@link #CHANNEL_WIDTH_INVALID}. + */ + @WifiAnnotations.Bandwidth + public int getBandwidth() { + return mBandwidth; + } + + /** + * Set AP Channel bandwidth. + * @hide + */ + public void setBandwidth(@WifiAnnotations.Bandwidth int bandwidth) { + mBandwidth = bandwidth; + } + + /** + * @hide + */ + public SoftApInfo(@Nullable SoftApInfo source) { + if (source != null) { + mFrequency = source.mFrequency; + mBandwidth = source.mBandwidth; + } + } + + /** + * @hide + */ + public SoftApInfo() { + } + + @Override + /** Implement the Parcelable interface. */ + public int describeContents() { + return 0; + } + + @Override + /** Implement the Parcelable interface */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mFrequency); + dest.writeInt(mBandwidth); + } + + @NonNull + /** Implement the Parcelable interface */ + public static final Creator<SoftApInfo> CREATOR = new Creator<SoftApInfo>() { + public SoftApInfo createFromParcel(Parcel in) { + SoftApInfo info = new SoftApInfo(); + info.mFrequency = in.readInt(); + info.mBandwidth = in.readInt(); + return info; + } + + public SoftApInfo[] newArray(int size) { + return new SoftApInfo[size]; + } + }; + + @NonNull + @Override + public String toString() { + return "SoftApInfo{" + + "bandwidth= " + mBandwidth + + ",frequency= " + mFrequency + + '}'; + } + + @Override + public boolean equals(@NonNull Object o) { + if (this == o) return true; + if (!(o instanceof SoftApInfo)) return false; + SoftApInfo softApInfo = (SoftApInfo) o; + return mFrequency == softApInfo.mFrequency + && mBandwidth == softApInfo.mBandwidth; + } + + @Override + public int hashCode() { + return Objects.hash(mFrequency, mBandwidth); + } +} diff --git a/wifi/java/android/net/wifi/SynchronousExecutor.java b/wifi/java/android/net/wifi/SynchronousExecutor.java new file mode 100644 index 000000000000..9926b1b5f7dc --- /dev/null +++ b/wifi/java/android/net/wifi/SynchronousExecutor.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 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 java.util.concurrent.Executor; + +/** + * An executor implementation that runs synchronously on the current thread. + * @hide + */ +public class SynchronousExecutor implements Executor { + @Override + public void execute(Runnable r) { + r.run(); + } +} diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java deleted file mode 100644 index 0f7fc2d48b7d..000000000000 --- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (C) 2014 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.os.Parcel; -import android.os.Parcelable; - -import java.util.Arrays; - -/** - * Record of energy and activity information from controller and - * underlying wifi stack state. Timestamp the record with elapsed - * real-time. - * @hide - */ -public final class WifiActivityEnergyInfo implements Parcelable { - /** - * @hide - */ - public long mTimestamp; - - /** - * @hide - */ - public int mStackState; - - /** - * @hide - */ - public long mControllerTxTimeMs; - - /** - * @hide - */ - public long[] mControllerTxTimePerLevelMs; - - /** - * @hide - */ - public long mControllerRxTimeMs; - - /** - * @hide - */ - public long mControllerScanTimeMs; - - /** - * @hide - */ - public long mControllerIdleTimeMs; - - /** - * @hide - */ - public long mControllerEnergyUsed; - - public static final int STACK_STATE_INVALID = 0; - public static final int STACK_STATE_STATE_ACTIVE = 1; - public static final int STACK_STATE_STATE_SCANNING = 2; - public static final int STACK_STATE_STATE_IDLE = 3; - - public WifiActivityEnergyInfo(long timestamp, int stackState, - long txTime, long[] txTimePerLevel, long rxTime, long scanTime, - long idleTime, long energyUsed) { - mTimestamp = timestamp; - mStackState = stackState; - mControllerTxTimeMs = txTime; - mControllerTxTimePerLevelMs = txTimePerLevel; - mControllerRxTimeMs = rxTime; - mControllerScanTimeMs = scanTime; - mControllerIdleTimeMs = idleTime; - mControllerEnergyUsed = energyUsed; - } - - @Override - public String toString() { - return "WifiActivityEnergyInfo{" - + " timestamp=" + mTimestamp - + " mStackState=" + mStackState - + " mControllerTxTimeMs=" + mControllerTxTimeMs - + " mControllerTxTimePerLevelMs=" + Arrays.toString(mControllerTxTimePerLevelMs) - + " mControllerRxTimeMs=" + mControllerRxTimeMs - + " mControllerScanTimeMs=" + mControllerScanTimeMs - + " mControllerIdleTimeMs=" + mControllerIdleTimeMs - + " mControllerEnergyUsed=" + mControllerEnergyUsed - + " }"; - } - - public static final @android.annotation.NonNull Parcelable.Creator<WifiActivityEnergyInfo> CREATOR = - new Parcelable.Creator<WifiActivityEnergyInfo>() { - public WifiActivityEnergyInfo createFromParcel(Parcel in) { - long timestamp = in.readLong(); - int stackState = in.readInt(); - long txTime = in.readLong(); - long[] txTimePerLevel = in.createLongArray(); - long rxTime = in.readLong(); - long scanTime = in.readLong(); - long idleTime = in.readLong(); - long energyUsed = in.readLong(); - return new WifiActivityEnergyInfo(timestamp, stackState, - txTime, txTimePerLevel, rxTime, scanTime, idleTime, energyUsed); - } - public WifiActivityEnergyInfo[] newArray(int size) { - return new WifiActivityEnergyInfo[size]; - } - }; - - public void writeToParcel(Parcel out, int flags) { - out.writeLong(mTimestamp); - out.writeInt(mStackState); - out.writeLong(mControllerTxTimeMs); - out.writeLongArray(mControllerTxTimePerLevelMs); - out.writeLong(mControllerRxTimeMs); - out.writeLong(mControllerScanTimeMs); - out.writeLong(mControllerIdleTimeMs); - out.writeLong(mControllerEnergyUsed); - } - - public int describeContents() { - return 0; - } - - /** - * @return bt stack reported state - */ - public int getStackState() { - return mStackState; - } - - /** - * @return tx time in ms - */ - public long getControllerTxTimeMillis() { - return mControllerTxTimeMs; - } - - /** - * @return tx time at power level provided in ms - */ - public long getControllerTxTimeMillisAtLevel(int level) { - if (level < mControllerTxTimePerLevelMs.length) { - return mControllerTxTimePerLevelMs[level]; - } - return 0; - } - - /** - * @return rx time in ms - */ - public long getControllerRxTimeMillis() { - return mControllerRxTimeMs; - } - - /** - * @return scan time in ms - */ - public long getControllerScanTimeMillis() { - return mControllerScanTimeMs; - } - - /** - * @return idle time in ms - */ - public long getControllerIdleTimeMillis() { - return mControllerIdleTimeMs; - } - - /** - * product of current(mA), voltage(V) and time(ms) - * @return energy used - */ - public long getControllerEnergyUsed() { - return mControllerEnergyUsed; - } - /** - * @return timestamp(wall clock) of record creation - */ - public long getTimeStamp() { - return mTimestamp; - } - - /** - * @return if the record is valid - */ - public boolean isValid() { - return ((mControllerTxTimeMs >=0) && - (mControllerRxTimeMs >=0) && - (mControllerScanTimeMs >=0) && - (mControllerIdleTimeMs >=0)); - } -}
\ No newline at end of file diff --git a/wifi/java/android/net/wifi/WifiAnnotations.java b/wifi/java/android/net/wifi/WifiAnnotations.java new file mode 100644 index 000000000000..acda7e06c95d --- /dev/null +++ b/wifi/java/android/net/wifi/WifiAnnotations.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2019 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.annotation.IntDef; +import android.annotation.StringDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Wifi annotations meant to be statically linked into client modules, since they cannot be + * exposed as @SystemApi. + * + * e.g. {@link IntDef}, {@link StringDef} + * + * @hide + */ +public final class WifiAnnotations { + private WifiAnnotations() {} + + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SCAN_TYPE_"}, value = { + WifiScanner.SCAN_TYPE_LOW_LATENCY, + WifiScanner.SCAN_TYPE_LOW_POWER, + WifiScanner.SCAN_TYPE_HIGH_ACCURACY}) + public @interface ScanType {} + + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"WIFI_BAND_"}, value = { + WifiScanner.WIFI_BAND_UNSPECIFIED, + WifiScanner.WIFI_BAND_24_GHZ, + WifiScanner.WIFI_BAND_5_GHZ, + WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY, + WifiScanner.WIFI_BAND_6_GHZ}) + public @interface WifiBandBasic {} + + @IntDef(prefix = { "CHANNEL_WIDTH_" }, value = { + SoftApInfo.CHANNEL_WIDTH_INVALID, + SoftApInfo.CHANNEL_WIDTH_20MHZ_NOHT, + SoftApInfo.CHANNEL_WIDTH_20MHZ, + SoftApInfo.CHANNEL_WIDTH_40MHZ, + SoftApInfo.CHANNEL_WIDTH_80MHZ, + SoftApInfo.CHANNEL_WIDTH_80MHZ_PLUS_MHZ, + SoftApInfo.CHANNEL_WIDTH_160MHZ, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Bandwidth {} + + @IntDef(prefix = { "CHANNEL_WIDTH_" }, value = { + ScanResult.CHANNEL_WIDTH_20MHZ, + ScanResult.CHANNEL_WIDTH_40MHZ, + ScanResult.CHANNEL_WIDTH_80MHZ, + ScanResult.CHANNEL_WIDTH_160MHZ, + ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ChannelWidth{} + + @IntDef(prefix = { "WIFI_STANDARD_" }, value = { + ScanResult.WIFI_STANDARD_UNKNOWN, + ScanResult.WIFI_STANDARD_LEGACY, + ScanResult.WIFI_STANDARD_11N, + ScanResult.WIFI_STANDARD_11AC, + ScanResult.WIFI_STANDARD_11AX, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface WifiStandard{} + + @IntDef(prefix = { "PROTOCOL_" }, value = { + ScanResult.PROTOCOL_NONE, + ScanResult.PROTOCOL_WPA, + ScanResult.PROTOCOL_RSN, + ScanResult.PROTOCOL_OSEN, + ScanResult.PROTOCOL_WAPI + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Protocol {} + + @IntDef(prefix = { "KEY_MGMT_" }, value = { + ScanResult.KEY_MGMT_NONE, + ScanResult.KEY_MGMT_PSK, + ScanResult.KEY_MGMT_EAP, + ScanResult.KEY_MGMT_FT_PSK, + ScanResult.KEY_MGMT_FT_EAP, + ScanResult.KEY_MGMT_PSK_SHA256, + ScanResult.KEY_MGMT_EAP_SHA256, + ScanResult.KEY_MGMT_OSEN, + ScanResult.KEY_MGMT_SAE, + ScanResult.KEY_MGMT_OWE, + ScanResult.KEY_MGMT_EAP_SUITE_B_192, + ScanResult.KEY_MGMT_FT_SAE, + ScanResult.KEY_MGMT_OWE_TRANSITION, + ScanResult.KEY_MGMT_WAPI_PSK, + ScanResult.KEY_MGMT_WAPI_CERT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface KeyMgmt {} + + @IntDef(prefix = { "CIPHER_" }, value = { + ScanResult.CIPHER_NONE, + ScanResult.CIPHER_NO_GROUP_ADDRESSED, + ScanResult.CIPHER_TKIP, + ScanResult.CIPHER_CCMP, + ScanResult.CIPHER_GCMP_256, + ScanResult.CIPHER_SMS4 + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Cipher {} +} diff --git a/wifi/java/android/net/wifi/WifiClient.java b/wifi/java/android/net/wifi/WifiClient.java index 3e09580802ce..3794566f3d8f 100644 --- a/wifi/java/android/net/wifi/WifiClient.java +++ b/wifi/java/android/net/wifi/WifiClient.java @@ -22,8 +22,6 @@ import android.net.MacAddress; import android.os.Parcel; import android.os.Parcelable; -import com.android.internal.util.Preconditions; - import java.util.Objects; /** @hide */ @@ -46,7 +44,7 @@ public final class WifiClient implements Parcelable { /** @hide */ public WifiClient(@NonNull MacAddress macAddress) { - Preconditions.checkNotNull(macAddress, "mMacAddress must not be null."); + Objects.requireNonNull(macAddress, "mMacAddress must not be null."); this.mMacAddress = macAddress; } diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index e0a4200b7d9e..71f0ab8087ab 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -18,6 +18,7 @@ package android.net.wifi; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.PackageManager; @@ -34,19 +35,18 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; import android.os.UserHandle; +import android.telephony.TelephonyManager; import android.text.TextUtils; -import android.util.BackupUtils; import android.util.Log; -import android.util.TimeUtils; +import android.util.SparseArray; + +import com.android.internal.annotations.VisibleForTesting; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.BitSet; +import java.util.Calendar; import java.util.HashMap; /** @@ -85,7 +85,12 @@ public class WifiConfiguration implements Parcelable { public static final String pmfVarName = "ieee80211w"; /** {@hide} */ public static final String updateIdentiferVarName = "update_identifier"; - /** {@hide} */ + /** + * The network ID for an invalid network. + * + * @hide + */ + @SystemApi public static final int INVALID_NETWORK_ID = -1; /** {@hide} */ public static final int LOCAL_ONLY_NETWORK_ID = -2; @@ -101,20 +106,45 @@ public class WifiConfiguration implements Parcelable { public static class KeyMgmt { private KeyMgmt() { } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + NONE, + WPA_PSK, + WPA_EAP, + IEEE8021X, + WPA2_PSK, + OSEN, + FT_PSK, + FT_EAP, + SAE, + OWE, + SUITE_B_192, + WPA_PSK_SHA256, + WPA_EAP_SHA256, + WAPI_PSK, + WAPI_CERT, + FILS_SHA256, + FILS_SHA384}) + public @interface KeyMgmtScheme {} + /** WPA is not used; plaintext or static WEP could be used. */ public static final int NONE = 0; /** WPA pre-shared key (requires {@code preSharedKey} to be specified). */ public static final int WPA_PSK = 1; /** WPA using EAP authentication. Generally used with an external authentication server. */ public static final int WPA_EAP = 2; - /** IEEE 802.1X using EAP authentication and (optionally) dynamically - * generated WEP keys. */ + /** + * IEEE 802.1X using EAP authentication and (optionally) dynamically + * generated WEP keys. + */ public static final int IEEE8021X = 3; - /** WPA2 pre-shared key for use with soft access point - * (requires {@code preSharedKey} to be specified). - * @hide - */ + /** + * WPA2 pre-shared key for use with soft access point + * (requires {@code preSharedKey} to be specified). + * @hide + */ @SystemApi public static final int WPA2_PSK = 4; /** @@ -162,11 +192,37 @@ public class WifiConfiguration implements Parcelable { */ public static final int WPA_EAP_SHA256 = 12; + /** + * WAPI pre-shared key (requires {@code preSharedKey} to be specified). + * @hide + */ + @SystemApi + public static final int WAPI_PSK = 13; + + /** + * WAPI certificate to be specified. + * @hide + */ + @SystemApi + public static final int WAPI_CERT = 14; + + /** + * IEEE 802.11ai FILS SK with SHA256 + * @hide + */ + public static final int FILS_SHA256 = 15; + /** + * IEEE 802.11ai FILS SK with SHA384: + * @hide + */ + public static final int FILS_SHA384 = 16; + public static final String varName = "key_mgmt"; public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X", "WPA2_PSK", "OSEN", "FT_PSK", "FT_EAP", - "SAE", "OWE", "SUITE_B_192", "WPA_PSK_SHA256", "WPA_EAP_SHA256" }; + "SAE", "OWE", "SUITE_B_192", "WPA_PSK_SHA256", "WPA_EAP_SHA256", + "WAPI_PSK", "WAPI_CERT", "FILS_SHA256", "FILS_SHA384" }; } /** @@ -187,9 +243,14 @@ public class WifiConfiguration implements Parcelable { */ public static final int OSEN = 2; + /** + * WAPI Protocol + */ + public static final int WAPI = 3; + public static final String varName = "proto"; - public static final String[] strings = { "WPA", "RSN", "OSEN" }; + public static final String[] strings = { "WPA", "RSN", "OSEN", "WAPI" }; } /** @@ -208,9 +269,12 @@ public class WifiConfiguration implements Parcelable { /** LEAP/Network EAP (only used with LEAP) */ public static final int LEAP = 2; + /** SAE (Used only for WPA3-Personal) */ + public static final int SAE = 3; + public static final String varName = "auth_alg"; - public static final String[] strings = { "OPEN", "SHARED", "LEAP" }; + public static final String[] strings = { "OPEN", "SHARED", "LEAP", "SAE" }; } /** @@ -232,10 +296,14 @@ public class WifiConfiguration implements Parcelable { * AES in Galois/Counter Mode */ public static final int GCMP_256 = 3; + /** + * SMS4 cipher for WAPI + */ + public static final int SMS4 = 4; public static final String varName = "pairwise"; - public static final String[] strings = { "NONE", "TKIP", "CCMP", "GCMP_256" }; + public static final String[] strings = { "NONE", "TKIP", "CCMP", "GCMP_256", "SMS4" }; } /** @@ -273,12 +341,17 @@ public class WifiConfiguration implements Parcelable { * AES in Galois/Counter Mode */ public static final int GCMP_256 = 5; + /** + * SMS4 cipher for WAPI + */ + public static final int SMS4 = 6; public static final String varName = "group"; public static final String[] strings = { /* deprecated */ "WEP40", /* deprecated */ "WEP104", - "TKIP", "CCMP", "GTK_NOT_USED", "GCMP_256" }; + "TKIP", "CCMP", "GTK_NOT_USED", "GCMP_256", + "SMS4" }; } /** @@ -343,25 +416,29 @@ public class WifiConfiguration implements Parcelable { public static final String[] strings = { "current", "disabled", "enabled" }; } - /** - * Security types we support. - */ - /** @hide */ + /** Security type for an open network. */ public static final int SECURITY_TYPE_OPEN = 0; - /** @hide */ + /** Security type for a WEP network. */ public static final int SECURITY_TYPE_WEP = 1; - /** @hide */ + /** Security type for a PSK network. */ public static final int SECURITY_TYPE_PSK = 2; - /** @hide */ + /** Security type for an EAP network. */ public static final int SECURITY_TYPE_EAP = 3; - /** @hide */ + /** Security type for an SAE network. */ public static final int SECURITY_TYPE_SAE = 4; - /** @hide */ + /** Security type for an EAP Suite B network. */ public static final int SECURITY_TYPE_EAP_SUITE_B = 5; - /** @hide */ + /** Security type for an OWE network. */ public static final int SECURITY_TYPE_OWE = 6; + /** Security type for a WAPI PSK network. */ + public static final int SECURITY_TYPE_WAPI_PSK = 7; + /** Security type for a WAPI Certificate network. */ + public static final int SECURITY_TYPE_WAPI_CERT = 8; - /** @hide */ + /** + * Security types we support. + * @hide + */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "SECURITY_TYPE_" }, value = { SECURITY_TYPE_OPEN, @@ -370,15 +447,26 @@ public class WifiConfiguration implements Parcelable { SECURITY_TYPE_EAP, SECURITY_TYPE_SAE, SECURITY_TYPE_EAP_SUITE_B, - SECURITY_TYPE_OWE + SECURITY_TYPE_OWE, + SECURITY_TYPE_WAPI_PSK, + SECURITY_TYPE_WAPI_CERT }) public @interface SecurityType {} /** - * @hide - * Set security params (sets the various bitsets exposed in WifiConfiguration). + * Set the various security params to correspond to the provided security type. + * This is accomplished by setting the various BitSets exposed in WifiConfiguration. * - * @param securityType One of the security types from {@link SecurityType}. + * @param securityType One of the following security types: + * {@link #SECURITY_TYPE_OPEN}, + * {@link #SECURITY_TYPE_WEP}, + * {@link #SECURITY_TYPE_PSK}, + * {@link #SECURITY_TYPE_EAP}, + * {@link #SECURITY_TYPE_SAE}, + * {@link #SECURITY_TYPE_EAP_SUITE_B}, + * {@link #SECURITY_TYPE_OWE}, + * {@link #SECURITY_TYPE_WAPI_PSK}, or + * {@link #SECURITY_TYPE_WAPI_CERT} */ public void setSecurityParams(@SecurityType int securityType) { // Clear all the bitsets. @@ -407,20 +495,46 @@ public class WifiConfiguration implements Parcelable { allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); break; case SECURITY_TYPE_SAE: + allowedProtocols.set(WifiConfiguration.Protocol.RSN); allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE); - requirePMF = true; + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256); + allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); + requirePmf = true; break; case SECURITY_TYPE_EAP_SUITE_B: + allowedProtocols.set(WifiConfiguration.Protocol.RSN); + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192); + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256); allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); allowedGroupManagementCiphers.set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256); // Note: allowedSuiteBCiphers bitset will be set by the service once the // certificates are attached to this profile - requirePMF = true; + requirePmf = true; break; case SECURITY_TYPE_OWE: + allowedProtocols.set(WifiConfiguration.Protocol.RSN); allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE); - requirePMF = true; + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256); + allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); + requirePmf = true; + break; + case SECURITY_TYPE_WAPI_PSK: + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WAPI_PSK); + allowedProtocols.set(WifiConfiguration.Protocol.WAPI); + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.SMS4); + allowedGroupCiphers.set(WifiConfiguration.GroupCipher.SMS4); + break; + case SECURITY_TYPE_WAPI_CERT: + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WAPI_CERT); + allowedProtocols.set(WifiConfiguration.Protocol.WAPI); + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.SMS4); + allowedGroupCiphers.set(WifiConfiguration.GroupCipher.SMS4); break; default: throw new IllegalArgumentException("unknown security type " + securityType); @@ -461,6 +575,14 @@ public class WifiConfiguration implements Parcelable { */ public String BSSID; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"AP_BAND_"}, value = { + AP_BAND_2GHZ, + AP_BAND_5GHZ, + AP_BAND_ANY}) + public @interface ApBand {} + /** * 2GHz band. * @hide @@ -481,12 +603,14 @@ public class WifiConfiguration implements Parcelable { public static final int AP_BAND_ANY = -1; /** - * The band which AP resides on - * -1:Any 0:2G 1:5G - * By default, 2G is chosen + * The band which the AP resides on. + * One of {@link #AP_BAND_2GHZ}, {@link #AP_BAND_5GHZ}, or {@link #AP_BAND_ANY}. + * By default, {@link #AP_BAND_2GHZ} is chosen. + * * @hide */ @UnsupportedAppUsage + @ApBand public int apBand = AP_BAND_2GHZ; /** @@ -546,10 +670,11 @@ public class WifiConfiguration implements Parcelable { public boolean hiddenSSID; /** - * This is a network that requries Protected Management Frames (PMF). + * True if the network requires Protected Management Frames (PMF), false otherwise. * @hide */ - public boolean requirePMF; + @SystemApi + public boolean requirePmf; /** * Update identifier, for Passpoint network. @@ -600,8 +725,8 @@ public class WifiConfiguration implements Parcelable { public BitSet allowedGroupManagementCiphers; /** * The set of SuiteB ciphers supported by this configuration. - * To be used for WPA3-Enterprise mode. - * See {@link SuiteBCipher} for descriptions of the values. + * To be used for WPA3-Enterprise mode. Set automatically by the framework based on the + * certificate type that is used in this configuration. */ @NonNull public BitSet allowedSuiteBCiphers; @@ -636,11 +761,12 @@ public class WifiConfiguration implements Parcelable { public long[] roamingConsortiumIds; /** + * True if this network configuration is visible to and usable by other users on the + * same device, false otherwise. + * * @hide - * This network configuration is visible to and usable by other users on the - * same device. */ - @UnsupportedAppUsage + @SystemApi public boolean shared; /** @@ -724,47 +850,25 @@ public class WifiConfiguration implements Parcelable { public String lastUpdateName; /** + * The carrier ID identifies the operator who provides this network configuration. + * see {@link TelephonyManager#getSimCarrierId()} * @hide - * Status of user approval for connection */ - public int userApproved = USER_UNSPECIFIED; + @SystemApi + public int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID; - /** The Below RSSI thresholds are used to configure AutoJoin - * - GOOD/LOW/BAD thresholds are used so as to calculate link score - * - UNWANTED_SOFT are used by the blacklisting logic so as to handle - * the unwanted network message coming from CS - * - UNBLACKLIST thresholds are used so as to tweak the speed at which - * the network is unblacklisted (i.e. if - * it is seen with good RSSI, it is blacklisted faster) - * - INITIAL_AUTOJOIN_ATTEMPT, used to determine how close from - * the network we need to be before autojoin kicks in + /** + * @hide + * Auto-join is allowed by user for this network. + * Default true. */ + @SystemApi + public boolean allowAutojoin = true; + /** @hide **/ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static int INVALID_RSSI = -127; - // States for the userApproved field - /** - * @hide - * User hasn't specified if connection is okay - */ - public static final int USER_UNSPECIFIED = 0; - /** - * @hide - * User has approved this for connection - */ - public static final int USER_APPROVED = 1; - /** - * @hide - * User has banned this from connection - */ - public static final int USER_BANNED = 2; - /** - * @hide - * Waiting for user input - */ - public static final int USER_PENDING = 3; - /** * @hide * Number of reports indicating no Internet Access @@ -774,18 +878,6 @@ public class WifiConfiguration implements Parcelable { /** * @hide - * For debug: date at which the config was last updated - */ - public String updateTime; - - /** - * @hide - * For debug: date at which the config was last updated - */ - public String creationTime; - - /** - * @hide * The WiFi configuration is considered to have no internet access for purpose of autojoining * if there has been a report of it having no internet access, and, it never have had * internet access in the past. @@ -838,21 +930,13 @@ public class WifiConfiguration implements Parcelable { * This boolean is cleared if we get a connect/save/ update or * any wifiManager command that indicate the user interacted with the configuration * since we will now consider that the configuration belong to him. + * @deprecated only kept for @UnsupportedAppUsage * @hide */ @UnsupportedAppUsage public boolean selfAdded; /** - * Set if the configuration was self added by the framework - * This boolean is set once and never cleared. It is used - * so as we never loose track of who created the - * configuration in the first place. - * @hide - */ - public boolean didSelfAdd; - - /** * Peer WifiConfiguration this WifiConfiguration was added for * @hide */ @@ -876,51 +960,86 @@ public class WifiConfiguration implements Parcelable { } /** - * Indicate whther the network is trusted or not. Networks are considered trusted + * Indicate whether the network is trusted or not. Networks are considered trusted * if the user explicitly allowed this network connection. + * This bit can be used by suggestion network, see + * {@link WifiNetworkSuggestion.Builder#setUntrusted(boolean)} * @hide */ public boolean trusted; /** - * This Wifi configuration is created from a {@link WifiNetworkSuggestion} + * True if this Wifi configuration is created from a {@link WifiNetworkSuggestion}, + * false otherwise. + * * @hide */ + @SystemApi public boolean fromWifiNetworkSuggestion; /** - * This Wifi configuration is created from a {@link WifiNetworkSpecifier} + * True if this Wifi configuration is created from a {@link WifiNetworkSpecifier}, + * false otherwise. + * * @hide */ + @SystemApi public boolean fromWifiNetworkSpecifier; /** - * Indicates if the creator of this configuration has expressed that it - * should be considered metered. + * True if the creator of this configuration has expressed that it + * should be considered metered, false otherwise. * * @see #isMetered(WifiConfiguration, WifiInfo) + * * @hide */ @SystemApi public boolean meteredHint; - /** {@hide} */ + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"METERED_OVERRIDE_"}, value = { + METERED_OVERRIDE_NONE, + METERED_OVERRIDE_METERED, + METERED_OVERRIDE_NOT_METERED}) + public @interface MeteredOverride {} + + /** + * No metered override. + * @hide + */ + @SystemApi public static final int METERED_OVERRIDE_NONE = 0; - /** {@hide} */ + /** + * Override network to be metered. + * @hide + */ + @SystemApi public static final int METERED_OVERRIDE_METERED = 1; - /** {@hide} */ + /** + * Override network to be unmetered. + * @hide + */ + @SystemApi public static final int METERED_OVERRIDE_NOT_METERED = 2; /** * Indicates if the end user has expressed an explicit opinion about the * meteredness of this network, such as through the Settings app. + * This value is one of {@link #METERED_OVERRIDE_NONE}, {@link #METERED_OVERRIDE_METERED}, + * or {@link #METERED_OVERRIDE_NOT_METERED}. * <p> * This should always override any values from {@link #meteredHint} or * {@link WifiInfo#getMeteredHint()}. * + * By default this field is set to {@link #METERED_OVERRIDE_NONE}. + * * @see #isMetered(WifiConfiguration, WifiInfo) * @hide */ + @SystemApi + @MeteredOverride public int meteredOverride = METERED_OVERRIDE_NONE; /** @@ -929,7 +1048,8 @@ public class WifiConfiguration implements Parcelable { * * @hide */ - public static boolean isMetered(WifiConfiguration config, WifiInfo info) { + @SystemApi + public static boolean isMetered(@Nullable WifiConfiguration config, @Nullable WifiInfo info) { boolean metered = false; if (info != null && info.getMeteredHint()) { metered = true; @@ -1006,21 +1126,34 @@ public class WifiConfiguration implements Parcelable { @SystemApi public int numAssociation; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"RANDOMIZATION_"}, value = { + RANDOMIZATION_NONE, + RANDOMIZATION_PERSISTENT}) + public @interface MacRandomizationSetting {} + /** - * @hide * Use factory MAC when connecting to this network + * @hide */ + @SystemApi public static final int RANDOMIZATION_NONE = 0; /** - * @hide * Generate a randomized MAC once and reuse it for all connections to this network + * @hide */ + @SystemApi public static final int RANDOMIZATION_PERSISTENT = 1; /** + * Level of MAC randomization for this network. + * One of {@link #RANDOMIZATION_NONE} or {@link #RANDOMIZATION_PERSISTENT}. + * By default this field is set to {@link #RANDOMIZATION_PERSISTENT}. * @hide - * Level of MAC randomization for this network */ + @SystemApi + @MacRandomizationSetting public int macRandomizationSetting = RANDOMIZATION_PERSISTENT; /** @@ -1032,6 +1165,13 @@ public class WifiConfiguration implements Parcelable { /** * @hide + * The wall clock time of when |mRandomizedMacAddress| should be re-randomized in aggressive + * randomization mode. + */ + public long randomizedMacExpirationTimeMs = 0; + + /** + * @hide * Checks if the given MAC address can be used for Connected Mac Randomization * by verifying that it is non-null, unicast, locally assigned, and not default mac. * @param mac MacAddress to check @@ -1043,26 +1183,6 @@ public class WifiConfiguration implements Parcelable { } /** - * @hide - * Returns Randomized MAC address to use with the network. - * If it is not set/valid, creates a new randomized address. - * If it can't generate a valid mac, returns the default MAC. - */ - public @NonNull MacAddress getOrCreateRandomizedMacAddress() { - int randomMacGenerationCount = 0; - while (!isValidMacAddressForRandomization(mRandomizedMacAddress) - && randomMacGenerationCount < MAXIMUM_RANDOM_MAC_GENERATION_RETRY) { - mRandomizedMacAddress = MacAddressUtils.createRandomUnicastAddress(); - randomMacGenerationCount++; - } - - if (!isValidMacAddressForRandomization(mRandomizedMacAddress)) { - mRandomizedMacAddress = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS); - } - return mRandomizedMacAddress; - } - - /** * Returns MAC address set to be the local randomized MAC address. * Depending on user preference, the device may or may not use the returned MAC address for * connections to this network. @@ -1097,145 +1217,279 @@ public class WifiConfiguration implements Parcelable { public static final int HOME_NETWORK_RSSI_BOOST = 5; /** + * This class is used to contain all the information and API used for quality network selection. * @hide - * This class is used to contain all the information and API used for quality network selection */ + @SystemApi public static class NetworkSelectionStatus { - /** - * Quality Network Selection Status enable, temporary disabled, permanently disabled - */ - /** - * This network is allowed to join Quality Network Selection + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "NETWORK_SELECTION_", + value = { + NETWORK_SELECTION_ENABLED, + NETWORK_SELECTION_TEMPORARY_DISABLED, + NETWORK_SELECTION_PERMANENTLY_DISABLED}) + public @interface NetworkEnabledStatus {} + /** + * This network will be considered as a potential candidate to connect to during network + * selection. */ public static final int NETWORK_SELECTION_ENABLED = 0; /** - * network was temporary disabled. Can be re-enabled after a time period expire + * This network was temporary disabled. May be re-enabled after a time out. */ - public static final int NETWORK_SELECTION_TEMPORARY_DISABLED = 1; + public static final int NETWORK_SELECTION_TEMPORARY_DISABLED = 1; /** - * network was permanently disabled. + * This network was permanently disabled. */ - public static final int NETWORK_SELECTION_PERMANENTLY_DISABLED = 2; + public static final int NETWORK_SELECTION_PERMANENTLY_DISABLED = 2; /** * Maximum Network selection status + * @hide */ public static final int NETWORK_SELECTION_STATUS_MAX = 3; /** * Quality network selection status String (for debug purpose). Use Quality network * selection status value as index to extec the corresponding debug string + * @hide */ public static final String[] QUALITY_NETWORK_SELECTION_STATUS = { "NETWORK_SELECTION_ENABLED", "NETWORK_SELECTION_TEMPORARY_DISABLED", "NETWORK_SELECTION_PERMANENTLY_DISABLED"}; - //Quality Network disabled reasons - /** - * Default value. Means not disabled - */ - public static final int NETWORK_SELECTION_ENABLE = 0; - /** - * The starting index for network selection disabled reasons + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "DISABLED_", value = { + DISABLED_NONE, + DISABLED_ASSOCIATION_REJECTION, + DISABLED_AUTHENTICATION_FAILURE, + DISABLED_DHCP_FAILURE, + DISABLED_NO_INTERNET_TEMPORARY, + DISABLED_AUTHENTICATION_NO_CREDENTIALS, + DISABLED_NO_INTERNET_PERMANENT, + DISABLED_BY_WIFI_MANAGER, + DISABLED_BY_WRONG_PASSWORD, + DISABLED_AUTHENTICATION_NO_SUBSCRIPTION}) + public @interface NetworkSelectionDisableReason {} + + // Quality Network disabled reasons + /** Default value. Means not disabled. */ + public static final int DISABLED_NONE = 0; + /** + * The starting index for network selection disabled reasons. + * @hide */ public static final int NETWORK_SELECTION_DISABLED_STARTING_INDEX = 1; /** - * @deprecated it is not used any more. - * This network is disabled because higher layer (>2) network is bad - */ - public static final int DISABLED_BAD_LINK = 1; - /** - * This network is disabled because multiple association rejects - */ - public static final int DISABLED_ASSOCIATION_REJECTION = 2; - /** - * This network is disabled because multiple authentication failure - */ - public static final int DISABLED_AUTHENTICATION_FAILURE = 3; - /** - * This network is disabled because multiple DHCP failure - */ - public static final int DISABLED_DHCP_FAILURE = 4; - /** - * This network is disabled because of security network but no credentials - */ - public static final int DISABLED_DNS_FAILURE = 5; - /** - * This network is temporarily disabled because it has no Internet access. - */ - public static final int DISABLED_NO_INTERNET_TEMPORARY = 6; - /** - * This network is disabled because we started WPS - */ - public static final int DISABLED_WPS_START = 7; - /** - * This network is disabled because EAP-TLS failure - */ - public static final int DISABLED_TLS_VERSION_MISMATCH = 8; - // Values above are for temporary disablement; values below are for permanent disablement. - /** - * This network is disabled due to absence of user credentials - */ - public static final int DISABLED_AUTHENTICATION_NO_CREDENTIALS = 9; - /** - * This network is permanently disabled because it has no Internet access and user does not - * want to stay connected. + * The starting index for network selection temporarily disabled reasons. + * @hide */ - public static final int DISABLED_NO_INTERNET_PERMANENT = 10; - /** - * This network is disabled due to WifiManager disable it explicitly + public static final int TEMPORARILY_DISABLED_STARTING_INDEX = 1; + /** This network is disabled because of multiple association rejections. */ + public static final int DISABLED_ASSOCIATION_REJECTION = 1; + /** This network is disabled because of multiple authentication failure. */ + public static final int DISABLED_AUTHENTICATION_FAILURE = 2; + /** This network is disabled because of multiple DHCP failure. */ + public static final int DISABLED_DHCP_FAILURE = 3; + /** This network is temporarily disabled because it has no Internet access. */ + public static final int DISABLED_NO_INTERNET_TEMPORARY = 4; + /** + * The starting index for network selection permanently disabled reasons. + * @hide */ - public static final int DISABLED_BY_WIFI_MANAGER = 11; + public static final int PERMANENTLY_DISABLED_STARTING_INDEX = 5; + /** This network is disabled due to absence of user credentials */ + public static final int DISABLED_AUTHENTICATION_NO_CREDENTIALS = 5; /** - * This network is disabled due to user switching + * This network is permanently disabled because it has no Internet access and the user does + * not want to stay connected. */ - public static final int DISABLED_DUE_TO_USER_SWITCH = 12; + public static final int DISABLED_NO_INTERNET_PERMANENT = 6; + /** This network is disabled due to WifiManager disabling it explicitly. */ + public static final int DISABLED_BY_WIFI_MANAGER = 7; + /** This network is disabled due to wrong password. */ + public static final int DISABLED_BY_WRONG_PASSWORD = 8; + /** This network is disabled because service is not subscribed. */ + public static final int DISABLED_AUTHENTICATION_NO_SUBSCRIPTION = 9; /** - * This network is disabled due to wrong password + * All other disable reasons should be strictly less than this value. + * @hide */ - public static final int DISABLED_BY_WRONG_PASSWORD = 13; + public static final int NETWORK_SELECTION_DISABLED_MAX = 10; + /** - * This network is disabled because service is not subscribed + * Get an integer that is equal to the maximum integer value of all the + * DISABLED_* reasons + * e.g. {@link #DISABLED_NONE}, {@link #DISABLED_ASSOCIATION_REJECTION}, etc. + * + * All DISABLED_* constants will be contiguous in the range + * 0, 1, 2, 3, ..., getMaxNetworkSelectionDisableReasons() + * + * <br /> + * For example, this can be used to iterate through all the network selection + * disable reasons like so: + * <pre>{@code + * for (int reason = 0; reason <= getMaxNetworkSelectionDisableReasons(); reason++) { + * ... + * } + * }</pre> */ - public static final int DISABLED_AUTHENTICATION_NO_SUBSCRIPTION = 14; + public static int getMaxNetworkSelectionDisableReason() { + return NETWORK_SELECTION_DISABLED_MAX - 1; + } + /** - * This Maximum disable reason value + * Contains info about disable reasons. + * @hide */ - public static final int NETWORK_SELECTION_DISABLED_MAX = 15; + public static final class DisableReasonInfo { + /** + * String representation for the disable reason. + * Note that these strings are persisted in + * {@link + * com.android.server.wifi.util.XmlUtil.NetworkSelectionStatusXmlUtil#writeToXml}, + * so do not change the string values to maintain backwards compatibility. + */ + public final String mReasonStr; + /** + * Network Selection disable reason threshold, used to debounce network failures before + * we disable them. + */ + public final int mDisableThreshold; + /** + * Network Selection disable timeout for the error. After the timeout milliseconds, + * enable the network again. + */ + public final int mDisableTimeoutMillis; + + /** + * Constructor + * @param reasonStr string representation of the error + * @param disableThreshold number of failures before we disable the network + * @param disableTimeoutMillis the timeout, in milliseconds, before we re-enable the + * network after disabling it + */ + public DisableReasonInfo(String reasonStr, int disableThreshold, + int disableTimeoutMillis) { + mReasonStr = reasonStr; + mDisableThreshold = disableThreshold; + mDisableTimeoutMillis = disableTimeoutMillis; + } + } /** - * Quality network selection disable reason String (for debug purpose) + * Quality network selection disable reason infos. + * @hide */ - public static final String[] QUALITY_NETWORK_SELECTION_DISABLE_REASON = { - "NETWORK_SELECTION_ENABLE", - "NETWORK_SELECTION_DISABLED_BAD_LINK", // deprecated - "NETWORK_SELECTION_DISABLED_ASSOCIATION_REJECTION ", - "NETWORK_SELECTION_DISABLED_AUTHENTICATION_FAILURE", - "NETWORK_SELECTION_DISABLED_DHCP_FAILURE", - "NETWORK_SELECTION_DISABLED_DNS_FAILURE", - "NETWORK_SELECTION_DISABLED_NO_INTERNET_TEMPORARY", - "NETWORK_SELECTION_DISABLED_WPS_START", - "NETWORK_SELECTION_DISABLED_TLS_VERSION", - "NETWORK_SELECTION_DISABLED_AUTHENTICATION_NO_CREDENTIALS", - "NETWORK_SELECTION_DISABLED_NO_INTERNET_PERMANENT", - "NETWORK_SELECTION_DISABLED_BY_WIFI_MANAGER", - "NETWORK_SELECTION_DISABLED_BY_USER_SWITCH", - "NETWORK_SELECTION_DISABLED_BY_WRONG_PASSWORD", - "NETWORK_SELECTION_DISABLED_AUTHENTICATION_NO_SUBSCRIPTION" - }; + public static final SparseArray<DisableReasonInfo> DISABLE_REASON_INFOS = + buildDisableReasonInfos(); + + private static SparseArray<DisableReasonInfo> buildDisableReasonInfos() { + SparseArray<DisableReasonInfo> reasons = new SparseArray<>(); + + reasons.append(DISABLED_NONE, + new DisableReasonInfo( + // Note that these strings are persisted in + // XmlUtil.NetworkSelectionStatusXmlUtil#writeToXml, + // so do not change the string values to maintain backwards + // compatibility. + "NETWORK_SELECTION_ENABLE", + -1, + Integer.MAX_VALUE)); + + reasons.append(DISABLED_ASSOCIATION_REJECTION, + new DisableReasonInfo( + // Note that there is a space at the end of this string. Cannot fix + // since this string is persisted. + "NETWORK_SELECTION_DISABLED_ASSOCIATION_REJECTION ", + 5, + 5 * 60 * 1000)); + + reasons.append(DISABLED_AUTHENTICATION_FAILURE, + new DisableReasonInfo( + "NETWORK_SELECTION_DISABLED_AUTHENTICATION_FAILURE", + 5, + 5 * 60 * 1000)); + + reasons.append(DISABLED_DHCP_FAILURE, + new DisableReasonInfo( + "NETWORK_SELECTION_DISABLED_DHCP_FAILURE", + 5, + 5 * 60 * 1000)); + + reasons.append(DISABLED_NO_INTERNET_TEMPORARY, + new DisableReasonInfo( + "NETWORK_SELECTION_DISABLED_NO_INTERNET_TEMPORARY", + 1, + 10 * 60 * 1000)); + + reasons.append(DISABLED_AUTHENTICATION_NO_CREDENTIALS, + new DisableReasonInfo( + "NETWORK_SELECTION_DISABLED_AUTHENTICATION_NO_CREDENTIALS", + 1, + Integer.MAX_VALUE)); + + reasons.append(DISABLED_NO_INTERNET_PERMANENT, + new DisableReasonInfo( + "NETWORK_SELECTION_DISABLED_NO_INTERNET_PERMANENT", + 1, + Integer.MAX_VALUE)); + + reasons.append(DISABLED_BY_WIFI_MANAGER, + new DisableReasonInfo( + "NETWORK_SELECTION_DISABLED_BY_WIFI_MANAGER", + 1, + Integer.MAX_VALUE)); + + reasons.append(DISABLED_BY_WRONG_PASSWORD, + new DisableReasonInfo( + "NETWORK_SELECTION_DISABLED_BY_WRONG_PASSWORD", + 1, + Integer.MAX_VALUE)); + + reasons.append(DISABLED_AUTHENTICATION_NO_SUBSCRIPTION, + new DisableReasonInfo( + "NETWORK_SELECTION_DISABLED_AUTHENTICATION_NO_SUBSCRIPTION", + 1, + Integer.MAX_VALUE)); + + return reasons; + } + + /** + * Get the {@link NetworkSelectionDisableReason} int code by its string value. + * @return the NetworkSelectionDisableReason int code corresponding to the reason string, + * or -1 if the reason string is unrecognized. + * @hide + */ + @NetworkSelectionDisableReason + public static int getDisableReasonByString(@NonNull String reasonString) { + for (int i = 0; i < DISABLE_REASON_INFOS.size(); i++) { + int key = DISABLE_REASON_INFOS.keyAt(i); + DisableReasonInfo value = DISABLE_REASON_INFOS.valueAt(i); + if (value != null && TextUtils.equals(reasonString, value.mReasonStr)) { + return key; + } + } + Log.e(TAG, "Unrecognized network disable reason: " + reasonString); + return -1; + } /** * Invalid time stamp for network selection disable + * @hide */ public static final long INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP = -1L; /** - * This constant indicates the current configuration has connect choice set + * This constant indicates the current configuration has connect choice set */ private static final int CONNECT_CHOICE_EXISTS = 1; /** - * This constant indicates the current configuration does not have connect choice set + * This constant indicates the current configuration does not have connect choice set */ private static final int CONNECT_CHOICE_NOT_EXISTS = -1; @@ -1244,11 +1498,13 @@ public class WifiConfiguration implements Parcelable { * Network selection status, should be in one of three status: enable, temporaily disabled * or permanently disabled */ + @NetworkEnabledStatus private int mStatus; /** * Reason for disable this network */ + @NetworkSelectionDisableReason private int mNetworkSelectionDisableReason; /** @@ -1273,12 +1529,6 @@ public class WifiConfiguration implements Parcelable { private String mConnectChoice; /** - * The system timestamp when we records the connectChoice. This value is obtained from - * System.currentTimeMillis - */ - private long mConnectChoiceTimestamp = INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP; - - /** * Used to cache the temporary candidate during the network selection procedure. It will be * kept updating once a new scan result has a higher score than current one */ @@ -1309,30 +1559,9 @@ public class WifiConfiguration implements Parcelable { private boolean mHasEverConnected; /** - * Boolean indicating whether {@link com.android.server.wifi.RecommendedNetworkEvaluator} - * chose not to connect to this network in the last qualified network selection process. - */ - private boolean mNotRecommended; - - /** - * Set whether {@link com.android.server.wifi.RecommendedNetworkEvaluator} does not - * recommend connecting to this network. - */ - public void setNotRecommended(boolean notRecommended) { - mNotRecommended = notRecommended; - } - - /** - * Returns whether {@link com.android.server.wifi.RecommendedNetworkEvaluator} does not - * recommend connecting to this network. - */ - public boolean isNotRecommended() { - return mNotRecommended; - } - - /** * set whether this network is visible in latest Qualified Network Selection * @param seen value set to candidate + * @hide */ public void setSeenInLastQualifiedNetworkSelection(boolean seen) { mSeenInLastQualifiedNetworkSelection = seen; @@ -1342,6 +1571,7 @@ public class WifiConfiguration implements Parcelable { * get whether this network is visible in latest Qualified Network Selection * @return returns true -- network is visible in latest Qualified Network Selection * false -- network is invisible in latest Qualified Network Selection + * @hide */ public boolean getSeenInLastQualifiedNetworkSelection() { return mSeenInLastQualifiedNetworkSelection; @@ -1349,6 +1579,7 @@ public class WifiConfiguration implements Parcelable { /** * set the temporary candidate of current network selection procedure * @param scanCandidate {@link ScanResult} the candidate set to mCandidate + * @hide */ public void setCandidate(ScanResult scanCandidate) { mCandidate = scanCandidate; @@ -1358,6 +1589,7 @@ public class WifiConfiguration implements Parcelable { * get the temporary candidate of current network selection procedure * @return returns {@link ScanResult} temporary candidate of current network selection * procedure + * @hide */ public ScanResult getCandidate() { return mCandidate; @@ -1366,6 +1598,7 @@ public class WifiConfiguration implements Parcelable { /** * set the score of the temporary candidate of current network selection procedure * @param score value set to mCandidateScore + * @hide */ public void setCandidateScore(int score) { mCandidateScore = score; @@ -1374,6 +1607,7 @@ public class WifiConfiguration implements Parcelable { /** * get the score of the temporary candidate of current network selection procedure * @return returns score of the temporary candidate of current network selection procedure + * @hide */ public int getCandidateScore() { return mCandidateScore; @@ -1381,7 +1615,8 @@ public class WifiConfiguration implements Parcelable { /** * get user preferred choice over this configuration - *@return returns configKey of user preferred choice over this configuration + * @return returns configKey of user preferred choice over this configuration + * @hide */ public String getConnectChoice() { return mConnectChoice; @@ -1390,77 +1625,126 @@ public class WifiConfiguration implements Parcelable { /** * set user preferred choice over this configuration * @param newConnectChoice, the configKey of user preferred choice over this configuration + * @hide */ public void setConnectChoice(String newConnectChoice) { mConnectChoice = newConnectChoice; } - /** - * get the timeStamp when user select a choice over this configuration - * @return returns when current connectChoice is set (time from System.currentTimeMillis) - */ - public long getConnectChoiceTimestamp() { - return mConnectChoiceTimestamp; - } - - /** - * set the timeStamp when user select a choice over this configuration - * @param timeStamp, the timestamp set to connectChoiceTimestamp, expected timestamp should - * be obtained from System.currentTimeMillis - */ - public void setConnectChoiceTimestamp(long timeStamp) { - mConnectChoiceTimestamp = timeStamp; - } - - /** - * get current Quality network selection status - * @return returns current Quality network selection status in String (for debug purpose) - */ + /** Get the current Quality network selection status as a String (for debugging). */ + @NonNull public String getNetworkStatusString() { return QUALITY_NETWORK_SELECTION_STATUS[mStatus]; } + /** @hide */ public void setHasEverConnected(boolean value) { mHasEverConnected = value; } - public boolean getHasEverConnected() { + /** True if the device has ever connected to this network, false otherwise. */ + public boolean hasEverConnected() { return mHasEverConnected; } + /** @hide */ public NetworkSelectionStatus() { // previously stored configs will not have this parameter, so we default to false. mHasEverConnected = false; - }; + } + + /** + * NetworkSelectionStatus exports an immutable public API. + * However, test code has a need to construct a NetworkSelectionStatus in a specific state. + * (Note that mocking using Mockito does not work if the object needs to be parceled and + * unparceled.) + * Export a @SystemApi Builder to allow tests to construct a NetworkSelectionStatus object + * in the desired state, without sacrificing NetworkSelectionStatus's immutability. + */ + @VisibleForTesting + public static final class Builder { + private final NetworkSelectionStatus mNetworkSelectionStatus = + new NetworkSelectionStatus(); + + /** + * Set the current network selection status. + * One of: + * {@link #NETWORK_SELECTION_ENABLED}, + * {@link #NETWORK_SELECTION_TEMPORARY_DISABLED}, + * {@link #NETWORK_SELECTION_PERMANENTLY_DISABLED} + * @see NetworkSelectionStatus#getNetworkSelectionStatus() + */ + @NonNull + public Builder setNetworkSelectionStatus(@NetworkEnabledStatus int status) { + mNetworkSelectionStatus.setNetworkSelectionStatus(status); + return this; + } + + /** + * + * Set the current network's disable reason. + * One of the {@link #DISABLED_NONE} or DISABLED_* constants. + * e.g. {@link #DISABLED_ASSOCIATION_REJECTION}. + * @see NetworkSelectionStatus#getNetworkSelectionDisableReason() + */ + @NonNull + public Builder setNetworkSelectionDisableReason( + @NetworkSelectionDisableReason int reason) { + mNetworkSelectionStatus.setNetworkSelectionDisableReason(reason); + return this; + } + + /** + * Build a NetworkSelectionStatus object. + */ + @NonNull + public NetworkSelectionStatus build() { + NetworkSelectionStatus status = new NetworkSelectionStatus(); + status.copy(mNetworkSelectionStatus); + return status; + } + } /** - * @param reason specific error reason - * @return corresponding network disable reason String (for debug purpose) + * Get the network disable reason string for a reason code (for debugging). + * @param reason specific error reason. One of the {@link #DISABLED_NONE} or + * DISABLED_* constants e.g. {@link #DISABLED_ASSOCIATION_REJECTION}. + * @return network disable reason string, or null if the reason is invalid. */ - public static String getNetworkDisableReasonString(int reason) { - if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) { - return QUALITY_NETWORK_SELECTION_DISABLE_REASON[reason]; - } else { + @Nullable + public static String getNetworkSelectionDisableReasonString( + @NetworkSelectionDisableReason int reason) { + DisableReasonInfo info = DISABLE_REASON_INFOS.get(reason); + if (info == null) { return null; + } else { + return info.mReasonStr; } } /** * get current network disable reason * @return current network disable reason in String (for debug purpose) + * @hide */ - public String getNetworkDisableReasonString() { - return QUALITY_NETWORK_SELECTION_DISABLE_REASON[mNetworkSelectionDisableReason]; + public String getNetworkSelectionDisableReasonString() { + return getNetworkSelectionDisableReasonString(mNetworkSelectionDisableReason); } /** - * get current network network selection status - * @return return current network network selection status + * Get the current network network selection status. + * One of: + * {@link #NETWORK_SELECTION_ENABLED}, + * {@link #NETWORK_SELECTION_TEMPORARY_DISABLED}, + * {@link #NETWORK_SELECTION_PERMANENTLY_DISABLED} */ + @NetworkEnabledStatus public int getNetworkSelectionStatus() { return mStatus; } + /** - * @return whether current network is enabled to join network selection + * True if the current network is enabled to join network selection, false otherwise. + * @hide */ public boolean isNetworkEnabled() { return mStatus == NETWORK_SELECTION_ENABLED; @@ -1468,21 +1752,24 @@ public class WifiConfiguration implements Parcelable { /** * @return whether current network is temporary disabled + * @hide */ public boolean isNetworkTemporaryDisabled() { return mStatus == NETWORK_SELECTION_TEMPORARY_DISABLED; } /** - * @return returns whether current network is permanently disabled + * True if the current network is permanently disabled, false otherwise. + * @hide */ public boolean isNetworkPermanentlyDisabled() { return mStatus == NETWORK_SELECTION_PERMANENTLY_DISABLED; } /** - * set current networ work selection status + * set current network selection status * @param status network selection status to set + * @hide */ public void setNetworkSelectionStatus(int status) { if (status >= 0 && status < NETWORK_SELECTION_STATUS_MAX) { @@ -1491,17 +1778,21 @@ public class WifiConfiguration implements Parcelable { } /** - * @return returns current network's disable reason + * Returns the current network's disable reason. + * One of the {@link #DISABLED_NONE} or DISABLED_* constants + * e.g. {@link #DISABLED_ASSOCIATION_REJECTION}. */ + @NetworkSelectionDisableReason public int getNetworkSelectionDisableReason() { return mNetworkSelectionDisableReason; } /** * set Network disable reason - * @param reason Network disable reason + * @param reason Network disable reason + * @hide */ - public void setNetworkSelectionDisableReason(int reason) { + public void setNetworkSelectionDisableReason(@NetworkSelectionDisableReason int reason) { if (reason >= 0 && reason < NETWORK_SELECTION_DISABLED_MAX) { mNetworkSelectionDisableReason = reason; } else { @@ -1510,39 +1801,31 @@ public class WifiConfiguration implements Parcelable { } /** - * check whether network is disabled by this reason - * @param reason a specific disable reason - * @return true -- network is disabled for this reason - * false -- network is not disabled for this reason - */ - public boolean isDisabledByReason(int reason) { - return mNetworkSelectionDisableReason == reason; - } - - /** * @param timeStamp Set when current network is disabled in millisecond since January 1, * 1970 00:00:00.0 UTC + * @hide */ public void setDisableTime(long timeStamp) { mTemporarilyDisabledTimestamp = timeStamp; } /** - * @return returns when current network is disabled in millisecond since January 1, - * 1970 00:00:00.0 UTC + * Returns when the current network was disabled, in milliseconds since January 1, + * 1970 00:00:00.0 UTC. */ public long getDisableTime() { return mTemporarilyDisabledTimestamp; } /** - * get the disable counter of a specific reason - * @param reason specific failure reason - * @exception throw IllegalArgumentException for illegal input + * Get the disable counter of a specific reason. + * @param reason specific failure reason. One of the {@link #DISABLED_NONE} or + * DISABLED_* constants e.g. {@link #DISABLED_ASSOCIATION_REJECTION}. + * @exception IllegalArgumentException for invalid reason * @return counter number for specific error reason. */ - public int getDisableReasonCounter(int reason) { - if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) { + public int getDisableReasonCounter(@NetworkSelectionDisableReason int reason) { + if (reason >= DISABLED_NONE && reason < NETWORK_SELECTION_DISABLED_MAX) { return mNetworkSeclectionDisableCounter[reason]; } else { throw new IllegalArgumentException("Illegal reason value: " + reason); @@ -1554,9 +1837,10 @@ public class WifiConfiguration implements Parcelable { * @param reason reason for disable error * @param value the counter value for this specific reason * @exception throw IllegalArgumentException for illegal input + * @hide */ public void setDisableReasonCounter(int reason, int value) { - if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) { + if (reason >= DISABLED_NONE && reason < NETWORK_SELECTION_DISABLED_MAX) { mNetworkSeclectionDisableCounter[reason] = value; } else { throw new IllegalArgumentException("Illegal reason value: " + reason); @@ -1567,9 +1851,10 @@ public class WifiConfiguration implements Parcelable { * increment the counter of a specific failure reason * @param reason a specific failure reason * @exception throw IllegalArgumentException for illegal input + * @hide */ public void incrementDisableReasonCounter(int reason) { - if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) { + if (reason >= DISABLED_NONE && reason < NETWORK_SELECTION_DISABLED_MAX) { mNetworkSeclectionDisableCounter[reason]++; } else { throw new IllegalArgumentException("Illegal reason value: " + reason); @@ -1578,13 +1863,13 @@ public class WifiConfiguration implements Parcelable { /** * clear the counter of a specific failure reason - * @hide * @param reason a specific failure reason * @exception throw IllegalArgumentException for illegal input + * @hide */ public void clearDisableReasonCounter(int reason) { - if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) { - mNetworkSeclectionDisableCounter[reason] = NETWORK_SELECTION_ENABLE; + if (reason >= DISABLED_NONE && reason < NETWORK_SELECTION_DISABLED_MAX) { + mNetworkSeclectionDisableCounter[reason] = DISABLED_NONE; } else { throw new IllegalArgumentException("Illegal reason value: " + reason); } @@ -1592,9 +1877,10 @@ public class WifiConfiguration implements Parcelable { /** * clear all the failure reason counters + * @hide */ public void clearDisableReasonCounter() { - Arrays.fill(mNetworkSeclectionDisableCounter, NETWORK_SELECTION_ENABLE); + Arrays.fill(mNetworkSeclectionDisableCounter, DISABLED_NONE); } /** @@ -1605,6 +1891,7 @@ public class WifiConfiguration implements Parcelable { /** * get current network Selection BSSID * @return current network Selection BSSID + * @hide */ public String getNetworkSelectionBSSID() { return mNetworkSelectionBSSID; @@ -1613,15 +1900,17 @@ public class WifiConfiguration implements Parcelable { /** * set network Selection BSSID * @param bssid The target BSSID for assocaition + * @hide */ public void setNetworkSelectionBSSID(String bssid) { mNetworkSelectionBSSID = bssid; } + /** @hide */ public void copy(NetworkSelectionStatus source) { mStatus = source.mStatus; mNetworkSelectionDisableReason = source.mNetworkSelectionDisableReason; - for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX; + for (int index = DISABLED_NONE; index < NETWORK_SELECTION_DISABLED_MAX; index++) { mNetworkSeclectionDisableCounter[index] = source.mNetworkSeclectionDisableCounter[index]; @@ -1632,15 +1921,14 @@ public class WifiConfiguration implements Parcelable { setCandidate(source.getCandidate()); setCandidateScore(source.getCandidateScore()); setConnectChoice(source.getConnectChoice()); - setConnectChoiceTimestamp(source.getConnectChoiceTimestamp()); - setHasEverConnected(source.getHasEverConnected()); - setNotRecommended(source.isNotRecommended()); + setHasEverConnected(source.hasEverConnected()); } + /** @hide */ public void writeToParcel(Parcel dest) { dest.writeInt(getNetworkSelectionStatus()); dest.writeInt(getNetworkSelectionDisableReason()); - for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX; + for (int index = DISABLED_NONE; index < NETWORK_SELECTION_DISABLED_MAX; index++) { dest.writeInt(getDisableReasonCounter(index)); } @@ -1649,18 +1937,17 @@ public class WifiConfiguration implements Parcelable { if (getConnectChoice() != null) { dest.writeInt(CONNECT_CHOICE_EXISTS); dest.writeString(getConnectChoice()); - dest.writeLong(getConnectChoiceTimestamp()); } else { dest.writeInt(CONNECT_CHOICE_NOT_EXISTS); } - dest.writeInt(getHasEverConnected() ? 1 : 0); - dest.writeInt(isNotRecommended() ? 1 : 0); + dest.writeInt(hasEverConnected() ? 1 : 0); } + /** @hide */ public void readFromParcel(Parcel in) { setNetworkSelectionStatus(in.readInt()); setNetworkSelectionDisableReason(in.readInt()); - for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX; + for (int index = DISABLED_NONE; index < NETWORK_SELECTION_DISABLED_MAX; index++) { setDisableReasonCounter(index, in.readInt()); } @@ -1668,13 +1955,10 @@ public class WifiConfiguration implements Parcelable { setNetworkSelectionBSSID(in.readString()); if (in.readInt() == CONNECT_CHOICE_EXISTS) { setConnectChoice(in.readString()); - setConnectChoiceTimestamp(in.readLong()); } else { setConnectChoice(null); - setConnectChoiceTimestamp(INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); } setHasEverConnected(in.readInt() != 0); - setNotRecommended(in.readInt() != 0); } } @@ -1685,65 +1969,104 @@ public class WifiConfiguration implements Parcelable { private NetworkSelectionStatus mNetworkSelectionStatus = new NetworkSelectionStatus(); /** - * @hide * This class is intended to store extra failure reason information for the most recent * connection attempt, so that it may be surfaced to the settings UI + * @hide */ + // TODO(b/148626966): called by SUW via reflection, remove once SUW is updated public static class RecentFailure { - /** - * No recent failure, or no specific reason given for the recent connection failure - */ - public static final int NONE = 0; - /** - * Connection to this network recently failed due to Association Rejection Status 17 - * (AP is full) - */ - public static final int STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17; + private RecentFailure() {} + /** * Association Rejection Status code (NONE for success/non-association-rejection-fail) */ - private int mAssociationStatus = NONE; + @RecentFailureReason + private int mAssociationStatus = RECENT_FAILURE_NONE; /** * @param status the association status code for the recent failure */ - public void setAssociationStatus(int status) { + public void setAssociationStatus(@RecentFailureReason int status) { mAssociationStatus = status; } /** * Sets the RecentFailure to NONE */ public void clear() { - mAssociationStatus = NONE; + mAssociationStatus = RECENT_FAILURE_NONE; } /** - * Get the recent failure code + * Get the recent failure code. One of {@link #RECENT_FAILURE_NONE} or + * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA}. */ + @RecentFailureReason public int getAssociationStatus() { return mAssociationStatus; } } /** - * @hide * RecentFailure member + * @hide */ - final public RecentFailure recentFailure = new RecentFailure(); + // TODO(b/148626966): called by SUW via reflection, once SUW is updated, make private and + // rename to mRecentFailure + @NonNull + public final RecentFailure recentFailure = new RecentFailure(); + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "RECENT_FAILURE_", value = { + RECENT_FAILURE_NONE, + RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA}) + public @interface RecentFailureReason {} /** + * No recent failure, or no specific reason given for the recent connection failure + * @hide + */ + @SystemApi + public static final int RECENT_FAILURE_NONE = 0; + /** + * Connection to this network recently failed due to Association Rejection Status 17 + * (AP is full) + * @hide + */ + @SystemApi + public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; + + /** + * Get the failure reason for the most recent connection attempt, or + * {@link #RECENT_FAILURE_NONE} if there was no failure. + * + * Failure reasons include: + * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA} + * * @hide - * @return network selection status */ + @RecentFailureReason + @SystemApi + public int getRecentFailureReason() { + return recentFailure.getAssociationStatus(); + } + + /** + * Get the network selection status. + * @hide + */ + @NonNull + @SystemApi public NetworkSelectionStatus getNetworkSelectionStatus() { return mNetworkSelectionStatus; } /** - * Set the network selection status + * Set the network selection status. * @hide */ - public void setNetworkSelectionStatus(NetworkSelectionStatus status) { + @SystemApi + public void setNetworkSelectionStatus(@NonNull NetworkSelectionStatus status) { mNetworkSelectionStatus = status; } @@ -1777,8 +2100,6 @@ public class WifiConfiguration implements Parcelable { wepKeys[i] = null; } enterpriseConfig = new WifiEnterpriseConfig(); - selfAdded = false; - didSelfAdd = false; ephemeral = false; osu = false; trusted = true; // Networks are considered trusted by default. @@ -1803,7 +2124,8 @@ public class WifiConfiguration implements Parcelable { return !TextUtils.isEmpty(FQDN) && !TextUtils.isEmpty(providerFriendlyName) && enterpriseConfig != null - && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE; + && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE + && !TextUtils.isEmpty(mPasspointUniqueId); } /** @@ -1813,8 +2135,8 @@ public class WifiConfiguration implements Parcelable { public boolean isLinked(WifiConfiguration config) { if (config != null) { if (config.linkedConfigurations != null && linkedConfigurations != null) { - if (config.linkedConfigurations.get(configKey()) != null - && linkedConfigurations.get(config.configKey()) != null) { + if (config.linkedConfigurations.get(getKey()) != null + && linkedConfigurations.get(config.getKey()) != null) { return true; } } @@ -1830,11 +2152,22 @@ public class WifiConfiguration implements Parcelable { public boolean isEnterprise() { return (allowedKeyManagement.get(KeyMgmt.WPA_EAP) || allowedKeyManagement.get(KeyMgmt.IEEE8021X) - || allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) + || allowedKeyManagement.get(KeyMgmt.SUITE_B_192) + || allowedKeyManagement.get(KeyMgmt.WAPI_CERT)) && enterpriseConfig != null && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE; } + private static String logTimeOfDay(long millis) { + Calendar c = Calendar.getInstance(); + if (millis >= 0) { + c.setTimeInMillis(millis); + return String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c); + } else { + return Long.toString(millis); + } + } + @Override public String toString() { StringBuilder sbuf = new StringBuilder(); @@ -1846,35 +2179,38 @@ public class WifiConfiguration implements Parcelable { sbuf.append("ID: ").append(this.networkId).append(" SSID: ").append(this.SSID). append(" PROVIDER-NAME: ").append(this.providerFriendlyName). append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN) + .append(" HOME-PROVIDER-NETWORK: ").append(this.isHomeProviderNetwork) .append(" PRIO: ").append(this.priority) .append(" HIDDEN: ").append(this.hiddenSSID) - .append(" PMF: ").append(this.requirePMF) + .append(" PMF: ").append(this.requirePmf) + .append("CarrierId: ").append(this.carrierId) .append('\n'); sbuf.append(" NetworkSelectionStatus ") - .append(mNetworkSelectionStatus.getNetworkStatusString() + "\n"); + .append(mNetworkSelectionStatus.getNetworkStatusString()) + .append("\n"); if (mNetworkSelectionStatus.getNetworkSelectionDisableReason() > 0) { sbuf.append(" mNetworkSelectionDisableReason ") - .append(mNetworkSelectionStatus.getNetworkDisableReasonString() + "\n"); + .append(mNetworkSelectionStatus.getNetworkSelectionDisableReasonString()) + .append("\n"); - for (int index = mNetworkSelectionStatus.NETWORK_SELECTION_ENABLE; - index < mNetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; index++) { + for (int index = NetworkSelectionStatus.DISABLED_NONE; + index < NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; index++) { if (mNetworkSelectionStatus.getDisableReasonCounter(index) != 0) { - sbuf.append(NetworkSelectionStatus.getNetworkDisableReasonString(index) - + " counter:" + mNetworkSelectionStatus.getDisableReasonCounter(index) - + "\n"); + sbuf.append( + NetworkSelectionStatus.getNetworkSelectionDisableReasonString(index)) + .append(" counter:") + .append(mNetworkSelectionStatus.getDisableReasonCounter(index)) + .append("\n"); } } } if (mNetworkSelectionStatus.getConnectChoice() != null) { sbuf.append(" connect choice: ").append(mNetworkSelectionStatus.getConnectChoice()); - sbuf.append(" connect choice set time: ") - .append(TimeUtils.logTimeOfDay( - mNetworkSelectionStatus.getConnectChoiceTimestamp())); } sbuf.append(" hasEverConnected: ") - .append(mNetworkSelectionStatus.getHasEverConnected()).append("\n"); + .append(mNetworkSelectionStatus.hasEverConnected()).append("\n"); if (this.numAssociation > 0) { sbuf.append(" numAssociation ").append(this.numAssociation).append("\n"); @@ -1883,14 +2219,6 @@ public class WifiConfiguration implements Parcelable { sbuf.append(" numNoInternetAccessReports "); sbuf.append(this.numNoInternetAccessReports).append("\n"); } - if (this.updateTime != null) { - sbuf.append(" update ").append(this.updateTime).append("\n"); - } - if (this.creationTime != null) { - sbuf.append(" creation ").append(this.creationTime).append("\n"); - } - if (this.didSelfAdd) sbuf.append(" didSelfAdd"); - if (this.selfAdded) sbuf.append(" selfAdded"); if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess"); if (this.ephemeral) sbuf.append(" ephemeral"); if (this.osu) sbuf.append(" osu"); @@ -1899,9 +2227,9 @@ public class WifiConfiguration implements Parcelable { if (this.fromWifiNetworkSpecifier) sbuf.append(" fromWifiNetworkSpecifier"); if (this.meteredHint) sbuf.append(" meteredHint"); if (this.useExternalScores) sbuf.append(" useExternalScores"); - if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess - || this.ephemeral || this.trusted || this.fromWifiNetworkSuggestion - || this.fromWifiNetworkSpecifier || this.meteredHint || this.useExternalScores) { + if (this.validatedInternetAccess || this.ephemeral || this.trusted + || this.fromWifiNetworkSuggestion || this.fromWifiNetworkSpecifier + || this.meteredHint || this.useExternalScores) { sbuf.append("\n"); } if (this.meteredOverride != METERED_OVERRIDE_NONE) { @@ -1909,6 +2237,9 @@ public class WifiConfiguration implements Parcelable { } sbuf.append(" macRandomizationSetting: ").append(macRandomizationSetting).append("\n"); sbuf.append(" mRandomizedMacAddress: ").append(mRandomizedMacAddress).append("\n"); + sbuf.append(" randomizedMacExpirationTimeMs: ") + .append(randomizedMacExpirationTimeMs == 0 ? "<none>" + : logTimeOfDay(randomizedMacExpirationTimeMs)).append("\n"); sbuf.append(" KeyMgmt:"); for (int k = 0; k < this.allowedKeyManagement.size(); k++) { if (this.allowedKeyManagement.get(k)) { @@ -2023,13 +2354,15 @@ public class WifiConfiguration implements Parcelable { if (lastUpdateName != null) sbuf.append(" lname=" + lastUpdateName); if (updateIdentifier != null) sbuf.append(" updateIdentifier=" + updateIdentifier); sbuf.append(" lcuid=" + lastConnectUid); - sbuf.append(" userApproved=" + userApprovedAsString(userApproved)); + sbuf.append(" allowAutojoin=" + allowAutojoin); sbuf.append(" noInternetAccessExpected=" + noInternetAccessExpected); + sbuf.append(" mostRecentlyConnected=" + isMostRecentlyConnected); + sbuf.append(" "); if (this.lastConnected != 0) { sbuf.append('\n'); - sbuf.append("lastConnected: ").append(TimeUtils.logTimeOfDay(this.lastConnected)); + sbuf.append("lastConnected: ").append(logTimeOfDay(this.lastConnected)); sbuf.append(" "); } sbuf.append('\n'); @@ -2044,8 +2377,13 @@ public class WifiConfiguration implements Parcelable { return sbuf.toString(); } - /** {@hide} */ - @UnsupportedAppUsage + /** + * Get the SSID in a human-readable format, with all additional formatting removed + * e.g. quotation marks around the SSID, "P" prefix + * @hide + */ + @NonNull + @SystemApi public String getPrintableSsid() { if (SSID == null) return ""; final int length = SSID.length(); @@ -2053,7 +2391,7 @@ public class WifiConfiguration implements Parcelable { return SSID.substring(1, length - 1); } - /** The ascii-encoded string format is P"<ascii-encoded-string>" + /* The ascii-encoded string format is P"<ascii-encoded-string>" * The decoding is implemented in the supplicant for a newly configured * network. */ @@ -2066,20 +2404,6 @@ public class WifiConfiguration implements Parcelable { return SSID; } - /** @hide **/ - public static String userApprovedAsString(int userApproved) { - switch (userApproved) { - case USER_APPROVED: - return "USER_APPROVED"; - case USER_BANNED: - return "USER_BANNED"; - case USER_UNSPECIFIED: - return "USER_UNSPECIFIED"; - default: - return "INVALID"; - } - } - /** * Get an identifier for associating credentials with this config * @param current configuration contains values for additional fields @@ -2109,11 +2433,13 @@ public class WifiConfiguration implements Parcelable { if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) { keyMgmt += KeyMgmt.strings[KeyMgmt.SUITE_B_192]; } + if (allowedKeyManagement.get(KeyMgmt.WAPI_CERT)) { + keyMgmt += KeyMgmt.strings[KeyMgmt.WAPI_CERT]; + } if (TextUtils.isEmpty(keyMgmt)) { throw new IllegalStateException("Not an EAP network"); } - String keyId = trimStringForKeyId(SSID) + "_" + keyMgmt + "_" + trimStringForKeyId(enterpriseConfig.getKeyId(current != null ? current.enterpriseConfig : null)); @@ -2156,8 +2482,13 @@ public class WifiConfiguration implements Parcelable { } } - /** @hide */ - @UnsupportedAppUsage + /** + * Get the authentication type of the network. + * @return One of the {@link KeyMgmt} constants. e.g. {@link KeyMgmt#WPA2_PSK}. + * @hide + */ + @SystemApi + @KeyMgmt.KeyMgmtScheme public int getAuthType() { if (allowedKeyManagement.cardinality() > 1) { throw new IllegalStateException("More than one auth type set"); @@ -2176,36 +2507,32 @@ public class WifiConfiguration implements Parcelable { return KeyMgmt.OWE; } else if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) { return KeyMgmt.SUITE_B_192; + } else if (allowedKeyManagement.get(KeyMgmt.WAPI_PSK)) { + return KeyMgmt.WAPI_PSK; + } else if (allowedKeyManagement.get(KeyMgmt.WAPI_CERT)) { + return KeyMgmt.WAPI_CERT; } return KeyMgmt.NONE; } - /* @hide - * Cache the config key, this seems useful as a speed up since a lot of - * lookups in the config store are done and based on this key. + /** + * Return a String that can be used to uniquely identify this WifiConfiguration. + * <br /> + * Note: Do not persist this value! This value is not guaranteed to remain backwards compatible. */ - String mCachedConfigKey; + @NonNull + public String getKey() { + // Passpoint ephemeral networks have their unique identifier set. Return it as is to be + // able to match internally. + if (mPasspointUniqueId != null) { + return mPasspointUniqueId; + } - /** @hide - * return the string used to calculate the hash in WifiConfigStore - * and uniquely identify this WifiConfiguration - */ - public String configKey(boolean allowCached) { - String key; - if (allowCached && mCachedConfigKey != null) { - key = mCachedConfigKey; - } else if (providerFriendlyName != null) { - key = FQDN + KeyMgmt.strings[KeyMgmt.WPA_EAP]; - if (!shared) { - key += "-" + Integer.toString(UserHandle.getUserId(creatorUid)); - } - } else { - key = getSsidAndSecurityTypeString(); - if (!shared) { - key += "-" + Integer.toString(UserHandle.getUserId(creatorUid)); - } - mCachedConfigKey = key; + String key = getSsidAndSecurityTypeString(); + if (!shared) { + key += "-" + UserHandle.getUserHandleForUid(creatorUid).getIdentifier(); } + return key; } @@ -2219,7 +2546,8 @@ public class WifiConfiguration implements Parcelable { } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP) || allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { key = SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP]; - } else if (wepKeys[0] != null) { + } else if (wepTxKeyIndex >= 0 && wepTxKeyIndex < wepKeys.length + && wepKeys[wepTxKeyIndex] != null) { key = SSID + "WEP"; } else if (allowedKeyManagement.get(KeyMgmt.OWE)) { key = SSID + KeyMgmt.strings[KeyMgmt.OWE]; @@ -2227,33 +2555,46 @@ public class WifiConfiguration implements Parcelable { key = SSID + KeyMgmt.strings[KeyMgmt.SAE]; } else if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) { key = SSID + KeyMgmt.strings[KeyMgmt.SUITE_B_192]; + } else if (allowedKeyManagement.get(KeyMgmt.WAPI_PSK)) { + key = SSID + KeyMgmt.strings[KeyMgmt.WAPI_PSK]; + } else if (allowedKeyManagement.get(KeyMgmt.WAPI_CERT)) { + key = SSID + KeyMgmt.strings[KeyMgmt.WAPI_CERT]; + } else if (allowedKeyManagement.get(KeyMgmt.OSEN)) { + key = SSID + KeyMgmt.strings[KeyMgmt.OSEN]; } else { key = SSID + KeyMgmt.strings[KeyMgmt.NONE]; } return key; } - /** @hide - * get configKey, force calculating the config string + /** + * Get the IpConfiguration object associated with this WifiConfiguration. + * @hide */ - public String configKey() { - return configKey(false); - } - - /** @hide */ - @UnsupportedAppUsage + @NonNull + @SystemApi public IpConfiguration getIpConfiguration() { - return mIpConfiguration; + return new IpConfiguration(mIpConfiguration); } - /** @hide */ - @UnsupportedAppUsage - public void setIpConfiguration(IpConfiguration ipConfiguration) { + /** + * Set the {@link IpConfiguration} for this network. + * @param ipConfiguration the {@link IpConfiguration} to set, or null to use the default + * constructor {@link IpConfiguration#IpConfiguration()}. + * @hide + */ + @SystemApi + public void setIpConfiguration(@Nullable IpConfiguration ipConfiguration) { if (ipConfiguration == null) ipConfiguration = new IpConfiguration(); mIpConfiguration = ipConfiguration; } - /** @hide */ + /** + * Get the {@link StaticIpConfiguration} for this network. + * @return the {@link StaticIpConfiguration}, or null if unset. + * @hide + */ + @Nullable @UnsupportedAppUsage public StaticIpConfiguration getStaticIpConfiguration() { return mIpConfiguration.getStaticIpConfiguration(); @@ -2265,28 +2606,36 @@ public class WifiConfiguration implements Parcelable { mIpConfiguration.setStaticIpConfiguration(staticIpConfiguration); } - /** @hide */ + /** + * Get the {@link IpConfiguration.IpAssignment} for this network. + * @hide + */ + @NonNull @UnsupportedAppUsage public IpConfiguration.IpAssignment getIpAssignment() { - return mIpConfiguration.ipAssignment; + return mIpConfiguration.getIpAssignment(); } /** @hide */ @UnsupportedAppUsage public void setIpAssignment(IpConfiguration.IpAssignment ipAssignment) { - mIpConfiguration.ipAssignment = ipAssignment; + mIpConfiguration.setIpAssignment(ipAssignment); } - /** @hide */ + /** + * Get the {@link IpConfiguration.ProxySettings} for this network. + * @hide + */ + @NonNull @UnsupportedAppUsage public IpConfiguration.ProxySettings getProxySettings() { - return mIpConfiguration.proxySettings; + return mIpConfiguration.getProxySettings(); } /** @hide */ @UnsupportedAppUsage public void setProxySettings(IpConfiguration.ProxySettings proxySettings) { - mIpConfiguration.proxySettings = proxySettings; + mIpConfiguration.setProxySettings(proxySettings); } /** @@ -2295,10 +2644,10 @@ public class WifiConfiguration implements Parcelable { * WifiConfiguration, or {@code null} if no proxy is specified. */ public ProxyInfo getHttpProxy() { - if (mIpConfiguration.proxySettings == IpConfiguration.ProxySettings.NONE) { + if (mIpConfiguration.getProxySettings() == IpConfiguration.ProxySettings.NONE) { return null; } - return new ProxyInfo(mIpConfiguration.httpProxy); + return new ProxyInfo(mIpConfiguration.getHttpProxy()); } /** @@ -2323,12 +2672,12 @@ public class WifiConfiguration implements Parcelable { if (!Uri.EMPTY.equals(httpProxy.getPacFileUrl())) { proxySettingCopy = IpConfiguration.ProxySettings.PAC; // Construct a new PAC URL Proxy - httpProxyCopy = new ProxyInfo(httpProxy.getPacFileUrl(), httpProxy.getPort()); + httpProxyCopy = ProxyInfo.buildPacProxy(httpProxy.getPacFileUrl(), httpProxy.getPort()); } else { proxySettingCopy = IpConfiguration.ProxySettings.STATIC; // Construct a new HTTP Proxy - httpProxyCopy = new ProxyInfo(httpProxy.getHost(), httpProxy.getPort(), - httpProxy.getExclusionListAsString()); + httpProxyCopy = ProxyInfo.buildDirectProxy(httpProxy.getHost(), httpProxy.getPort(), + Arrays.asList(httpProxy.getExclusionList())); } if (!httpProxyCopy.isValid()) { throw new IllegalArgumentException("Invalid ProxyInfo: " + httpProxyCopy.toString()); @@ -2337,11 +2686,14 @@ public class WifiConfiguration implements Parcelable { mIpConfiguration.setHttpProxy(httpProxyCopy); } - /** @hide */ + /** + * Set the {@link ProxySettings} and {@link ProxyInfo} for this network. + * @hide + */ @UnsupportedAppUsage - public void setProxy(ProxySettings settings, ProxyInfo proxy) { - mIpConfiguration.proxySettings = settings; - mIpConfiguration.httpProxy = proxy; + public void setProxy(@NonNull ProxySettings settings, @NonNull ProxyInfo proxy) { + mIpConfiguration.setProxySettings(settings); + mIpConfiguration.setHttpProxy(proxy); } /** Implement the Parcelable interface {@hide} */ @@ -2359,9 +2711,8 @@ public class WifiConfiguration implements Parcelable { return mPasspointManagementObjectTree; } - /** copy constructor {@hide} */ - @UnsupportedAppUsage - public WifiConfiguration(WifiConfiguration source) { + /** Copy constructor */ + public WifiConfiguration(@NonNull WifiConfiguration source) { if (source != null) { networkId = source.networkId; status = source.status; @@ -2403,8 +2754,6 @@ public class WifiConfiguration implements Parcelable { linkedConfigurations = new HashMap<String, Integer>(); linkedConfigurations.putAll(source.linkedConfigurations); } - mCachedConfigKey = null; //force null configKey - selfAdded = source.selfAdded; validatedInternetAccess = source.validatedInternetAccess; isLegacyPasspointConfig = source.isLegacyPasspointConfig; ephemeral = source.ephemeral; @@ -2416,7 +2765,6 @@ public class WifiConfiguration implements Parcelable { meteredOverride = source.meteredOverride; useExternalScores = source.useExternalScores; - didSelfAdd = source.didSelfAdd; lastConnectUid = source.lastConnectUid; lastUpdateUid = source.lastUpdateUid; creatorUid = source.creatorUid; @@ -2429,17 +2777,18 @@ public class WifiConfiguration implements Parcelable { numScorerOverride = source.numScorerOverride; numScorerOverrideAndSwitchedNetwork = source.numScorerOverrideAndSwitchedNetwork; numAssociation = source.numAssociation; - userApproved = source.userApproved; + allowAutojoin = source.allowAutojoin; numNoInternetAccessReports = source.numNoInternetAccessReports; noInternetAccessExpected = source.noInternetAccessExpected; - creationTime = source.creationTime; - updateTime = source.updateTime; shared = source.shared; recentFailure.setAssociationStatus(source.recentFailure.getAssociationStatus()); mRandomizedMacAddress = source.mRandomizedMacAddress; macRandomizationSetting = source.macRandomizationSetting; - requirePMF = source.requirePMF; + randomizedMacExpirationTimeMs = source.randomizedMacExpirationTimeMs; + requirePmf = source.requirePmf; updateIdentifier = source.updateIdentifier; + carrierId = source.carrierId; + mPasspointUniqueId = source.mPasspointUniqueId; } } @@ -2467,7 +2816,7 @@ public class WifiConfiguration implements Parcelable { dest.writeInt(wepTxKeyIndex); dest.writeInt(priority); dest.writeInt(hiddenSSID ? 1 : 0); - dest.writeInt(requirePMF ? 1 : 0); + dest.writeInt(requirePmf ? 1 : 0); dest.writeString(updateIdentifier); writeBitSet(dest, allowedKeyManagement); @@ -2483,8 +2832,6 @@ public class WifiConfiguration implements Parcelable { dest.writeParcelable(mIpConfiguration, flags); dest.writeString(dhcpServer); dest.writeString(defaultGwMacAddress); - dest.writeInt(selfAdded ? 1 : 0); - dest.writeInt(didSelfAdd ? 1 : 0); dest.writeInt(validatedInternetAccess ? 1 : 0); dest.writeInt(isLegacyPasspointConfig ? 1 : 0); dest.writeInt(ephemeral ? 1 : 0); @@ -2502,7 +2849,7 @@ public class WifiConfiguration implements Parcelable { dest.writeInt(numScorerOverride); dest.writeInt(numScorerOverrideAndSwitchedNetwork); dest.writeInt(numAssociation); - dest.writeInt(userApproved); + dest.writeBoolean(allowAutojoin); dest.writeInt(numNoInternetAccessReports); dest.writeInt(noInternetAccessExpected ? 1 : 0); dest.writeInt(shared ? 1 : 0); @@ -2511,6 +2858,9 @@ public class WifiConfiguration implements Parcelable { dest.writeParcelable(mRandomizedMacAddress, flags); dest.writeInt(macRandomizationSetting); dest.writeInt(osu ? 1 : 0); + dest.writeLong(randomizedMacExpirationTimeMs); + dest.writeInt(carrierId); + dest.writeString(mPasspointUniqueId); } /** Implement the Parcelable interface {@hide} */ @@ -2541,7 +2891,7 @@ public class WifiConfiguration implements Parcelable { config.wepTxKeyIndex = in.readInt(); config.priority = in.readInt(); config.hiddenSSID = in.readInt() != 0; - config.requirePMF = in.readInt() != 0; + config.requirePmf = in.readInt() != 0; config.updateIdentifier = in.readString(); config.allowedKeyManagement = readBitSet(in); @@ -2556,8 +2906,6 @@ public class WifiConfiguration implements Parcelable { config.setIpConfiguration(in.readParcelable(null)); config.dhcpServer = in.readString(); config.defaultGwMacAddress = in.readString(); - config.selfAdded = in.readInt() != 0; - config.didSelfAdd = in.readInt() != 0; config.validatedInternetAccess = in.readInt() != 0; config.isLegacyPasspointConfig = in.readInt() != 0; config.ephemeral = in.readInt() != 0; @@ -2575,7 +2923,7 @@ public class WifiConfiguration implements Parcelable { config.numScorerOverride = in.readInt(); config.numScorerOverrideAndSwitchedNetwork = in.readInt(); config.numAssociation = in.readInt(); - config.userApproved = in.readInt(); + config.allowAutojoin = in.readBoolean(); config.numNoInternetAccessReports = in.readInt(); config.noInternetAccessExpected = in.readInt() != 0; config.shared = in.readInt() != 0; @@ -2584,6 +2932,9 @@ public class WifiConfiguration implements Parcelable { config.mRandomizedMacAddress = in.readParcelable(null); config.macRandomizationSetting = in.readInt(); config.osu = in.readInt() != 0; + config.randomizedMacExpirationTimeMs = in.readLong(); + config.carrierId = in.readInt(); + config.mPasspointUniqueId = in.readString(); return config; } @@ -2593,45 +2944,44 @@ public class WifiConfiguration implements Parcelable { }; /** - * Serializes the object for backup + * Passpoint Unique identifier * @hide */ - public byte[] getBytesForBackup() throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(baos); + private String mPasspointUniqueId = null; - out.writeInt(BACKUP_VERSION); - BackupUtils.writeString(out, SSID); - out.writeInt(apBand); - out.writeInt(apChannel); - BackupUtils.writeString(out, preSharedKey); - out.writeInt(getAuthType()); - out.writeBoolean(hiddenSSID); - return baos.toByteArray(); + /** + * Set the Passpoint unique identifier + * @param uniqueId Passpoint unique identifier to be set + * @hide + */ + public void setPasspointUniqueId(String uniqueId) { + mPasspointUniqueId = uniqueId; } /** - * Deserializes a byte array into the WiFiConfiguration Object + * Set the Passpoint unique identifier * @hide */ - public static WifiConfiguration getWifiConfigFromBackup(DataInputStream in) throws IOException, - BackupUtils.BadVersionException { - WifiConfiguration config = new WifiConfiguration(); - int version = in.readInt(); - if (version < 1 || version > BACKUP_VERSION) { - throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version"); - } + public String getPasspointUniqueId() { + return mPasspointUniqueId; + } - if (version == 1) return null; // Version 1 is a bad dataset. + /** + * If network is one of the most recently connected. + * For framework internal use only. Do not parcel. + * @hide + */ + public boolean isMostRecentlyConnected = false; - config.SSID = BackupUtils.readString(in); - config.apBand = in.readInt(); - config.apChannel = in.readInt(); - config.preSharedKey = BackupUtils.readString(in); - config.allowedKeyManagement.set(in.readInt()); - if (version >= 3) { - config.hiddenSSID = in.readBoolean(); - } - return config; + /** + * Whether the key mgmt indicates if the WifiConfiguration needs a preSharedKey or not. + * @return true if preSharedKey is needed, false otherwise. + * @hide + */ + public boolean needsPreSharedKey() { + return allowedKeyManagement.get(KeyMgmt.WPA_PSK) + || allowedKeyManagement.get(KeyMgmt.SAE) + || allowedKeyManagement.get(KeyMgmt.WAPI_PSK); } + } diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index a9c2eb57291a..77fa673f1960 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -15,14 +15,18 @@ */ package android.net.wifi; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; -import android.security.Credentials; import android.text.TextUtils; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.cert.X509Certificate; @@ -37,6 +41,36 @@ import java.util.Map; */ public class WifiEnterpriseConfig implements Parcelable { + /** Key prefix for WAPI AS certificates. */ + public static final String WAPI_AS_CERTIFICATE = "WAPIAS_"; + + /** Key prefix for WAPI user certificates. */ + public static final String WAPI_USER_CERTIFICATE = "WAPIUSR_"; + + /** + * Intent extra: name for WAPI AS certificates + */ + public static final String EXTRA_WAPI_AS_CERTIFICATE_NAME = + "android.net.wifi.extra.WAPI_AS_CERTIFICATE_NAME"; + + /** + * Intent extra: data for WAPI AS certificates + */ + public static final String EXTRA_WAPI_AS_CERTIFICATE_DATA = + "android.net.wifi.extra.WAPI_AS_CERTIFICATE_DATA"; + + /** + * Intent extra: name for WAPI USER certificates + */ + public static final String EXTRA_WAPI_USER_CERTIFICATE_NAME = + "android.net.wifi.extra.WAPI_USER_CERTIFICATE_NAME"; + + /** + * Intent extra: data for WAPI USER certificates + */ + public static final String EXTRA_WAPI_USER_CERTIFICATE_DATA = + "android.net.wifi.extra.WAPI_USER_CERTIFICATE_DATA"; + /** @hide */ public static final String EMPTY_VALUE = "NULL"; /** @hide */ @@ -57,6 +91,11 @@ public class WifiEnterpriseConfig implements Parcelable { public static final String DOM_SUFFIX_MATCH_KEY = "domain_suffix_match"; /** @hide */ public static final String OPP_KEY_CACHING = "proactive_key_caching"; + /** @hide */ + public static final String EAP_ERP = "eap_erp"; + /** @hide */ + public static final String OCSP = "ocsp"; + /** * String representing the keystore OpenSSL ENGINE's ID. * @hide @@ -88,10 +127,26 @@ public class WifiEnterpriseConfig implements Parcelable { */ public static final String ENGINE_DISABLE = "0"; + /** + * Key prefix for CA certificates. + * Note: copied from {@link android.security.Credentials#CA_CERTIFICATE} since it is @hide. + */ + private static final String CA_CERTIFICATE = "CACERT_"; + /** + * Key prefix for user certificates. + * Note: copied from {@link android.security.Credentials#USER_CERTIFICATE} since it is @hide. + */ + private static final String USER_CERTIFICATE = "USRCERT_"; + /** + * Key prefix for user private and secret keys. + * Note: copied from {@link android.security.Credentials#USER_PRIVATE_KEY} since it is @hide. + */ + private static final String USER_PRIVATE_KEY = "USRPKEY_"; + /** @hide */ - public static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE; + public static final String CA_CERT_PREFIX = KEYSTORE_URI + CA_CERTIFICATE; /** @hide */ - public static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + Credentials.USER_CERTIFICATE; + public static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + USER_CERTIFICATE; /** @hide */ public static final String CLIENT_CERT_KEY = "client_cert"; /** @hide */ @@ -110,6 +165,53 @@ public class WifiEnterpriseConfig implements Parcelable { public static final String PLMN_KEY = "plmn"; /** @hide */ public static final String CA_CERT_ALIAS_DELIMITER = " "; + /** @hide */ + public static final String WAPI_CERT_SUITE_KEY = "wapi_cert_suite"; + + /** + * Do not use OCSP stapling (TLS certificate status extension) + * @hide + */ + @SystemApi + public static final int OCSP_NONE = 0; + + /** + * Try to use OCSP stapling, but not require response + * @hide + */ + @SystemApi + public static final int OCSP_REQUEST_CERT_STATUS = 1; + + /** + * Require valid OCSP stapling response + * @hide + */ + @SystemApi + public static final int OCSP_REQUIRE_CERT_STATUS = 2; + + /** + * Require valid OCSP stapling response for all not-trusted certificates in the server + * certificate chain + * @hide + */ + @SystemApi + public static final int OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS = 3; + + /** @hide */ + @IntDef(prefix = {"OCSP_"}, value = { + OCSP_NONE, + OCSP_REQUEST_CERT_STATUS, + OCSP_REQUIRE_CERT_STATUS, + OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Ocsp {} + + /** + * Whether to use/require OCSP (Online Certificate Status Protocol) to check server certificate. + * @hide + */ + private @Ocsp int mOcsp = OCSP_NONE; // Fields to copy verbatim from wpa_supplicant. private static final String[] SUPPLICANT_CONFIG_KEYS = new String[] { @@ -130,7 +232,8 @@ public class WifiEnterpriseConfig implements Parcelable { /** * Fields that have unquoted values in {@link #mFields}. */ - private static final List<String> UNQUOTED_KEYS = Arrays.asList(ENGINE_KEY, OPP_KEY_CACHING); + private static final List<String> UNQUOTED_KEYS = Arrays.asList(ENGINE_KEY, OPP_KEY_CACHING, + EAP_ERP); @UnsupportedAppUsage private HashMap<String, String> mFields = new HashMap<String, String>(); @@ -185,6 +288,7 @@ public class WifiEnterpriseConfig implements Parcelable { mPhase2Method = source.mPhase2Method; mIsAppInstalledDeviceKeyAndCert = source.mIsAppInstalledDeviceKeyAndCert; mIsAppInstalledCaCert = source.mIsAppInstalledCaCert; + mOcsp = source.mOcsp; } /** @@ -230,6 +334,7 @@ public class WifiEnterpriseConfig implements Parcelable { ParcelUtil.writeCertificates(dest, mClientCertificateChain); dest.writeBoolean(mIsAppInstalledDeviceKeyAndCert); dest.writeBoolean(mIsAppInstalledCaCert); + dest.writeInt(mOcsp); } public static final @android.annotation.NonNull Creator<WifiEnterpriseConfig> CREATOR = @@ -251,6 +356,7 @@ public class WifiEnterpriseConfig implements Parcelable { enterpriseConfig.mClientCertificateChain = ParcelUtil.readCertificates(in); enterpriseConfig.mIsAppInstalledDeviceKeyAndCert = in.readBoolean(); enterpriseConfig.mIsAppInstalledCaCert = in.readBoolean(); + enterpriseConfig.mOcsp = in.readInt(); return enterpriseConfig; } @@ -280,9 +386,12 @@ public class WifiEnterpriseConfig implements Parcelable { public static final int AKA_PRIME = 6; /** Hotspot 2.0 r2 OSEN */ public static final int UNAUTH_TLS = 7; + /** WAPI Certificate */ + public static final int WAPI_CERT = 8; /** @hide */ public static final String[] strings = - { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA", "AKA'", "WFA-UNAUTH-TLS" }; + { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA", "AKA'", "WFA-UNAUTH-TLS", + "WAPI_CERT" }; /** Prevent initialization */ private Eap() {} @@ -375,7 +484,7 @@ public class WifiEnterpriseConfig implements Parcelable { return false; } - if (mEapMethod != Eap.TLS && mPhase2Method != Phase2.NONE) { + if (mEapMethod != Eap.TLS && mEapMethod != Eap.UNAUTH_TLS && mPhase2Method != Phase2.NONE) { boolean is_autheap = mEapMethod == Eap.TTLS && mPhase2Method == Phase2.GTC; String prefix = is_autheap ? Phase2.AUTHEAP_PREFIX : Phase2.AUTH_PREFIX; String value = convertToQuotedString(prefix + Phase2.strings[mPhase2Method]); @@ -426,6 +535,10 @@ public class WifiEnterpriseConfig implements Parcelable { public void setEapMethod(int eapMethod) { switch (eapMethod) { /** Valid methods */ + case Eap.WAPI_CERT: + mEapMethod = eapMethod; + setPhase2Method(Phase2.NONE); + break; case Eap.TLS: case Eap.UNAUTH_TLS: setPhase2Method(Phase2.NONE); @@ -588,9 +701,11 @@ public class WifiEnterpriseConfig implements Parcelable { * <p> See the {@link android.security.KeyChain} for details on installing or choosing * a certificate. * </p> - * @param aliases identifies the certificate + * @param aliases identifies the certificate. Can be null to indicate the absence of a + * certificate. * @hide */ + @SystemApi public void setCaCertificateAliases(@Nullable String[] aliases) { if (aliases == null) { setFieldValue(CA_CERT_KEY, null, CA_CERT_PREFIX); @@ -604,7 +719,7 @@ public class WifiEnterpriseConfig implements Parcelable { if (i > 0) { sb.append(CA_CERT_ALIAS_DELIMITER); } - sb.append(encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + aliases[i])); + sb.append(encodeCaCertificateAlias(CA_CERTIFICATE + aliases[i])); } setFieldValue(CA_CERT_KEY, sb.toString(), KEYSTORES_URI); } @@ -621,11 +736,13 @@ public class WifiEnterpriseConfig implements Parcelable { } /** - * Get CA certificate aliases - * @return alias to the CA certificate + * Get CA certificate aliases. + * @return alias to the CA certificate, or null if unset. * @hide */ - @Nullable public String[] getCaCertificateAliases() { + @Nullable + @SystemApi + public String[] getCaCertificateAliases() { String value = getFieldValue(CA_CERT_KEY); if (value.startsWith(CA_CERT_PREFIX)) { // Backwards compatibility: parse the original alias prefix. @@ -636,8 +753,8 @@ public class WifiEnterpriseConfig implements Parcelable { String[] aliases = TextUtils.split(values, CA_CERT_ALIAS_DELIMITER); for (int i = 0; i < aliases.length; i++) { aliases[i] = decodeCaCertificateAlias(aliases[i]); - if (aliases[i].startsWith(Credentials.CA_CERTIFICATE)) { - aliases[i] = aliases[i].substring(Credentials.CA_CERTIFICATE.length()); + if (aliases[i].startsWith(CA_CERTIFICATE)) { + aliases[i] = aliases[i].substring(CA_CERTIFICATE.length()); } } return aliases.length != 0 ? aliases : null; @@ -654,6 +771,10 @@ public class WifiEnterpriseConfig implements Parcelable { * certificate when the config is saved and removing the certificate when * the config is removed. * + * Note: If no certificate is set for an Enterprise configuration, either by not calling this + * API (or the {@link #setCaCertificates(X509Certificate[])}, or by calling it with null, then + * the server certificate validation is skipped - which means that the connection is not secure. + * * @param cert X.509 CA certificate * @throws IllegalArgumentException if not a CA certificate */ @@ -693,6 +814,11 @@ public class WifiEnterpriseConfig implements Parcelable { * certificates when the config is saved and removing the certificates when * the config is removed. * + * Note: If no certificates are set for an Enterprise configuration, either by not calling this + * API (or the {@link #setCaCertificate(X509Certificate)}, or by calling it with null, then the + * server certificate validation is skipped - which means that the + * connection is not secure. + * * @param certs X.509 CA certificates * @throws IllegalArgumentException if any of the provided certificates is * not a CA certificate @@ -744,34 +870,45 @@ public class WifiEnterpriseConfig implements Parcelable { * like /etc/ssl/certs. If configured, these certificates are added to the * list of trusted CAs. ca_cert may also be included in that case, but it is * not required. - * @param domain The path for CA certificate files + * + * Note: If no certificate path is set for an Enterprise configuration, either by not calling + * this API, or by calling it with null, and no certificate is set by + * {@link #setCaCertificate(X509Certificate)} or {@link #setCaCertificates(X509Certificate[])}, + * then the server certificate validation is skipped - which means that the connection is not + * secure. + * + * @param path The path for CA certificate files, or empty string to clear. * @hide */ - public void setCaPath(String path) { + @SystemApi + public void setCaPath(@NonNull String path) { setFieldValue(CA_PATH_KEY, path); } /** - * Get the domain_suffix_match value. See setDomSuffixMatch. - * @return The path for CA certificate files. + * Get the ca_path directive from wpa_supplicant. + * @return The path for CA certificate files, or an empty string if unset. * @hide */ + @NonNull + @SystemApi public String getCaPath() { return getFieldValue(CA_PATH_KEY); } - /** Set Client certificate alias. + /** + * Set Client certificate alias. * * <p> See the {@link android.security.KeyChain} for details on installing or choosing * a certificate * </p> - * @param alias identifies the certificate + * @param alias identifies the certificate, or empty string to clear. * @hide */ - @UnsupportedAppUsage - public void setClientCertificateAlias(String alias) { + @SystemApi + public void setClientCertificateAlias(@NonNull String alias) { setFieldValue(CLIENT_CERT_KEY, alias, CLIENT_CERT_PREFIX); - setFieldValue(PRIVATE_KEY_ID_KEY, alias, Credentials.USER_PRIVATE_KEY); + setFieldValue(PRIVATE_KEY_ID_KEY, alias, USER_PRIVATE_KEY); // Also, set engine parameters if (TextUtils.isEmpty(alias)) { setFieldValue(ENGINE_KEY, ENGINE_DISABLE); @@ -783,11 +920,12 @@ public class WifiEnterpriseConfig implements Parcelable { } /** - * Get client certificate alias - * @return alias to the client certificate + * Get client certificate alias. + * @return alias to the client certificate, or an empty string if unset. * @hide */ - @UnsupportedAppUsage + @NonNull + @SystemApi public String getClientCertificateAlias() { return getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); } @@ -911,8 +1049,10 @@ public class WifiEnterpriseConfig implements Parcelable { } /** - * @hide + * Get the client private key as supplied in {@link #setClientKeyEntryWithCertificateChain}, or + * null if unset. */ + @Nullable public PrivateKey getClientPrivateKey() { return mClientPrivateKey; } @@ -939,6 +1079,12 @@ public class WifiEnterpriseConfig implements Parcelable { /** * Set alternate subject match. This is the substring to be matched against the * alternate subject of the authentication server certificate. + * + * Note: If no alternate subject is set for an Enterprise configuration, either by not calling + * this API, or by calling it with null, or not setting domain suffix match using the + * {@link #setDomainSuffixMatch(String)}, then the server certificate validation is incomplete - + * which means that the connection is not secure. + * * @param altSubjectMatch substring to be matched, for example * DNS:server.example.com;EMAIL:server@example.com */ @@ -973,6 +1119,12 @@ public class WifiEnterpriseConfig implements Parcelable { * ORed ogether. * <p>For example, domain_suffix_match=example.com would match test.example.com but would not * match test-example.com. + * + * Note: If no domain suffix is set for an Enterprise configuration, either by not calling this + * API, or by calling it with null, or not setting alternate subject match using the + * {@link #setAltSubjectMatch(String)}, then the server certificate + * validation is incomplete - which means that the connection is not secure. + * * @param domain The domain value */ public void setDomainSuffixMatch(String domain) { @@ -1141,6 +1293,7 @@ public class WifiEnterpriseConfig implements Parcelable { if (mPhase2Method > 0 && mPhase2Method < Phase2.strings.length) { sb.append("phase2_method: ").append(Phase2.strings[mPhase2Method]).append("\n"); } + sb.append(" ocsp: ").append(mOcsp).append("\n"); return sb.toString(); } @@ -1190,4 +1343,103 @@ public class WifiEnterpriseConfig implements Parcelable { public boolean isAppInstalledCaCert() { return mIsAppInstalledCaCert; } + + /** + * Set the OCSP type. + * @param ocsp is one of {@link ##OCSP_NONE}, {@link #OCSP_REQUEST_CERT_STATUS}, + * {@link #OCSP_REQUIRE_CERT_STATUS} or + * {@link #OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS} + * @throws IllegalArgumentException if the OCSP type is invalid + * @hide + */ + @SystemApi + public void setOcsp(@Ocsp int ocsp) { + if (ocsp >= OCSP_NONE && ocsp <= OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS) { + mOcsp = ocsp; + } else { + throw new IllegalArgumentException("Invalid OCSP type."); + } + } + + /** + * Get the OCSP type. + * @hide + */ + @SystemApi + public @Ocsp int getOcsp() { + return mOcsp; + } + + /** + * Utility method to determine whether the configuration's authentication method is SIM-based. + * + * @return true if the credential information requires SIM card for current authentication + * method, otherwise it returns false. + */ + public boolean isAuthenticationSimBased() { + if (mEapMethod == Eap.SIM || mEapMethod == Eap.AKA || mEapMethod == Eap.AKA_PRIME) { + return true; + } + if (mEapMethod == Eap.PEAP) { + return mPhase2Method == Phase2.SIM || mPhase2Method == Phase2.AKA + || mPhase2Method == Phase2.AKA_PRIME; + } + return false; + } + + /** + * Set the WAPI certificate suite name on wpa_supplicant. + * + * If this field is not specified, WAPI-CERT uses ASU ID from WAI packet + * as the certificate suite name automatically. + * + * @param wapiCertSuite The name for WAPI certificate suite, or empty string to clear. + * @hide + */ + @SystemApi + public void setWapiCertSuite(@NonNull String wapiCertSuite) { + setFieldValue(WAPI_CERT_SUITE_KEY, wapiCertSuite); + } + + /** + * Get the WAPI certificate suite name + * @return the certificate suite name + * @hide + */ + @NonNull + @SystemApi + public String getWapiCertSuite() { + return getFieldValue(WAPI_CERT_SUITE_KEY); + } + + /** + * Method determines whether the Enterprise configuration is insecure. An insecure + * configuration is one where EAP method requires a CA certification, i.e. PEAP, TLS, or + * TTLS, and any of the following conditions are met: + * - Both certificate and CA path are not configured. + * - Both alternative subject match and domain suffix match are not set. + * + * Note: this method does not exhaustively check security of the configuration - i.e. a return + * value of {@code false} is not a guarantee that the configuration is secure. + * @hide + */ + public boolean isInsecure() { + if (mEapMethod != Eap.PEAP && mEapMethod != Eap.TLS && mEapMethod != Eap.TTLS) { + return false; + } + if (TextUtils.isEmpty(getAltSubjectMatch()) + && TextUtils.isEmpty(getDomainSuffixMatch())) { + // Both subject and domain match are not set, it's insecure. + return true; + } + if (mIsAppInstalledCaCert) { + // CA certificate is installed by App, it's secure. + return false; + } + if (getCaCertificateAliases() != null) { + // CA certificate alias from keyStore is set, it's secure. + return false; + } + return TextUtils.isEmpty(getCaPath()); + } } diff --git a/wifi/java/android/net/wifi/WifiFrameworkInitializer.java b/wifi/java/android/net/wifi/WifiFrameworkInitializer.java new file mode 100644 index 000000000000..1507199b0264 --- /dev/null +++ b/wifi/java/android/net/wifi/WifiFrameworkInitializer.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2019 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.annotation.SystemApi; +import android.app.SystemServiceRegistry; +import android.content.Context; +import android.net.wifi.aware.IWifiAwareManager; +import android.net.wifi.aware.WifiAwareManager; +import android.net.wifi.p2p.IWifiP2pManager; +import android.net.wifi.p2p.WifiP2pManager; +import android.net.wifi.rtt.IWifiRttManager; +import android.net.wifi.rtt.WifiRttManager; +import android.os.HandlerThread; +import android.os.Looper; + +/** + * Class for performing registration for all Wifi services. + * + * @hide + */ +@SystemApi +public class WifiFrameworkInitializer { + + /** + * A class implementing the lazy holder idiom: the unique static instance + * of {@link #INSTANCE} is instantiated in a thread-safe way (guaranteed by + * the language specs) the first time that NoPreloadHolder is referenced in getInstanceLooper(). + * + * This is necessary because we can't spawn a new thread in {@link #registerServiceWrappers()}. + * {@link #registerServiceWrappers()} is called during the Zygote phase, which disallows + * spawning new threads. Naming the class "NoPreloadHolder" ensures that the classloader will + * not preload this class, inadvertently spawning the thread too early. + */ + private static class NoPreloadHolder { + private static final HandlerThread INSTANCE = createInstance(); + + private static HandlerThread createInstance() { + HandlerThread thread = new HandlerThread("WifiManagerThread"); + thread.start(); + return thread; + } + } + + private static Looper getInstanceLooper() { + return NoPreloadHolder.INSTANCE.getLooper(); + } + + private WifiFrameworkInitializer() {} + + /** + * Called by {@link SystemServiceRegistry}'s static initializer and registers all Wifi services + * to {@link Context}, so that {@link Context#getSystemService} can return them. + * + * @throws IllegalStateException if this is called from anywhere besides + * {@link SystemServiceRegistry} + */ + public static void registerServiceWrappers() { + SystemServiceRegistry.registerContextAwareService( + Context.WIFI_SERVICE, + WifiManager.class, + (context, serviceBinder) -> { + IWifiManager service = IWifiManager.Stub.asInterface(serviceBinder); + return new WifiManager(context, service, getInstanceLooper()); + } + ); + SystemServiceRegistry.registerStaticService( + Context.WIFI_P2P_SERVICE, + WifiP2pManager.class, + serviceBinder -> { + IWifiP2pManager service = IWifiP2pManager.Stub.asInterface(serviceBinder); + return new WifiP2pManager(service); + } + ); + SystemServiceRegistry.registerContextAwareService( + Context.WIFI_AWARE_SERVICE, + WifiAwareManager.class, + (context, serviceBinder) -> { + IWifiAwareManager service = IWifiAwareManager.Stub.asInterface(serviceBinder); + return new WifiAwareManager(context, service); + } + ); + SystemServiceRegistry.registerContextAwareService( + Context.WIFI_SCANNING_SERVICE, + WifiScanner.class, + (context, serviceBinder) -> { + IWifiScanner service = IWifiScanner.Stub.asInterface(serviceBinder); + return new WifiScanner(context, service, getInstanceLooper()); + } + ); + SystemServiceRegistry.registerContextAwareService( + Context.WIFI_RTT_RANGING_SERVICE, + WifiRttManager.class, + (context, serviceBinder) -> { + IWifiRttManager service = IWifiRttManager.Stub.asInterface(serviceBinder); + return new WifiRttManager(context, service); + } + ); + SystemServiceRegistry.registerContextAwareService( + Context.WIFI_RTT_SERVICE, + RttManager.class, + context -> { + WifiRttManager wifiRttManager = context.getSystemService(WifiRttManager.class); + return new RttManager(context, wifiRttManager); + } + ); + } +} diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index 0204113b614b..53883674e058 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -17,16 +17,18 @@ package android.net.wifi; import android.annotation.IntRange; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.net.NetworkInfo.DetailedState; -import android.net.NetworkUtils; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import com.android.net.module.util.Inet4AddressUtils; + import java.net.Inet4Address; import java.net.InetAddress; import java.net.UnknownHostException; @@ -34,7 +36,7 @@ import java.util.EnumMap; import java.util.Locale; /** - * Describes the state of any Wifi connection that is active or + * Describes the state of any Wi-Fi connection that is active or * is in the process of being set up. */ public class WifiInfo implements Parcelable { @@ -53,7 +55,7 @@ public class WifiInfo implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00"; static { @@ -79,8 +81,12 @@ public class WifiInfo implements Parcelable { private WifiSsid mWifiSsid; private int mNetworkId; - /** @hide **/ - @UnsupportedAppUsage + /** + * Used to indicate that the RSSI is invalid, for example if no RSSI measurements are available + * yet. + * @hide + */ + @SystemApi public static final int INVALID_RSSI = -127; /** @hide **/ @@ -96,6 +102,11 @@ public class WifiInfo implements Parcelable { private int mRssi; /** + * Wi-Fi standard for the connection + */ + private @WifiAnnotations.WifiStandard int mWifiStandard; + + /** * The unit in which links speeds are expressed. */ public static final String LINK_SPEED_UNITS = "Mbps"; @@ -112,11 +123,21 @@ public class WifiInfo implements Parcelable { private int mTxLinkSpeed; /** + * Max supported Tx(transmit) link speed in Mbps + */ + private int mMaxSupportedTxLinkSpeed; + + /** * Rx(receive) Link speed in Mbps */ private int mRxLinkSpeed; /** + * Max supported Rx(receive) link speed in Mbps + */ + private int mMaxSupportedRxLinkSpeed; + + /** * Frequency in MHz */ public static final String FREQUENCY_UNITS = "MHz"; @@ -156,7 +177,7 @@ public class WifiInfo implements Parcelable { * If connected to a network suggestion or specifier, store the package name of the app, * else null. */ - private String mNetworkSuggestionOrSpecifierPackageName; + private String mRequestingPackageName; /** * Running total count of lost (not ACKed) transmitted unicast data packets. @@ -179,32 +200,89 @@ public class WifiInfo implements Parcelable { */ public long rxSuccess; + private double mLostTxPacketsPerSecond; + /** * Average rate of lost transmitted packets, in units of packets per second. * @hide */ - public double txBadRate; + @SystemApi + public double getLostTxPacketsPerSecond() { + return mLostTxPacketsPerSecond; + } + + /** @hide */ + public void setLostTxPacketsPerSecond(double lostTxPacketsPerSecond) { + mLostTxPacketsPerSecond = lostTxPacketsPerSecond; + } + + private double mTxRetriedTxPacketsPerSecond; + /** * Average rate of transmitted retry packets, in units of packets per second. * @hide */ - public double txRetriesRate; + @SystemApi + public double getRetriedTxPacketsPerSecond() { + return mTxRetriedTxPacketsPerSecond; + } + + /** @hide */ + public void setRetriedTxPacketsRate(double txRetriedTxPacketsPerSecond) { + mTxRetriedTxPacketsPerSecond = txRetriedTxPacketsPerSecond; + } + + private double mSuccessfulTxPacketsPerSecond; + /** * Average rate of successfully transmitted unicast packets, in units of packets per second. * @hide */ - public double txSuccessRate; + @SystemApi + public double getSuccessfulTxPacketsPerSecond() { + return mSuccessfulTxPacketsPerSecond; + } + + /** @hide */ + public void setSuccessfulTxPacketsPerSecond(double successfulTxPacketsPerSecond) { + mSuccessfulTxPacketsPerSecond = successfulTxPacketsPerSecond; + } + + private double mSuccessfulRxPacketsPerSecond; + /** * Average rate of received unicast data packets, in units of packets per second. * @hide */ - public double rxSuccessRate; + @SystemApi + public double getSuccessfulRxPacketsPerSecond() { + return mSuccessfulRxPacketsPerSecond; + } + + /** @hide */ + public void setSuccessfulRxPacketsPerSecond(double successfulRxPacketsPerSecond) { + mSuccessfulRxPacketsPerSecond = successfulRxPacketsPerSecond; + } + + /** @hide */ + @UnsupportedAppUsage + public int score; /** + * The current Wifi score. + * NOTE: this value should only be used for debugging purposes. Do not rely on this value for + * any computations. The meaning of this value can and will change at any time without warning. * @hide */ - @UnsupportedAppUsage - public int score; + @SystemApi + public int getScore() { + return score; + } + + /** @hide */ + public void setScore(int score) { + this.score = score; + } /** * Flag indicating that AP has hinted that upstream connection is metered, @@ -212,6 +290,11 @@ public class WifiInfo implements Parcelable { */ private boolean mMeteredHint; + /** + * Passpoint unique key + */ + private String mPasspointUniqueId; + /** @hide */ @UnsupportedAppUsage public WifiInfo() { @@ -234,21 +317,24 @@ public class WifiInfo implements Parcelable { setLinkSpeed(LINK_SPEED_UNKNOWN); setTxLinkSpeedMbps(LINK_SPEED_UNKNOWN); setRxLinkSpeedMbps(LINK_SPEED_UNKNOWN); + setMaxSupportedTxLinkSpeedMbps(LINK_SPEED_UNKNOWN); + setMaxSupportedRxLinkSpeedMbps(LINK_SPEED_UNKNOWN); setFrequency(-1); setMeteredHint(false); setEphemeral(false); setOsuAp(false); - setNetworkSuggestionOrSpecifierPackageName(null); + setRequestingPackageName(null); setFQDN(null); setProviderFriendlyName(null); + setPasspointUniqueId(null); txBad = 0; txSuccess = 0; rxSuccess = 0; txRetries = 0; - txBadRate = 0; - txSuccessRate = 0; - rxSuccessRate = 0; - txRetriesRate = 0; + mLostTxPacketsPerSecond = 0; + mSuccessfulTxPacketsPerSecond = 0; + mSuccessfulRxPacketsPerSecond = 0; + mTxRetriedTxPacketsPerSecond = 0; score = 0; } @@ -272,8 +358,8 @@ public class WifiInfo implements Parcelable { mMeteredHint = source.mMeteredHint; mEphemeral = source.mEphemeral; mTrusted = source.mTrusted; - mNetworkSuggestionOrSpecifierPackageName = - source.mNetworkSuggestionOrSpecifierPackageName; + mRequestingPackageName = + source.mRequestingPackageName; mOsuAp = source.mOsuAp; mFqdn = source.mFqdn; mProviderFriendlyName = source.mProviderFriendlyName; @@ -281,11 +367,68 @@ public class WifiInfo implements Parcelable { txRetries = source.txRetries; txSuccess = source.txSuccess; rxSuccess = source.rxSuccess; - txBadRate = source.txBadRate; - txRetriesRate = source.txRetriesRate; - txSuccessRate = source.txSuccessRate; - rxSuccessRate = source.rxSuccessRate; + mLostTxPacketsPerSecond = source.mLostTxPacketsPerSecond; + mTxRetriedTxPacketsPerSecond = source.mTxRetriedTxPacketsPerSecond; + mSuccessfulTxPacketsPerSecond = source.mSuccessfulTxPacketsPerSecond; + mSuccessfulRxPacketsPerSecond = source.mSuccessfulRxPacketsPerSecond; score = source.score; + mWifiStandard = source.mWifiStandard; + mMaxSupportedTxLinkSpeed = source.mMaxSupportedTxLinkSpeed; + mMaxSupportedRxLinkSpeed = source.mMaxSupportedRxLinkSpeed; + mPasspointUniqueId = source.mPasspointUniqueId; + } + } + + /** Builder for WifiInfo */ + public static final class Builder { + private final WifiInfo mWifiInfo = new WifiInfo(); + + /** + * Set the SSID, in the form of a raw byte array. + * @see WifiInfo#getSSID() + */ + @NonNull + public Builder setSsid(@NonNull byte[] ssid) { + mWifiInfo.setSSID(WifiSsid.createFromByteArray(ssid)); + return this; + } + + /** + * Set the BSSID. + * @see WifiInfo#getBSSID() + */ + @NonNull + public Builder setBssid(@NonNull String bssid) { + mWifiInfo.setBSSID(bssid); + return this; + } + + /** + * Set the RSSI, in dBm. + * @see WifiInfo#getRssi() + */ + @NonNull + public Builder setRssi(int rssi) { + mWifiInfo.setRssi(rssi); + return this; + } + + /** + * Set the network ID. + * @see WifiInfo#getNetworkId() + */ + @NonNull + public Builder setNetworkId(int networkId) { + mWifiInfo.setNetworkId(networkId); + return this; + } + + /** + * Build a WifiInfo object. + */ + @NonNull + public WifiInfo build() { + return new WifiInfo(mWifiInfo); } } @@ -299,9 +442,8 @@ public class WifiInfo implements Parcelable { * <p> * If the SSID can be decoded as UTF-8, it will be returned surrounded by double * quotation marks. Otherwise, it is returned as a string of hex digits. - * The SSID may be - * <lt><unknown ssid>, if there is no network currently connected or if the caller has - * insufficient permissions to access the SSID.<lt> + * The SSID may be {@link WifiManager#UNKNOWN_SSID}, if there is no network currently connected + * or if the caller has insufficient permissions to access the SSID. * </p> * <p> * Prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method @@ -317,10 +459,10 @@ public class WifiInfo implements Parcelable { return "\"" + unicode + "\""; } else { String hex = mWifiSsid.getHexString(); - return (hex != null) ? hex : WifiSsid.NONE; + return (hex != null) ? hex : WifiManager.UNKNOWN_SSID; } } - return WifiSsid.NONE; + return WifiManager.UNKNOWN_SSID; } /** @hide */ @@ -374,6 +516,22 @@ public class WifiInfo implements Parcelable { } /** + * Sets the Wi-Fi standard + * @hide + */ + public void setWifiStandard(@WifiAnnotations.WifiStandard int wifiStandard) { + mWifiStandard = wifiStandard; + } + + /** + * Get connection Wi-Fi standard + * @return the connection Wi-Fi standard + */ + public @WifiAnnotations.WifiStandard int getWifiStandard() { + return mWifiStandard; + } + + /** * Returns the current link speed in {@link #LINK_SPEED_UNITS}. * @return the link speed or {@link #LINK_SPEED_UNKNOWN} if link speed is unknown. * @see #LINK_SPEED_UNITS @@ -400,6 +558,15 @@ public class WifiInfo implements Parcelable { } /** + * Returns the maximum supported transmit link speed in Mbps + * @return the max supported tx link speed or {@link #LINK_SPEED_UNKNOWN} if link speed is + * unknown. @see #LINK_SPEED_UNKNOWN + */ + public int getMaxSupportedTxLinkSpeedMbps() { + return mMaxSupportedTxLinkSpeed; + } + + /** * Update the last transmitted packet bit rate in Mbps. * @hide */ @@ -408,6 +575,14 @@ public class WifiInfo implements Parcelable { } /** + * Set the maximum supported transmit link speed in Mbps + * @hide + */ + public void setMaxSupportedTxLinkSpeedMbps(int maxSupportedTxLinkSpeed) { + mMaxSupportedTxLinkSpeed = maxSupportedTxLinkSpeed; + } + + /** * Returns the current receive link speed in Mbps. * @return the Rx link speed or {@link #LINK_SPEED_UNKNOWN} if link speed is unknown. * @see #LINK_SPEED_UNKNOWN @@ -418,6 +593,15 @@ public class WifiInfo implements Parcelable { } /** + * Returns the maximum supported receive link speed in Mbps + * @return the max supported Rx link speed or {@link #LINK_SPEED_UNKNOWN} if link speed is + * unknown. @see #LINK_SPEED_UNKNOWN + */ + public int getMaxSupportedRxLinkSpeedMbps() { + return mMaxSupportedRxLinkSpeed; + } + + /** * Update the last received packet bit rate in Mbps. * @hide */ @@ -426,6 +610,14 @@ public class WifiInfo implements Parcelable { } /** + * Set the maximum supported receive link speed in Mbps + * @hide + */ + public void setMaxSupportedRxLinkSpeedMbps(int maxSupportedRxLinkSpeed) { + mMaxSupportedRxLinkSpeed = maxSupportedRxLinkSpeed; + } + + /** * Returns the current frequency in {@link #FREQUENCY_UNITS}. * @return the frequency. * @see #FREQUENCY_UNITS @@ -441,7 +633,6 @@ public class WifiInfo implements Parcelable { /** * @hide - * TODO: makes real freq boundaries */ public boolean is24GHz() { return ScanResult.is24GHz(mFrequency); @@ -449,7 +640,6 @@ public class WifiInfo implements Parcelable { /** * @hide - * TODO: makes real freq boundaries */ @UnsupportedAppUsage public boolean is5GHz() { @@ -457,6 +647,13 @@ public class WifiInfo implements Parcelable { } /** + * @hide + */ + public boolean is6GHz() { + return ScanResult.is6GHz(mFrequency); + } + + /** * Record the MAC address of the WLAN interface * @param macAddress the MAC address in {@code XX:XX:XX:XX:XX:XX} form * @hide @@ -501,8 +698,15 @@ public class WifiInfo implements Parcelable { mEphemeral = ephemeral; } - /** {@hide} */ - @UnsupportedAppUsage + /** + * Returns true if the current Wifi network is ephemeral, false otherwise. + * An ephemeral network is a network that is temporary and not persisted in the system. + * Ephemeral networks cannot be forgotten, only disabled with + * {@link WifiManager#disableEphemeralNetwork(String)}. + * + * @hide + */ + @SystemApi public boolean isEphemeral() { return mEphemeral; } @@ -541,6 +745,11 @@ public class WifiInfo implements Parcelable { /** * Returns the Fully Qualified Domain Name of the network if it is a Passpoint network. + * <p> + * The FQDN may be + * <lt>{@code null} if no network currently connected, currently connected network is not + * passpoint network or the caller has insufficient permissions to access the FQDN.</lt> + * </p> */ public @Nullable String getPasspointFqdn() { return mFqdn; @@ -553,19 +762,31 @@ public class WifiInfo implements Parcelable { /** * Returns the Provider Friendly Name of the network if it is a Passpoint network. + * <p> + * The Provider Friendly Name may be + * <lt>{@code null} if no network currently connected, currently connected network is not + * passpoint network or the caller has insufficient permissions to access the Provider Friendly + * Name. </lt> + * </p> */ public @Nullable String getPasspointProviderFriendlyName() { return mProviderFriendlyName; } /** {@hide} */ - public void setNetworkSuggestionOrSpecifierPackageName(@Nullable String packageName) { - mNetworkSuggestionOrSpecifierPackageName = packageName; + public void setRequestingPackageName(@Nullable String packageName) { + mRequestingPackageName = packageName; } - /** {@hide} */ - public @Nullable String getNetworkSuggestionOrSpecifierPackageName() { - return mNetworkSuggestionOrSpecifierPackageName; + /** + * If this network was created in response to an app request (e.g. through Network Suggestion + * or Network Specifier), return the package name of the app that made the request. + * Null otherwise. + * @hide + */ + @SystemApi + public @Nullable String getRequestingPackageName() { + return mRequestingPackageName; } @@ -612,7 +833,7 @@ public class WifiInfo implements Parcelable { public int getIpAddress() { int result = 0; if (mIpAddress instanceof Inet4Address) { - result = NetworkUtils.inetAddressToInt((Inet4Address)mIpAddress); + result = Inet4AddressUtils.inet4AddressToIntHTL((Inet4Address) mIpAddress); } return result; } @@ -626,7 +847,7 @@ public class WifiInfo implements Parcelable { return mWifiSsid.isHidden(); } - /** + /** * Map a supplicant state into a fine-grained network connectivity state. * @param suppState the supplicant state * @return the corresponding {@link DetailedState} @@ -686,15 +907,20 @@ public class WifiInfo implements Parcelable { StringBuffer sb = new StringBuffer(); String none = "<none>"; - sb.append("SSID: ").append(mWifiSsid == null ? WifiSsid.NONE : mWifiSsid) + sb.append("SSID: ").append(mWifiSsid == null ? WifiManager.UNKNOWN_SSID : mWifiSsid) .append(", BSSID: ").append(mBSSID == null ? none : mBSSID) .append(", MAC: ").append(mMacAddress == null ? none : mMacAddress) .append(", Supplicant state: ") .append(mSupplicantState == null ? none : mSupplicantState) + .append(", Wi-Fi standard: ").append(mWifiStandard) .append(", RSSI: ").append(mRssi) .append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS) .append(", Tx Link speed: ").append(mTxLinkSpeed).append(LINK_SPEED_UNITS) + .append(", Max Supported Tx Link speed: ") + .append(mMaxSupportedTxLinkSpeed).append(LINK_SPEED_UNITS) .append(", Rx Link speed: ").append(mRxLinkSpeed).append(LINK_SPEED_UNITS) + .append(", Max Supported Rx Link speed: ") + .append(mMaxSupportedRxLinkSpeed).append(LINK_SPEED_UNITS) .append(", Frequency: ").append(mFrequency).append(FREQUENCY_UNITS) .append(", Net ID: ").append(mNetworkId) .append(", Metered hint: ").append(mMeteredHint) @@ -734,18 +960,22 @@ public class WifiInfo implements Parcelable { dest.writeInt(mTrusted ? 1 : 0); dest.writeInt(score); dest.writeLong(txSuccess); - dest.writeDouble(txSuccessRate); + dest.writeDouble(mSuccessfulTxPacketsPerSecond); dest.writeLong(txRetries); - dest.writeDouble(txRetriesRate); + dest.writeDouble(mTxRetriedTxPacketsPerSecond); dest.writeLong(txBad); - dest.writeDouble(txBadRate); + dest.writeDouble(mLostTxPacketsPerSecond); dest.writeLong(rxSuccess); - dest.writeDouble(rxSuccessRate); + dest.writeDouble(mSuccessfulRxPacketsPerSecond); mSupplicantState.writeToParcel(dest, flags); dest.writeInt(mOsuAp ? 1 : 0); - dest.writeString(mNetworkSuggestionOrSpecifierPackageName); + dest.writeString(mRequestingPackageName); dest.writeString(mFqdn); dest.writeString(mProviderFriendlyName); + dest.writeInt(mWifiStandard); + dest.writeInt(mMaxSupportedTxLinkSpeed); + dest.writeInt(mMaxSupportedRxLinkSpeed); + dest.writeString(mPasspointUniqueId); } /** Implement the Parcelable interface {@hide} */ @@ -775,18 +1005,22 @@ public class WifiInfo implements Parcelable { info.mTrusted = in.readInt() != 0; info.score = in.readInt(); info.txSuccess = in.readLong(); - info.txSuccessRate = in.readDouble(); + info.mSuccessfulTxPacketsPerSecond = in.readDouble(); info.txRetries = in.readLong(); - info.txRetriesRate = in.readDouble(); + info.mTxRetriedTxPacketsPerSecond = in.readDouble(); info.txBad = in.readLong(); - info.txBadRate = in.readDouble(); + info.mLostTxPacketsPerSecond = in.readDouble(); info.rxSuccess = in.readLong(); - info.rxSuccessRate = in.readDouble(); + info.mSuccessfulRxPacketsPerSecond = in.readDouble(); info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in); info.mOsuAp = in.readInt() != 0; - info.mNetworkSuggestionOrSpecifierPackageName = in.readString(); + info.mRequestingPackageName = in.readString(); info.mFqdn = in.readString(); info.mProviderFriendlyName = in.readString(); + info.mWifiStandard = in.readInt(); + info.mMaxSupportedTxLinkSpeed = in.readInt(); + info.mMaxSupportedRxLinkSpeed = in.readInt(); + info.mPasspointUniqueId = in.readString(); return info; } @@ -794,4 +1028,24 @@ public class WifiInfo implements Parcelable { return new WifiInfo[size]; } }; + + /** + * Set the Passpoint unique identifier for the current connection + * + * @param passpointUniqueId Unique identifier + * @hide + */ + public void setPasspointUniqueId(@Nullable String passpointUniqueId) { + mPasspointUniqueId = passpointUniqueId; + } + + /** + * Get the Passpoint unique identifier for the current connection + * + * @return Passpoint unique identifier + * @hide + */ + public @Nullable String getPasspointUniqueId() { + return mPasspointUniqueId; + } } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 29e09a11590b..fb6af5b550b0 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -22,11 +22,13 @@ import static android.Manifest.permission.READ_WIFI_CREDENTIAL; import android.annotation.CallbackExecutor; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.app.ActivityManager; @@ -35,9 +37,8 @@ import android.content.Context; import android.content.pm.ParceledListSlice; import android.net.ConnectivityManager; import android.net.DhcpInfo; +import android.net.MacAddress; import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; import android.net.NetworkStack; import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.wifi.hotspot2.OsuProvider; @@ -46,26 +47,24 @@ import android.net.wifi.hotspot2.ProvisioningCallback; import android.os.Binder; import android.os.Build; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; -import android.os.Message; -import android.os.Messenger; import android.os.RemoteException; import android.os.WorkSource; +import android.os.connectivity.WifiActivityEnergyInfo; +import android.text.TextUtils; +import android.util.CloseGuard; import android.util.Log; import android.util.Pair; import android.util.SparseArray; 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; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.net.InetAddress; import java.util.ArrayList; @@ -73,8 +72,9 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; -import java.util.concurrent.CountDownLatch; +import java.util.StringTokenizer; import java.util.concurrent.Executor; /** @@ -153,12 +153,11 @@ public class WifiManager { @Deprecated public static final int ERROR_AUTH_FAILURE_EAP_FAILURE = 3; - /** - * Maximum number of active network suggestions allowed per app. - * @hide - */ - public static final int NETWORK_SUGGESTIONS_MAX_PER_APP = - ActivityManager.isLowRamDeviceStatic() ? 256 : 1024; + /** @hide */ + public static final int NETWORK_SUGGESTIONS_MAX_PER_APP_LOW_RAM = 256; + + /** @hide */ + public static final int NETWORK_SUGGESTIONS_MAX_PER_APP_HIGH_RAM = 1024; /** * Reason code if all of the network suggestions were successfully added or removed. @@ -180,6 +179,8 @@ public class WifiManager { /** * Reason code if one or more of the network suggestions added already exists in platform's * database. + * Note: this code will not be returned with Android 11 as in-place modification is allowed, + * please check {@link #addNetworkSuggestions(List)}. * @see WifiNetworkSuggestion#equals(Object) */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 3; @@ -187,6 +188,8 @@ public class WifiManager { /** * Reason code if the number of network suggestions provided by the app crosses the max * threshold set per app. + * The framework will reject all suggestions provided by {@link #addNetworkSuggestions(List)} if + * the total size exceeds the limit. * @see #getMaxNumberOfNetworkSuggestionsPerApp() */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 4; @@ -194,9 +197,30 @@ public class WifiManager { /** * Reason code if one or more of the network suggestions removed does not exist in platform's * database. + * The framework won't remove any suggestions if one or more of suggestions provided + * by {@link #removeNetworkSuggestions(List)} does not exist in database. + * @see WifiNetworkSuggestion#equals(Object) */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5; + /** + * Reason code if one or more of the network suggestions added is not allowed. + * The framework will reject all suggestions provided by {@link #addNetworkSuggestions(List)} + * if one or more of them is not allowed. + * This error may be caused by suggestion is using SIM-based encryption method, but calling app + * is not carrier privileged. + */ + public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED = 6; + + /** + * Reason code if one or more of the network suggestions added is invalid. Framework will reject + * all the suggestions in the list. + * The framework will reject all suggestions provided by {@link #addNetworkSuggestions(List)} + * if one or more of them is invalid. + * Please use {@link WifiNetworkSuggestion.Builder} to create network suggestions. + */ + public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID = 7; + /** @hide */ @IntDef(prefix = { "STATUS_NETWORK_SUGGESTIONS_" }, value = { STATUS_NETWORK_SUGGESTIONS_SUCCESS, @@ -205,21 +229,54 @@ public class WifiManager { STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE, STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP, STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID, + STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED, + STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID, }) @Retention(RetentionPolicy.SOURCE) public @interface NetworkSuggestionsStatusCode {} /** - * Broadcast intent action indicating whether Wi-Fi scanning is allowed currently - * @hide + * Reason code if suggested network connection attempt failed with an unknown failure. + */ + public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_UNKNOWN = 0; + /** + * Reason code if suggested network connection attempt failed with association failure. */ - public static final String WIFI_SCAN_AVAILABLE = "wifi_scan_available"; + public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_ASSOCIATION = 1; + /** + * Reason code if suggested network connection attempt failed with an authentication failure. + */ + public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION = 2; + /** + * Reason code if suggested network connection attempt failed with an IP provision failure. + */ + public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_IP_PROVISIONING = 3; + + /** @hide */ + @IntDef(prefix = {"STATUS_SUGGESTION_CONNECTION_FAILURE_"}, + value = {STATUS_SUGGESTION_CONNECTION_FAILURE_UNKNOWN, + STATUS_SUGGESTION_CONNECTION_FAILURE_ASSOCIATION, + STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION, + STATUS_SUGGESTION_CONNECTION_FAILURE_IP_PROVISIONING + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SuggestionConnectionStatusCode {} /** - * Extra int indicating scan availability, WIFI_STATE_ENABLED and WIFI_STATE_DISABLED - * @hide + * Broadcast intent action indicating whether Wi-Fi scanning is currently available. + * Available extras: + * - {@link #EXTRA_SCAN_AVAILABLE} */ - public static final String EXTRA_SCAN_AVAILABLE = "scan_enabled"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_WIFI_SCAN_AVAILABILITY_CHANGED = + "android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED"; + + /** + * A boolean extra indicating whether scanning is currently available. + * Sent in the broadcast {@link #ACTION_WIFI_SCAN_AVAILABILITY_CHANGED}. + * Its value is true if scanning is currently available, false otherwise. + */ + public static final String EXTRA_SCAN_AVAILABLE = "android.net.wifi.extra.SCAN_AVAILABLE"; /** * Broadcast intent action indicating that the credential of a Wi-Fi network @@ -355,14 +412,6 @@ public class WifiManager { * @hide */ public static final String EXTRA_DELAY = "android.net.wifi.extra.DELAY"; - /** - * String representation of an URL. - * - * Retrieve with {@link android.content.Intent#getStringExtra(String)}. - * - * @hide - */ - public static final String EXTRA_URL = "android.net.wifi.extra.URL"; /** * Broadcast intent action indicating a Passpoint subscription remediation frame has been @@ -395,34 +444,49 @@ public class WifiManager { "android.net.wifi.extra.SUBSCRIPTION_REMEDIATION_METHOD"; /** - * Activity Action: lunch OSU (Online Sign Up) view. + * Activity Action: Receiver should launch Passpoint OSU (Online Sign Up) view. * Included extras: * * {@link #EXTRA_OSU_NETWORK}: {@link Network} instance associated with OSU AP. * {@link #EXTRA_URL}: String representation of a server URL used for OSU process. * - * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered - * components will be launched. - * * @hide */ + @SystemApi @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_PASSPOINT_LAUNCH_OSU_VIEW = "android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW"; /** - * The lookup key for a {@link android.net.Network} associated with OSU server. + * The lookup key for a {@link android.net.Network} associated with a Passpoint OSU server. + * Included in the {@link #ACTION_PASSPOINT_LAUNCH_OSU_VIEW} broadcast. * * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}. * * @hide */ + @SystemApi public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK"; /** + * String representation of an URL for Passpoint OSU. + * Included in the {@link #ACTION_PASSPOINT_LAUNCH_OSU_VIEW} broadcast. + * + * Retrieve with {@link android.content.Intent#getStringExtra(String)}. + * + * @hide + */ + @SystemApi + public static final String EXTRA_URL = "android.net.wifi.extra.URL"; + + /** * Broadcast intent action indicating that Wi-Fi has been enabled, disabled, * enabling, disabling, or unknown. One extra provides this state as an int. - * Another extra provides the previous state, if available. + * Another extra provides the previous state, if available. No network-related + * permissions are required to subscribe to this broadcast. + * + * <p class="note">This broadcast is not delivered to manifest receivers in + * applications that target API version 26 or later. * * @see #EXTRA_WIFI_STATE * @see #EXTRA_PREVIOUS_WIFI_STATE @@ -515,14 +579,22 @@ public class WifiManager { public static final String EXTRA_WIFI_AP_STATE = "wifi_state"; /** - * The look up key for an int that indicates why softAP started failed - * currently support general and no_channel - * @see #SAP_START_FAILURE_GENERAL - * @see #SAP_START_FAILURE_NO_CHANNEL + * An extra containing the int error code for Soft AP start failure. + * Can be obtained from the {@link #WIFI_AP_STATE_CHANGED_ACTION} using + * {@link android.content.Intent#getIntExtra}. + * This extra will only be attached if {@link #EXTRA_WIFI_AP_STATE} is + * attached and is equal to {@link #WIFI_AP_STATE_FAILED}. + * + * The error code will be one of: + * {@link #SAP_START_FAILURE_GENERAL}, + * {@link #SAP_START_FAILURE_NO_CHANNEL}, + * {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION} * * @hide */ - public static final String EXTRA_WIFI_AP_FAILURE_REASON = "wifi_ap_error_code"; + @SystemApi + public static final String EXTRA_WIFI_AP_FAILURE_REASON = + "android.net.wifi.extra.WIFI_AP_FAILURE_REASON"; /** * The previous Wi-Fi state. * @@ -622,15 +694,18 @@ public class WifiManager { @IntDef(flag = false, prefix = { "SAP_START_FAILURE_" }, value = { SAP_START_FAILURE_GENERAL, SAP_START_FAILURE_NO_CHANNEL, + SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION, }) @Retention(RetentionPolicy.SOURCE) public @interface SapStartFailure {} /** - * All other reasons for AP start failure besides {@link #SAP_START_FAILURE_NO_CHANNEL}. + * All other reasons for AP start failure besides {@link #SAP_START_FAILURE_NO_CHANNEL} and + * {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}. * * @hide */ + @SystemApi public static final int SAP_START_FAILURE_GENERAL= 0; /** @@ -639,8 +714,57 @@ public class WifiManager { * * @hide */ + @SystemApi public static final int SAP_START_FAILURE_NO_CHANNEL = 1; + /** + * If Wi-Fi AP start failed, this reason code means that the specified configuration + * is not supported by the current HAL version. + * + * @hide + */ + @SystemApi + public static final int SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION = 2; + + + /** @hide */ + @IntDef(flag = false, prefix = { "SAP_CLIENT_BLOCKED_REASON_" }, value = { + SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER, + SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SapClientBlockedReason {} + + /** + * If Soft Ap client is blocked, this reason code means that client doesn't exist in the + * specified configuration {@link SoftApConfiguration.Builder#setBlockedClientList(List)} + * and {@link SoftApConfiguration.Builder#setAllowedClientList(List)} + * and the {@link SoftApConfiguration.Builder#setClientControlByUserEnabled(boolean)} + * is configured as well. + * @hide + */ + @SystemApi + public static final int SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER = 0; + + /** + * If Soft Ap client is blocked, this reason code means that no more clients can be + * associated to this AP since it reached maximum capacity. The maximum capacity is + * the minimum of {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)} and + * {@link SoftApCapability#getMaxSupportedClients} which get from + * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)}. + * + * @hide + */ + @SystemApi + public static final int SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS = 1; + + /** + * Client disconnected for unspecified reason. This could for example be because the AP is being + * shut down. + * @hide + */ + public static final int SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED = 2; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"IFACE_IP_MODE_"}, value = { @@ -693,9 +817,14 @@ public class WifiManager { /** * Broadcast intent action indicating that the wifi network settings * had been reset. + * + * Note: This intent is sent as a directed broadcast to each manifest registered receiver. + * Intent will not be received by dynamically registered receivers. * @hide */ - public static final String WIFI_NETWORK_SETTINGS_RESET_ACTION = + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) + public static final String ACTION_NETWORK_SETTINGS_RESET = "android.net.wifi.action.NETWORK_SETTINGS_RESET"; /** @@ -723,7 +852,11 @@ public class WifiManager { /** * Broadcast intent action indicating that the state of Wi-Fi connectivity * has changed. An extra provides the new state - * in the form of a {@link android.net.NetworkInfo} object. + * in the form of a {@link android.net.NetworkInfo} object. No network-related + * permissions are required to subscribe to this broadcast. + * + * <p class="note">This broadcast is not delivered to manifest receivers in + * applications that target API version 26 or later. * @see #EXTRA_NETWORK_INFO */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @@ -795,19 +928,26 @@ public class WifiManager { /** * Broadcast intent action indicating that the configured networks changed. - * This can be as a result of adding/updating/deleting a network. If - * {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is set to true the new configuration - * can be retreived with the {@link #EXTRA_WIFI_CONFIGURATION} extra. If multiple - * Wi-Fi configurations changed, {@link #EXTRA_WIFI_CONFIGURATION} will not be present. + * This can be as a result of adding/updating/deleting a network. + * <br /> + * {@link #EXTRA_CHANGE_REASON} contains whether the configuration was added/changed/removed. + * {@link #EXTRA_WIFI_CONFIGURATION} is never set starting in Android 11. + * {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is set for backwards compatibility reasons, but + * its value is always true, even if only a single network changed. + * <br /> + * The {@link android.Manifest.permission#ACCESS_WIFI_STATE ACCESS_WIFI_STATE} permission is + * required to receive this broadcast. + * * @hide */ @SystemApi public static final String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE"; /** - * The lookup key for a (@link android.net.wifi.WifiConfiguration} object representing + * The lookup key for a {@link android.net.wifi.WifiConfiguration} object representing * the changed Wi-Fi configuration when the {@link #CONFIGURED_NETWORKS_CHANGED_ACTION} * broadcast is sent. + * Note: this extra is never set starting in Android 11. * @hide */ @SystemApi @@ -815,14 +955,16 @@ public class WifiManager { /** * Multiple network configurations have changed. * @see #CONFIGURED_NETWORKS_CHANGED_ACTION - * + * Note: this extra is always true starting in Android 11. * @hide */ @SystemApi public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges"; /** * The lookup key for an integer indicating the reason a Wi-Fi network configuration - * has changed. Only present if {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is {@code false} + * has changed. One of {@link #CHANGE_REASON_ADDED}, {@link #CHANGE_REASON_REMOVED}, + * {@link #CHANGE_REASON_CONFIG_CHANGE}. + * * @see #CONFIGURED_NETWORKS_CHANGED_ACTION * @hide */ @@ -902,21 +1044,37 @@ public class WifiManager { public static final String EXTRA_NEW_RSSI = "newRssi"; /** - * Broadcast intent action indicating that the link configuration - * changed on wifi. + * @see #ACTION_LINK_CONFIGURATION_CHANGED * @hide */ @UnsupportedAppUsage public static final String LINK_CONFIGURATION_CHANGED_ACTION = - "android.net.wifi.LINK_CONFIGURATION_CHANGED"; + "android.net.wifi.LINK_CONFIGURATION_CHANGED"; + + /** + * Broadcast intent action indicating that the link configuration changed on wifi. + * <br />Included Extras: + * <br />{@link #EXTRA_LINK_PROPERTIES}: {@link android.net.LinkProperties} object associated + * with the Wi-Fi network. + * <br /> No permissions are required to listen to this broadcast. + * @hide + */ + @SystemApi + public static final String ACTION_LINK_CONFIGURATION_CHANGED = + // should be android.net.wifi.action.LINK_CONFIGURATION_CHANGED, but due to + // @UnsupportedAppUsage leaving it as android.net.wifi.LINK_CONFIGURATION_CHANGED. + LINK_CONFIGURATION_CHANGED_ACTION; /** * The lookup key for a {@link android.net.LinkProperties} object associated with the - * Wi-Fi network. Retrieve with - * {@link android.content.Intent#getParcelableExtra(String)}. + * Wi-Fi network. + * Included in the {@link #ACTION_LINK_CONFIGURATION_CHANGED} broadcast. + * + * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}. * @hide */ - public static final String EXTRA_LINK_PROPERTIES = "linkProperties"; + @SystemApi + public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES"; /** * The lookup key for a {@link android.net.NetworkCapabilities} object associated with the @@ -956,24 +1114,26 @@ public class WifiManager { public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"; /** - * Activity Action: Show UI to get user approval to enable WiFi. + * Activity Action: Receiver should show UI to get user approval to enable WiFi. * <p>Input: {@link android.content.Intent#EXTRA_PACKAGE_NAME} string extra with * the name of the app requesting the action. * <p>Output: Nothing. - * + * <p>No permissions are required to send this action. * @hide */ + @SystemApi @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_ENABLE = "android.net.wifi.action.REQUEST_ENABLE"; /** - * Activity Action: Show UI to get user approval to disable WiFi. + * Activity Action: Receiver should show UI to get user approval to disable WiFi. * <p>Input: {@link android.content.Intent#EXTRA_PACKAGE_NAME} string extra with * the name of the app requesting the action. * <p>Output: Nothing. - * + * <p>No permissions are required to send this action. * @hide */ + @SystemApi @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_DISABLE = "android.net.wifi.action.REQUEST_DISABLE"; @@ -1015,7 +1175,7 @@ public class WifiManager { * @deprecated This API is non-functional and will have no impact. */ @Deprecated - public static final int WIFI_MODE_FULL = WifiProtoEnums.WIFI_MODE_FULL; // 1 + public static final int WIFI_MODE_FULL = 1; /** * In this Wi-Fi lock mode, Wi-Fi will be kept active, @@ -1029,7 +1189,7 @@ public class WifiManager { * @deprecated This API is non-functional and will have no impact. */ @Deprecated - public static final int WIFI_MODE_SCAN_ONLY = WifiProtoEnums.WIFI_MODE_SCAN_ONLY; // 2 + public static final int WIFI_MODE_SCAN_ONLY = 2; /** * In this Wi-Fi lock mode, Wi-Fi will not go to power save. @@ -1048,7 +1208,7 @@ public class WifiManager { * When there is no support from the hardware, the {@link #WIFI_MODE_FULL_HIGH_PERF} * lock will have no impact. */ - public static final int WIFI_MODE_FULL_HIGH_PERF = WifiProtoEnums.WIFI_MODE_FULL_HIGH_PERF; // 3 + public static final int WIFI_MODE_FULL_HIGH_PERF = 3; /** * In this Wi-Fi lock mode, Wi-Fi will operate with a priority to achieve low latency. @@ -1078,8 +1238,8 @@ public class WifiManager { * lock will be effective when app is running in foreground and screen is on, * while the {@link #WIFI_MODE_FULL_HIGH_PERF} lock will take effect otherwise. */ - public static final int WIFI_MODE_FULL_LOW_LATENCY = - WifiProtoEnums.WIFI_MODE_FULL_LOW_LATENCY; // 4 + public static final int WIFI_MODE_FULL_LOW_LATENCY = 4; + /** Anything worse than or equal to this will show 0 bars. */ @UnsupportedAppUsage @@ -1090,13 +1250,16 @@ public class WifiManager { private static final int MAX_RSSI = -55; /** - * Number of RSSI levels used in the framework to initiate - * {@link #RSSI_CHANGED_ACTION} broadcast + * Number of RSSI levels used in the framework to initiate {@link #RSSI_CHANGED_ACTION} + * broadcast, where each level corresponds to a range of RSSI values. + * The {@link #RSSI_CHANGED_ACTION} broadcast will only fire if the RSSI + * change is significant enough to change the RSSI signal level. * @hide */ @UnsupportedAppUsage public static final int RSSI_LEVELS = 5; + //TODO (b/146346676): This needs to be removed, not used in the code. /** * Auto settings in the driver. The driver could choose to operate on both * 2.4 GHz and 5 GHz or make a dynamic decision on selecting the band. @@ -1122,7 +1285,8 @@ public class WifiManager { /** @hide */ public static final boolean DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED = false; - /* Maximum number of active locks we allow. + /** + * Maximum number of active locks we allow. * This limit was added to prevent apps from creating a ridiculous number * of locks and crashing the system by overflowing the global ref table. */ @@ -1131,6 +1295,10 @@ public class WifiManager { /** Indicates an invalid SSID. */ public static final String UNKNOWN_SSID = "<unknown ssid>"; + /** @hide */ + public static final MacAddress ALL_ZEROS_MAC_ADDRESS = + MacAddress.fromString("00:00:00:00:00:00"); + /* Number of currently active WifiLocks and MulticastLocks */ @UnsupportedAppUsage private int mActiveLockCount; @@ -1140,26 +1308,9 @@ public class WifiManager { IWifiManager mService; private final int mTargetSdkVersion; - private static final int INVALID_KEY = 0; - private int mListenerKey = 1; - private final SparseArray mListenerMap = new SparseArray(); - private final Object mListenerMapLock = new Object(); - - private AsyncChannel mAsyncChannel; - private CountDownLatch mConnected; private Looper mLooper; private boolean mVerboseLoggingEnabled = false; - /* LocalOnlyHotspot callback message types */ - /** @hide */ - public static final int HOTSPOT_STARTED = 0; - /** @hide */ - public static final int HOTSPOT_STOPPED = 1; - /** @hide */ - public static final int HOTSPOT_FAILED = 2; - /** @hide */ - public static final int HOTSPOT_OBSERVER_REGISTERED = 3; - private final Object mLock = new Object(); // lock guarding access to the following vars @GuardedBy("mLock") private LocalOnlyHotspotCallbackProxy mLOHSCallbackProxy; @@ -1169,14 +1320,17 @@ public class WifiManager { /** * Create a new WifiManager instance. * Applications will almost always want to use - * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve + * {@link android.content.Context#getSystemService Context.getSystemService} to retrieve * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}. + * * @param context the application context * @param service the Binder interface + * @param looper the Looper used to deliver callbacks * @hide - hide this because it takes in a parameter of type IWifiManager, which * is a system private class. */ - public WifiManager(Context context, IWifiManager service, Looper looper) { + public WifiManager(@NonNull Context context, @NonNull IWifiManager service, + @NonNull Looper looper) { mContext = context; mService = service; mLooper = looper; @@ -1212,10 +1366,12 @@ public class WifiManager { * {@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 return an empty list, - * except for: + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return an + * empty list. + * <p> + * Deprecation Exemptions: * <ul> - * <li>Device Owner (DO) & Profile Owner (PO) apps will have access to the full list. + * <li>Device Owner (DO), Profile Owner (PO) and system apps will have access to the full list. * <li>Callers with Carrier privilege will receive a restricted list only containing * configurations which they created. * </ul> @@ -1225,7 +1381,8 @@ public class WifiManager { public List<WifiConfiguration> getConfiguredNetworks() { try { ParceledListSlice<WifiConfiguration> parceledList = - mService.getConfiguredNetworks(mContext.getOpPackageName()); + mService.getConfiguredNetworks(mContext.getOpPackageName(), + mContext.getAttributionTag()); if (parceledList == null) { return Collections.emptyList(); } @@ -1241,7 +1398,8 @@ public class WifiManager { public List<WifiConfiguration> getPrivilegedConfiguredNetworks() { try { ParceledListSlice<WifiConfiguration> parceledList = - mService.getPrivilegedConfiguredNetworks(mContext.getOpPackageName()); + mService.getPrivilegedConfiguredNetworks(mContext.getOpPackageName(), + mContext.getAttributionTag()); if (parceledList == null) { return Collections.emptyList(); } @@ -1273,8 +1431,7 @@ public class WifiManager { List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> configs = new ArrayList<>(); try { Map<String, Map<Integer, List<ScanResult>>> results = - mService.getAllMatchingFqdnsForScanResults( - scanResults); + mService.getAllMatchingPasspointProfilesForScanResults(scanResults); if (results.isEmpty()) { return configs; } @@ -1282,8 +1439,8 @@ public class WifiManager { mService.getWifiConfigsForPasspointProfiles( new ArrayList<>(results.keySet())); for (WifiConfiguration configuration : wifiConfigurations) { - Map<Integer, List<ScanResult>> scanResultsPerNetworkType = results.get( - configuration.FQDN); + Map<Integer, List<ScanResult>> scanResultsPerNetworkType = + results.get(configuration.getKey()); if (scanResultsPerNetworkType != null) { configs.add(Pair.create(configuration, scanResultsPerNetworkType)); } @@ -1296,6 +1453,36 @@ public class WifiManager { } /** + * Retrieve a list of {@link WifiConfiguration} for available {@link WifiNetworkSuggestion} + * matching the given list of {@link ScanResult}. + * + * An available {@link WifiNetworkSuggestion} must satisfy: + * <ul> + * <li> Matching one of the {@link ScanResult} from the given list. + * <li> and {@link WifiNetworkSuggestion.Builder#setIsUserAllowedToManuallyConnect(boolean)} set + * to true. + * </ul> + * + * @param scanResults a list of scanResult. + * @return a list of @link WifiConfiguration} for available {@link WifiNetworkSuggestion} + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD + }) + @NonNull + public List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser( + @NonNull List<ScanResult> scanResults) { + try { + return mService.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(scanResults); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** * Returns a list of unique Hotspot 2.0 OSU (Online Sign-Up) providers associated with a given * list of ScanResult. * @@ -1374,7 +1561,13 @@ public class WifiManager { * {@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}. + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return + * {@code -1}. + * <p> + * Deprecation Exemptions: + * <ul> + * <li>Device Owner (DO), Profile Owner (PO) and system apps. + * </ul> */ @Deprecated public int addNetwork(WifiConfiguration config) { @@ -1409,7 +1602,13 @@ public class WifiManager { * {@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}. + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return + * {@code -1}. + * <p> + * Deprecation Exemptions: + * <ul> + * <li>Device Owner (DO), Profile Owner (PO) and system apps. + * </ul> */ @Deprecated public int updateNetwork(WifiConfiguration config) { @@ -1446,40 +1645,45 @@ public class WifiManager { * {@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); + @SuppressLint("CallbackMethodName") + default void select(@NonNull WifiConfiguration wifiConfiguration) {} /** * User rejected the app's request. */ - void reject(); + @SuppressLint("CallbackMethodName") + default void reject() {} } /** * Interface for network request callback. Should be implemented by applications and passed when - * calling {@link #registerNetworkRequestMatchCallback(NetworkRequestMatchCallback, Handler)}. + * calling {@link #registerNetworkRequestMatchCallback(Executor, + * WifiManager.NetworkRequestMatchCallback)}. * * 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 + * object passed 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); + default void onUserSelectionCallbackRegistration( + @NonNull NetworkRequestUserSelectionCallback userSelectionCallback) {} /** * Invoked when the active network request is aborted, either because @@ -1488,7 +1692,7 @@ public class WifiManager { * This signals the end of processing for the current request and should stop the UI * component. No subsequent calls from the UI component will be handled by the platform. */ - void onAbort(); + default void onAbort() {} /** * Invoked when a network request initiated by an app matches some networks in scan results. @@ -1498,7 +1702,7 @@ public class WifiManager { * @param scanResults List of {@link ScanResult} objects corresponding to the networks * matching the request. */ - void onMatch(@NonNull List<ScanResult> scanResults); + default void onMatch(@NonNull List<ScanResult> scanResults) {} /** * Invoked on a successful connection with the network that the user selected @@ -1507,7 +1711,7 @@ public class WifiManager { * @param wifiConfiguration WifiConfiguration object corresponding to the network that the * user selected. */ - void onUserSelectionConnectSuccess(@NonNull WifiConfiguration wifiConfiguration); + default void onUserSelectionConnectSuccess(@NonNull WifiConfiguration wifiConfiguration) {} /** * Invoked on failure to establish connection with the network that the user selected @@ -1516,7 +1720,7 @@ public class WifiManager { * @param wifiConfiguration WifiConfiguration object corresponding to the network * user selected. */ - void onUserSelectionConnectFailure(@NonNull WifiConfiguration wifiConfiguration); + default void onUserSelectionConnectFailure(@NonNull WifiConfiguration wifiConfiguration) {} } /** @@ -1565,11 +1769,11 @@ public class WifiManager { * @hide */ private class NetworkRequestMatchCallbackProxy extends INetworkRequestMatchCallback.Stub { - private final Handler mHandler; + private final Executor mExecutor; private final NetworkRequestMatchCallback mCallback; - NetworkRequestMatchCallbackProxy(Looper looper, NetworkRequestMatchCallback callback) { - mHandler = new Handler(looper); + NetworkRequestMatchCallbackProxy(Executor executor, NetworkRequestMatchCallback callback) { + mExecutor = executor; mCallback = callback; } @@ -1580,7 +1784,8 @@ public class WifiManager { Log.v(TAG, "NetworkRequestMatchCallbackProxy: " + "onUserSelectionCallbackRegistration callback: " + userSelectionCallback); } - mHandler.post(() -> { + Binder.clearCallingIdentity(); + mExecutor.execute(() -> { mCallback.onUserSelectionCallbackRegistration( new NetworkRequestUserSelectionCallbackProxy(userSelectionCallback)); }); @@ -1591,7 +1796,8 @@ public class WifiManager { if (mVerboseLoggingEnabled) { Log.v(TAG, "NetworkRequestMatchCallbackProxy: onAbort"); } - mHandler.post(() -> { + Binder.clearCallingIdentity(); + mExecutor.execute(() -> { mCallback.onAbort(); }); } @@ -1602,7 +1808,8 @@ public class WifiManager { Log.v(TAG, "NetworkRequestMatchCallbackProxy: onMatch scanResults: " + scanResults); } - mHandler.post(() -> { + Binder.clearCallingIdentity(); + mExecutor.execute(() -> { mCallback.onMatch(scanResults); }); } @@ -1613,7 +1820,8 @@ public class WifiManager { Log.v(TAG, "NetworkRequestMatchCallbackProxy: onUserSelectionConnectSuccess " + " wificonfiguration: " + wifiConfiguration); } - mHandler.post(() -> { + Binder.clearCallingIdentity(); + mExecutor.execute(() -> { mCallback.onUserSelectionConnectSuccess(wifiConfiguration); }); } @@ -1624,7 +1832,8 @@ public class WifiManager { Log.v(TAG, "NetworkRequestMatchCallbackProxy: onUserSelectionConnectFailure" + " wificonfiguration: " + wifiConfiguration); } - mHandler.post(() -> { + Binder.clearCallingIdentity(); + mExecutor.execute(() -> { mCallback.onUserSelectionConnectFailure(wifiConfiguration); }); } @@ -1640,23 +1849,24 @@ public class WifiManager { * 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. + * @param executor The Executor on whose thread to execute the callbacks of the {@code callback} + * object. + * @param callback Callback for network match events to register. * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) - public void registerNetworkRequestMatchCallback(@NonNull NetworkRequestMatchCallback callback, - @Nullable Handler handler) { + public void registerNetworkRequestMatchCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull NetworkRequestMatchCallback callback) { + if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "registerNetworkRequestMatchCallback: callback=" + callback - + ", handler=" + handler); + + ", executor=" + executor); - Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper(); Binder binder = new Binder(); try { mService.registerNetworkRequestMatchCallback( - binder, new NetworkRequestMatchCallbackProxy(looper, callback), + binder, new NetworkRequestMatchCallbackProxy(executor, callback), callback.hashCode()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1671,9 +1881,10 @@ public class WifiManager { * without the permission will trigger a {@link java.lang.SecurityException}. * <p> * - * @param callback Callback for network match events + * @param callback Callback for network match events to unregister. * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterNetworkRequestMatchCallback( @NonNull NetworkRequestMatchCallback callback) { @@ -1692,30 +1903,41 @@ public class WifiManager { * for a detailed explanation of the parameters. * When the device decides to connect to one of the provided network suggestions, platform sends * a directed broadcast {@link #ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION} to the app if - * the network was created with {@link WifiNetworkSuggestion.Builder - * #setIsAppInteractionRequired()} flag set and the app holds - * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission. + * the network was created with + * {@link WifiNetworkSuggestion.Builder#setIsAppInteractionRequired(boolean)} flag set and the + * app holds {@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> + * <li> When an app is uninstalled or disabled, 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> If user reset network settings, all added suggestions will be discarded. Apps can use + * {@link #getNetworkSuggestions()} to check if their suggestions are in the device.</li> + * <li> In-place modification of existing suggestions are allowed. + * <li> If the provided suggestions include any previously provided suggestions by the app, + * previous suggestions will be updated.</li> + * <li>If one of the provided suggestions marks a previously unmetered suggestion as metered and + * the device is currently connected to that suggested network, then the device will disconnect + * from that network. The system will immediately re-evaluate all the network candidates + * and possibly reconnect back to the same suggestion. This disconnect is to make sure that any + * traffic flowing over unmetered networks isn't accidentally continued over a metered network. + * </li> + * </li> * * @param networkSuggestions List of network suggestions provided by the app. * @return Status code for the operation. One of the STATUS_NETWORK_SUGGESTIONS_ values. - * {@link WifiNetworkSuggestion#equals(Object)} any previously provided suggestions by the app. * @throws {@link SecurityException} if the caller is missing required permissions. + * @see WifiNetworkSuggestion#equals(Object) */ @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public @NetworkSuggestionsStatusCode int addNetworkSuggestions( @NonNull List<WifiNetworkSuggestion> networkSuggestions) { try { - return mService.addNetworkSuggestions(networkSuggestions, mContext.getOpPackageName()); + return mService.addNetworkSuggestions( + networkSuggestions, mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1723,6 +1945,9 @@ public class WifiManager { /** * Remove some or all of the network suggestions that were previously provided by the app. + * If one of the suggestions being removed was used to establish connection to the current + * network, then the device will immediately disconnect from that network. + * * See {@link WifiNetworkSuggestion} for a detailed explanation of the parameters. * See {@link WifiNetworkSuggestion#equals(Object)} for the equivalence evaluation used. * @@ -1744,12 +1969,35 @@ public class WifiManager { } /** + * Get all network suggestions provided by the calling app. + * See {@link #addNetworkSuggestions(List)} + * See {@link #removeNetworkSuggestions(List)} + * @return a list of {@link WifiNetworkSuggestion} + */ + @RequiresPermission(ACCESS_WIFI_STATE) + public @NonNull List<WifiNetworkSuggestion> getNetworkSuggestions() { + try { + return mService.getNetworkSuggestions(mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** * Returns the max number of network suggestions that are allowed per app on the device. * @see #addNetworkSuggestions(List) * @see #removeNetworkSuggestions(List) */ public int getMaxNumberOfNetworkSuggestionsPerApp() { - return NETWORK_SUGGESTIONS_MAX_PER_APP; + return getMaxNumberOfNetworkSuggestionsPerApp( + mContext.getSystemService(ActivityManager.class).isLowRamDevice()); + } + + /** @hide */ + public static int getMaxNumberOfNetworkSuggestionsPerApp(boolean isLowRamDevice) { + return isLowRamDevice + ? NETWORK_SUGGESTIONS_MAX_PER_APP_LOW_RAM + : NETWORK_SUGGESTIONS_MAX_PER_APP_HIGH_RAM; } /** @@ -1757,13 +2005,29 @@ public class WifiManager { * for connecting to Passpoint networks that are operated by the Passpoint * service provider specified in the configuration. * - * Each configuration is uniquely identified by its FQDN (Fully Qualified Domain - * Name). In the case when there is an existing configuration with the same - * FQDN, the new configuration will replace the existing configuration. + * Each configuration is uniquely identified by a unique key which depends on the contents of + * the configuration. This allows the caller to install multiple profiles with the same FQDN + * (Fully qualified domain name). Therefore, in order to update an existing profile, it is + * first required to remove it using {@link WifiManager#removePasspointConfiguration(String)}. + * Otherwise, a new profile will be added with both configuration. * * @param config The Passpoint configuration to be added * @throws IllegalArgumentException if configuration is invalid or Passpoint is not enabled on * the device. + * + * Deprecated for general app usage - except DO/PO apps. + * See {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} to + * create a passpoint suggestion. + * See {@link #addNetworkSuggestions(List)}, {@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#R} or above, this API will always fail and throw + * {@link IllegalArgumentException}. + * <p> + * Deprecation Exemptions: + * <ul> + * <li>Device Owner (DO), Profile Owner (PO) and system apps. + * </ul> */ public void addOrUpdatePasspointConfiguration(PasspointConfiguration config) { try { @@ -1785,7 +2049,10 @@ public class WifiManager { * @deprecated This will be non-functional in a future release. */ @Deprecated - @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_CARRIER_PROVISIONING + }) public void removePasspointConfiguration(String fqdn) { try { if (!mService.removePasspointConfiguration(fqdn, mContext.getOpPackageName())) { @@ -1856,6 +2123,8 @@ public class WifiManager { * @param holdoff hold off time in milliseconds * @param ess set if the hold off pertains to an ESS rather than a BSS * @hide + * + * TODO (140167680): This needs to be removed, the implementation is empty! */ public void deauthenticateNetwork(long holdoff, boolean ess) { try { @@ -1884,7 +2153,13 @@ public class WifiManager { * {@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. + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return + * {@code false}. + * <p> + * Deprecation Exemptions: + * <ul> + * <li>Device Owner (DO), Profile Owner (PO) and system apps. + * </ul> */ @Deprecated public boolean removeNetwork(int netId) { @@ -1928,32 +2203,20 @@ public class WifiManager { * {@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. + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return + * {@code false}. + * Deprecation Exemptions: + * <ul> + * <li>Device Owner (DO), Profile Owner (PO) and system apps. + * </ul> */ @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()); + return mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - - if (pin && !success) { - NetworkPinner.unpin(); - } - - return success; } /** @@ -1975,7 +2238,13 @@ public class WifiManager { * {@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. + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return + * {@code false}. + * <p> + * Deprecation Exemptions: + * <ul> + * <li>Device Owner (DO), Profile Owner (PO) and system apps. + * </ul> */ @Deprecated public boolean disableNetwork(int netId) { @@ -1998,7 +2267,13 @@ public class WifiManager { * {@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. + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return + * {@code false}. + * <p> + * Deprecation Exemptions: + * <ul> + * <li>Device Owner (DO), Profile Owner (PO) and system apps. + * </ul> */ @Deprecated public boolean disconnect() { @@ -2022,7 +2297,13 @@ public class WifiManager { * {@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. + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return + * {@code false}. + * <p> + * Deprecation Exemptions: + * <ul> + * <li>Device Owner (DO), Profile Owner (PO) and system apps. + * </ul> */ @Deprecated public boolean reconnect() { @@ -2071,8 +2352,6 @@ public class WifiManager { /** @hide */ public static final long WIFI_FEATURE_INFRA = 0x0001L; // Basic infrastructure mode /** @hide */ - public static final long WIFI_FEATURE_INFRA_5G = 0x0002L; // Support for 5 GHz Band - /** @hide */ public static final long WIFI_FEATURE_PASSPOINT = 0x0004L; // Support for GAS/ANQP /** @hide */ public static final long WIFI_FEATURE_P2P = 0x0008L; // Wifi-Direct @@ -2133,7 +2412,23 @@ public class WifiManager { /** @hide */ public static final long WIFI_FEATURE_DPP = 0x80000000L; // DPP (Easy-Connect) /** @hide */ - public static final long WIFI_FEATURE_P2P_RAND_MAC = 0x100000000L; // Random P2P MAC + public static final long WIFI_FEATURE_P2P_RAND_MAC = 0x100000000L; // Random P2P MAC + /** @hide */ + public static final long WIFI_FEATURE_CONNECTED_RAND_MAC = 0x200000000L; // Random STA MAC + /** @hide */ + public static final long WIFI_FEATURE_AP_RAND_MAC = 0x400000000L; // Random AP MAC + /** @hide */ + public static final long WIFI_FEATURE_MBO = 0x800000000L; // MBO Support + /** @hide */ + public static final long WIFI_FEATURE_OCE = 0x1000000000L; // OCE Support + /** @hide */ + public static final long WIFI_FEATURE_WAPI = 0x2000000000L; // WAPI + + /** @hide */ + public static final long WIFI_FEATURE_FILS_SHA256 = 0x4000000000L; // FILS-SHA256 + + /** @hide */ + public static final long WIFI_FEATURE_FILS_SHA384 = 0x8000000000L; // FILS-SHA384 private long getSupportedFeatures() { try { @@ -2146,12 +2441,6 @@ public class WifiManager { private boolean isFeatureSupported(long feature) { return (getSupportedFeatures() & feature) == feature; } - /** - * @return true if this adapter supports 5 GHz band - */ - public boolean is5GHzBandSupported() { - return isFeatureSupported(WIFI_FEATURE_INFRA_5G); - } /** * @return true if this adapter supports Passpoint @@ -2195,6 +2484,15 @@ public class WifiManager { } /** + * Query whether the device supports Station (STA) + Access point (AP) concurrency or not. + * + * @return true if this device supports STA + AP concurrency, false otherwise. + */ + public boolean isStaApConcurrencySupported() { + return isFeatureSupported(WIFI_FEATURE_AP_STA); + } + + /** * @deprecated Please use {@link android.content.pm.PackageManager#hasSystemFeature(String)} * with {@link android.content.pm.PackageManager#FEATURE_WIFI_RTT} and * {@link android.content.pm.PackageManager#FEATURE_WIFI_AWARE}. @@ -2257,20 +2555,132 @@ public class WifiManager { } /** - * Return the record of {@link WifiActivityEnergyInfo} object that - * has the activity and energy info. This can be used to ascertain what - * the controller has been up to, since the last sample. - * - * @return a record with {@link WifiActivityEnergyInfo} or null if - * report is unavailable or unsupported + * @return true if this device supports connected MAC randomization. * @hide */ - public WifiActivityEnergyInfo getControllerActivityEnergyInfo() { - if (mService == null) return null; + @SystemApi + public boolean isConnectedMacRandomizationSupported() { + return isFeatureSupported(WIFI_FEATURE_CONNECTED_RAND_MAC); + } + + /** + * @return true if this device supports connected MAC randomization. + * @hide + */ + @SystemApi + public boolean isApMacRandomizationSupported() { + return isFeatureSupported(WIFI_FEATURE_AP_RAND_MAC); + } + + /** + * Check if the chipset supports 5GHz band. + * @return {@code true} if supported, {@code false} otherwise. + */ + public boolean is5GHzBandSupported() { try { - synchronized(this) { - return mService.reportActivityInfo(); + return mService.is5GHzBandSupported(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Check if the chipset supports 6GHz band. + * @return {@code true} if supported, {@code false} otherwise. + */ + public boolean is6GHzBandSupported() { + try { + return mService.is6GHzBandSupported(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Check if the chipset supports a certain Wi-Fi standard. + * @param standard the IEEE 802.11 standard to check on. + * valid values from {@link ScanResult}'s {@code WIFI_STANDARD_} + * @return {@code true} if supported, {@code false} otherwise. + */ + public boolean isWifiStandardSupported(@WifiAnnotations.WifiStandard int standard) { + try { + return mService.isWifiStandardSupported(standard); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Interface for Wi-Fi activity energy info listener. Should be implemented by applications and + * set when calling {@link WifiManager#getWifiActivityEnergyInfoAsync}. + * + * @hide + */ + @SystemApi + public interface OnWifiActivityEnergyInfoListener { + /** + * Called when Wi-Fi activity energy info is available. + * Note: this listener is triggered at most once for each call to + * {@link #getWifiActivityEnergyInfoAsync}. + * + * @param info the latest {@link WifiActivityEnergyInfo}, or null if unavailable. + */ + void onWifiActivityEnergyInfo(@Nullable WifiActivityEnergyInfo info); + } + + private static class OnWifiActivityEnergyInfoProxy + extends IOnWifiActivityEnergyInfoListener.Stub { + private final Object mLock = new Object(); + @Nullable @GuardedBy("mLock") private Executor mExecutor; + @Nullable @GuardedBy("mLock") private OnWifiActivityEnergyInfoListener mListener; + + OnWifiActivityEnergyInfoProxy(Executor executor, + OnWifiActivityEnergyInfoListener listener) { + mExecutor = executor; + mListener = listener; + } + + @Override + public void onWifiActivityEnergyInfo(WifiActivityEnergyInfo info) { + Executor executor; + OnWifiActivityEnergyInfoListener listener; + synchronized (mLock) { + if (mExecutor == null || mListener == null) { + return; + } + executor = mExecutor; + listener = mListener; + // null out to allow garbage collection, prevent triggering listener more than once + mExecutor = null; + mListener = null; } + Binder.clearCallingIdentity(); + executor.execute(() -> listener.onWifiActivityEnergyInfo(info)); + } + } + + /** + * Request to get the current {@link WifiActivityEnergyInfo} asynchronously. + * Note: This method will return null if {@link #isEnhancedPowerReportingSupported()} returns + * false. + * + * @param executor the executor that the listener will be invoked on + * @param listener the listener that will receive the {@link WifiActivityEnergyInfo} object + * when it becomes available. The listener will be triggered at most once for + * each call to this method. + * + * @hide + */ + @SystemApi + @RequiresPermission(ACCESS_WIFI_STATE) + public void getWifiActivityEnergyInfoAsync( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnWifiActivityEnergyInfoListener listener) { + Objects.requireNonNull(executor, "executor cannot be null"); + Objects.requireNonNull(listener, "listener cannot be null"); + try { + mService.getWifiActivityEnergyInfoAsync( + new OnWifiActivityEnergyInfoProxy(executor, listener)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2309,7 +2719,8 @@ public class WifiManager { public boolean startScan(WorkSource workSource) { try { String packageName = mContext.getOpPackageName(); - return mService.startScan(packageName); + String attributionTag = mContext.getAttributionTag(); + return mService.startScan(packageName, attributionTag); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2333,12 +2744,15 @@ public class WifiManager { * the same permissions as {@link #getScanResults}. If such access is not allowed, * {@link WifiInfo#getSSID} will return {@link #UNKNOWN_SSID} and * {@link WifiInfo#getBSSID} will return {@code "02:00:00:00:00:00"}. + * {@link WifiInfo#getPasspointFqdn()} will return null. + * {@link WifiInfo#getPasspointProviderFriendlyName()} will return null. * * @return the Wi-Fi information, contained in {@link WifiInfo}. */ public WifiInfo getConnectionInfo() { try { - return mService.getConnectionInfo(mContext.getOpPackageName()); + return mService.getConnectionInfo(mContext.getOpPackageName(), + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2352,7 +2766,59 @@ public class WifiManager { */ public List<ScanResult> getScanResults() { try { - return mService.getScanResults(mContext.getOpPackageName()); + return mService.getScanResults(mContext.getOpPackageName(), + mContext.getAttributionTag()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get the filtered ScanResults which match the network configurations specified by the + * {@code networkSuggestionsToMatch}. Suggestions which use {@link WifiConfiguration} use + * SSID and the security type to match. Suggestions which use {@link PasspointConfigration} + * use the matching rules of Hotspot 2.0. + * @param networkSuggestionsToMatch The list of {@link WifiNetworkSuggestion} to match against. + * These may or may not be suggestions which are installed on the device. + * @param scanResults The scan results to be filtered. Optional - if not provided(empty list), + * the Wi-Fi service will use the most recent scan results which the system has. + * @return The map of {@link WifiNetworkSuggestion} to the list of {@link ScanResult} + * corresponding to networks which match them. + * @hide + */ + @SystemApi + @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE}) + @NonNull + public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults( + @NonNull List<WifiNetworkSuggestion> networkSuggestionsToMatch, + @Nullable List<ScanResult> scanResults) { + if (networkSuggestionsToMatch == null) { + throw new IllegalArgumentException("networkSuggestions must not be null."); + } + try { + return mService.getMatchingScanResults( + networkSuggestionsToMatch, scanResults, + mContext.getOpPackageName(), mContext.getAttributionTag()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set if scanning is always available. + * + * If set to {@code true}, apps can issue {@link #startScan} and fetch scan results + * even when Wi-Fi is turned off. + * + * @param isAvailable true to enable, false to disable. + * @hide + * @see #isScanAlwaysAvailable() + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void setScanAlwaysAvailable(boolean isAvailable) { + try { + mService.setScanAlwaysAvailable(isAvailable); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2395,57 +2861,17 @@ public class WifiManager { } /** - * Set the country code. - * @param countryCode country code in ISO 3166 format. - * + * Get the country code. + * @return the country code in ISO 3166 alpha-2 (2-letter) uppercase format, or null if + * there is no country code configured. * @hide */ - public void setCountryCode(@NonNull String country) { - try { - mService.setCountryCode(country); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * get the country code. - * @return the country code in ISO 3166 format. - * - * @hide - */ - @UnsupportedAppUsage + @Nullable + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCountryCode() { - try { - String country = mService.getCountryCode(); - return country; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Check if the chipset supports dual frequency band (2.4 GHz and 5 GHz) - * @return {@code true} if supported, {@code false} otherwise. - * @hide - */ - @UnsupportedAppUsage - public boolean isDualBandSupported() { - try { - return mService.isDualBandSupported(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Check if the chipset requires conversion of 5GHz Only apBand to ANY. - * @return {@code true} if required, {@code false} otherwise. - * @hide - */ - public boolean isDualModeSupported() { try { - return mService.needs5GHzToAnyApBandConversion(); + return mService.getCountryCode(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2478,9 +2904,14 @@ public class WifiManager { * @deprecated Starting with Build.VERSION_CODES#Q, applications are not allowed to * enable/disable Wi-Fi. * <b>Compatibility Note:</b> For applications targeting - * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return {@code false} - * and will have no effect. If apps are targeting an older SDK ( - * {@link android.os.Build.VERSION_CODES#P} or below), they can continue to use this API. + * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return + * {@code false}. If apps are targeting an older SDK ({@link android.os.Build.VERSION_CODES#P} + * or below), they can continue to use this API. + * <p> + * Deprecation Exemptions: + * <ul> + * <li>Device Owner (DO), Profile Owner (PO) and system apps. + * </ul> */ @Deprecated public boolean setWifiEnabled(boolean enabled) { @@ -2516,25 +2947,17 @@ public class WifiManager { } /** - * Return TX packet counter, for CTS test of WiFi watchdog. - * @param listener is the interface to receive result - * - * @hide for CTS test only - */ - public void getTxPacketCount(TxPacketCountListener listener) { - getChannel().sendMessage(RSSI_PKTCNT_FETCH, 0, putListener(listener)); - } - - /** * Calculates the level of the signal. This should be used any time a signal * is being shown. * * @param rssi The power of the signal measured in RSSI. - * @param numLevels The number of levels to consider in the calculated - * level. - * @return A level of the signal, given in the range of 0 to numLevels-1 - * (both inclusive). + * @param numLevels The number of levels to consider in the calculated level. + * @return A level of the signal, given in the range of 0 to numLevels-1 (both inclusive). + * @deprecated Callers should use {@link #calculateSignalLevel(int)} instead to get the + * signal level using the system default RSSI thresholds, or otherwise compute the RSSI level + * themselves using their own formula. */ + @Deprecated public static int calculateSignalLevel(int rssi, int numLevels) { if (rssi <= MIN_RSSI) { return 0; @@ -2548,6 +2971,32 @@ public class WifiManager { } /** + * Given a raw RSSI, return the RSSI signal quality rating using the system default RSSI + * quality rating thresholds. + * @param rssi a raw RSSI value, in dBm, usually between -55 and -90 + * @return the RSSI signal quality rating, in the range + * [0, {@link #getMaxSignalLevel()}], where 0 is the lowest (worst signal) RSSI + * rating and {@link #getMaxSignalLevel()} is the highest (best signal) RSSI rating. + */ + @IntRange(from = 0) + public int calculateSignalLevel(int rssi) { + try { + return mService.calculateSignalLevel(rssi); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get the system default maximum signal level. + * This is the maximum RSSI level returned by {@link #calculateSignalLevel(int)}. + */ + @IntRange(from = 0) + public int getMaxSignalLevel() { + return calculateSignalLevel(Integer.MAX_VALUE); + } + + /** * Compares two signal strengths. * * @param rssiA The power of the first signal measured in RSSI. @@ -2586,7 +3035,7 @@ public class WifiManager { } /** - * Start Soft AP (hotspot) mode with the specified configuration. + * Start Soft AP (hotspot) mode for tethering purposes with the specified configuration. * Note that starting Soft AP mode may disable station mode operation if the device does not * support concurrency. * @param wifiConfig SSID, security and channel details as part of WifiConfiguration, or null to @@ -2596,7 +3045,6 @@ public class WifiManager { * * @hide */ - @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_STACK, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK @@ -2610,6 +3058,31 @@ public class WifiManager { } /** + * Start Soft AP (hotspot) mode for tethering purposes with the specified configuration. + * Note that starting Soft AP mode may disable station mode operation if the device does not + * support concurrency. + * @param softApConfig A valid SoftApConfiguration specifying the configuration of the SAP, + * or null to use the persisted Soft AP configuration that was previously + * set using {@link #setSoftApConfiguration(softApConfiguration)}. + * @return {@code true} if the operation succeeded, {@code false} otherwise + * + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_STACK, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK + }) + public boolean startTetheredHotspot(@Nullable SoftApConfiguration softApConfig) { + try { + return mService.startTetheredHotspot(softApConfig); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + + /** * Stop SoftAp mode. * Note that stopping softap mode will restore the previous wifi mode. * @return {@code true} if the operation succeeds, {@code false} otherwise @@ -2641,7 +3114,7 @@ public class WifiManager { * Each application can make a single active call to this method. The {@link * LocalOnlyHotspotCallback#onStarted(LocalOnlyHotspotReservation)} callback supplies the * requestor with a {@link LocalOnlyHotspotReservation} that contains a - * {@link WifiConfiguration} with the SSID, security type and credentials needed to connect + * {@link SoftApConfiguration} with the SSID, security type and credentials needed to connect * to the hotspot. Communicating this information is up to the application. * <p> * If the LocalOnlyHotspot cannot be created, the {@link LocalOnlyHotspotCallback#onFailed(int)} @@ -2684,19 +3157,70 @@ public class WifiManager { * @param handler Handler to be used for callbacks. If the caller passes a null Handler, the * main thread will be used. */ + @RequiresPermission(allOf = { + android.Manifest.permission.CHANGE_WIFI_STATE, + android.Manifest.permission.ACCESS_FINE_LOCATION}) public void startLocalOnlyHotspot(LocalOnlyHotspotCallback callback, @Nullable Handler handler) { + Executor executor = handler == null ? null : new HandlerExecutor(handler); + startLocalOnlyHotspotInternal(null, executor, callback); + } + + /** + * Starts a local-only hotspot with a specific configuration applied. See + * {@link #startLocalOnlyHotspot(LocalOnlyHotspotCallback, Handler)}. + * + * Applications need either {@link android.Manifest.permission#NETWORK_SETUP_WIZARD} or + * {@link android.Manifest.permission#NETWORK_SETTINGS} to call this method. + * + * Since custom configuration settings may be incompatible with each other, the hotspot started + * through this method cannot coexist with another hotspot created through + * startLocalOnlyHotspot. If this is attempted, the first hotspot request wins and others + * receive {@link LocalOnlyHotspotCallback#ERROR_GENERIC} through + * {@link LocalOnlyHotspotCallback#onFailed}. + * + * @param config Custom configuration for the hotspot. See {@link SoftApConfiguration}. + * @param executor Executor to run callback methods on, or null to use the main thread. + * @param callback Callback object for updates about hotspot status, or null for no updates. + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) + public void startLocalOnlyHotspot(@NonNull SoftApConfiguration config, + @Nullable Executor executor, + @Nullable LocalOnlyHotspotCallback callback) { + Objects.requireNonNull(config); + startLocalOnlyHotspotInternal(config, executor, callback); + } + + /** + * Common implementation of both configurable and non-configurable LOHS. + * + * @param config App-specified configuration, or null. When present, additional privileges are + * required, and the hotspot cannot be shared with other clients. + * @param executor Executor to run callback methods on, or null to use the main thread. + * @param callback Callback object for updates about hotspot status, or null for no updates. + */ + private void startLocalOnlyHotspotInternal( + @Nullable SoftApConfiguration config, + @Nullable Executor executor, + @Nullable LocalOnlyHotspotCallback callback) { + if (executor == null) { + executor = mContext.getMainExecutor(); + } synchronized (mLock) { - Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper(); LocalOnlyHotspotCallbackProxy proxy = - new LocalOnlyHotspotCallbackProxy(this, looper, callback); + new LocalOnlyHotspotCallbackProxy(this, executor, callback); try { String packageName = mContext.getOpPackageName(); - int returnCode = mService.startLocalOnlyHotspot( - proxy.getMessenger(), new Binder(), packageName); + String featureId = mContext.getAttributionTag(); + int returnCode = mService.startLocalOnlyHotspot(proxy, packageName, featureId, + config); if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) { // Send message to the proxy to make sure we call back on the correct thread - proxy.notifyFailed(returnCode); + proxy.onHotspotFailed(returnCode); return; } mLOHSCallbackProxy = proxy; @@ -2755,7 +3279,7 @@ public class WifiManager { * Allow callers (Settings UI) to watch LocalOnlyHotspot state changes. Callers will * receive a {@link LocalOnlyHotspotSubscription} object as a parameter of the * {@link LocalOnlyHotspotObserver#onRegistered(LocalOnlyHotspotSubscription)}. The registered - * callers will receive the {@link LocalOnlyHotspotObserver#onStarted(WifiConfiguration)} and + * callers will receive the {@link LocalOnlyHotspotObserver#onStarted(SoftApConfiguration)} and * {@link LocalOnlyHotspotObserver#onStopped()} callbacks. * <p> * Applications should have the @@ -2770,12 +3294,13 @@ public class WifiManager { */ public void watchLocalOnlyHotspot(LocalOnlyHotspotObserver observer, @Nullable Handler handler) { + Executor executor = handler == null ? mContext.getMainExecutor() + : new HandlerExecutor(handler); synchronized (mLock) { - Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper(); - mLOHSObserverProxy = new LocalOnlyHotspotObserverProxy(this, looper, observer); + mLOHSObserverProxy = + new LocalOnlyHotspotObserverProxy(this, executor, observer); try { - mService.startWatchLocalOnlyHotspot( - mLOHSObserverProxy.getMessenger(), new Binder()); + mService.startWatchLocalOnlyHotspot(mLOHSObserverProxy); mLOHSObserverProxy.registered(); } catch (RemoteException e) { mLOHSObserverProxy = null; @@ -2806,7 +3331,7 @@ public class WifiManager { } /** - * Gets the Wi-Fi enabled state. + * Gets the tethered Wi-Fi hotspot enabled state. * @return One of {@link #WIFI_AP_STATE_DISABLED}, * {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED}, * {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED} @@ -2825,8 +3350,8 @@ public class WifiManager { } /** - * Return whether Wi-Fi AP is enabled or disabled. - * @return {@code true} if Wi-Fi AP is enabled + * Return whether tethered Wi-Fi AP is enabled or disabled. + * @return {@code true} if tethered Wi-Fi AP is enabled * @see #getWifiApState() * * @hide @@ -2838,13 +3363,19 @@ public class WifiManager { } /** - * Gets the Wi-Fi AP Configuration. + * Gets the tethered Wi-Fi AP Configuration. * @return AP details in WifiConfiguration * + * Note that AP detail may contain configuration which is cannot be represented + * by the legacy WifiConfiguration, in such cases a null will be returned. + * + * @deprecated This API is deprecated. Use {@link #getSoftApConfiguration()} instead. * @hide */ + @Nullable @SystemApi @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) + @Deprecated public WifiConfiguration getWifiApConfiguration() { try { return mService.getWifiApConfiguration(); @@ -2854,14 +3385,33 @@ public class WifiManager { } /** - * Sets the Wi-Fi AP Configuration. The AP configuration must either be open or - * WPA2 PSK networks. + * Gets the Wi-Fi tethered AP Configuration. + * @return AP details in {@link SoftApConfiguration} + * + * @hide + */ + @NonNull + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public SoftApConfiguration getSoftApConfiguration() { + try { + return mService.getSoftApConfiguration(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets the tethered Wi-Fi AP Configuration. * @return {@code true} if the operation succeeded, {@code false} otherwise * + * @deprecated This API is deprecated. Use {@link #setSoftApConfiguration(SoftApConfiguration)} + * instead. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) + @Deprecated public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) { try { return mService.setWifiApConfiguration(wifiConfig, mContext.getOpPackageName()); @@ -2871,15 +3421,30 @@ public class WifiManager { } /** - * Method that triggers a notification to the user about a conversion to their saved AP config. + * Sets the tethered Wi-Fi AP Configuration. + * + * If the API is called while the tethered soft AP is enabled, the configuration will apply to + * the current soft AP if the new configuration only includes + * {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)} + * or {@link SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)} + * or {@link SoftApConfiguration.Builder#setClientControlByUserEnabled(boolean)} + * or {@link SoftApConfiguration.Builder#setBlockedClientList(List)} + * or {@link SoftApConfiguration.Builder#setAllowedClientList(List)} + * + * Otherwise, the configuration changes will be applied when the Soft AP is next started + * (the framework will not stop/start the AP). + * + * @param softApConfig A valid SoftApConfiguration specifying the configuration of the SAP. + * @return {@code true} if the operation succeeded, {@code false} otherwise * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) - public void notifyUserOfApBandConversion() { - Log.d(TAG, "apBand was converted, notify the user"); + public boolean setSoftApConfiguration(@NonNull SoftApConfiguration softApConfig) { try { - mService.notifyUserOfApBandConversion(mContext.getOpPackageName()); + return mService.setSoftApConfiguration( + softApConfig, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2927,76 +3492,6 @@ public class WifiManager { } } - /* TODO: deprecate synchronous API and open up the following API */ - - private static final int BASE = Protocol.BASE_WIFI_MANAGER; - - /* Commands to WifiService */ - /** @hide */ - public static final int CONNECT_NETWORK = BASE + 1; - /** @hide */ - public static final int CONNECT_NETWORK_FAILED = BASE + 2; - /** @hide */ - public static final int CONNECT_NETWORK_SUCCEEDED = BASE + 3; - - /** @hide */ - public static final int FORGET_NETWORK = BASE + 4; - /** @hide */ - public static final int FORGET_NETWORK_FAILED = BASE + 5; - /** @hide */ - public static final int FORGET_NETWORK_SUCCEEDED = BASE + 6; - - /** @hide */ - public static final int SAVE_NETWORK = BASE + 7; - /** @hide */ - public static final int SAVE_NETWORK_FAILED = BASE + 8; - /** @hide */ - public static final int SAVE_NETWORK_SUCCEEDED = BASE + 9; - - /** @hide - * @deprecated This is deprecated - */ - public static final int START_WPS = BASE + 10; - /** @hide - * @deprecated This is deprecated - */ - public static final int START_WPS_SUCCEEDED = BASE + 11; - /** @hide - * @deprecated This is deprecated - */ - public static final int WPS_FAILED = BASE + 12; - /** @hide - * @deprecated This is deprecated - */ - public static final int WPS_COMPLETED = BASE + 13; - - /** @hide - * @deprecated This is deprecated - */ - public static final int CANCEL_WPS = BASE + 14; - /** @hide - * @deprecated This is deprecated - */ - public static final int CANCEL_WPS_FAILED = BASE + 15; - /** @hide - * @deprecated This is deprecated - */ - public static final int CANCEL_WPS_SUCCEDED = BASE + 16; - - /** @hide */ - public static final int DISABLE_NETWORK = BASE + 17; - /** @hide */ - public static final int DISABLE_NETWORK_FAILED = BASE + 18; - /** @hide */ - public static final int DISABLE_NETWORK_SUCCEEDED = BASE + 19; - - /** @hide */ - public static final int RSSI_PKTCNT_FETCH = BASE + 20; - /** @hide */ - public static final int RSSI_PKTCNT_FETCH_SUCCEEDED = BASE + 21; - /** @hide */ - public static final int RSSI_PKTCNT_FETCH_FAILED = BASE + 22; - /** * Passed with {@link ActionListener#onFailure}. * Indicates that the operation failed due to an internal error. @@ -3019,6 +3514,11 @@ public class WifiManager { */ public static final int BUSY = 2; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ERROR, IN_PROGRESS, BUSY}) + public @interface ActionListenerFailureReason {} + /* WPS specific errors */ /** WPS overlap detected * @deprecated This is deprecated @@ -3063,20 +3563,13 @@ public class WifiManager { public interface ActionListener { /** * The operation succeeded. - * This is called when the scan request has been validated and ready - * to sent to driver. */ - public void onSuccess(); + void onSuccess(); /** * The operation failed. - * This is called when the scan request failed. - * @param reason The reason for failure could be one of the following: - * {@link #REASON_INVALID_REQUEST}} is specified when scan request parameters are invalid. - * {@link #REASON_NOT_AUTHORIZED} is specified when requesting app doesn't have the required - * permission to request a scan. - * {@link #REASON_UNSPECIFIED} is specified when driver reports a scan failure. + * @param reason The reason for failure depends on the operation. */ - public void onFailure(int reason); + void onFailure(@ActionListenerFailureReason int reason); } /** Interface for callback invocation on a start WPS action @@ -3105,21 +3598,6 @@ public class WifiManager { public abstract void onFailed(int reason); } - /** Interface for callback invocation on a TX packet count poll action {@hide} */ - public interface TxPacketCountListener { - /** - * The operation succeeded - * @param count TX packet counter - */ - public void onSuccess(int count); - /** - * The operation failed - * @param reason The reason for failure could be one of - * {@link #ERROR}, {@link #IN_PROGRESS} or {@link #BUSY} - */ - public void onFailure(int reason); - } - /** * Base class for soft AP callback. Should be extended by applications and set when calling * {@link WifiManager#registerSoftApCallback(Executor, SoftApCallback)}. @@ -3131,22 +3609,57 @@ public class WifiManager { /** * Called when soft AP state changes. * - * @param state new new AP state. One of {@link #WIFI_AP_STATE_DISABLED}, + * @param state the new AP state. One of {@link #WIFI_AP_STATE_DISABLED}, * {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED}, * {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED} * @param failureReason reason when in failed state. One of * {@link #SAP_START_FAILURE_GENERAL}, - * {@link #SAP_START_FAILURE_NO_CHANNEL} + * {@link #SAP_START_FAILURE_NO_CHANNEL}, + * {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION} */ - void onStateChanged(@WifiApState int state, - @SapStartFailure int failureReason); + default void onStateChanged(@WifiApState int state, @SapStartFailure int failureReason) {} /** * Called when the connected clients to soft AP changes. * * @param clients the currently connected clients */ - void onConnectedClientsChanged(@NonNull List<WifiClient> clients); + default void onConnectedClientsChanged(@NonNull List<WifiClient> clients) {} + + /** + * Called when information of softap changes. + * + * @param softApInfo is the softap information. {@link SoftApInfo} + */ + default void onInfoChanged(@NonNull SoftApInfo softApInfo) { + // Do nothing: can be updated to add SoftApInfo details (e.g. channel) to the UI. + } + + /** + * Called when capability of softap changes. + * + * @param softApCapability is the softap capability. {@link SoftApCapability} + */ + default void onCapabilityChanged(@NonNull SoftApCapability softApCapability) { + // Do nothing: can be updated to add SoftApCapability details (e.g. meximum supported + // client number) to the UI. + } + + /** + * Called when client trying to connect but device blocked the client with specific reason. + * + * Can be used to ask user to update client to allowed list or blocked list + * when reason is {@link SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER}, or + * indicate the block due to maximum supported client number limitation when reason is + * {@link SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS}. + * + * @param client the currently blocked client. + * @param blockedReason one of blocked reason from {@link SapClientBlockedReason} + */ + default void onBlockedClientConnecting(@NonNull WifiClient client, + @SapClientBlockedReason int blockedReason) { + // Do nothing: can be used to ask user to update client to allowed list or blocked list. + } } /** @@ -3188,14 +3701,60 @@ public class WifiManager { mCallback.onConnectedClientsChanged(clients); }); } + + @Override + public void onInfoChanged(SoftApInfo softApInfo) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "SoftApCallbackProxy: onInfoChange: softApInfo=" + softApInfo); + } + + Binder.clearCallingIdentity(); + mExecutor.execute(() -> { + mCallback.onInfoChanged(softApInfo); + }); + } + + @Override + public void onCapabilityChanged(SoftApCapability capability) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "SoftApCallbackProxy: onCapabilityChanged: SoftApCapability=" + + capability); + } + + Binder.clearCallingIdentity(); + mExecutor.execute(() -> { + mCallback.onCapabilityChanged(capability); + }); + } + + @Override + public void onBlockedClientConnecting(@NonNull WifiClient client, int blockedReason) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "SoftApCallbackProxy: onBlockedClientConnecting: client=" + client + + " with reason = " + blockedReason); + } + + Binder.clearCallingIdentity(); + mExecutor.execute(() -> { + mCallback.onBlockedClientConnecting(client, blockedReason); + }); + } } /** - * Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the current - * soft AP state and number of connected devices immediately after a successful call to this API - * via callback. Note that receiving an immediate WIFI_AP_STATE_FAILED value for soft AP state - * indicates that the latest attempt to start soft AP has failed. Caller can unregister a - * previously registered callback using {@link #unregisterSoftApCallback} + * Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the + * following callbacks on registration: + * <ul> + * <li> {@link SoftApCallback#onStateChanged(int, int)}</li> + * <li> {@link SoftApCallback#onConnectedClientsChanged(List<WifiClient>)}</li> + * <li> {@link SoftApCallback#onInfoChanged(SoftApInfo)}</li> + * <li> {@link SoftApCallback#onCapabilityChanged(SoftApCapability)}</li> + * </ul> + * These will be dispatched on registration to provide the caller with the current state + * (and are not an indication of any current change). Note that receiving an immediate + * WIFI_AP_STATE_FAILED value for soft AP state indicates that the latest attempt to start + * soft AP has failed. Caller can unregister a previously registered callback using + * {@link #unregisterSoftApCallback} * <p> * Applications should have the * {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers @@ -3205,21 +3764,20 @@ public class WifiManager { * @param executor The Executor on whose thread to execute the callbacks of the {@code callback} * object. * @param callback Callback for soft AP events - * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull SoftApCallback callback) { + @NonNull SoftApCallback callback) { if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "registerSoftApCallback: callback=" + callback + ", executor=" + executor); Binder binder = new Binder(); try { - mService.registerSoftApCallback(binder, new SoftApCallbackProxy(executor, callback), - callback.hashCode()); + mService.registerSoftApCallback( + binder, new SoftApCallbackProxy(executor, callback), callback.hashCode()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3233,6 +3791,7 @@ public class WifiManager { * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterSoftApCallback(@NonNull SoftApCallback callback) { if (callback == null) throw new IllegalArgumentException("callback cannot be null"); @@ -3246,13 +3805,13 @@ public class WifiManager { } /** - * LocalOnlyHotspotReservation that contains the {@link WifiConfiguration} for the active + * LocalOnlyHotspotReservation that contains the {@link SoftApConfiguration} for the active * LocalOnlyHotspot request. * <p> * Applications requesting LocalOnlyHotspot for sharing will receive an instance of the * LocalOnlyHotspotReservation in the * {@link LocalOnlyHotspotCallback#onStarted(LocalOnlyHotspotReservation)} call. This - * reservation contains the relevant {@link WifiConfiguration}. + * reservation contains the relevant {@link SoftApConfiguration}. * When an application is done with the LocalOnlyHotspot, they should call {@link * LocalOnlyHotspotReservation#close()}. Once this happens, the application will not receive * any further callbacks. If the LocalOnlyHotspot is stopped due to a @@ -3261,19 +3820,39 @@ public class WifiManager { */ public class LocalOnlyHotspotReservation implements AutoCloseable { - private final CloseGuard mCloseGuard = CloseGuard.get(); - private final WifiConfiguration mConfig; + private final CloseGuard mCloseGuard = new CloseGuard(); + private final SoftApConfiguration mSoftApConfig; + private final WifiConfiguration mWifiConfig; private boolean mClosed = false; /** @hide */ @VisibleForTesting - public LocalOnlyHotspotReservation(WifiConfiguration config) { - mConfig = config; + public LocalOnlyHotspotReservation(SoftApConfiguration config) { + mSoftApConfig = config; + mWifiConfig = config.toWifiConfiguration(); mCloseGuard.open("close"); } + /** + * Returns the {@link WifiConfiguration} of the current Local Only Hotspot (LOHS). + * May be null if hotspot enabled and security type is not + * {@code WifiConfiguration.KeyMgmt.None} or {@code WifiConfiguration.KeyMgmt.WPA2_PSK}. + * + * @deprecated Use {@code WifiManager#getSoftApConfiguration()} to get the + * LOHS configuration. + */ + @Deprecated + @Nullable public WifiConfiguration getWifiConfiguration() { - return mConfig; + return mWifiConfig; + } + + /** + * Returns the {@link SoftApConfiguration} of the current Local Only Hotspot (LOHS). + */ + @NonNull + public SoftApConfiguration getSoftApConfiguration() { + return mSoftApConfig; } @Override @@ -3288,6 +3867,8 @@ public class WifiManager { } } catch (Exception e) { Log.e(TAG, "Failed to stop Local Only Hotspot."); + } finally { + Reference.reachabilityFence(this); } } @@ -3345,82 +3926,63 @@ public class WifiManager { /** * Callback proxy for LocalOnlyHotspotCallback objects. */ - private static class LocalOnlyHotspotCallbackProxy { - private final Handler mHandler; + private static class LocalOnlyHotspotCallbackProxy extends ILocalOnlyHotspotCallback.Stub { private final WeakReference<WifiManager> mWifiManager; - private final Looper mLooper; - private final Messenger mMessenger; + private final Executor mExecutor; + private final LocalOnlyHotspotCallback mCallback; /** - * Constructs a {@link LocalOnlyHotspotCallback} using the specified looper. All callbacks - * will be delivered on the thread of the specified looper. + * Constructs a {@link LocalOnlyHotspotCallbackProxy} using the specified executor. All + * callbacks will run using the given executor. * * @param manager WifiManager - * @param looper Looper for delivering callbacks - * @param callback LocalOnlyHotspotCallback to notify the calling application. + * @param executor Executor for delivering callbacks. + * @param callback LocalOnlyHotspotCallback to notify the calling application, or null. */ - LocalOnlyHotspotCallbackProxy(WifiManager manager, Looper looper, - final LocalOnlyHotspotCallback callback) { + LocalOnlyHotspotCallbackProxy( + @NonNull WifiManager manager, + @NonNull Executor executor, + @Nullable LocalOnlyHotspotCallback callback) { mWifiManager = new WeakReference<>(manager); - mLooper = looper; - - mHandler = new Handler(looper) { - @Override - public void handleMessage(Message msg) { - Log.d(TAG, "LocalOnlyHotspotCallbackProxy: handle message what: " - + msg.what + " msg: " + msg); - - WifiManager manager = mWifiManager.get(); - if (manager == null) { - Log.w(TAG, "LocalOnlyHotspotCallbackProxy: handle message post GC"); - return; - } + mExecutor = executor; + mCallback = callback; + } - switch (msg.what) { - case HOTSPOT_STARTED: - WifiConfiguration config = (WifiConfiguration) msg.obj; - if (config == null) { - Log.e(TAG, "LocalOnlyHotspotCallbackProxy: config cannot be null."); - callback.onFailed(LocalOnlyHotspotCallback.ERROR_GENERIC); - return; - } - callback.onStarted(manager.new LocalOnlyHotspotReservation(config)); - break; - case HOTSPOT_STOPPED: - Log.w(TAG, "LocalOnlyHotspotCallbackProxy: hotspot stopped"); - callback.onStopped(); - break; - case HOTSPOT_FAILED: - int reasonCode = msg.arg1; - Log.w(TAG, "LocalOnlyHotspotCallbackProxy: failed to start. reason: " - + reasonCode); - callback.onFailed(reasonCode); - Log.w(TAG, "done with the callback..."); - break; - default: - Log.e(TAG, "LocalOnlyHotspotCallbackProxy unhandled message. type: " - + msg.what); - } - } - }; - mMessenger = new Messenger(mHandler); + @Override + public void onHotspotStarted(SoftApConfiguration config) { + WifiManager manager = mWifiManager.get(); + if (manager == null) return; + + if (config == null) { + Log.e(TAG, "LocalOnlyHotspotCallbackProxy: config cannot be null."); + onHotspotFailed(LocalOnlyHotspotCallback.ERROR_GENERIC); + return; + } + final LocalOnlyHotspotReservation reservation = + manager.new LocalOnlyHotspotReservation(config); + if (mCallback == null) return; + mExecutor.execute(() -> mCallback.onStarted(reservation)); } - public Messenger getMessenger() { - return mMessenger; + @Override + public void onHotspotStopped() { + WifiManager manager = mWifiManager.get(); + if (manager == null) return; + + Log.w(TAG, "LocalOnlyHotspotCallbackProxy: hotspot stopped"); + if (mCallback == null) return; + mExecutor.execute(() -> mCallback.onStopped()); } - /** - * Helper method allowing the the incoming application call to move the onFailed callback - * over to the desired callback thread. - * - * @param reason int representing the error type - */ - public void notifyFailed(int reason) throws RemoteException { - Message msg = Message.obtain(); - msg.what = HOTSPOT_FAILED; - msg.arg1 = reason; - mMessenger.send(msg); + @Override + public void onHotspotFailed(int reason) { + WifiManager manager = mWifiManager.get(); + if (manager == null) return; + + Log.w(TAG, "LocalOnlyHotspotCallbackProxy: failed to start. reason: " + + reason); + if (mCallback == null) return; + mExecutor.execute(() -> mCallback.onFailed(reason)); } } @@ -3431,7 +3993,7 @@ public class WifiManager { * @hide */ public class LocalOnlyHotspotSubscription implements AutoCloseable { - private final CloseGuard mCloseGuard = CloseGuard.get(); + private final CloseGuard mCloseGuard = new CloseGuard(); /** @hide */ @VisibleForTesting @@ -3446,6 +4008,8 @@ public class WifiManager { mCloseGuard.close(); } catch (Exception e) { Log.e(TAG, "Failed to unregister LocalOnlyHotspotObserver."); + } finally { + Reference.reachabilityFence(this); } } @@ -3477,7 +4041,7 @@ public class WifiManager { /** * LocalOnlyHotspot started with the supplied config. */ - public void onStarted(WifiConfiguration config) {}; + public void onStarted(SoftApConfiguration config) {}; /** * LocalOnlyHotspot stopped. @@ -3488,191 +4052,111 @@ public class WifiManager { /** * Callback proxy for LocalOnlyHotspotObserver objects. */ - private static class LocalOnlyHotspotObserverProxy { - private final Handler mHandler; + private static class LocalOnlyHotspotObserverProxy extends ILocalOnlyHotspotCallback.Stub { private final WeakReference<WifiManager> mWifiManager; - private final Looper mLooper; - private final Messenger mMessenger; + private final Executor mExecutor; + private final LocalOnlyHotspotObserver mObserver; /** * Constructs a {@link LocalOnlyHotspotObserverProxy} using the specified looper. * All callbacks will be delivered on the thread of the specified looper. * * @param manager WifiManager - * @param looper Looper for delivering callbacks + * @param executor Executor for delivering callbacks * @param observer LocalOnlyHotspotObserver to notify the calling application. */ - LocalOnlyHotspotObserverProxy(WifiManager manager, Looper looper, + LocalOnlyHotspotObserverProxy(WifiManager manager, Executor executor, final LocalOnlyHotspotObserver observer) { mWifiManager = new WeakReference<>(manager); - mLooper = looper; - - mHandler = new Handler(looper) { - @Override - public void handleMessage(Message msg) { - Log.d(TAG, "LocalOnlyHotspotObserverProxy: handle message what: " - + msg.what + " msg: " + msg); - - WifiManager manager = mWifiManager.get(); - if (manager == null) { - Log.w(TAG, "LocalOnlyHotspotObserverProxy: handle message post GC"); - return; - } - - switch (msg.what) { - case HOTSPOT_OBSERVER_REGISTERED: - observer.onRegistered(manager.new LocalOnlyHotspotSubscription()); - break; - case HOTSPOT_STARTED: - WifiConfiguration config = (WifiConfiguration) msg.obj; - if (config == null) { - Log.e(TAG, "LocalOnlyHotspotObserverProxy: config cannot be null."); - return; - } - observer.onStarted(config); - break; - case HOTSPOT_STOPPED: - observer.onStopped(); - break; - default: - Log.e(TAG, "LocalOnlyHotspotObserverProxy unhandled message. type: " - + msg.what); - } - } - }; - mMessenger = new Messenger(mHandler); - } - - public Messenger getMessenger() { - return mMessenger; + mExecutor = executor; + mObserver = observer; } public void registered() throws RemoteException { - Message msg = Message.obtain(); - msg.what = HOTSPOT_OBSERVER_REGISTERED; - mMessenger.send(msg); - } - } + WifiManager manager = mWifiManager.get(); + if (manager == null) return; - // Ensure that multiple ServiceHandler threads do not interleave message dispatch. - private static final Object sServiceHandlerDispatchLock = new Object(); - - private class ServiceHandler extends Handler { - ServiceHandler(Looper looper) { - super(looper); + mExecutor.execute(() -> + mObserver.onRegistered(manager.new LocalOnlyHotspotSubscription())); } @Override - public void handleMessage(Message message) { - synchronized (sServiceHandlerDispatchLock) { - dispatchMessageToListeners(message); + public void onHotspotStarted(SoftApConfiguration config) { + WifiManager manager = mWifiManager.get(); + if (manager == null) return; + + if (config == null) { + Log.e(TAG, "LocalOnlyHotspotObserverProxy: config cannot be null."); + return; } + mExecutor.execute(() -> mObserver.onStarted(config)); } - private void dispatchMessageToListeners(Message message) { - Object listener = removeListener(message.arg2); - switch (message.what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: - if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); - } else { - Log.e(TAG, "Failed to set up channel connection"); - // This will cause all further async API calls on the WifiManager - // to fail and throw an exception - mAsyncChannel = null; - } - mConnected.countDown(); - break; - case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: - // Ignore - break; - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - Log.e(TAG, "Channel connection lost"); - // This will cause all further async API calls on the WifiManager - // to fail and throw an exception - mAsyncChannel = null; - getLooper().quit(); - break; - /* ActionListeners grouped together */ - case WifiManager.CONNECT_NETWORK_FAILED: - case WifiManager.FORGET_NETWORK_FAILED: - case WifiManager.SAVE_NETWORK_FAILED: - case WifiManager.DISABLE_NETWORK_FAILED: - if (listener != null) { - ((ActionListener) listener).onFailure(message.arg1); - } - break; - /* ActionListeners grouped together */ - case WifiManager.CONNECT_NETWORK_SUCCEEDED: - case WifiManager.FORGET_NETWORK_SUCCEEDED: - case WifiManager.SAVE_NETWORK_SUCCEEDED: - case WifiManager.DISABLE_NETWORK_SUCCEEDED: - if (listener != null) { - ((ActionListener) listener).onSuccess(); - } - break; - case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED: - if (listener != null) { - RssiPacketCountInfo info = (RssiPacketCountInfo) message.obj; - if (info != null) - ((TxPacketCountListener) listener).onSuccess(info.txgood + info.txbad); - else - ((TxPacketCountListener) listener).onFailure(ERROR); - } - break; - case WifiManager.RSSI_PKTCNT_FETCH_FAILED: - if (listener != null) { - ((TxPacketCountListener) listener).onFailure(message.arg1); - } - break; - default: - //ignore - break; - } + @Override + public void onHotspotStopped() { + WifiManager manager = mWifiManager.get(); + if (manager == null) return; + + mExecutor.execute(() -> mObserver.onStopped()); } - } - private int putListener(Object listener) { - if (listener == null) return INVALID_KEY; - int key; - synchronized (mListenerMapLock) { - do { - key = mListenerKey++; - } while (key == INVALID_KEY); - mListenerMap.put(key, listener); + @Override + public void onHotspotFailed(int reason) { + // do nothing } - return key; } - private Object removeListener(int key) { - if (key == INVALID_KEY) return null; - synchronized (mListenerMapLock) { - Object listener = mListenerMap.get(key); - mListenerMap.remove(key); - return listener; + /** + * Callback proxy for ActionListener objects. + */ + private class ActionListenerProxy extends IActionListener.Stub { + private final String mActionTag; + private final Handler mHandler; + private final ActionListener mCallback; + + ActionListenerProxy(String actionTag, Looper looper, ActionListener callback) { + mActionTag = actionTag; + mHandler = new Handler(looper); + mCallback = callback; } - } - private synchronized AsyncChannel getChannel() { - if (mAsyncChannel == null) { - Messenger messenger = getWifiServiceMessenger(); - if (messenger == null) { - throw new IllegalStateException( - "getWifiServiceMessenger() returned null! This is invalid."); + @Override + public void onSuccess() { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "ActionListenerProxy:" + mActionTag + ": onSuccess"); } + mHandler.post(() -> { + mCallback.onSuccess(); + }); + } - mAsyncChannel = new AsyncChannel(); - mConnected = new CountDownLatch(1); - - Handler handler = new ServiceHandler(mLooper); - mAsyncChannel.connect(mContext, handler, messenger); - try { - mConnected.await(); - } catch (InterruptedException e) { - Log.e(TAG, "interrupted wait at init"); + @Override + public void onFailure(@ActionListenerFailureReason int reason) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "ActionListenerProxy:" + mActionTag + ": onFailure=" + reason); } + mHandler.post(() -> { + mCallback.onFailure(reason); + }); + } + } + + private void connectInternal(@Nullable WifiConfiguration config, int networkId, + @Nullable ActionListener listener) { + ActionListenerProxy listenerProxy = null; + Binder binder = null; + if (listener != null) { + listenerProxy = new ActionListenerProxy("connect", mLooper, listener); + binder = new Binder(); + } + try { + mService.connect(config, networkId, binder, listenerProxy, + listener == null ? 0 : listener.hashCode()); + } catch (RemoteException e) { + if (listenerProxy != null) listenerProxy.onFailure(ERROR); + } catch (SecurityException e) { + if (listenerProxy != null) listenerProxy.onFailure(NOT_AUTHORIZED); } - return mAsyncChannel; } /** @@ -3698,10 +4182,7 @@ public class WifiManager { }) public void connect(@NonNull WifiConfiguration config, @Nullable ActionListener listener) { if (config == null) throw new IllegalArgumentException("config cannot be null"); - // Use INVALID_NETWORK_ID for arg1 when passing a config object - // arg1 is used to pass network id when the network already exists - getChannel().sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID, - putListener(listener), config); + connectInternal(config, WifiConfiguration.INVALID_NETWORK_ID, listener); } /** @@ -3709,6 +4190,10 @@ public class WifiManager { * * This function is used instead of a enableNetwork() and reconnect() * + * <li> This API will cause reconnect if the credentials of the current active + * connection has been changed.</li> + * <li> This API will cause reconnect if the current active connection is marked metered.</li> + * * @param networkId the ID of the network as returned by {@link #addNetwork} or {@link * getConfiguredNetworks}. * @param listener for callbacks on success or failure. Can be null. @@ -3724,7 +4209,7 @@ public class WifiManager { }) public void connect(int networkId, @Nullable ActionListener listener) { if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative"); - getChannel().sendMessage(CONNECT_NETWORK, networkId, putListener(listener)); + connectInternal(null, networkId, listener); } /** @@ -3737,8 +4222,9 @@ public class WifiManager { * * For an existing network, it accomplishes the task of updateNetwork() * - * This API will cause reconnect if the crecdentials of the current active - * connection has been changed. + * <li> This API will cause reconnect if the credentials of the current active + * connection has been changed.</li> + * <li> This API will cause disconnect if the current active connection is marked metered.</li> * * @param config the set of variables that describe the configuration, * contained in a {@link WifiConfiguration} object. @@ -3755,7 +4241,20 @@ public class WifiManager { }) public void save(@NonNull WifiConfiguration config, @Nullable ActionListener listener) { if (config == null) throw new IllegalArgumentException("config cannot be null"); - getChannel().sendMessage(SAVE_NETWORK, 0, putListener(listener), config); + ActionListenerProxy listenerProxy = null; + Binder binder = null; + if (listener != null) { + listenerProxy = new ActionListenerProxy("save", mLooper, listener); + binder = new Binder(); + } + try { + mService.save(config, binder, listenerProxy, + listener == null ? 0 : listener.hashCode()); + } catch (RemoteException e) { + if (listenerProxy != null) listenerProxy.onFailure(ERROR); + } catch (SecurityException e) { + if (listenerProxy != null) listenerProxy.onFailure(NOT_AUTHORIZED); + } } /** @@ -3779,7 +4278,20 @@ public class WifiManager { }) public void forget(int netId, @Nullable ActionListener listener) { if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative"); - getChannel().sendMessage(FORGET_NETWORK, netId, putListener(listener)); + ActionListenerProxy listenerProxy = null; + Binder binder = null; + if (listener != null) { + listenerProxy = new ActionListenerProxy("forget", mLooper, listener); + binder = new Binder(); + } + try { + mService.forget(netId, binder, listenerProxy, + listener == null ? 0 : listener.hashCode()); + } catch (RemoteException e) { + if (listenerProxy != null) listenerProxy.onFailure(ERROR); + } catch (SecurityException e) { + if (listenerProxy != null) listenerProxy.onFailure(NOT_AUTHORIZED); + } } /** @@ -3789,6 +4301,7 @@ public class WifiManager { * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be * initialized again + * @deprecated This API is deprecated. Use {@link #disableNetwork(int)} instead. * @hide */ @SystemApi @@ -3797,25 +4310,131 @@ public class WifiManager { android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK }) + @Deprecated public void disable(int netId, @Nullable ActionListener listener) { if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative"); - getChannel().sendMessage(DISABLE_NETWORK, netId, putListener(listener)); + // Simple wrapper which forwards the call to disableNetwork. This is a temporary + // implementation until we can remove this API completely. + boolean status = disableNetwork(netId); + if (listener != null) { + if (status) { + listener.onSuccess(); + } else { + listener.onFailure(ERROR); + } + } } /** - * Disable ephemeral Network + * Enable/disable auto-join globally. * - * @param SSID, in the format of WifiConfiguration's SSID. + * @param allowAutojoin true to allow auto-join, false to disallow auto-join * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void allowAutojoinGlobal(boolean allowAutojoin) { + try { + mService.allowAutojoinGlobal(allowAutojoin); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + + /** + * Sets the user choice for allowing auto-join to a network. + * The updated choice will be made available through the updated config supplied by the + * CONFIGURED_NETWORKS_CHANGED broadcast. + * + * @param netId the id of the network to allow/disallow auto-join for. + * @param allowAutojoin true to allow auto-join, false to disallow auto-join + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void allowAutojoin(int netId, boolean allowAutojoin) { + try { + mService.allowAutojoin(netId, allowAutojoin); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Configure auto-join settings for a Passpoint profile. + * + * @param fqdn the FQDN (fully qualified domain name) of the passpoint profile. + * @param allowAutojoin true to enable auto-join, false to disable auto-join. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void allowAutojoinPasspoint(@NonNull String fqdn, boolean allowAutojoin) { + try { + mService.allowAutojoinPasspoint(fqdn, allowAutojoin); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Configure MAC randomization setting for a Passpoint profile. + * MAC randomization is enabled by default. + * + * @param fqdn the FQDN (fully qualified domain name) of the passpoint profile. + * @param enable true to enable MAC randomization, false to disable MAC randomization. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void setMacRandomizationSettingPasspointEnabled(@NonNull String fqdn, boolean enable) { + try { + mService.setMacRandomizationSettingPasspointEnabled(fqdn, enable); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets the user's choice of metered override for a Passpoint profile. + * + * @param fqdn the FQDN (fully qualified domain name) of the passpoint profile. + * @param meteredOverride One of three values: {@link WifiConfiguration#METERED_OVERRIDE_NONE}, + * {@link WifiConfiguration#METERED_OVERRIDE_METERED}, + * {@link WifiConfiguration#METERED_OVERRIDE_NOT_METERED} + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void setPasspointMeteredOverride(@NonNull String fqdn, + @WifiConfiguration.MeteredOverride int meteredOverride) { + try { + mService.setPasspointMeteredOverride(fqdn, meteredOverride); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Temporarily disable a network. Should always trigger with user disconnect network. + * + * @param network Input can be SSID or FQDN. And caller must ensure that the SSID passed thru + * this API matched the WifiConfiguration.SSID rules, and thus be surrounded by + * quotes. + * @hide + */ + @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK }) - public void disableEphemeralNetwork(String SSID) { - if (SSID == null) throw new IllegalArgumentException("SSID cannot be null"); + public void disableEphemeralNetwork(@NonNull String network) { + if (TextUtils.isEmpty(network)) { + throw new IllegalArgumentException("SSID cannot be null or empty!"); + } try { - mService.disableEphemeralNetwork(SSID, mContext.getOpPackageName()); + mService.disableEphemeralNetwork(network, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3851,22 +4470,6 @@ public class WifiManager { } /** - * Get a reference to WifiService handler. This is used by a client to establish - * an AsyncChannel communication with WifiService - * - * @return Messenger pointing to the WifiService handler - */ - @UnsupportedAppUsage - private Messenger getWifiServiceMessenger() { - try { - return mService.getWifiServiceMessenger(mContext.getOpPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - - /** * Allows an application to keep the Wi-Fi radio awake. * Normally the Wi-Fi radio may turn off when the user has not used the device in a while. * Acquiring a WifiLock will keep the radio on until the lock is released. Multiple @@ -4002,7 +4605,7 @@ public class WifiManager { if (ws == null) { mWorkSource = null; } else { - ws.clearNames(); + ws = ws.withoutNames(); if (mWorkSource == null) { changed = mWorkSource != null; mWorkSource = new WorkSource(ws); @@ -4298,22 +4901,25 @@ public class WifiManager { } } - protected void finalize() throws Throwable { - try { - if (mAsyncChannel != null) { - mAsyncChannel.disconnect(); - } - } finally { - super.finalize(); - } - } - /** - * Set wifi verbose log. Called from developer settings. + * Set Wi-Fi verbose logging level from developer settings. + * + * @param enable true to enable verbose logging, false to disable. + * * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void setVerboseLoggingEnabled(boolean enable) { + enableVerboseLogging(enable ? 1 : 0); + } + + /** @hide */ + @UnsupportedAppUsage( + maxTargetSdk = Build.VERSION_CODES.Q, + publicAlternatives = "Use {@code #setVerboseLoggingEnabled(boolean)} instead." + ) @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) - @UnsupportedAppUsage public void enableVerboseLogging (int verbose) { try { mService.enableVerboseLogging(verbose); @@ -4324,11 +4930,26 @@ public class WifiManager { } /** - * Get the WiFi verbose logging level.This is used by settings - * to decide what to show within the picker. + * Get the persisted Wi-Fi verbose logging level, set by + * {@link #setVerboseLoggingEnabled(boolean)}. + * No permissions are required to call this method. + * + * @return true to indicate that verbose logging is enabled, false to indicate that verbose + * logging is disabled. + * * @hide */ - @UnsupportedAppUsage + @SystemApi + public boolean isVerboseLoggingEnabled() { + return getVerboseLoggingLevel() > 0; + } + + /** @hide */ + // TODO(b/145484145): remove once SUW stops calling this via reflection + @UnsupportedAppUsage( + maxTargetSdk = Build.VERSION_CODES.Q, + publicAlternatives = "Use {@code #isVerboseLoggingEnabled()} instead." + ) public int getVerboseLoggingLevel() { try { return mService.getVerboseLoggingLevel(); @@ -4338,10 +4959,13 @@ public class WifiManager { } /** - * Removes all saved wifi networks. + * Removes all saved Wi-Fi networks, Passpoint configurations, ephemeral networks, Network + * Requests, and Network Suggestions. * * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset() { try { mService.factoryReset(mContext.getOpPackageName()); @@ -4351,11 +4975,15 @@ public class WifiManager { } /** - * Get Network object of current wifi network - * @return Get Network object of current wifi network + * Get {@link Network} object of current wifi network, or null if not connected. * @hide */ - @UnsupportedAppUsage + @Nullable + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD + }) public Network getCurrentNetwork() { try { return mService.getCurrentNetwork(); @@ -4385,36 +5013,67 @@ public class WifiManager { } /** - * Enable/disable WifiConnectivityManager + * Returns a byte stream representing the data that needs to be backed up to save the + * current Wifi state. + * This Wifi state can be restored by calling {@link #restoreBackupData(byte[])}. * @hide */ - public void enableWifiConnectivityManager(boolean enabled) { + @NonNull + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public byte[] retrieveBackupData() { try { - mService.enableWifiConnectivityManager(enabled); + return mService.retrieveBackupData(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Retrieve the data to be backed to save the current state. + * Restore state from the backed up data. + * @param data byte stream in the same format produced by {@link #retrieveBackupData()} * @hide */ - public byte[] retrieveBackupData() { + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void restoreBackupData(@NonNull byte[] data) { try { - return mService.retrieveBackupData(); + mService.restoreBackupData(data); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Restore state from the backed up data. + * Returns a byte stream representing the data that needs to be backed up to save the + * current soft ap config data. + * + * This soft ap config can be restored by calling {@link #restoreSoftApBackupData(byte[])} * @hide */ - public void restoreBackupData(byte[] data) { + @NonNull + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public byte[] retrieveSoftApBackupData() { try { - mService.restoreBackupData(data); + return mService.retrieveSoftApBackupData(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns soft ap config from the backed up data or null if data is invalid. + * @param data byte stream in the same format produced by {@link #retrieveSoftApBackupData()} + * + * @hide + */ + @Nullable + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public SoftApConfiguration restoreSoftApBackupData(@NonNull byte[] data) { + try { + return mService.restoreSoftApBackupData(data); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -4424,11 +5083,14 @@ public class WifiManager { * Restore state from the older version of back up data. * The old backup data was essentially a backup of wpa_supplicant.conf * and ipconfig.txt file. - * @deprecated this is no longer supported. + * @param supplicantData bytes representing wpa_supplicant.conf + * @param ipConfigData bytes representing ipconfig.txt * @hide */ - @Deprecated - public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) { + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void restoreSupplicantBackupData( + @NonNull byte[] supplicantData, @NonNull byte[] ipConfigData) { try { mService.restoreSupplicantBackupData(supplicantData, ipConfigData); } catch (RemoteException e) { @@ -4495,22 +5157,29 @@ public class WifiManager { } /** - * Base class for Traffic state callback. Should be extended by applications and set when - * calling {@link WifiManager#registerTrafficStateCallback(TrafficStateCallback, Handler)}. + * Interface for Traffic state callback. Should be extended by applications and set when + * calling {@link #registerTrafficStateCallback(Executor, WifiManager.TrafficStateCallback)}. * @hide */ + @SystemApi public interface TrafficStateCallback { - /** - * Lowest bit indicates data reception and the second lowest - * bit indicates data transmitted - */ /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"DATA_ACTIVITY_"}, value = { + DATA_ACTIVITY_NONE, + DATA_ACTIVITY_IN, + DATA_ACTIVITY_OUT, + DATA_ACTIVITY_INOUT}) + @interface DataActivity {} + + // Lowest bit indicates data reception and the second lowest bit indicates data transmitted + /** No data in or out */ int DATA_ACTIVITY_NONE = 0x00; - /** @hide */ + /** Data in, no data out */ int DATA_ACTIVITY_IN = 0x01; - /** @hide */ + /** Data out, no data in */ int DATA_ACTIVITY_OUT = 0x02; - /** @hide */ + /** Data in and out */ int DATA_ACTIVITY_INOUT = 0x03; /** @@ -4518,9 +5187,8 @@ public class WifiManager { * * @param state One of the values: {@link #DATA_ACTIVITY_NONE}, {@link #DATA_ACTIVITY_IN}, * {@link #DATA_ACTIVITY_OUT} & {@link #DATA_ACTIVITY_INOUT}. - * @hide */ - void onStateChanged(int state); + void onStateChanged(@DataActivity int state); } /** @@ -4529,11 +5197,11 @@ public class WifiManager { * @hide */ private class TrafficStateCallbackProxy extends ITrafficStateCallback.Stub { - private final Handler mHandler; + private final Executor mExecutor; private final TrafficStateCallback mCallback; - TrafficStateCallbackProxy(Looper looper, TrafficStateCallback callback) { - mHandler = new Handler(looper); + TrafficStateCallbackProxy(Executor executor, TrafficStateCallback callback) { + mExecutor = executor; mCallback = callback; } @@ -4542,7 +5210,8 @@ public class WifiManager { if (mVerboseLoggingEnabled) { Log.v(TAG, "TrafficStateCallbackProxy: onStateChanged state=" + state); } - mHandler.post(() -> { + Binder.clearCallingIdentity(); + mExecutor.execute(() -> { mCallback.onStateChanged(state); }); } @@ -4559,22 +5228,23 @@ public class WifiManager { * without the permission will trigger a {@link java.lang.SecurityException}. * <p> * + * @param executor The Executor on whose thread to execute the callbacks of the {@code callback} + * object. * @param callback Callback for traffic state 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 registerTrafficStateCallback(@NonNull TrafficStateCallback callback, - @Nullable Handler handler) { + public void registerTrafficStateCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull TrafficStateCallback callback) { + if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (callback == null) throw new IllegalArgumentException("callback cannot be null"); - Log.v(TAG, "registerTrafficStateCallback: callback=" + callback + ", handler=" + handler); + Log.v(TAG, "registerTrafficStateCallback: callback=" + callback + ", executor=" + executor); - Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper(); Binder binder = new Binder(); try { mService.registerTrafficStateCallback( - binder, new TrafficStateCallbackProxy(looper, callback), callback.hashCode()); + binder, new TrafficStateCallbackProxy(executor, callback), callback.hashCode()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -4587,6 +5257,7 @@ public class WifiManager { * @param callback Callback to unregister for traffic state events * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterTrafficStateCallback(@NonNull TrafficStateCallback callback) { if (callback == null) throw new IllegalArgumentException("callback cannot be null"); @@ -4604,7 +5275,7 @@ public class WifiManager { * level from wifi service. */ private void updateVerboseLoggingEnabledFromService() { - mVerboseLoggingEnabled = getVerboseLoggingLevel() > 0; + mVerboseLoggingEnabled = isVerboseLoggingEnabled(); } /** @@ -4641,11 +5312,20 @@ public class WifiManager { } /** + * @return true if this device supports WAPI. + */ + public boolean isWapiSupported() { + return isFeatureSupported(WIFI_FEATURE_WAPI); + } + + /** * Gets the factory Wi-Fi MAC addresses. * @return Array of String representing Wi-Fi MAC addresses sorted lexically or an empty Array * if failed. * @hide */ + @NonNull + @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses() { try { @@ -4845,6 +5525,7 @@ public class WifiManager { @Override public void onSuccessConfigReceived(int newNetworkId) { Log.d(TAG, "Easy Connect onSuccessConfigReceived callback"); + Binder.clearCallingIdentity(); mExecutor.execute(() -> { mEasyConnectStatusCallback.onEnrolleeSuccess(newNetworkId); }); @@ -4853,22 +5534,28 @@ public class WifiManager { @Override public void onSuccess(int status) { Log.d(TAG, "Easy Connect onSuccess callback"); + Binder.clearCallingIdentity(); mExecutor.execute(() -> { mEasyConnectStatusCallback.onConfiguratorSuccess(status); }); } @Override - public void onFailure(int status) { + public void onFailure(int status, String ssid, String channelList, + int[] operatingClassArray) { Log.d(TAG, "Easy Connect onFailure callback"); + Binder.clearCallingIdentity(); mExecutor.execute(() -> { - mEasyConnectStatusCallback.onFailure(status); + SparseArray<int[]> channelListArray = parseDppChannelList(channelList); + mEasyConnectStatusCallback.onFailure(status, ssid, channelListArray, + operatingClassArray); }); } @Override public void onProgress(int status) { Log.d(TAG, "Easy Connect onProgress callback"); + Binder.clearCallingIdentity(); mExecutor.execute(() -> { mEasyConnectStatusCallback.onProgress(status); }); @@ -4929,9 +5616,9 @@ public class WifiManager { Log.v(TAG, "OnWifiUsabilityStatsListener: " + "onWifiUsabilityStats: seqNum=" + seqNum); } - Binder.withCleanCallingIdentity(() -> - executor.execute(() -> listener.onWifiUsabilityStats(seqNum, - isSameBssidAndFreq, stats))); + Binder.clearCallingIdentity(); + executor.execute(() -> listener.onWifiUsabilityStats( + seqNum, isSameBssidAndFreq, stats)); } }, listener.hashCode() @@ -4985,4 +5672,564 @@ public class WifiManager { throw e.rethrowFromSystemServer(); } } + + /** + * Abstract class for scan results callback. Should be extended by applications and set when + * calling {@link WifiManager#registerScanResultsCallback(Executor, ScanResultsCallback)}. + */ + public abstract static class ScanResultsCallback { + private final ScanResultsCallbackProxy mScanResultsCallbackProxy; + + public ScanResultsCallback() { + mScanResultsCallbackProxy = new ScanResultsCallbackProxy(); + } + + /** + * Called when new scan results are available. + * Clients should use {@link WifiManager#getScanResults()} to get the scan results. + */ + public abstract void onScanResultsAvailable(); + + /*package*/ @NonNull ScanResultsCallbackProxy getProxy() { + return mScanResultsCallbackProxy; + } + + private static class ScanResultsCallbackProxy extends IScanResultsCallback.Stub { + private final Object mLock = new Object(); + @Nullable @GuardedBy("mLock") private Executor mExecutor; + @Nullable @GuardedBy("mLock") private ScanResultsCallback mCallback; + + ScanResultsCallbackProxy() { + mCallback = null; + mExecutor = null; + } + + /*package*/ void initProxy(@NonNull Executor executor, + @NonNull ScanResultsCallback callback) { + synchronized (mLock) { + mExecutor = executor; + mCallback = callback; + } + } + + /*package*/ void cleanUpProxy() { + synchronized (mLock) { + mExecutor = null; + mCallback = null; + } + } + + @Override + public void onScanResultsAvailable() { + ScanResultsCallback callback; + Executor executor; + synchronized (mLock) { + executor = mExecutor; + callback = mCallback; + } + if (callback == null || executor == null) { + return; + } + Binder.clearCallingIdentity(); + executor.execute(callback::onScanResultsAvailable); + } + } + + } + + /** + * Register a callback for Scan Results. See {@link ScanResultsCallback}. + * Caller will receive the event when scan results are available. + * Caller should use {@link WifiManager#getScanResults()} requires + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} to get the scan results. + * Caller can remove a previously registered callback using + * {@link WifiManager#unregisterScanResultsCallback(ScanResultsCallback)} + * Same caller can add multiple listeners. + * <p> + * Applications should have the + * {@link android.Manifest.permission#ACCESS_WIFI_STATE} permission. Callers + * without the permission will trigger a {@link java.lang.SecurityException}. + * <p> + * + * @param executor The executor to execute the callback of the {@code callback} object. + * @param callback callback for Scan Results events + */ + + @RequiresPermission(ACCESS_WIFI_STATE) + public void registerScanResultsCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull ScanResultsCallback callback) { + if (executor == null) throw new IllegalArgumentException("executor cannot be null"); + if (callback == null) throw new IllegalArgumentException("callback cannot be null"); + + Log.v(TAG, "registerScanResultsCallback: callback=" + callback + + ", executor=" + executor); + ScanResultsCallback.ScanResultsCallbackProxy proxy = callback.getProxy(); + proxy.initProxy(executor, callback); + try { + mService.registerScanResultsCallback(proxy); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Allow callers to unregister a previously registered callback. After calling this method, + * applications will no longer receive Scan Results events. + * + * @param callback callback to unregister for Scan Results events + */ + @RequiresPermission(ACCESS_WIFI_STATE) + public void unregisterScanResultsCallback(@NonNull ScanResultsCallback callback) { + if (callback == null) throw new IllegalArgumentException("callback cannot be null"); + Log.v(TAG, "unregisterScanResultsCallback: Callback=" + callback); + ScanResultsCallback.ScanResultsCallbackProxy proxy = callback.getProxy(); + try { + mService.unregisterScanResultsCallback(proxy); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } finally { + proxy.cleanUpProxy(); + } + } + + /** + * Interface for suggestion connection status listener. + * Should be implemented by applications and set when calling + * {@link WifiManager#addSuggestionConnectionStatusListener( + * Executor, SuggestionConnectionStatusListener)}. + */ + public interface SuggestionConnectionStatusListener { + + /** + * Called when the framework attempted to connect to a suggestion provided by the + * registering app, but the connection to the suggestion failed. + * @param wifiNetworkSuggestion The suggestion which failed to connect. + * @param failureReason the connection failure reason code. One of + * {@link #STATUS_SUGGESTION_CONNECTION_FAILURE_ASSOCIATION}, + * {@link #STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION}, + * {@link #STATUS_SUGGESTION_CONNECTION_FAILURE_IP_PROVISIONING} + * {@link #STATUS_SUGGESTION_CONNECTION_FAILURE_UNKNOWN} + */ + void onConnectionStatus( + @NonNull WifiNetworkSuggestion wifiNetworkSuggestion, + @SuggestionConnectionStatusCode int failureReason); + } + + private class SuggestionConnectionStatusListenerProxy extends + ISuggestionConnectionStatusListener.Stub { + private final Executor mExecutor; + private final SuggestionConnectionStatusListener mListener; + + SuggestionConnectionStatusListenerProxy(@NonNull Executor executor, + @NonNull SuggestionConnectionStatusListener listener) { + mExecutor = executor; + mListener = listener; + } + + @Override + public void onConnectionStatus(@NonNull WifiNetworkSuggestion wifiNetworkSuggestion, + int failureReason) { + mExecutor.execute(() -> + mListener.onConnectionStatus(wifiNetworkSuggestion, failureReason)); + } + + } + + /** + * Add a listener for suggestion networks. See {@link SuggestionConnectionStatusListener}. + * Caller will receive the event when suggested network have connection failure. + * Caller can remove a previously registered listener using + * {@link WifiManager#removeSuggestionConnectionStatusListener( + * SuggestionConnectionStatusListener)} + * Same caller can add multiple listeners to monitor the event. + * <p> + * Applications should have the + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and + * {@link android.Manifest.permission#ACCESS_WIFI_STATE} permissions. + * Callers without the permission will trigger a {@link java.lang.SecurityException}. + * <p> + * + * @param executor The executor to execute the listener of the {@code listener} object. + * @param listener listener for suggestion network connection failure. + */ + @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE}) + public void addSuggestionConnectionStatusListener(@NonNull @CallbackExecutor Executor executor, + @NonNull SuggestionConnectionStatusListener listener) { + if (listener == null) throw new IllegalArgumentException("Listener cannot be null"); + if (executor == null) throw new IllegalArgumentException("Executor cannot be null"); + Log.v(TAG, "addSuggestionConnectionStatusListener listener=" + listener + + ", executor=" + executor); + try { + mService.registerSuggestionConnectionStatusListener(new Binder(), + new SuggestionConnectionStatusListenerProxy(executor, listener), + listener.hashCode(), mContext.getOpPackageName(), mContext.getAttributionTag()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + } + + /** + * Allow callers to remove a previously registered listener. After calling this method, + * applications will no longer receive suggestion connection events through that listener. + * + * @param listener listener to remove. + */ + @RequiresPermission(ACCESS_WIFI_STATE) + public void removeSuggestionConnectionStatusListener( + @NonNull SuggestionConnectionStatusListener listener) { + if (listener == null) throw new IllegalArgumentException("Listener cannot be null"); + Log.v(TAG, "removeSuggestionConnectionStatusListener: listener=" + listener); + try { + mService.unregisterSuggestionConnectionStatusListener(listener.hashCode(), + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Parse the list of channels the DPP enrollee reports when it fails to find an AP. + * + * @param channelList List of channels in the format defined in the DPP specification. + * @return A parsed sparse array, where the operating class is the key. + * @hide + */ + @VisibleForTesting + public static SparseArray<int[]> parseDppChannelList(String channelList) { + SparseArray<int[]> channelListArray = new SparseArray<>(); + + if (TextUtils.isEmpty(channelList)) { + return channelListArray; + } + StringTokenizer str = new StringTokenizer(channelList, ","); + String classStr = null; + List<Integer> channelsInClass = new ArrayList<>(); + + try { + while (str.hasMoreElements()) { + String cur = str.nextToken(); + + /** + * Example for a channel list: + * + * 81/1,2,3,4,5,6,7,8,9,10,11,115/36,40,44,48,118/52,56,60,64,121/100,104,108,112, + * 116,120,124,128,132,136,140,0/144,124/149,153,157,161,125/165 + * + * Detect operating class by the delimiter of '/' and use a string tokenizer with + * ',' as a delimiter. + */ + int classDelim = cur.indexOf('/'); + if (classDelim != -1) { + if (classStr != null) { + // Store the last channel array in the sparse array, where the operating + // class is the key (as an integer). + int[] channelsArray = new int[channelsInClass.size()]; + for (int i = 0; i < channelsInClass.size(); i++) { + channelsArray[i] = channelsInClass.get(i); + } + channelListArray.append(Integer.parseInt(classStr), channelsArray); + channelsInClass = new ArrayList<>(); + } + + // Init a new operating class and store the first channel + classStr = cur.substring(0, classDelim); + String channelStr = cur.substring(classDelim + 1); + channelsInClass.add(Integer.parseInt(channelStr)); + } else { + if (classStr == null) { + // Invalid format + Log.e(TAG, "Cannot parse DPP channel list"); + return new SparseArray<>(); + } + channelsInClass.add(Integer.parseInt(cur)); + } + } + + // Store the last array + if (classStr != null) { + int[] channelsArray = new int[channelsInClass.size()]; + for (int i = 0; i < channelsInClass.size(); i++) { + channelsArray[i] = channelsInClass.get(i); + } + channelListArray.append(Integer.parseInt(classStr), channelsArray); + } + return channelListArray; + } catch (NumberFormatException e) { + Log.e(TAG, "Cannot parse DPP channel list"); + return new SparseArray<>(); + } + } + + /** + * Callback interface for framework to receive network status updates and trigger of updating + * {@link WifiUsabilityStatsEntry}. + * + * @hide + */ + @SystemApi + public interface ScoreUpdateObserver { + /** + * Called by applications to indicate network status. + * + * @param sessionId The ID to indicate current Wi-Fi network connection obtained from + * {@link WifiConnectedNetworkScorer#onStart(int)}. + * @param score The score representing link quality of current Wi-Fi network connection. + * Populated by connected network scorer in applications.. + */ + void notifyScoreUpdate(int sessionId, int score); + + /** + * Called by applications to trigger an update of {@link WifiUsabilityStatsEntry}. + * To receive update applications need to add WifiUsabilityStatsEntry listener. See + * {@link addOnWifiUsabilityStatsListener(Executor, OnWifiUsabilityStatsListener)}. + * + * @param sessionId The ID to indicate current Wi-Fi network connection obtained from + * {@link WifiConnectedNetworkScorer#onStart(int)}. + */ + void triggerUpdateOfWifiUsabilityStats(int sessionId); + } + + /** + * Callback proxy for {@link ScoreUpdateObserver} objects. + * + * @hide + */ + private class ScoreUpdateObserverProxy implements ScoreUpdateObserver { + private final IScoreUpdateObserver mScoreUpdateObserver; + + private ScoreUpdateObserverProxy(IScoreUpdateObserver observer) { + mScoreUpdateObserver = observer; + } + + @Override + public void notifyScoreUpdate(int sessionId, int score) { + try { + mScoreUpdateObserver.notifyScoreUpdate(sessionId, score); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void triggerUpdateOfWifiUsabilityStats(int sessionId) { + try { + mScoreUpdateObserver.triggerUpdateOfWifiUsabilityStats(sessionId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Interface for Wi-Fi connected network scorer. Should be implemented by applications and set + * when calling + * {@link WifiManager#setWifiConnectedNetworkScorer(Executor, WifiConnectedNetworkScorer)}. + * + * @hide + */ + @SystemApi + public interface WifiConnectedNetworkScorer { + /** + * Called by framework to indicate the start of a network connection. + * @param sessionId The ID to indicate current Wi-Fi network connection. + */ + void onStart(int sessionId); + + /** + * Called by framework to indicate the end of a network connection. + * @param sessionId The ID to indicate current Wi-Fi network connection obtained from + * {@link WifiConnectedNetworkScorer#onStart(int)}. + */ + void onStop(int sessionId); + + /** + * Framework sets callback for score change events after application sets its scorer. + * @param observerImpl The instance for {@link WifiManager#ScoreUpdateObserver}. Should be + * implemented and instantiated by framework. + */ + void onSetScoreUpdateObserver(@NonNull ScoreUpdateObserver observerImpl); + } + + /** + * Callback proxy for {@link WifiConnectedNetworkScorer} objects. + * + * @hide + */ + private class WifiConnectedNetworkScorerProxy extends IWifiConnectedNetworkScorer.Stub { + private Executor mExecutor; + private WifiConnectedNetworkScorer mScorer; + + WifiConnectedNetworkScorerProxy(Executor executor, WifiConnectedNetworkScorer scorer) { + mExecutor = executor; + mScorer = scorer; + } + + @Override + public void onStart(int sessionId) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "WifiConnectedNetworkScorer: " + "onStart: sessionId=" + sessionId); + } + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mScorer.onStart(sessionId)); + } + + @Override + public void onStop(int sessionId) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "WifiConnectedNetworkScorer: " + "onStop: sessionId=" + sessionId); + } + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mScorer.onStop(sessionId)); + } + + @Override + public void onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "WifiConnectedNetworkScorer: " + + "onSetScoreUpdateObserver: observerImpl=" + observerImpl); + } + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mScorer.onSetScoreUpdateObserver( + new ScoreUpdateObserverProxy(observerImpl))); + } + } + + /** + * Set a callback for Wi-Fi connected network scorer. See {@link WifiConnectedNetworkScorer}. + * Only a single scorer can be set. Caller will be invoked periodically by framework to inform + * client about start and stop of Wi-Fi connection. Caller can clear a previously set scorer + * using {@link clearWifiConnectedNetworkScorer()}. + * + * @param executor The executor on which callback will be invoked. + * @param scorer Scorer for Wi-Fi network implemented by application. + * @return true Scorer is set successfully. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) + public boolean setWifiConnectedNetworkScorer(@NonNull @CallbackExecutor Executor executor, + @NonNull WifiConnectedNetworkScorer scorer) { + if (executor == null) throw new IllegalArgumentException("executor cannot be null"); + if (scorer == null) throw new IllegalArgumentException("scorer cannot be null"); + if (mVerboseLoggingEnabled) { + Log.v(TAG, "setWifiConnectedNetworkScorer: scorer=" + scorer); + } + try { + return mService.setWifiConnectedNetworkScorer(new Binder(), + new WifiConnectedNetworkScorerProxy(executor, scorer)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Allow caller to clear a previously set scorer. After calling this method, + * client will no longer receive information about start and stop of Wi-Fi connection. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) + public void clearWifiConnectedNetworkScorer() { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "clearWifiConnectedNetworkScorer"); + } + try { + mService.clearWifiConnectedNetworkScorer(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Enable/disable wifi scan throttling from 3rd party apps. + * + * <p> + * The throttling limits for apps are described in + * <a href="Wi-Fi Scan Throttling"> + * https://developer.android.com/guide/topics/connectivity/wifi-scan#wifi-scan-throttling</a> + * </p> + * + * @param enable true to allow scan throttling, false to disallow scan throttling. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void setScanThrottleEnabled(boolean enable) { + try { + mService.setScanThrottleEnabled(enable); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get the persisted Wi-Fi scan throttle state. Defaults to true, unless changed by the user via + * Developer options. + * + * <p> + * The throttling limits for apps are described in + * <a href="Wi-Fi Scan Throttling"> + * https://developer.android.com/guide/topics/connectivity/wifi-scan#wifi-scan-throttling</a> + * </p> + * + * @return true to indicate that scan throttling is enabled, false to indicate that scan + * throttling is disabled. + */ + @RequiresPermission(ACCESS_WIFI_STATE) + public boolean isScanThrottleEnabled() { + try { + return mService.isScanThrottleEnabled(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Enable/disable wifi auto wakeup feature. + * + * <p> + * The feature is described in + * <a href="Wi-Fi Turn on automatically"> + * https://source.android.com/devices/tech/connect/wifi-infrastructure + * #turn_on_wi-fi_automatically + * </a> + * + * @param enable true to enable, false to disable. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void setAutoWakeupEnabled(boolean enable) { + try { + mService.setAutoWakeupEnabled(enable); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get the persisted Wi-Fi auto wakeup feature state. Defaults to false, unless changed by the + * user via Settings. + * + * <p> + * The feature is described in + * <a href="Wi-Fi Turn on automatically"> + * https://source.android.com/devices/tech/connect/wifi-infrastructure + * #turn_on_wi-fi_automatically + * </a> + * + * @return true to indicate that wakeup feature is enabled, false to indicate that wakeup + * feature is disabled. + */ + @RequiresPermission(ACCESS_WIFI_STATE) + public boolean isAutoWakeupEnabled() { + try { + return mService.isAutoWakeupEnabled(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/wifi/java/android/net/wifi/WifiMigration.java b/wifi/java/android/net/wifi/WifiMigration.java new file mode 100755 index 000000000000..5792d27a94f9 --- /dev/null +++ b/wifi/java/android/net/wifi/WifiMigration.java @@ -0,0 +1,558 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi; + +import static android.os.Environment.getDataMiscCeDirectory; +import static android.os.Environment.getDataMiscDirectory; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.AtomicFile; +import android.util.SparseArray; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Class used to provide one time hooks for existing OEM devices to migrate their config store + * data and other settings to the wifi apex. + * @hide + */ +@SystemApi +public final class WifiMigration { + /** + * Directory to read the wifi config store files from under. + */ + private static final String LEGACY_WIFI_STORE_DIRECTORY_NAME = "wifi"; + /** + * Config store file for general shared store file. + * AOSP Path on Android 10: /data/misc/wifi/WifiConfigStore.xml + */ + public static final int STORE_FILE_SHARED_GENERAL = 0; + /** + * Config store file for softap shared store file. + * AOSP Path on Android 10: /data/misc/wifi/softap.conf + */ + public static final int STORE_FILE_SHARED_SOFTAP = 1; + /** + * Config store file for general user store file. + * AOSP Path on Android 10: /data/misc_ce/<userId>/wifi/WifiConfigStore.xml + */ + public static final int STORE_FILE_USER_GENERAL = 2; + /** + * Config store file for network suggestions user store file. + * AOSP Path on Android 10: /data/misc_ce/<userId>/wifi/WifiConfigStoreNetworkSuggestions.xml + */ + public static final int STORE_FILE_USER_NETWORK_SUGGESTIONS = 3; + + /** @hide */ + @IntDef(prefix = { "STORE_FILE_SHARED_" }, value = { + STORE_FILE_SHARED_GENERAL, + STORE_FILE_SHARED_SOFTAP, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SharedStoreFileId { } + + /** @hide */ + @IntDef(prefix = { "STORE_FILE_USER_" }, value = { + STORE_FILE_USER_GENERAL, + STORE_FILE_USER_NETWORK_SUGGESTIONS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UserStoreFileId { } + + /** + * Mapping of Store file Id to Store file names. + * + * NOTE: This is the default path for the files on AOSP devices. If the OEM has modified + * the path or renamed the files, please edit this appropriately. + */ + private static final SparseArray<String> STORE_ID_TO_FILE_NAME = + new SparseArray<String>() {{ + put(STORE_FILE_SHARED_GENERAL, "WifiConfigStore.xml"); + put(STORE_FILE_SHARED_SOFTAP, "WifiConfigStoreSoftAp.xml"); + put(STORE_FILE_USER_GENERAL, "WifiConfigStore.xml"); + put(STORE_FILE_USER_NETWORK_SUGGESTIONS, "WifiConfigStoreNetworkSuggestions.xml"); + }}; + + /** + * Pre-apex wifi shared folder. + */ + private static File getLegacyWifiSharedDirectory() { + return new File(getDataMiscDirectory(), LEGACY_WIFI_STORE_DIRECTORY_NAME); + } + + /** + * Pre-apex wifi user folder. + */ + private static File getLegacyWifiUserDirectory(int userId) { + return new File(getDataMiscCeDirectory(userId), LEGACY_WIFI_STORE_DIRECTORY_NAME); + } + + /** + * Legacy files were stored as AtomicFile. So, always use AtomicFile to operate on it to ensure + * data integrity. + */ + private static AtomicFile getSharedAtomicFile(@SharedStoreFileId int storeFileId) { + return new AtomicFile(new File( + getLegacyWifiSharedDirectory(), + STORE_ID_TO_FILE_NAME.get(storeFileId))); + } + + /** + * Legacy files were stored as AtomicFile. So, always use AtomicFile to operate on it to ensure + * data integrity. + */ + private static AtomicFile getUserAtomicFile(@UserStoreFileId int storeFileId, int userId) { + return new AtomicFile(new File( + getLegacyWifiUserDirectory(userId), + STORE_ID_TO_FILE_NAME.get(storeFileId))); + } + + private WifiMigration() { } + + /** + * Load data from legacy shared wifi config store file. + * <p> + * Expected AOSP format is available in the sample files under {@code /frameworks/base/wifi/ + * java/android/net/wifi/migration_samples}. + * </p> + * <p> + * Note: + * <li>OEMs need to change the implementation of + * {@link #convertAndRetrieveSharedConfigStoreFile(int)} only if their existing config store + * format or file locations differs from the vanilla AOSP implementation.</li> + * <li>The wifi apex will invoke + * {@link #convertAndRetrieveSharedConfigStoreFile(int)} + * method on every bootup, it is the responsibility of the OEM implementation to ensure that + * they perform the necessary in place conversion of their config store file to conform to the + * AOSP format. The OEM should ensure that the method should only return the + * {@link InputStream} stream for the data to be migrated only on the first bootup.</li> + * <li>Once the migration is done, the apex will invoke + * {@link #removeSharedConfigStoreFile(int)} to delete the store file.</li> + * <li>The only relevant invocation of {@link #convertAndRetrieveSharedConfigStoreFile(int)} + * occurs when a previously released device upgrades to the wifi apex from an OEM + * implementation of the wifi stack. + * <li>Ensure that the legacy file paths are accessible to the wifi module (sepolicy rules, file + * permissions, etc). Since the wifi service continues to run inside system_server process, this + * method will be called from the same context (so ideally the file should still be accessible). + * </li> + * + * @param storeFileId Identifier for the config store file. One of + * {@link #STORE_FILE_SHARED_GENERAL} or {@link #STORE_FILE_SHARED_GENERAL} + * @return Instance of {@link InputStream} for migrating data, null if no migration is + * necessary. + * @throws IllegalArgumentException on invalid storeFileId. + */ + @Nullable + public static InputStream convertAndRetrieveSharedConfigStoreFile( + @SharedStoreFileId int storeFileId) { + if (storeFileId != STORE_FILE_SHARED_GENERAL && storeFileId != STORE_FILE_SHARED_SOFTAP) { + throw new IllegalArgumentException("Invalid shared store file id"); + } + try { + // OEMs should do conversions necessary here before returning the stream. + return getSharedAtomicFile(storeFileId).openRead(); + } catch (FileNotFoundException e) { + // Special handling for softap.conf. + // Note: OEM devices upgrading from Q -> R will only have the softap.conf file. + // Test devices running previous R builds however may have already migrated to the + // XML format. So, check for that above before falling back to check for legacy file. + if (storeFileId == STORE_FILE_SHARED_SOFTAP) { + return SoftApConfToXmlMigrationUtil.convert(); + } + return null; + } + } + + /** + * Remove the legacy shared wifi config store file. + * + * @param storeFileId Identifier for the config store file. One of + * {@link #STORE_FILE_SHARED_GENERAL} or {@link #STORE_FILE_SHARED_GENERAL} + * @throws IllegalArgumentException on invalid storeFileId. + */ + public static void removeSharedConfigStoreFile(@SharedStoreFileId int storeFileId) { + if (storeFileId != STORE_FILE_SHARED_GENERAL && storeFileId != STORE_FILE_SHARED_SOFTAP) { + throw new IllegalArgumentException("Invalid shared store file id"); + } + AtomicFile file = getSharedAtomicFile(storeFileId); + if (file.exists()) { + file.delete(); + return; + } + // Special handling for softap.conf. + // Note: OEM devices upgrading from Q -> R will only have the softap.conf file. + // Test devices running previous R builds however may have already migrated to the + // XML format. So, check for that above before falling back to check for legacy file. + if (storeFileId == STORE_FILE_SHARED_SOFTAP) { + SoftApConfToXmlMigrationUtil.remove(); + } + } + + /** + * Load data from legacy user wifi config store file. + * <p> + * Expected AOSP format is available in the sample files under {@code /frameworks/base/wifi/ + * java/android/net/wifi/migration_samples}. + * </p> + * <p> + * Note: + * <li>OEMs need to change the implementation of + * {@link #convertAndRetrieveUserConfigStoreFile(int, UserHandle)} only if their existing config + * store format or file locations differs from the vanilla AOSP implementation.</li> + * <li>The wifi apex will invoke + * {@link #convertAndRetrieveUserConfigStoreFile(int, UserHandle)} + * method on every bootup, it is the responsibility of the OEM implementation to ensure that + * they perform the necessary in place conversion of their config store file to conform to the + * AOSP format. The OEM should ensure that the method should only return the + * {@link InputStream} stream for the data to be migrated only on the first bootup.</li> + * <li>Once the migration is done, the apex will invoke + * {@link #removeUserConfigStoreFile(int, UserHandle)} to delete the store file.</li> + * <li>The only relevant invocation of + * {@link #convertAndRetrieveUserConfigStoreFile(int, UserHandle)} occurs when a previously + * released device upgrades to the wifi apex from an OEM implementation of the wifi + * stack. + * </li> + * <li>Ensure that the legacy file paths are accessible to the wifi module (sepolicy rules, file + * permissions, etc). Since the wifi service continues to run inside system_server process, this + * method will be called from the same context (so ideally the file should still be accessible). + * </li> + * + * @param storeFileId Identifier for the config store file. One of + * {@link #STORE_FILE_USER_GENERAL} or {@link #STORE_FILE_USER_NETWORK_SUGGESTIONS} + * @param userHandle User handle. + * @return Instance of {@link InputStream} for migrating data, null if no migration is + * necessary. + * @throws IllegalArgumentException on invalid storeFileId or userHandle. + */ + @Nullable + public static InputStream convertAndRetrieveUserConfigStoreFile( + @UserStoreFileId int storeFileId, @NonNull UserHandle userHandle) { + if (storeFileId != STORE_FILE_USER_GENERAL + && storeFileId != STORE_FILE_USER_NETWORK_SUGGESTIONS) { + throw new IllegalArgumentException("Invalid user store file id"); + } + Objects.requireNonNull(userHandle); + try { + // OEMs should do conversions necessary here before returning the stream. + return getUserAtomicFile(storeFileId, userHandle.getIdentifier()).openRead(); + } catch (FileNotFoundException e) { + return null; + } + } + + /** + * Remove the legacy user wifi config store file. + * + * @param storeFileId Identifier for the config store file. One of + * {@link #STORE_FILE_USER_GENERAL} or {@link #STORE_FILE_USER_NETWORK_SUGGESTIONS} + * @param userHandle User handle. + * @throws IllegalArgumentException on invalid storeFileId or userHandle. + */ + public static void removeUserConfigStoreFile( + @UserStoreFileId int storeFileId, @NonNull UserHandle userHandle) { + if (storeFileId != STORE_FILE_USER_GENERAL + && storeFileId != STORE_FILE_USER_NETWORK_SUGGESTIONS) { + throw new IllegalArgumentException("Invalid user store file id"); + } + Objects.requireNonNull(userHandle); + AtomicFile file = getUserAtomicFile(storeFileId, userHandle.getIdentifier()); + if (file.exists()) { + file.delete(); + } + } + + /** + * Container for all the wifi settings data to migrate. + */ + public static final class SettingsMigrationData implements Parcelable { + private final boolean mScanAlwaysAvailable; + private final boolean mP2pFactoryResetPending; + private final String mP2pDeviceName; + private final boolean mSoftApTimeoutEnabled; + private final boolean mWakeupEnabled; + private final boolean mScanThrottleEnabled; + private final boolean mVerboseLoggingEnabled; + + private SettingsMigrationData(boolean scanAlwaysAvailable, boolean p2pFactoryResetPending, + @Nullable String p2pDeviceName, boolean softApTimeoutEnabled, boolean wakeupEnabled, + boolean scanThrottleEnabled, boolean verboseLoggingEnabled) { + mScanAlwaysAvailable = scanAlwaysAvailable; + mP2pFactoryResetPending = p2pFactoryResetPending; + mP2pDeviceName = p2pDeviceName; + mSoftApTimeoutEnabled = softApTimeoutEnabled; + mWakeupEnabled = wakeupEnabled; + mScanThrottleEnabled = scanThrottleEnabled; + mVerboseLoggingEnabled = verboseLoggingEnabled; + } + + public static final @NonNull Parcelable.Creator<SettingsMigrationData> CREATOR = + new Parcelable.Creator<SettingsMigrationData>() { + @Override + public SettingsMigrationData createFromParcel(Parcel in) { + boolean scanAlwaysAvailable = in.readBoolean(); + boolean p2pFactoryResetPending = in.readBoolean(); + String p2pDeviceName = in.readString(); + boolean softApTimeoutEnabled = in.readBoolean(); + boolean wakeupEnabled = in.readBoolean(); + boolean scanThrottleEnabled = in.readBoolean(); + boolean verboseLoggingEnabled = in.readBoolean(); + return new SettingsMigrationData( + scanAlwaysAvailable, p2pFactoryResetPending, + p2pDeviceName, softApTimeoutEnabled, wakeupEnabled, + scanThrottleEnabled, verboseLoggingEnabled); + } + + @Override + public SettingsMigrationData[] newArray(int size) { + return new SettingsMigrationData[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeBoolean(mScanAlwaysAvailable); + dest.writeBoolean(mP2pFactoryResetPending); + dest.writeString(mP2pDeviceName); + dest.writeBoolean(mSoftApTimeoutEnabled); + dest.writeBoolean(mWakeupEnabled); + dest.writeBoolean(mScanThrottleEnabled); + dest.writeBoolean(mVerboseLoggingEnabled); + } + + /** + * @return True if scans are allowed even when wifi is toggled off, false otherwise. + */ + public boolean isScanAlwaysAvailable() { + return mScanAlwaysAvailable; + } + + /** + * @return indicate whether factory reset request is pending. + */ + public boolean isP2pFactoryResetPending() { + return mP2pFactoryResetPending; + } + + /** + * @return the Wi-Fi peer-to-peer device name + */ + public @Nullable String getP2pDeviceName() { + return mP2pDeviceName; + } + + /** + * @return Whether soft AP will shut down after a timeout period when no devices are + * connected. + */ + public boolean isSoftApTimeoutEnabled() { + return mSoftApTimeoutEnabled; + } + + /** + * @return whether Wi-Fi Wakeup feature is enabled. + */ + public boolean isWakeUpEnabled() { + return mWakeupEnabled; + } + + /** + * @return Whether wifi scan throttle is enabled or not. + */ + public boolean isScanThrottleEnabled() { + return mScanThrottleEnabled; + } + + /** + * @return Whether to enable verbose logging in Wi-Fi. + */ + public boolean isVerboseLoggingEnabled() { + return mVerboseLoggingEnabled; + } + + /** + * Builder to create instance of {@link SettingsMigrationData}. + */ + public static final class Builder { + private boolean mScanAlwaysAvailable; + private boolean mP2pFactoryResetPending; + private String mP2pDeviceName; + private boolean mSoftApTimeoutEnabled; + private boolean mWakeupEnabled; + private boolean mScanThrottleEnabled; + private boolean mVerboseLoggingEnabled; + + public Builder() { + } + + /** + * Setting to allow scans even when wifi is toggled off. + * + * @param available true if available, false otherwise. + * @return Instance of {@link Builder} to enable chaining of the builder method. + */ + public @NonNull Builder setScanAlwaysAvailable(boolean available) { + mScanAlwaysAvailable = available; + return this; + } + + /** + * Indicate whether factory reset request is pending. + * + * @param pending true if pending, false otherwise. + * @return Instance of {@link Builder} to enable chaining of the builder method. + */ + public @NonNull Builder setP2pFactoryResetPending(boolean pending) { + mP2pFactoryResetPending = pending; + return this; + } + + /** + * The Wi-Fi peer-to-peer device name + * + * @param name Name if set, null otherwise. + * @return Instance of {@link Builder} to enable chaining of the builder method. + */ + public @NonNull Builder setP2pDeviceName(@Nullable String name) { + mP2pDeviceName = name; + return this; + } + + /** + * Whether soft AP will shut down after a timeout period when no devices are connected. + * + * @param enabled true if enabled, false otherwise. + * @return Instance of {@link Builder} to enable chaining of the builder method. + */ + public @NonNull Builder setSoftApTimeoutEnabled(boolean enabled) { + mSoftApTimeoutEnabled = enabled; + return this; + } + + /** + * Value to specify if Wi-Fi Wakeup feature is enabled. + * + * @param enabled true if enabled, false otherwise. + * @return Instance of {@link Builder} to enable chaining of the builder method. + */ + public @NonNull Builder setWakeUpEnabled(boolean enabled) { + mWakeupEnabled = enabled; + return this; + } + + /** + * Whether wifi scan throttle is enabled or not. + * + * @param enabled true if enabled, false otherwise. + * @return Instance of {@link Builder} to enable chaining of the builder method. + */ + public @NonNull Builder setScanThrottleEnabled(boolean enabled) { + mScanThrottleEnabled = enabled; + return this; + } + + /** + * Setting to enable verbose logging in Wi-Fi. + * + * @param enabled true if enabled, false otherwise. + * @return Instance of {@link Builder} to enable chaining of the builder method. + */ + public @NonNull Builder setVerboseLoggingEnabled(boolean enabled) { + mVerboseLoggingEnabled = enabled; + return this; + } + + /** + * Build an instance of {@link SettingsMigrationData}. + * + * @return Instance of {@link SettingsMigrationData}. + */ + public @NonNull SettingsMigrationData build() { + return new SettingsMigrationData(mScanAlwaysAvailable, mP2pFactoryResetPending, + mP2pDeviceName, mSoftApTimeoutEnabled, mWakeupEnabled, mScanThrottleEnabled, + mVerboseLoggingEnabled); + } + } + } + + /** + * Load data from Settings.Global values. + * + * <p> + * Note: + * <li> This is method is invoked once on the first bootup. OEM can safely delete these settings + * once the migration is complete. The first & only relevant invocation of + * {@link #loadFromSettings(Context)} ()} occurs when a previously released + * device upgrades to the wifi apex from an OEM implementation of the wifi stack. + * </li> + * + * @param context Context to use for loading the settings provider. + * @return Instance of {@link SettingsMigrationData} for migrating data. + */ + @NonNull + public static SettingsMigrationData loadFromSettings(@NonNull Context context) { + if (Settings.Global.getInt( + context.getContentResolver(), Settings.Global.WIFI_MIGRATION_COMPLETED, 0) == 1) { + // migration already complete, ignore. + return null; + } + SettingsMigrationData data = new SettingsMigrationData.Builder() + .setScanAlwaysAvailable( + Settings.Global.getInt(context.getContentResolver(), + Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1) + .setP2pFactoryResetPending( + Settings.Global.getInt(context.getContentResolver(), + Settings.Global.WIFI_P2P_PENDING_FACTORY_RESET, 0) == 1) + .setP2pDeviceName( + Settings.Global.getString(context.getContentResolver(), + Settings.Global.WIFI_P2P_DEVICE_NAME)) + .setSoftApTimeoutEnabled( + Settings.Global.getInt(context.getContentResolver(), + Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1) == 1) + .setWakeUpEnabled( + Settings.Global.getInt(context.getContentResolver(), + Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1) + .setScanThrottleEnabled( + Settings.Global.getInt(context.getContentResolver(), + Settings.Global.WIFI_SCAN_THROTTLE_ENABLED, 1) == 1) + .setVerboseLoggingEnabled( + Settings.Global.getInt(context.getContentResolver(), + Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 0) == 1) + .build(); + Settings.Global.putInt( + context.getContentResolver(), Settings.Global.WIFI_MIGRATION_COMPLETED, 1); + return data; + + } +} diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java index d384298c60c4..0d13805a08d8 100644 --- a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java @@ -23,12 +23,10 @@ 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 android.text.TextUtils; import java.util.Objects; @@ -42,33 +40,10 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements */ 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; - - /** - * The package name 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 null if the device - * auto-connected to a wifi network. - */ - private final String mOriginalRequestorPackageName; - - public WifiNetworkAgentSpecifier(@NonNull WifiConfiguration wifiConfiguration, - int originalRequestorUid, - @Nullable String originalRequestorPackageName) { + public WifiNetworkAgentSpecifier(@NonNull WifiConfiguration wifiConfiguration) { checkNotNull(wifiConfiguration); mWifiConfiguration = wifiConfiguration; - mOriginalRequestorUid = originalRequestorUid; - mOriginalRequestorPackageName = originalRequestorPackageName; } /** @@ -79,10 +54,7 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements @Override public WifiNetworkAgentSpecifier createFromParcel(@NonNull Parcel in) { WifiConfiguration wifiConfiguration = in.readParcelable(null); - int originalRequestorUid = in.readInt(); - String originalRequestorPackageName = in.readString(); - return new WifiNetworkAgentSpecifier( - wifiConfiguration, originalRequestorUid, originalRequestorPackageName); + return new WifiNetworkAgentSpecifier(wifiConfiguration); } @Override @@ -99,8 +71,6 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeParcelable(mWifiConfiguration, flags); - dest.writeInt(mOriginalRequestorUid); - dest.writeString(mOriginalRequestorPackageName); } @Override @@ -120,7 +90,7 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements /** * Match {@link WifiNetworkSpecifier} in app's {@link NetworkRequest} with the - * {@link WifiNetworkAgentSpecifier} in wifi platform's {@link NetworkAgent}. + * {@link WifiNetworkAgentSpecifier} in wifi platform's {@link android.net.NetworkAgent}. */ public boolean satisfiesNetworkSpecifier(@NonNull WifiNetworkSpecifier ns) { // None of these should be null by construction. @@ -150,12 +120,6 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements this.mWifiConfiguration.allowedKeyManagement)) { return false; } - if (ns.requestorUid != this.mOriginalRequestorUid) { - return false; - } - if (!TextUtils.equals(ns.requestorPackageName, this.mOriginalRequestorPackageName)) { - return false; - } return true; } @@ -164,9 +128,7 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements return Objects.hash( mWifiConfiguration.SSID, mWifiConfiguration.BSSID, - mWifiConfiguration.allowedKeyManagement, - mOriginalRequestorUid, - mOriginalRequestorPackageName); + mWifiConfiguration.allowedKeyManagement); } @Override @@ -181,10 +143,7 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements 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 - && TextUtils.equals(mOriginalRequestorPackageName, - lhs.mOriginalRequestorPackageName); + lhs.mWifiConfiguration.allowedKeyManagement); } @Override @@ -193,8 +152,6 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements sb.append("WifiConfiguration=") .append(", SSID=").append(mWifiConfiguration.SSID) .append(", BSSID=").append(mWifiConfiguration.BSSID) - .append(", mOriginalRequestorUid=").append(mOriginalRequestorUid) - .append(", mOriginalRequestorPackageName=").append(mOriginalRequestorPackageName) .append("]"); return sb.toString(); } diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java index 5a212a824452..378549d62edf 100755 --- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java +++ b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java @@ -22,7 +22,6 @@ import android.annotation.Nullable; import android.content.Context; import android.net.INetworkScoreCache; import android.net.NetworkKey; -import android.net.NetworkScoreManager; import android.net.ScoredNetwork; import android.os.Handler; import android.os.Process; @@ -30,19 +29,19 @@ import android.util.Log; import android.util.LruCache; import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; +import java.util.Objects; /** * {@link INetworkScoreCache} implementation for Wifi Networks. * + * TODO: This should not be part of wifi mainline module. * @hide */ -public class WifiNetworkScoreCache extends INetworkScoreCache.Stub - implements NetworkScoreManager.NetworkScoreCallback { +public class WifiNetworkScoreCache extends INetworkScoreCache.Stub { private static final String TAG = "WifiNetworkScoreCache"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); @@ -248,17 +247,6 @@ public class WifiNetworkScoreCache extends INetworkScoreCache.Stub } @Override protected final void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); - dumpWithLatestScanResults(fd, writer, args, wifiManager.getScanResults()); - } - - /** - * This is directly invoked from within Wifi-Service (on it's instance of this class), hence - * avoid making the WifiManager.getScanResults() call to avoid a deadlock. - */ - public final void dumpWithLatestScanResults( - FileDescriptor fd, PrintWriter writer, String[] args, - List<ScanResult> latestScanResults) { mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG); String header = String.format("WifiNetworkScoreCache (%s/%d)", mContext.getPackageName(), Process.myUid()); @@ -269,7 +257,8 @@ public class WifiNetworkScoreCache extends INetworkScoreCache.Stub writer.println(" " + score); } writer.println(" Network scores for latest ScanResults:"); - for (ScanResult scanResult : latestScanResults) { + WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + for (ScanResult scanResult : wifiManager.getScanResults()) { writer.println( " " + buildNetworkKey(scanResult) + ": " + getNetworkScore(scanResult)); } @@ -301,7 +290,7 @@ public class WifiNetworkScoreCache extends INetworkScoreCache.Stub * This cannot be null. */ public CacheListener(@NonNull Handler handler) { - Preconditions.checkNotNull(handler); + Objects.requireNonNull(handler); mHandler = handler; } diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java index f08935af680d..b0213b0ef502 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -20,15 +20,12 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityThread; import android.net.MacAddress; -import android.net.MatchAllNetworkSpecifier; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.os.Parcel; import android.os.Parcelable; import android.os.PatternMatcher; -import android.os.Process; import android.text.TextUtils; import android.util.Pair; @@ -41,6 +38,7 @@ import java.util.Objects; * {@link WifiNetworkSpecifier.Builder} class to create an instance. */ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parcelable { + private static final String TAG = "WifiNetworkSpecifier"; /** * Builder used to create {@link WifiNetworkSpecifier} objects. @@ -49,11 +47,11 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc 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_PATTERN1 = - new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS); + new Pair<>(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS); private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN2 = - new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.BROADCAST_ADDRESS); + new Pair<>(WifiManager.ALL_ZEROS_MAC_ADDRESS, MacAddress.BROADCAST_ADDRESS); private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN = - new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS); + new Pair<>(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS); private static final MacAddress MATCH_EXACT_BSSID_PATTERN_MASK = MacAddress.BROADCAST_ADDRESS; @@ -157,7 +155,8 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc */ public @NonNull Builder setBssidPattern( @NonNull MacAddress baseAddress, @NonNull MacAddress mask) { - checkNotNull(baseAddress, mask); + checkNotNull(baseAddress); + checkNotNull(mask); mBssidPatternMatcher = Pair.create(baseAddress, mask); return this; } @@ -385,7 +384,8 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc * * For example: * To connect to an open network with a SSID prefix of "test" and a BSSID OUI of "10:03:23": - * {@code + * + * <pre>{@code * final NetworkSpecifier specifier = * new Builder() * .setSsidPattern(new PatternMatcher("test", PatterMatcher.PATTERN_PREFIX)) @@ -407,7 +407,7 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc * // etc. * }; * connectivityManager.requestNetwork(request, networkCallback); - * } + * }</pre> * * @return Instance of {@link NetworkSpecifier}. * @throws IllegalStateException on invalid params set. @@ -433,9 +433,7 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc return new WifiNetworkSpecifier( mSsidPatternMatcher, mBssidPatternMatcher, - buildWifiConfiguration(), - Process.myUid(), - ActivityThread.currentApplication().getApplicationContext().getOpPackageName()); + buildWifiConfiguration()); } } @@ -463,20 +461,6 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc */ 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. - * @hide - */ - public final int requestorUid; - - /** - * The package name of the app initializing this network specifier. - * @hide - */ - public final String requestorPackageName; - /** @hide */ public WifiNetworkSpecifier() throws IllegalAccessException { throw new IllegalAccessException("Use the builder to create an instance"); @@ -485,18 +469,14 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc /** @hide */ public WifiNetworkSpecifier(@NonNull PatternMatcher ssidPatternMatcher, @NonNull Pair<MacAddress, MacAddress> bssidPatternMatcher, - @NonNull WifiConfiguration wifiConfiguration, - int requestorUid, @NonNull String requestorPackageName) { + @NonNull WifiConfiguration wifiConfiguration) { checkNotNull(ssidPatternMatcher); checkNotNull(bssidPatternMatcher); checkNotNull(wifiConfiguration); - checkNotNull(requestorPackageName); this.ssidPatternMatcher = ssidPatternMatcher; this.bssidPatternMatcher = bssidPatternMatcher; this.wifiConfiguration = wifiConfiguration; - this.requestorUid = requestorUid; - this.requestorPackageName = requestorPackageName; } public static final @NonNull Creator<WifiNetworkSpecifier> CREATOR = @@ -509,10 +489,8 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc Pair<MacAddress, MacAddress> bssidPatternMatcher = Pair.create(baseAddress, mask); WifiConfiguration wifiConfiguration = in.readParcelable(null); - int requestorUid = in.readInt(); - String requestorPackageName = in.readString(); return new WifiNetworkSpecifier(ssidPatternMatcher, bssidPatternMatcher, - wifiConfiguration, requestorUid, requestorPackageName); + wifiConfiguration); } @Override @@ -532,18 +510,13 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc dest.writeParcelable(bssidPatternMatcher.first, flags); dest.writeParcelable(bssidPatternMatcher.second, flags); dest.writeParcelable(wifiConfiguration, flags); - dest.writeInt(requestorUid); - dest.writeString(requestorPackageName); } @Override public int hashCode() { return Objects.hash( - ssidPatternMatcher.getPath(), - ssidPatternMatcher.getType(), - bssidPatternMatcher, - wifiConfiguration.allowedKeyManagement, - requestorUid, requestorPackageName); + ssidPatternMatcher.getPath(), ssidPatternMatcher.getType(), bssidPatternMatcher, + wifiConfiguration.allowedKeyManagement); } @Override @@ -562,9 +535,7 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc && Objects.equals(this.bssidPatternMatcher, lhs.bssidPatternMatcher) && Objects.equals(this.wifiConfiguration.allowedKeyManagement, - lhs.wifiConfiguration.allowedKeyManagement) - && requestorUid == lhs.requestorUid - && TextUtils.equals(requestorPackageName, lhs.requestorPackageName); + lhs.wifiConfiguration.allowedKeyManagement); } @Override @@ -575,8 +546,6 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc .append(", BSSID Match pattern=").append(bssidPatternMatcher) .append(", SSID=").append(wifiConfiguration.SSID) .append(", BSSID=").append(wifiConfiguration.BSSID) - .append(", requestorUid=").append(requestorUid) - .append(", requestorPackageName=").append(requestorPackageName) .append("]") .toString(); } @@ -584,13 +553,6 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc /** @hide */ @Override public boolean canBeSatisfiedBy(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); } diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index 426201732359..4d3a2c02c686 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -21,11 +21,13 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityThread; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.net.MacAddress; +import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Parcel; import android.os.Parcelable; -import android.os.Process; +import android.telephony.TelephonyManager; import android.text.TextUtils; import java.nio.charset.CharsetEncoder; @@ -42,7 +44,6 @@ import java.util.Objects; * {@link WifiManager#addNetworkSuggestions(List)}. */ public final class WifiNetworkSuggestion implements Parcelable { - /** * Builder used to create {@link WifiNetworkSuggestion} objects. */ @@ -80,6 +81,10 @@ public final class WifiNetworkSuggestion implements Parcelable { */ private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig; /** + * The passpoint config for use with Hotspot 2.0 network + */ + private @Nullable PasspointConfiguration mPasspointConfiguration; + /** * This is a network that does not broadcast its SSID, so an * SSID-specific probe request must be used for scans. */ @@ -95,13 +100,50 @@ public final class WifiNetworkSuggestion implements Parcelable { /** * Whether this network is metered or not. */ - private boolean mIsMetered; + private int mMeteredOverride; /** * 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; + /** + * The carrier ID identifies the operator who provides this network configuration. + * see {@link TelephonyManager#getSimCarrierId()} + */ + private int mCarrierId; + + /** + * Whether this network is shared credential with user to allow user manually connect. + */ + private boolean mIsSharedWithUser; + + /** + * Whether the setCredentialSharedWithUser have been called. + */ + private boolean mIsSharedWithUserSet; + + /** + * Whether this network is initialized with auto-join enabled (the default) or not. + */ + private boolean mIsInitialAutojoinEnabled; + + /** + * Pre-shared key for use with WAPI-PSK networks. + */ + private @Nullable String mWapiPskPassphrase; + + /** + * The enterprise configuration details specifying the EAP method, + * certificates and other settings associated with the WAPI networks. + */ + private @Nullable WifiEnterpriseConfig mWapiEnterpriseConfig; + + /** + * Whether this network will be brought up as untrusted (TRUSTED capability bit removed). + */ + private boolean mIsNetworkUntrusted; + public Builder() { mSsid = null; mBssid = null; @@ -110,11 +152,19 @@ public final class WifiNetworkSuggestion implements Parcelable { mWpa3SaePassphrase = null; mWpa2EnterpriseConfig = null; mWpa3EnterpriseConfig = null; + mPasspointConfiguration = null; mIsHiddenSSID = false; mIsAppInteractionRequired = false; mIsUserInteractionRequired = false; - mIsMetered = false; + mMeteredOverride = WifiConfiguration.METERED_OVERRIDE_NONE; + mIsSharedWithUser = true; + mIsSharedWithUserSet = false; + mIsInitialAutojoinEnabled = true; mPriority = UNASSIGNED_PRIORITY; + mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; + mWapiPskPassphrase = null; + mWapiEnterpriseConfig = null; + mIsNetworkUntrusted = false; } /** @@ -207,33 +257,111 @@ public final class WifiNetworkSuggestion implements Parcelable { /** * Set the associated enterprise configuration for this network. Needed for authenticating - * to WPA2-EAP networks. See {@link WifiEnterpriseConfig} for description. + * to WPA2 enterprise networks. See {@link WifiEnterpriseConfig} for description. * * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}. * @return Instance of {@link Builder} to enable chaining of the builder method. + * @throws IllegalArgumentException if configuration CA certificate or + * AltSubjectMatch/DomainSuffixMatch is not set. */ public @NonNull Builder setWpa2EnterpriseConfig( @NonNull WifiEnterpriseConfig enterpriseConfig) { checkNotNull(enterpriseConfig); + if (enterpriseConfig.isInsecure()) { + throw new IllegalArgumentException("Enterprise configuration is insecure"); + } mWpa2EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig); return this; } /** * Set the associated enterprise configuration for this network. Needed for authenticating - * to WPA3-SuiteB networks. See {@link WifiEnterpriseConfig} for description. + * to WPA3 enterprise networks. See {@link WifiEnterpriseConfig} for description. * * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}. * @return Instance of {@link Builder} to enable chaining of the builder method. + * @throws IllegalArgumentException if configuration CA certificate or + * AltSubjectMatch/DomainSuffixMatch is not set. */ public @NonNull Builder setWpa3EnterpriseConfig( @NonNull WifiEnterpriseConfig enterpriseConfig) { checkNotNull(enterpriseConfig); + if (enterpriseConfig.isInsecure()) { + throw new IllegalArgumentException("Enterprise configuration is insecure"); + } mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig); return this; } /** + * Set the associated Passpoint configuration for this network. Needed for authenticating + * to Hotspot 2.0 networks. See {@link PasspointConfiguration} for description. + * + * @param passpointConfig Instance of {@link PasspointConfiguration}. + * @return Instance of {@link Builder} to enable chaining of the builder method. + * @throws IllegalArgumentException if passpoint configuration is invalid. + */ + public @NonNull Builder setPasspointConfig( + @NonNull PasspointConfiguration passpointConfig) { + checkNotNull(passpointConfig); + if (!passpointConfig.validate()) { + throw new IllegalArgumentException("Passpoint configuration is invalid"); + } + mPasspointConfiguration = passpointConfig; + return this; + } + + /** + * Set the carrier ID of the network operator. The carrier ID associates a Suggested + * network with a specific carrier (and therefore SIM). The carrier ID must be provided + * for any network which uses the SIM-based authentication: e.g. EAP-SIM, EAP-AKA, + * EAP-AKA', and EAP-PEAP with SIM-based phase 2 authentication. + * @param carrierId see {@link TelephonyManager#getSimCarrierId()}. + * @return Instance of {@link Builder} to enable chaining of the builder method. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) + public @NonNull Builder setCarrierId(int carrierId) { + mCarrierId = carrierId; + return this; + } + + /** + * Set the ASCII WAPI passphrase for this network. Needed for authenticating to + * WAPI-PSK networks. + * + * @param passphrase passphrase of the network. + * @return Instance of {@link Builder} to enable chaining of the builder method. + * @throws IllegalArgumentException if the passphrase is not ASCII encodable. + * + */ + public @NonNull Builder setWapiPassphrase(@NonNull String passphrase) { + checkNotNull(passphrase); + final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); + if (!asciiEncoder.canEncode(passphrase)) { + throw new IllegalArgumentException("passphrase not ASCII encodable"); + } + mWapiPskPassphrase = passphrase; + return this; + } + + /** + * Set the associated enterprise configuration for this network. Needed for authenticating + * to WAPI-CERT networks. See {@link WifiEnterpriseConfig} for description. + * + * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}. + * @return Instance of {@link Builder} to enable chaining of the builder method. + */ + public @NonNull Builder setWapiEnterpriseConfig( + @NonNull WifiEnterpriseConfig enterpriseConfig) { + checkNotNull(enterpriseConfig); + mWapiEnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig); + return this; + } + + /** * Specifies whether this represents a hidden network. * <p> * <li>If not set, defaults to false (i.e not a hidden network).</li> @@ -303,14 +431,81 @@ public final class WifiNetworkSuggestion implements Parcelable { /** * Specifies whether this network is metered. * <p> - * <li>If not set, defaults to false (i.e not metered).</li> + * <li>If not set, defaults to detect automatically.</li> * * @param isMetered {@code true} to indicate that the network is metered, {@code false} - * otherwise. + * for not metered. * @return Instance of {@link Builder} to enable chaining of the builder method. */ public @NonNull Builder setIsMetered(boolean isMetered) { - mIsMetered = isMetered; + if (isMetered) { + mMeteredOverride = WifiConfiguration.METERED_OVERRIDE_METERED; + } else { + mMeteredOverride = WifiConfiguration.METERED_OVERRIDE_NOT_METERED; + } + return this; + } + + /** + * Specifies whether the network credentials provided with this suggestion can be used by + * the user to explicitly (manually) connect to this network. If true this network will + * appear in the Wi-Fi Picker (in Settings) and the user will be able to select and connect + * to it with the provided credentials. If false, the user will need to enter network + * credentials and the resulting configuration will become a user saved network. + * <p> + * <li>Note: Only valid for secure (non-open) networks. + * <li>If not set, defaults to true (i.e. allow user to manually connect) for secure + * networks and false for open networks.</li> + * + * @param isShared {@code true} to indicate that the credentials may be used by the user to + * manually connect to the network, {@code false} otherwise. + * @return Instance of {@link Builder} to enable chaining of the builder method. + */ + public @NonNull Builder setCredentialSharedWithUser(boolean isShared) { + mIsSharedWithUser = isShared; + mIsSharedWithUserSet = true; + return this; + } + + /** + * Specifies whether the suggestion is created with auto-join enabled or disabled. The + * user may modify the auto-join configuration of a suggestion directly once the device + * associates to the network. + * <p> + * If auto-join is initialized as disabled the user may still be able to manually connect + * to the network. Therefore, disabling auto-join only makes sense if + * {@link #setCredentialSharedWithUser(boolean)} is set to true (the default) which + * itself implies a secure (non-open) network. + * <p> + * If not set, defaults to true (i.e. auto-join is initialized as enabled). + * + * @param enabled true for initializing with auto-join enabled (the default), false to + * initializing with auto-join disabled. + * @return Instance of {@link Builder} to enable chaining of the builder method. + */ + public @NonNull Builder setIsInitialAutojoinEnabled(boolean enabled) { + mIsInitialAutojoinEnabled = enabled; + return this; + } + + /** + * Specifies whether the system will bring up the network (if selected) as untrusted. An + * untrusted network has its {@link android.net.NetworkCapabilities#NET_CAPABILITY_TRUSTED} + * capability removed. The Wi-Fi network selection process may use this information to + * influence priority of the suggested network for Wi-Fi network selection (most likely to + * reduce it). The connectivity service may use this information to influence the overall + * network configuration of the device. + * <p> + * <li> An untrusted network's credentials may not be shared with the user using + * {@link #setCredentialSharedWithUser(boolean)}.</li> + * <li> If not set, defaults to false (i.e. network is trusted).</li> + * + * @param isUntrusted Boolean indicating whether the network should be brought up untrusted + * (if true) or trusted (if false). + * @return Instance of {@link Builder} to enable chaining of the builder method. + */ + public @NonNull Builder setUntrusted(boolean isUntrusted) { + mIsNetworkUntrusted = isUntrusted; return this; } @@ -332,6 +527,13 @@ public final class WifiNetworkSuggestion implements Parcelable { configuration.enterpriseConfig = mWpa3EnterpriseConfig; } else if (mIsEnhancedOpen) { // OWE network configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE); + } else if (!TextUtils.isEmpty(mWapiPskPassphrase)) { // WAPI-PSK network. + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WAPI_PSK); + // WifiConfiguration.preSharedKey needs quotes around ASCII password. + configuration.preSharedKey = "\"" + mWapiPskPassphrase + "\""; + } else if (mWapiEnterpriseConfig != null) { // WAPI-CERT network + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WAPI_CERT); + configuration.enterpriseConfig = mWapiEnterpriseConfig; } else { // Open network configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN); } @@ -353,9 +555,9 @@ public final class WifiNetworkSuggestion implements Parcelable { wifiConfiguration.hiddenSSID = mIsHiddenSSID; wifiConfiguration.priority = mPriority; - wifiConfiguration.meteredOverride = - mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED - : WifiConfiguration.METERED_OVERRIDE_NONE; + wifiConfiguration.meteredOverride = mMeteredOverride; + wifiConfiguration.carrierId = mCarrierId; + wifiConfiguration.trusted = !mIsNetworkUntrusted; return wifiConfiguration; } @@ -364,15 +566,31 @@ public final class WifiNetworkSuggestion implements Parcelable { numSecurityTypes += mIsEnhancedOpen ? 1 : 0; numSecurityTypes += !TextUtils.isEmpty(mWpa2PskPassphrase) ? 1 : 0; numSecurityTypes += !TextUtils.isEmpty(mWpa3SaePassphrase) ? 1 : 0; + numSecurityTypes += !TextUtils.isEmpty(mWapiPskPassphrase) ? 1 : 0; numSecurityTypes += mWpa2EnterpriseConfig != null ? 1 : 0; numSecurityTypes += mWpa3EnterpriseConfig != null ? 1 : 0; + numSecurityTypes += mWapiEnterpriseConfig != null ? 1 : 0; + numSecurityTypes += mPasspointConfiguration != null ? 1 : 0; if (numSecurityTypes > 1) { throw new IllegalStateException("only one of setIsEnhancedOpen, setWpa2Passphrase," - + "setWpa3Passphrase, setWpa2EnterpriseConfig or setWpa3EnterpriseConfig" - + " can be invoked for network specifier"); + + " setWpa3Passphrase, setWpa2EnterpriseConfig, setWpa3EnterpriseConfig" + + " setWapiPassphrase, setWapiCertSuite, setIsWapiCertSuiteAuto" + + " or setPasspointConfig can be invoked for network suggestion"); } } + private WifiConfiguration buildWifiConfigurationForPasspoint() { + WifiConfiguration wifiConfiguration = new WifiConfiguration(); + wifiConfiguration.FQDN = mPasspointConfiguration.getHomeSp().getFqdn(); + wifiConfiguration.setPasspointUniqueId(mPasspointConfiguration.getUniqueId()); + wifiConfiguration.priority = mPriority; + wifiConfiguration.meteredOverride = mMeteredOverride; + wifiConfiguration.trusted = !mIsNetworkUntrusted; + mPasspointConfiguration.setCarrierId(mCarrierId); + mPasspointConfiguration.setMeteredOverride(wifiConfiguration.meteredOverride); + return wifiConfiguration; + } + /** * Create a network suggestion object for use in * {@link WifiManager#addNetworkSuggestions(List)}. @@ -384,29 +602,36 @@ public final class WifiNetworkSuggestion implements Parcelable { * </p> * * For example: - * To provide credentials for one open, one WPA2 and one WPA3 network with their - * corresponding SSID's: + * To provide credentials for one open, one WPA2, one WPA3 network with their + * corresponding SSID's and one with Passpoint config: * * <pre>{@code * final WifiNetworkSuggestion suggestion1 = * new Builder() * .setSsid("test111111") - * .build() + * .build(); * final WifiNetworkSuggestion suggestion2 = * new Builder() * .setSsid("test222222") * .setWpa2Passphrase("test123456") - * .build() + * .build(); * final WifiNetworkSuggestion suggestion3 = * new Builder() * .setSsid("test333333") * .setWpa3Passphrase("test6789") - * .build() + * .build(); + * final PasspointConfiguration passpointConfig= new PasspointConfiguration(); + * // configure passpointConfig to include a valid Passpoint configuration + * final WifiNetworkSuggestion suggestion4 = + * new Builder() + * .setPasspointConfig(passpointConfig) + * .build(); * final List<WifiNetworkSuggestion> suggestionsList = * new ArrayList<WifiNetworkSuggestion> { { * add(suggestion1); * add(suggestion2); * add(suggestion3); + * add(suggestion4); * } }; * final WifiManager wifiManager = * context.getSystemService(Context.WIFI_SERVICE); @@ -419,25 +644,59 @@ public final class WifiNetworkSuggestion implements Parcelable { * @see WifiNetworkSuggestion */ public @NonNull WifiNetworkSuggestion build() { - if (mSsid == null) { - throw new IllegalStateException("setSsid should be invoked for suggestion"); + validateSecurityParams(); + WifiConfiguration wifiConfiguration; + if (mPasspointConfiguration != null) { + if (mSsid != null) { + throw new IllegalStateException("setSsid should not be invoked for suggestion " + + "with Passpoint configuration"); + } + if (mIsHiddenSSID) { + throw new IllegalStateException("setIsHiddenSsid should not be invoked for " + + "suggestion with Passpoint configuration"); + } + wifiConfiguration = buildWifiConfigurationForPasspoint(); + } else { + if (mSsid == null) { + throw new IllegalStateException("setSsid should be invoked for suggestion"); + } + if (TextUtils.isEmpty(mSsid)) { + throw new IllegalStateException("invalid ssid for suggestion"); + } + if (mBssid != null + && (mBssid.equals(MacAddress.BROADCAST_ADDRESS) + || mBssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS))) { + throw new IllegalStateException("invalid bssid for suggestion"); + } + wifiConfiguration = buildWifiConfiguration(); + if (wifiConfiguration.isOpenNetwork()) { + if (mIsSharedWithUserSet && mIsSharedWithUser) { + throw new IllegalStateException("Open network should not be " + + "setCredentialSharedWithUser to true"); + } + mIsSharedWithUser = false; + } } - if (TextUtils.isEmpty(mSsid)) { - throw new IllegalStateException("invalid ssid for suggestion"); + if (!mIsSharedWithUser && !mIsInitialAutojoinEnabled) { + throw new IllegalStateException("Should have not a network with both " + + "setCredentialSharedWithUser and " + + "setIsAutojoinEnabled set to false"); } - if (mBssid != null - && (mBssid.equals(MacAddress.BROADCAST_ADDRESS) - || mBssid.equals(MacAddress.ALL_ZEROS_ADDRESS))) { - throw new IllegalStateException("invalid bssid for suggestion"); + if (mIsNetworkUntrusted) { + if (mIsSharedWithUserSet && mIsSharedWithUser) { + throw new IllegalStateException("Should not be both" + + "setCredentialSharedWithUser and +" + + "setIsNetworkAsUntrusted to true"); + } + mIsSharedWithUser = false; } - validateSecurityParams(); - return new WifiNetworkSuggestion( - buildWifiConfiguration(), + wifiConfiguration, + mPasspointConfiguration, mIsAppInteractionRequired, mIsUserInteractionRequired, - Process.myUid(), - ActivityThread.currentApplication().getApplicationContext().getOpPackageName()); + mIsSharedWithUser, + mIsInitialAutojoinEnabled); } } @@ -445,9 +704,17 @@ public final class WifiNetworkSuggestion implements Parcelable { * Network configuration for the provided network. * @hide */ + @NonNull public final WifiConfiguration wifiConfiguration; /** + * Passpoint configuration for the provided network. + * @hide + */ + @Nullable + public final PasspointConfiguration passpointConfiguration; + + /** * Whether app needs to log in to captive portal to obtain Internet access. * @hide */ @@ -460,39 +727,43 @@ public final class WifiNetworkSuggestion implements Parcelable { public final boolean isUserInteractionRequired; /** - * The UID of the process initializing this network suggestion. + * Whether app share credential with the user, allow user use provided credential to + * connect network manually. * @hide */ - public final int suggestorUid; + public final boolean isUserAllowedToManuallyConnect; /** - * The package name of the process initializing this network suggestion. + * Whether the suggestion will be initialized as auto-joined or not. * @hide */ - public final String suggestorPackageName; + public final boolean isInitialAutoJoinEnabled; /** @hide */ public WifiNetworkSuggestion() { - this.wifiConfiguration = null; + this.wifiConfiguration = new WifiConfiguration(); + this.passpointConfiguration = null; this.isAppInteractionRequired = false; this.isUserInteractionRequired = false; - this.suggestorUid = -1; - this.suggestorPackageName = null; + this.isUserAllowedToManuallyConnect = true; + this.isInitialAutoJoinEnabled = true; } /** @hide */ - public WifiNetworkSuggestion(@NonNull WifiConfiguration wifiConfiguration, + public WifiNetworkSuggestion(@NonNull WifiConfiguration networkConfiguration, + @Nullable PasspointConfiguration passpointConfiguration, boolean isAppInteractionRequired, boolean isUserInteractionRequired, - int suggestorUid, @NonNull String suggestorPackageName) { - checkNotNull(wifiConfiguration); - checkNotNull(suggestorPackageName); + boolean isUserAllowedToManuallyConnect, + boolean isInitialAutoJoinEnabled) { + checkNotNull(networkConfiguration); + this.wifiConfiguration = networkConfiguration; + this.passpointConfiguration = passpointConfiguration; - this.wifiConfiguration = wifiConfiguration; this.isAppInteractionRequired = isAppInteractionRequired; this.isUserInteractionRequired = isUserInteractionRequired; - this.suggestorUid = suggestorUid; - this.suggestorPackageName = suggestorPackageName; + this.isUserAllowedToManuallyConnect = isUserAllowedToManuallyConnect; + this.isInitialAutoJoinEnabled = isInitialAutoJoinEnabled; } public static final @NonNull Creator<WifiNetworkSuggestion> CREATOR = @@ -501,10 +772,11 @@ public final class WifiNetworkSuggestion implements Parcelable { public WifiNetworkSuggestion createFromParcel(Parcel in) { return new WifiNetworkSuggestion( in.readParcelable(null), // wifiConfiguration + in.readParcelable(null), // PasspointConfiguration in.readBoolean(), // isAppInteractionRequired in.readBoolean(), // isUserInteractionRequired - in.readInt(), // suggestorUid - in.readString() // suggestorPackageName + in.readBoolean(), // isSharedCredentialWithUser + in.readBoolean() // isAutojoinEnabled ); } @@ -522,16 +794,17 @@ public final class WifiNetworkSuggestion implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(wifiConfiguration, flags); + dest.writeParcelable(passpointConfiguration, flags); dest.writeBoolean(isAppInteractionRequired); dest.writeBoolean(isUserInteractionRequired); - dest.writeInt(suggestorUid); - dest.writeString(suggestorPackageName); + dest.writeBoolean(isUserAllowedToManuallyConnect); + dest.writeBoolean(isInitialAutoJoinEnabled); } @Override public int hashCode() { return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.BSSID, - wifiConfiguration.allowedKeyManagement, suggestorUid, suggestorPackageName); + wifiConfiguration.allowedKeyManagement, wifiConfiguration.getKey()); } /** @@ -546,24 +819,144 @@ public final class WifiNetworkSuggestion implements Parcelable { return false; } WifiNetworkSuggestion lhs = (WifiNetworkSuggestion) obj; - return Objects.equals(this.wifiConfiguration.SSID, lhs.wifiConfiguration.SSID) - && Objects.equals(this.wifiConfiguration.BSSID, lhs.wifiConfiguration.BSSID) + if (this.passpointConfiguration == null ^ lhs.passpointConfiguration == null) { + return false; + } + + return TextUtils.equals(this.wifiConfiguration.SSID, lhs.wifiConfiguration.SSID) + && TextUtils.equals(this.wifiConfiguration.BSSID, lhs.wifiConfiguration.BSSID) && Objects.equals(this.wifiConfiguration.allowedKeyManagement, - lhs.wifiConfiguration.allowedKeyManagement) - && suggestorUid == lhs.suggestorUid - && TextUtils.equals(suggestorPackageName, lhs.suggestorPackageName); + lhs.wifiConfiguration.allowedKeyManagement) + && TextUtils.equals(this.wifiConfiguration.getKey(), + lhs.wifiConfiguration.getKey()); } @Override public String toString() { - StringBuilder sb = new StringBuilder("WifiNetworkSuggestion [") - .append(", SSID=").append(wifiConfiguration.SSID) + StringBuilder sb = new StringBuilder("WifiNetworkSuggestion[ ") + .append("SSID=").append(wifiConfiguration.SSID) .append(", BSSID=").append(wifiConfiguration.BSSID) + .append(", FQDN=").append(wifiConfiguration.FQDN) .append(", isAppInteractionRequired=").append(isAppInteractionRequired) .append(", isUserInteractionRequired=").append(isUserInteractionRequired) - .append(", suggestorUid=").append(suggestorUid) - .append(", suggestorPackageName=").append(suggestorPackageName) - .append("]"); + .append(", isCredentialSharedWithUser=").append(isUserAllowedToManuallyConnect) + .append(", isInitialAutoJoinEnabled=").append(isInitialAutoJoinEnabled) + .append(", isUnTrusted=").append(!wifiConfiguration.trusted) + .append(" ]"); return sb.toString(); } + + /** + * Get the {@link WifiConfiguration} associated with this Suggestion. + * @hide + */ + @SystemApi + @NonNull + public WifiConfiguration getWifiConfiguration() { + return wifiConfiguration; + } + + /** + * Get the BSSID, or null if unset. + * @see Builder#setBssid(MacAddress) + */ + @Nullable + public MacAddress getBssid() { + if (wifiConfiguration.BSSID == null) { + return null; + } + return MacAddress.fromString(wifiConfiguration.BSSID); + } + + /** @see Builder#setCredentialSharedWithUser(boolean) */ + public boolean isCredentialSharedWithUser() { + return isUserAllowedToManuallyConnect; + } + + /** @see Builder#setIsAppInteractionRequired(boolean) */ + public boolean isAppInteractionRequired() { + return isAppInteractionRequired; + } + + /** @see Builder#setIsEnhancedOpen(boolean) */ + public boolean isEnhancedOpen() { + return wifiConfiguration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE); + } + + /** @see Builder#setIsHiddenSsid(boolean) */ + public boolean isHiddenSsid() { + return wifiConfiguration.hiddenSSID; + } + + /** @see Builder#setIsInitialAutojoinEnabled(boolean) */ + public boolean isInitialAutojoinEnabled() { + return isInitialAutoJoinEnabled; + } + + /** @see Builder#setIsMetered(boolean) */ + public boolean isMetered() { + return wifiConfiguration.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED; + } + + /** @see Builder#setIsUserInteractionRequired(boolean) */ + public boolean isUserInteractionRequired() { + return isUserInteractionRequired; + } + + /** + * Get the {@link PasspointConfiguration} associated with this Suggestion, or null if this + * Suggestion is not for a Passpoint network. + */ + @Nullable + public PasspointConfiguration getPasspointConfig() { + return passpointConfiguration; + } + + /** @see Builder#setPriority(int) */ + @IntRange(from = 0) + public int getPriority() { + return wifiConfiguration.priority; + } + + /** + * Return the SSID of the network, or null if this is a Passpoint network. + * @see Builder#setSsid(String) + */ + @Nullable + public String getSsid() { + if (wifiConfiguration.SSID == null) { + return null; + } + return WifiInfo.sanitizeSsid(wifiConfiguration.SSID); + } + + /** @see Builder#setUntrusted(boolean) */ + public boolean isUntrusted() { + return !wifiConfiguration.trusted; + } + + /** + * Get the WifiEnterpriseConfig, or null if unset. + * @see Builder#setWapiEnterpriseConfig(WifiEnterpriseConfig) + * @see Builder#setWpa2EnterpriseConfig(WifiEnterpriseConfig) + * @see Builder#setWpa3EnterpriseConfig(WifiEnterpriseConfig) + */ + @Nullable + public WifiEnterpriseConfig getEnterpriseConfig() { + return wifiConfiguration.enterpriseConfig; + } + + /** + * Get the passphrase, or null if unset. + * @see Builder#setWapiPassphrase(String) + * @see Builder#setWpa2Passphrase(String) + * @see Builder#setWpa3Passphrase(String) + */ + @Nullable + public String getPassphrase() { + if (wifiConfiguration.preSharedKey == null) { + return null; + } + return WifiInfo.removeDoubleQuotes(wifiConfiguration.preSharedKey); + } } diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index 66dc992d6dc7..94771ac4ad78 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -17,11 +17,16 @@ package android.net.wifi; import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -31,16 +36,20 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.WorkSource; +import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import com.android.internal.util.AsyncChannel; -import com.android.internal.util.Preconditions; import com.android.internal.util.Protocol; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executor; /** * This class provides a way to scan the Wifi universe around the device @@ -50,26 +59,87 @@ import java.util.List; @SystemService(Context.WIFI_SCANNING_SERVICE) public class WifiScanner { - /** no band specified; use channel list instead */ - public static final int WIFI_BAND_UNSPECIFIED = 0; /* not specified */ + /** @hide */ + public static final int WIFI_BAND_INDEX_24_GHZ = 0; + /** @hide */ + public static final int WIFI_BAND_INDEX_5_GHZ = 1; + /** @hide */ + public static final int WIFI_BAND_INDEX_5_GHZ_DFS_ONLY = 2; + /** @hide */ + public static final int WIFI_BAND_INDEX_6_GHZ = 3; + /** @hide */ + public static final int WIFI_BAND_COUNT = 4; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"WIFI_BAND_INDEX_"}, value = { + WIFI_BAND_INDEX_24_GHZ, + WIFI_BAND_INDEX_5_GHZ, + WIFI_BAND_INDEX_5_GHZ_DFS_ONLY, + WIFI_BAND_INDEX_6_GHZ}) + public @interface WifiBandIndex {} + /** no band specified; use channel list instead */ + public static final int WIFI_BAND_UNSPECIFIED = 0; /** 2.4 GHz band */ - public static final int WIFI_BAND_24_GHZ = 1; /* 2.4 GHz band */ + public static final int WIFI_BAND_24_GHZ = 1 << WIFI_BAND_INDEX_24_GHZ; /** 5 GHz band excluding DFS channels */ - public static final int WIFI_BAND_5_GHZ = 2; /* 5 GHz band without DFS channels */ + public static final int WIFI_BAND_5_GHZ = 1 << WIFI_BAND_INDEX_5_GHZ; /** DFS channels from 5 GHz band only */ - public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; /* 5 GHz band with DFS channels */ - /** 5 GHz band including DFS channels */ - public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; /* 5 GHz band with DFS channels */ + public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 1 << WIFI_BAND_INDEX_5_GHZ_DFS_ONLY; + /** 6 GHz band */ + public static final int WIFI_BAND_6_GHZ = 1 << WIFI_BAND_INDEX_6_GHZ; + + /** + * Combination of bands + * Note that those are only the common band combinations, + * other combinations can be created by combining any of the basic bands above + */ /** Both 2.4 GHz band and 5 GHz band; no DFS channels */ - public static final int WIFI_BAND_BOTH = 3; /* both bands without DFS channels */ + public static final int WIFI_BAND_BOTH = WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ; + /** + * 2.4Ghz band + DFS channels from 5 GHz band only + * @hide + */ + public static final int WIFI_BAND_24_GHZ_WITH_5GHZ_DFS = + WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY; + /** 5 GHz band including DFS channels */ + public static final int WIFI_BAND_5_GHZ_WITH_DFS = WIFI_BAND_5_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY; /** Both 2.4 GHz band and 5 GHz band; with DFS channels */ - public static final int WIFI_BAND_BOTH_WITH_DFS = 7; /* both bands with DFS channels */ + public static final int WIFI_BAND_BOTH_WITH_DFS = + WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY; + /** 2.4 GHz band and 5 GHz band (no DFS channels) and 6 GHz */ + public static final int WIFI_BAND_24_5_6_GHZ = WIFI_BAND_BOTH | WIFI_BAND_6_GHZ; + /** 2.4 GHz band and 5 GHz band; with DFS channels and 6 GHz */ + public static final int WIFI_BAND_24_5_WITH_DFS_6_GHZ = + WIFI_BAND_BOTH_WITH_DFS | WIFI_BAND_6_GHZ; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"WIFI_BAND_"}, value = { + WIFI_BAND_UNSPECIFIED, + WIFI_BAND_24_GHZ, + WIFI_BAND_5_GHZ, + WIFI_BAND_BOTH, + WIFI_BAND_5_GHZ_DFS_ONLY, + WIFI_BAND_24_GHZ_WITH_5GHZ_DFS, + WIFI_BAND_5_GHZ_WITH_DFS, + WIFI_BAND_BOTH_WITH_DFS, + WIFI_BAND_6_GHZ, + WIFI_BAND_24_5_6_GHZ, + WIFI_BAND_24_5_WITH_DFS_6_GHZ}) + public @interface WifiBand {} + + /** + * All bands + * @hide + */ + public static final int WIFI_BAND_ALL = (1 << WIFI_BAND_COUNT) - 1; /** Minimum supported scanning period */ - public static final int MIN_SCAN_PERIOD_MS = 1000; /* minimum supported period */ + public static final int MIN_SCAN_PERIOD_MS = 1000; /** Maximum supported scanning period */ - public static final int MAX_SCAN_PERIOD_MS = 1024000; /* maximum supported period */ + public static final int MAX_SCAN_PERIOD_MS = 1024000; /** No Error */ public static final int REASON_SUCCEEDED = 0; @@ -98,16 +168,41 @@ public class WifiScanner { } /** - * gives you all the possible channels; channel is specified as an - * integer with frequency in MHz i.e. channel 1 is 2412 + * Test if scan is a full scan. i.e. scanning all available bands. + * For backward compatibility, since some apps don't include 6GHz in their requests yet, + * lacking 6GHz band does not cause the result to be false. + * + * @param bandScanned bands that are fully scanned + * @param excludeDfs when true, DFS band is excluded from the check + * @return true if all bands are scanned, false otherwise + * * @hide */ + public static boolean isFullBandScan(@WifiBand int bandScanned, boolean excludeDfs) { + return (bandScanned | WIFI_BAND_6_GHZ | (excludeDfs ? WIFI_BAND_5_GHZ_DFS_ONLY : 0)) + == WIFI_BAND_ALL; + } + + /** + * Returns a list of all the possible channels for the given band(s). + * + * @param band one of the WifiScanner#WIFI_BAND_* constants, e.g. {@link #WIFI_BAND_24_GHZ} + * @return a list of all the frequencies, in MHz, for the given band(s) e.g. channel 1 is + * 2412, or null if an error occurred. + * + * @hide + */ + @SystemApi + @NonNull + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public List<Integer> getAvailableChannels(int band) { try { - Bundle bundle = mService.getAvailableChannels(band); - return bundle.getIntegerArrayList(GET_AVAILABLE_CHANNELS_EXTRA); + Bundle bundle = mService.getAvailableChannels(band, mContext.getOpPackageName(), + mContext.getAttributionTag()); + List<Integer> channels = bundle.getIntegerArrayList(GET_AVAILABLE_CHANNELS_EXTRA); + return channels == null ? new ArrayList<>() : channels; } catch (RemoteException e) { - return null; + throw e.rethrowFromSystemServer(); } } @@ -162,23 +257,20 @@ public class WifiScanner { public static final int REPORT_EVENT_NO_BATCH = (1 << 2); /** - * This is used to indicate the purpose of the scan to the wifi chip in - * {@link ScanSettings#type}. - * On devices with multiple hardware radio chains (and hence different modes of scan), - * this type serves as an indication to the hardware on what mode of scan to perform. - * Only apps holding android.Manifest.permission.NETWORK_STACK permission can set this value. - * - * Note: This serves as an intent and not as a stipulation, the wifi chip - * might honor or ignore the indication based on the current radio conditions. Always - * use the {@link ScanResult#radioChainInfos} to figure out the radio chain configuration used - * to receive the corresponding scan result. + * Optimize the scan for lower latency. + * @see ScanSettings#type */ - /** {@hide} */ - public static final int TYPE_LOW_LATENCY = 0; - /** {@hide} */ - public static final int TYPE_LOW_POWER = 1; - /** {@hide} */ - public static final int TYPE_HIGH_ACCURACY = 2; + public static final int SCAN_TYPE_LOW_LATENCY = 0; + /** + * Optimize the scan for lower power usage. + * @see ScanSettings#type + */ + public static final int SCAN_TYPE_LOW_POWER = 1; + /** + * Optimize the scan for higher accuracy. + * @see ScanSettings#type + */ + public static final int SCAN_TYPE_HIGH_ACCURACY = 2; /** {@hide} */ public static final String SCAN_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings"; @@ -186,23 +278,21 @@ public class WifiScanner { public static final String SCAN_PARAMS_WORK_SOURCE_KEY = "WorkSource"; /** {@hide} */ public static final String REQUEST_PACKAGE_NAME_KEY = "PackageName"; + /** {@hide} */ + public static final String REQUEST_FEATURE_ID_KEY = "FeatureId"; /** * scan configuration parameters to be sent to {@link #startBackgroundScan} */ public static class ScanSettings implements Parcelable { - /** - * Hidden network to be scanned for. - * {@hide} - */ + /** Hidden network to be scanned for. */ public static class HiddenNetwork { /** SSID of the network */ - public String ssid; + @NonNull + public final String ssid; - /** - * Default constructor for HiddenNetwork. - */ - public HiddenNetwork(String ssid) { + /** Default constructor for HiddenNetwork. */ + public HiddenNetwork(@NonNull String ssid) { this.ssid = ssid; } } @@ -212,34 +302,64 @@ public class WifiScanner { /** list of channels; used when band is set to WIFI_BAND_UNSPECIFIED */ public ChannelSpec[] channels; /** - * list of hidden networks to scan for. Explicit probe requests are sent out for such + * List of hidden networks to scan for. Explicit probe requests are sent out for such * networks during scan. Only valid for single scan requests. - * {@hide} */ + @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_STACK) - public HiddenNetwork[] hiddenNetworks; - /** period of background scan; in millisecond, 0 => single shot scan */ + public final List<HiddenNetwork> hiddenNetworks = new ArrayList<>(); + /** + * period of background scan; in millisecond, 0 => single shot scan + * @deprecated Background scan support has always been hardware vendor dependent. This + * support may not be present on newer devices. Use {@link #startScan(ScanSettings, + * ScanListener)} instead for single scans. + */ + @Deprecated public int periodInMs; - /** must have a valid REPORT_EVENT value */ + /** + * must have a valid REPORT_EVENT value + * @deprecated Background scan support has always been hardware vendor dependent. This + * support may not be present on newer devices. Use {@link #startScan(ScanSettings, + * ScanListener)} instead for single scans. + */ + @Deprecated public int reportEvents; - /** defines number of bssids to cache from each scan */ + /** + * defines number of bssids to cache from each scan + * @deprecated Background scan support has always been hardware vendor dependent. This + * support may not be present on newer devices. Use {@link #startScan(ScanSettings, + * ScanListener)} instead for single scans. + */ + @Deprecated public int numBssidsPerScan; /** * defines number of scans to cache; use it with REPORT_EVENT_AFTER_BUFFER_FULL * to wake up at fixed interval + * @deprecated Background scan support has always been hardware vendor dependent. This + * support may not be present on newer devices. Use {@link #startScan(ScanSettings, + * ScanListener)} instead for single scans. */ + @Deprecated public int maxScansToCache; /** * if maxPeriodInMs is non zero or different than period, then this bucket is * a truncated binary exponential backoff bucket and the scan period will grow * exponentially as per formula: actual_period(N) = period * (2 ^ (N/stepCount)) * to maxPeriodInMs + * @deprecated Background scan support has always been hardware vendor dependent. This + * support may not be present on newer devices. Use {@link #startScan(ScanSettings, + * ScanListener)} instead for single scans. */ + @Deprecated public int maxPeriodInMs; /** * for truncated binary exponential back off bucket, number of scans to perform * for a given period + * @deprecated Background scan support has always been hardware vendor dependent. This + * support may not be present on newer devices. Use {@link #startScan(ScanSettings, + * ScanListener)} instead for single scans. */ + @Deprecated public int stepCount; /** * Flag to indicate if the scan settings are targeted for PNO scan. @@ -248,11 +368,24 @@ public class WifiScanner { public boolean isPnoScan; /** * Indicate the type of scan to be performed by the wifi chip. - * Default value: {@link #TYPE_LOW_LATENCY}. - * {@hide} + * + * On devices with multiple hardware radio chains (and hence different modes of scan), + * this type serves as an indication to the hardware on what mode of scan to perform. + * Only apps holding {@link android.Manifest.permission.NETWORK_STACK} permission can set + * this value. + * + * Note: This serves as an intent and not as a stipulation, the wifi chip + * might honor or ignore the indication based on the current radio conditions. Always + * use the {@link ScanResult#radioChainInfos} to figure out the radio chain configuration + * used to receive the corresponding scan result. + * + * One of {@link #SCAN_TYPE_LOW_LATENCY}, {@link #SCAN_TYPE_LOW_POWER}, + * {@link #SCAN_TYPE_HIGH_ACCURACY}. + * Default value: {@link #SCAN_TYPE_LOW_LATENCY}. */ + @WifiAnnotations.ScanType @RequiresPermission(android.Manifest.permission.NETWORK_STACK) - public int type = TYPE_LOW_LATENCY; + public int type = SCAN_TYPE_LOW_LATENCY; /** * This scan request may ignore location settings while receiving scans. This should only * be used in emergency situations. @@ -299,18 +432,14 @@ public class WifiScanner { } else { dest.writeInt(0); } - if (hiddenNetworks != null) { - dest.writeInt(hiddenNetworks.length); - for (int i = 0; i < hiddenNetworks.length; i++) { - dest.writeString(hiddenNetworks[i].ssid); - } - } else { - dest.writeInt(0); + dest.writeInt(hiddenNetworks.size()); + for (HiddenNetwork hiddenNetwork : hiddenNetworks) { + dest.writeString(hiddenNetwork.ssid); } } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<ScanSettings> CREATOR = + public static final @NonNull Creator<ScanSettings> CREATOR = new Creator<ScanSettings>() { public ScanSettings createFromParcel(Parcel in) { ScanSettings settings = new ScanSettings(); @@ -335,10 +464,10 @@ public class WifiScanner { settings.channels[i] = spec; } int numNetworks = in.readInt(); - settings.hiddenNetworks = new HiddenNetwork[numNetworks]; + settings.hiddenNetworks.clear(); for (int i = 0; i < numNetworks; i++) { String ssid = in.readString(); - settings.hiddenNetworks[i] = new HiddenNetwork(ssid);; + settings.hiddenNetworks.add(new HiddenNetwork(ssid)); } return settings; } @@ -347,7 +476,6 @@ public class WifiScanner { return new ScanSettings[size]; } }; - } /** @@ -375,19 +503,27 @@ public class WifiScanner { */ private int mBandScanned; /** all scan results discovered in this scan, sorted by timestamp in ascending order */ - private ScanResult mResults[]; + private final List<ScanResult> mResults; - ScanData() {} + ScanData() { + mResults = new ArrayList<>(); + } public ScanData(int id, int flags, ScanResult[] results) { mId = id; mFlags = flags; - mResults = results; + mResults = new ArrayList<>(Arrays.asList(results)); } /** {@hide} */ public ScanData(int id, int flags, int bucketsScanned, int bandScanned, ScanResult[] results) { + this(id, flags, bucketsScanned, bandScanned, new ArrayList<>(Arrays.asList(results))); + } + + /** {@hide} */ + public ScanData(int id, int flags, int bucketsScanned, int bandScanned, + List<ScanResult> results) { mId = id; mFlags = flags; mBucketsScanned = bucketsScanned; @@ -400,11 +536,9 @@ public class WifiScanner { mFlags = s.mFlags; mBucketsScanned = s.mBucketsScanned; mBandScanned = s.mBandScanned; - mResults = new ScanResult[s.mResults.length]; - for (int i = 0; i < s.mResults.length; i++) { - ScanResult result = s.mResults[i]; - ScanResult newResult = new ScanResult(result); - mResults[i] = newResult; + mResults = new ArrayList<>(); + for (ScanResult scanResult : s.mResults) { + mResults.add(new ScanResult(scanResult)); } } @@ -427,7 +561,14 @@ public class WifiScanner { } public ScanResult[] getResults() { - return mResults; + return mResults.toArray(new ScanResult[0]); + } + + /** {@hide} */ + public void addResults(@NonNull ScanResult[] newResults) { + for (ScanResult result : newResults) { + mResults.add(new ScanResult(result)); + } } /** Implement the Parcelable interface {@hide} */ @@ -437,34 +578,23 @@ public class WifiScanner { /** Implement the Parcelable interface {@hide} */ public void writeToParcel(Parcel dest, int flags) { - if (mResults != null) { - dest.writeInt(mId); - dest.writeInt(mFlags); - dest.writeInt(mBucketsScanned); - dest.writeInt(mBandScanned); - dest.writeInt(mResults.length); - for (int i = 0; i < mResults.length; i++) { - ScanResult result = mResults[i]; - result.writeToParcel(dest, flags); - } - } else { - dest.writeInt(0); - } + dest.writeInt(mId); + dest.writeInt(mFlags); + dest.writeInt(mBucketsScanned); + dest.writeInt(mBandScanned); + dest.writeParcelableList(mResults, 0); } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<ScanData> CREATOR = + public static final @NonNull Creator<ScanData> CREATOR = new Creator<ScanData>() { public ScanData createFromParcel(Parcel in) { int id = in.readInt(); int flags = in.readInt(); int bucketsScanned = in.readInt(); int bandScanned = in.readInt(); - int n = in.readInt(); - ScanResult results[] = new ScanResult[n]; - for (int i = 0; i < n; i++) { - results[i] = ScanResult.CREATOR.createFromParcel(in); - } + List<ScanResult> results = new ArrayList<>(); + in.readParcelableList(results, ScanResult.class.getClassLoader()); return new ScanData(id, flags, bucketsScanned, bandScanned, results); } @@ -505,7 +635,7 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<ParcelableScanData> CREATOR = + public static final @NonNull Creator<ParcelableScanData> CREATOR = new Creator<ParcelableScanData>() { public ParcelableScanData createFromParcel(Parcel in) { int n = in.readInt(); @@ -553,7 +683,7 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<ParcelableScanResults> CREATOR = + public static final @NonNull Creator<ParcelableScanResults> CREATOR = new Creator<ParcelableScanResults>() { public ParcelableScanResults createFromParcel(Parcel in) { int n = in.readInt(); @@ -631,6 +761,25 @@ public class WifiScanner { public PnoNetwork(String ssid) { this.ssid = ssid; } + + @Override + public int hashCode() { + return Objects.hash(ssid, flags, authBitField); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof PnoNetwork)) { + return false; + } + PnoNetwork lhs = (PnoNetwork) obj; + return TextUtils.equals(this.ssid, lhs.ssid) + && this.flags == lhs.flags + && this.authBitField == lhs.authBitField; + } } /** Connected vs Disconnected PNO flag {@hide} */ @@ -639,19 +788,8 @@ public class WifiScanner { public int min5GHzRssi; /** Minimum 2.4GHz RSSI for a BSSID to be considered */ public int min24GHzRssi; - /** Maximum score that a network can have before bonuses */ - public int initialScoreMax; - /** - * Only report when there is a network's score this much higher - * than the current connection. - */ - public int currentConnectionBonus; - /** score bonus for all networks with the same network flag */ - public int sameNetworkBonus; - /** score bonus for networks that are not open */ - public int secureBonus; - /** 5GHz RSSI score bonus (applied to all 5GHz networks) */ - public int band5GHzBonus; + /** Minimum 6GHz RSSI for a BSSID to be considered */ + public int min6GHzRssi; /** Pno Network filter list */ public PnoNetwork[] networkList; @@ -665,11 +803,7 @@ public class WifiScanner { dest.writeInt(isConnected ? 1 : 0); dest.writeInt(min5GHzRssi); dest.writeInt(min24GHzRssi); - dest.writeInt(initialScoreMax); - dest.writeInt(currentConnectionBonus); - dest.writeInt(sameNetworkBonus); - dest.writeInt(secureBonus); - dest.writeInt(band5GHzBonus); + dest.writeInt(min6GHzRssi); if (networkList != null) { dest.writeInt(networkList.length); for (int i = 0; i < networkList.length; i++) { @@ -684,18 +818,14 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<PnoSettings> CREATOR = + public static final @NonNull Creator<PnoSettings> CREATOR = new Creator<PnoSettings>() { public PnoSettings createFromParcel(Parcel in) { PnoSettings settings = new PnoSettings(); settings.isConnected = in.readInt() == 1; settings.min5GHzRssi = in.readInt(); settings.min24GHzRssi = in.readInt(); - settings.initialScoreMax = in.readInt(); - settings.currentConnectionBonus = in.readInt(); - settings.sameNetworkBonus = in.readInt(); - settings.secureBonus = in.readInt(); - settings.band5GHzBonus = in.readInt(); + settings.min6GHzRssi = in.readInt(); int numNetworks = in.readInt(); settings.networkList = new PnoNetwork[numNetworks]; for (int i = 0; i < numNetworks; i++) { @@ -724,7 +854,11 @@ public class WifiScanner { /** * Framework co-ordinates scans across multiple apps; so it may not give exactly the * same period requested. If period of a scan is changed; it is reported by this event. + * @deprecated Background scan support has always been hardware vendor dependent. This + * support may not be present on newer devices. Use {@link #startScan(ScanSettings, + * ScanListener)} instead for single scans. */ + @Deprecated public void onPeriodChanged(int periodInMs); /** * reports results retrieved from background scan and single shot scans @@ -751,8 +885,12 @@ public class WifiScanner { /** * Enable/Disable wifi scanning. * + * @param enable set to true to enable scanning, set to false to disable all types of scanning. + * + * @see WifiManager#ACTION_WIFI_SCAN_AVAILABILITY_CHANGED * {@hide} */ + @SystemApi @RequiresPermission(Manifest.permission.NETWORK_STACK) public void setScanningEnabled(boolean enable) { validateChannel(); @@ -760,34 +898,46 @@ public class WifiScanner { } /** - * Register a listener that will receive results from all single scans - * Either the onSuccess/onFailure will be called once when the listener is registered. After - * (assuming onSuccess was called) all subsequent single scan results will be delivered to the - * listener. It is possible that onFullResult will not be called for all results of the first - * scan if the listener was registered during the scan. + * Register a listener that will receive results from all single scans. + * Either the {@link ScanListener#onSuccess()} or {@link ScanListener#onFailure(int, String)} + * method will be called once when the listener is registered. + * Afterwards (assuming onSuccess was called), all subsequent single scan results will be + * delivered to the listener. It is possible that onFullResult will not be called for all + * results of the first scan if the listener was registered during the scan. * + * @param executor the Executor on which to run the callback. * @param listener specifies the object to report events to. This object is also treated as a * key for this request, and must also be specified to cancel the request. * Multiple requests should also not share this object. - * {@hide} */ @RequiresPermission(Manifest.permission.NETWORK_STACK) - public void registerScanListener(ScanListener listener) { - Preconditions.checkNotNull(listener, "listener cannot be null"); - int key = addListener(listener); + public void registerScanListener(@NonNull @CallbackExecutor Executor executor, + @NonNull ScanListener listener) { + Objects.requireNonNull(executor, "executor cannot be null"); + Objects.requireNonNull(listener, "listener cannot be null"); + int key = addListener(listener, executor); if (key == INVALID_KEY) return; validateChannel(); mAsyncChannel.sendMessage(CMD_REGISTER_SCAN_LISTENER, 0, key); } /** + * Overload of {@link #registerScanListener(Executor, ScanListener)} that executes the callback + * synchronously. + * @hide + */ + @RequiresPermission(Manifest.permission.NETWORK_STACK) + public void registerScanListener(@NonNull ScanListener listener) { + registerScanListener(new SynchronousExecutor(), listener); + } + + /** * Deregister a listener for ongoing single scans * @param listener specifies which scan to cancel; must be same object as passed in {@link * #registerScanListener} - * {@hide} */ - public void deregisterScanListener(ScanListener listener) { - Preconditions.checkNotNull(listener, "listener cannot be null"); + public void unregisterScanListener(@NonNull ScanListener listener) { + Objects.requireNonNull(listener, "listener cannot be null"); int key = removeListener(listener); if (key == INVALID_KEY) return; validateChannel(); @@ -813,11 +963,15 @@ public class WifiScanner { * @param listener specifies the object to report events to. This object is also treated as a * key for this scan, and must also be specified to cancel the scan. Multiple * scans should also not share this object. + * @deprecated Background scan support has always been hardware vendor dependent. This support + * may not be present on newer devices. Use {@link #startScan(ScanSettings, ScanListener)} + * instead for single scans. */ + @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(ScanSettings settings, ScanListener listener, WorkSource workSource) { - Preconditions.checkNotNull(listener, "listener cannot be null"); + Objects.requireNonNull(listener, "listener cannot be null"); int key = addListener(listener); if (key == INVALID_KEY) return; validateChannel(); @@ -825,6 +979,7 @@ public class WifiScanner { scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource); scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag()); mAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams); } @@ -832,27 +987,37 @@ public class WifiScanner { * stop an ongoing wifi scan * @param listener specifies which scan to cancel; must be same object as passed in {@link * #startBackgroundScan} + * @deprecated Background scan support has always been hardware vendor dependent. This support + * may not be present on newer devices. Use {@link #startScan(ScanSettings, ScanListener)} + * instead for single scans. */ + @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopBackgroundScan(ScanListener listener) { - Preconditions.checkNotNull(listener, "listener cannot be null"); + Objects.requireNonNull(listener, "listener cannot be null"); int key = removeListener(listener); if (key == INVALID_KEY) return; validateChannel(); Bundle scanParams = new Bundle(); scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag()); mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key, scanParams); } /** * reports currently available scan results on appropriate listeners * @return true if all scan results were reported correctly + * @deprecated Background scan support has always been hardware vendor dependent. This support + * may not be present on newer devices. Use {@link #startScan(ScanSettings, ScanListener)} + * instead for single scans. */ + @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean getScanResults() { validateChannel(); Bundle scanParams = new Bundle(); scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag()); Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0, 0, scanParams); return reply.what == CMD_OP_SUCCEEDED; @@ -875,21 +1040,39 @@ public class WifiScanner { * starts a single scan and reports results asynchronously * @param settings specifies various parameters for the scan; for more information look at * {@link ScanSettings} - * @param workSource WorkSource to blame for power usage * @param listener specifies the object to report events to. This object is also treated as a * key for this scan, and must also be specified to cancel the scan. Multiple * scans should also not share this object. + * @param workSource WorkSource to blame for power usage */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) { - Preconditions.checkNotNull(listener, "listener cannot be null"); - int key = addListener(listener); + startScan(settings, null, listener, workSource); + } + + /** + * starts a single scan and reports results asynchronously + * @param settings specifies various parameters for the scan; for more information look at + * {@link ScanSettings} + * @param executor the Executor on which to run the callback. + * @param listener specifies the object to report events to. This object is also treated as a + * key for this scan, and must also be specified to cancel the scan. Multiple + * scans should also not share this object. + * @param workSource WorkSource to blame for power usage + * @hide + */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) + public void startScan(ScanSettings settings, @Nullable @CallbackExecutor Executor executor, + ScanListener listener, WorkSource workSource) { + Objects.requireNonNull(listener, "listener cannot be null"); + int key = addListener(listener, executor); if (key == INVALID_KEY) return; validateChannel(); Bundle scanParams = new Bundle(); scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource); scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag()); mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams); } @@ -900,23 +1083,26 @@ public class WifiScanner { */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopScan(ScanListener listener) { - Preconditions.checkNotNull(listener, "listener cannot be null"); + Objects.requireNonNull(listener, "listener cannot be null"); int key = removeListener(listener); if (key == INVALID_KEY) return; validateChannel(); Bundle scanParams = new Bundle(); scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag()); mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key, scanParams); } /** * Retrieve the most recent scan results from a single scan request. - * {@hide} */ + @NonNull + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public List<ScanResult> getSingleScanResults() { validateChannel(); Bundle scanParams = new Bundle(); scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName()); + scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag()); Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0, 0, scanParams); if (reply.what == WifiScanner.CMD_OP_SUCCEEDED) { @@ -925,7 +1111,7 @@ public class WifiScanner { OperationResult result = (OperationResult) reply.obj; Log.e(TAG, "Error retrieving SingleScan results reason: " + result.reason + " description: " + result.description); - return new ArrayList<ScanResult>(); + return new ArrayList<>(); } private void startPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, int key) { @@ -943,16 +1129,17 @@ public class WifiScanner { * {@link ScanSettings} * @param pnoSettings specifies various parameters for PNO; for more information look at * {@link PnoSettings} + * @param executor the Executor on which to run the callback. * @param listener specifies the object to report events to. This object is also treated as a * key for this scan, and must also be specified to cancel the scan. Multiple * scans should also not share this object. * {@hide} */ public void startConnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, - PnoScanListener listener) { - Preconditions.checkNotNull(listener, "listener cannot be null"); - Preconditions.checkNotNull(pnoSettings, "pnoSettings cannot be null"); - int key = addListener(listener); + @NonNull @CallbackExecutor Executor executor, PnoScanListener listener) { + Objects.requireNonNull(listener, "listener cannot be null"); + Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null"); + int key = addListener(listener, executor); if (key == INVALID_KEY) return; validateChannel(); pnoSettings.isConnected = true; @@ -971,10 +1158,10 @@ public class WifiScanner { */ @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, - PnoScanListener listener) { - Preconditions.checkNotNull(listener, "listener cannot be null"); - Preconditions.checkNotNull(pnoSettings, "pnoSettings cannot be null"); - int key = addListener(listener); + @NonNull @CallbackExecutor Executor executor, PnoScanListener listener) { + Objects.requireNonNull(listener, "listener cannot be null"); + Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null"); + int key = addListener(listener, executor); if (key == INVALID_KEY) return; validateChannel(); pnoSettings.isConnected = false; @@ -988,7 +1175,7 @@ public class WifiScanner { */ @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void stopPnoScan(ScanListener listener) { - Preconditions.checkNotNull(listener, "listener cannot be null"); + Objects.requireNonNull(listener, "listener cannot be null"); int key = removeListener(listener); if (key == INVALID_KEY) return; validateChannel(); @@ -1029,7 +1216,7 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<WifiChangeSettings> CREATOR = + public static final @NonNull Creator<WifiChangeSettings> CREATOR = new Creator<WifiChangeSettings>() { public WifiChangeSettings createFromParcel(Parcel in) { return new WifiChangeSettings(); @@ -1140,7 +1327,7 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<HotlistSettings> CREATOR = + public static final @NonNull Creator<HotlistSettings> CREATOR = new Creator<HotlistSettings>() { public HotlistSettings createFromParcel(Parcel in) { HotlistSettings settings = new HotlistSettings(); @@ -1230,6 +1417,7 @@ public class WifiScanner { private int mListenerKey = 1; private final SparseArray mListenerMap = new SparseArray(); + private final SparseArray<Executor> mExecutorMap = new SparseArray<>(); private final Object mListenerMapLock = new Object(); private AsyncChannel mAsyncChannel; @@ -1240,12 +1428,15 @@ public class WifiScanner { * Applications will almost always want to use * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}. + * * @param context the application context - * @param service the Binder interface + * @param service the Binder interface for {@link Context#WIFI_SCANNING_SERVICE} * @param looper the Looper used to deliver callbacks + * * @hide */ - public WifiScanner(Context context, IWifiScanner service, Looper looper) { + public WifiScanner(@NonNull Context context, @NonNull IWifiScanner service, + @NonNull Looper looper) { mContext = context; mService = service; @@ -1274,10 +1465,14 @@ public class WifiScanner { "No permission to access and change wifi or a bad initialization"); } + private int addListener(ActionListener listener) { + return addListener(listener, null); + } + // Add a listener into listener map. If the listener already exists, return INVALID_KEY and // send an error message to internal handler; Otherwise add the listener to the listener map and // return the key of the listener. - private int addListener(ActionListener listener) { + private int addListener(ActionListener listener, Executor executor) { synchronized (mListenerMapLock) { boolean keyExists = (getListenerKey(listener) != INVALID_KEY); // Note we need to put the listener into listener map even if it's a duplicate as the @@ -1293,6 +1488,7 @@ public class WifiScanner { message.sendToTarget(); return INVALID_KEY; } else { + mExecutorMap.put(key, executor); return key; } } @@ -1310,11 +1506,22 @@ public class WifiScanner { return key; } - private Object getListener(int key) { - if (key == INVALID_KEY) return null; + private static class ListenerWithExecutor { + @Nullable final Object mListener; + @Nullable final Executor mExecutor; + + ListenerWithExecutor(@Nullable Object listener, @Nullable Executor executor) { + mListener = listener; + mExecutor = executor; + } + } + + private ListenerWithExecutor getListenerWithExecutor(int key) { + if (key == INVALID_KEY) return new ListenerWithExecutor(null, null); synchronized (mListenerMapLock) { Object listener = mListenerMap.get(key); - return listener; + Executor executor = mExecutorMap.get(key); + return new ListenerWithExecutor(listener, executor); } } @@ -1335,6 +1542,7 @@ public class WifiScanner { synchronized (mListenerMapLock) { Object listener = mListenerMap.get(key); mListenerMap.remove(key); + mExecutorMap.remove(key); return listener; } } @@ -1347,6 +1555,7 @@ public class WifiScanner { } synchronized (mListenerMapLock) { mListenerMap.remove(key); + mExecutorMap.remove(key); return key; } } @@ -1373,7 +1582,7 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<OperationResult> CREATOR = + public static final @NonNull Creator<OperationResult> CREATOR = new Creator<OperationResult>() { public OperationResult createFromParcel(Parcel in) { int reason = in.readInt(); @@ -1405,7 +1614,8 @@ public class WifiScanner { return; } - Object listener = getListener(msg.arg2); + ListenerWithExecutor listenerWithExecutor = getListenerWithExecutor(msg.arg2); + Object listener = listenerWithExecutor.mListener; if (listener == null) { if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2); @@ -1414,36 +1624,52 @@ public class WifiScanner { if (DBG) Log.d(TAG, "listener key = " + msg.arg2); } + Executor executor = listenerWithExecutor.mExecutor; + if (executor == null) { + executor = new SynchronousExecutor(); + } + switch (msg.what) { - /* ActionListeners grouped together */ - case CMD_OP_SUCCEEDED : - ((ActionListener) listener).onSuccess(); - break; - case CMD_OP_FAILED : { - OperationResult result = (OperationResult)msg.obj; - ((ActionListener) listener).onFailure(result.reason, result.description); - removeListener(msg.arg2); - } - break; - case CMD_SCAN_RESULT : - ((ScanListener) listener).onResults( - ((ParcelableScanData) msg.obj).getResults()); - return; - case CMD_FULL_SCAN_RESULT : + /* ActionListeners grouped together */ + case CMD_OP_SUCCEEDED: { + ActionListener actionListener = (ActionListener) listener; + Binder.clearCallingIdentity(); + executor.execute(actionListener::onSuccess); + } break; + case CMD_OP_FAILED: { + OperationResult result = (OperationResult) msg.obj; + ActionListener actionListener = (ActionListener) listener; + removeListener(msg.arg2); + Binder.clearCallingIdentity(); + executor.execute(() -> + actionListener.onFailure(result.reason, result.description)); + } break; + case CMD_SCAN_RESULT: { + ScanListener scanListener = (ScanListener) listener; + ParcelableScanData parcelableScanData = (ParcelableScanData) msg.obj; + Binder.clearCallingIdentity(); + executor.execute(() -> scanListener.onResults(parcelableScanData.getResults())); + } break; + case CMD_FULL_SCAN_RESULT: { ScanResult result = (ScanResult) msg.obj; - ((ScanListener) listener).onFullResult(result); - return; - case CMD_SINGLE_SCAN_COMPLETED: + ScanListener scanListener = ((ScanListener) listener); + Binder.clearCallingIdentity(); + executor.execute(() -> scanListener.onFullResult(result)); + } break; + case CMD_SINGLE_SCAN_COMPLETED: { if (DBG) Log.d(TAG, "removing listener for single scan"); removeListener(msg.arg2); - break; - case CMD_PNO_NETWORK_FOUND: - ((PnoScanListener) listener).onPnoNetworkFound( - ((ParcelableScanResults) msg.obj).getResults()); - return; - default: + } break; + case CMD_PNO_NETWORK_FOUND: { + PnoScanListener pnoScanListener = (PnoScanListener) listener; + ParcelableScanResults parcelableScanResults = (ParcelableScanResults) msg.obj; + Binder.clearCallingIdentity(); + executor.execute(() -> + pnoScanListener.onPnoNetworkFound(parcelableScanResults.getResults())); + } break; + default: { if (DBG) Log.d(TAG, "Ignoring message " + msg.what); - return; + } break; } } } diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java index a591f312028e..704ae81f71aa 100644 --- a/wifi/java/android/net/wifi/WifiSsid.java +++ b/wifi/java/android/net/wifi/WifiSsid.java @@ -16,6 +16,8 @@ package android.net.wifi; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -40,23 +42,29 @@ import java.util.Locale; * * @hide */ -public class WifiSsid implements Parcelable { +public final class WifiSsid implements Parcelable { private static final String TAG = "WifiSsid"; @UnsupportedAppUsage public final ByteArrayOutputStream octets = new ByteArrayOutputStream(32); private static final int HEX_RADIX = 16; + @UnsupportedAppUsage public static final String NONE = WifiManager.UNKNOWN_SSID; private WifiSsid() { } - public static WifiSsid createFromByteArray(byte ssid[]) { + /** + * Create a WifiSsid from a raw byte array. If the byte array is null, return an empty WifiSsid + * object. + */ + @NonNull + public static WifiSsid createFromByteArray(@Nullable byte[] ssid) { WifiSsid wifiSsid = new WifiSsid(); if (ssid != null) { - wifiSsid.octets.write(ssid, 0/* the start offset */, ssid.length);; + wifiSsid.octets.write(ssid, 0 /* the start offset */, ssid.length); } return wifiSsid; } @@ -174,6 +182,10 @@ public class WifiSsid implements Parcelable { } } + /** + * Converts this SSID to an unquoted UTF-8 String representation. + * @return the SSID string, or {@link WifiManager#UNKNOWN_SSID} if there was an error. + */ @Override public String toString() { byte[] ssidBytes = octets.toByteArray(); @@ -191,7 +203,7 @@ public class WifiSsid implements Parcelable { CoderResult result = decoder.decode(ByteBuffer.wrap(ssidBytes), out, true); out.flip(); if (result.isError()) { - return NONE; + return WifiManager.UNKNOWN_SSID; } return out.toString(); } @@ -241,32 +253,36 @@ public class WifiSsid implements Parcelable { return (octets.size() > 0) ? out : null; } - /** Implement the Parcelable interface {@hide} */ + /** Implement the Parcelable interface */ + @Override public int describeContents() { return 0; } - /** Implement the Parcelable interface {@hide} */ - public void writeToParcel(Parcel dest, int flags) { + /** Implement the Parcelable interface */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(octets.size()); dest.writeByteArray(octets.toByteArray()); } - /** Implement the Parcelable interface {@hide} */ + /** Implement the Parcelable interface */ @UnsupportedAppUsage - public static final @android.annotation.NonNull Creator<WifiSsid> CREATOR = - new Creator<WifiSsid>() { - public WifiSsid createFromParcel(Parcel in) { - WifiSsid ssid = new WifiSsid(); - int length = in.readInt(); - byte b[] = new byte[length]; - in.readByteArray(b); - ssid.octets.write(b, 0, length); - return ssid; - } + public static final @NonNull Creator<WifiSsid> CREATOR = + new Creator<WifiSsid>() { + @Override + public WifiSsid createFromParcel(Parcel in) { + WifiSsid ssid = new WifiSsid(); + int length = in.readInt(); + byte[] b = new byte[length]; + in.readByteArray(b); + ssid.octets.write(b, 0, length); + return ssid; + } - public WifiSsid[] newArray(int size) { - return new WifiSsid[size]; - } - }; + @Override + public WifiSsid[] newArray(int size) { + return new WifiSsid[size]; + } + }; } diff --git a/wifi/java/android/net/wifi/WpsResult.java b/wifi/java/android/net/wifi/WpsResult.java deleted file mode 100644 index f2ffb6f52520..000000000000 --- a/wifi/java/android/net/wifi/WpsResult.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2010 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.os.Parcel; -import android.os.Parcelable; - -/** - * A class representing the result of a WPS request - * @hide - */ -public class WpsResult implements Parcelable { - - public enum Status { - SUCCESS, - FAILURE, - IN_PROGRESS, - } - - public Status status; - - public String pin; - - public WpsResult() { - status = Status.FAILURE; - pin = null; - } - - public WpsResult(Status s) { - status = s; - pin = null; - } - - public String toString() { - StringBuffer sbuf = new StringBuffer(); - sbuf.append(" status: ").append(status.toString()); - sbuf.append('\n'); - sbuf.append(" pin: ").append(pin); - sbuf.append("\n"); - return sbuf.toString(); - } - - /** Implement the Parcelable interface {@hide} */ - public int describeContents() { - return 0; - } - - /** copy constructor {@hide} */ - public WpsResult(WpsResult source) { - if (source != null) { - status = source.status; - pin = source.pin; - } - } - - /** Implement the Parcelable interface {@hide} */ - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(status.name()); - dest.writeString(pin); - } - - /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<WpsResult> CREATOR = - new Creator<WpsResult>() { - public WpsResult createFromParcel(Parcel in) { - WpsResult result = new WpsResult(); - result.status = Status.valueOf(in.readString()); - result.pin = in.readString(); - return result; - } - - public WpsResult[] newArray(int size) { - return new WpsResult[size]; - } - }; -} diff --git a/wifi/java/android/net/wifi/aware/Characteristics.java b/wifi/java/android/net/wifi/aware/Characteristics.java index e2cf4dc02659..d5fd48e9e7b3 100644 --- a/wifi/java/android/net/wifi/aware/Characteristics.java +++ b/wifi/java/android/net/wifi/aware/Characteristics.java @@ -16,10 +16,14 @@ package android.net.wifi.aware; +import android.annotation.IntDef; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * The characteristics of the Wi-Fi Aware implementation. */ @@ -31,6 +35,8 @@ public final class Characteristics implements Parcelable { "key_max_service_specific_info_length"; /** @hide */ public static final String KEY_MAX_MATCH_FILTER_LENGTH = "key_max_match_filter_length"; + /** @hide */ + public static final String KEY_SUPPORTED_CIPHER_SUITES = "key_supported_cipher_suites"; private Bundle mCharacteristics = new Bundle(); @@ -71,12 +77,41 @@ public final class Characteristics implements Parcelable { * {@link PublishConfig.Builder#setMatchFilter(java.util.List)} and * {@link SubscribeConfig.Builder#setMatchFilter(java.util.List)}. * - * @return A positive integer, maximum legngth of byte array for Aware discovery match filter. + * @return A positive integer, maximum length of byte array for Aware discovery match filter. */ public int getMaxMatchFilterLength() { return mCharacteristics.getInt(KEY_MAX_MATCH_FILTER_LENGTH); } + /** @hide */ + @IntDef(flag = true, prefix = { "WIFI_AWARE_CIPHER_SUITE_" }, value = { + WIFI_AWARE_CIPHER_SUITE_NCS_SK_128, + WIFI_AWARE_CIPHER_SUITE_NCS_SK_256, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface WifiAwareCipherSuites {} + + /** + * Wi-Fi Aware supported ciphier suite representing NCS SK 128: 128 bit shared-key. + */ + public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_128 = 1 << 0; + + /** + * Wi-Fi Aware supported ciphier suite representing NCS SK 256: 256 bit shared-key. + */ + public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_256 = 1 << 1; + + /** + * Returns the set of cipher suites supported by the device for use in Wi-Fi Aware data-paths. + * The device automatically picks the strongest cipher suite when initiating a data-path setup. + * + * @return A set of flags from {@link #WIFI_AWARE_CIPHER_SUITE_NCS_SK_128}, or + * {@link #WIFI_AWARE_CIPHER_SUITE_NCS_SK_256}. + */ + public @WifiAwareCipherSuites int getSupportedCipherSuites() { + return mCharacteristics.getInt(KEY_SUPPORTED_CIPHER_SUITES); + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeBundle(mCharacteristics); diff --git a/wifi/java/android/net/wifi/aware/ConfigRequest.java b/wifi/java/android/net/wifi/aware/ConfigRequest.java index b07d8edde3d4..61ab92c9416f 100644 --- a/wifi/java/android/net/wifi/aware/ConfigRequest.java +++ b/wifi/java/android/net/wifi/aware/ConfigRequest.java @@ -47,6 +47,7 @@ public final class ConfigRequest implements Parcelable { */ public static final int NAN_BAND_24GHZ = 0; public static final int NAN_BAND_5GHZ = 1; + public static final int NAN_BAND_6GHZ = 2; /** * Magic values for Discovery Window (DW) interval configuration @@ -60,6 +61,11 @@ public final class ConfigRequest implements Parcelable { public final boolean mSupport5gBand; /** + * Indicates whether 6G band support is requested. + */ + public final boolean mSupport6gBand; + + /** * Specifies the desired master preference. */ public final int mMasterPreference; @@ -81,9 +87,10 @@ public final class ConfigRequest implements Parcelable { */ public final int mDiscoveryWindowInterval[]; - private ConfigRequest(boolean support5gBand, int masterPreference, int clusterLow, - int clusterHigh, int discoveryWindowInterval[]) { + private ConfigRequest(boolean support5gBand, boolean support6gBand, int masterPreference, + int clusterLow, int clusterHigh, int[] discoveryWindowInterval) { mSupport5gBand = support5gBand; + mSupport6gBand = support6gBand; mMasterPreference = masterPreference; mClusterLow = clusterLow; mClusterHigh = clusterHigh; @@ -92,10 +99,12 @@ public final class ConfigRequest implements Parcelable { @Override public String toString() { - return "ConfigRequest [mSupport5gBand=" + mSupport5gBand + ", mMasterPreference=" - + mMasterPreference + ", mClusterLow=" + mClusterLow + ", mClusterHigh=" - + mClusterHigh + ", mDiscoveryWindowInterval=" - + Arrays.toString(mDiscoveryWindowInterval) + "]"; + return "ConfigRequest [mSupport5gBand=" + mSupport5gBand + + ", mSupport6gBand=" + mSupport6gBand + + ", mMasterPreference=" + mMasterPreference + + ", mClusterLow=" + mClusterLow + + ", mClusterHigh=" + mClusterHigh + + ", mDiscoveryWindowInterval=" + Arrays.toString(mDiscoveryWindowInterval) + "]"; } @Override @@ -106,6 +115,7 @@ public final class ConfigRequest implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mSupport5gBand ? 1 : 0); + dest.writeInt(mSupport6gBand ? 1 : 0); dest.writeInt(mMasterPreference); dest.writeInt(mClusterLow); dest.writeInt(mClusterHigh); @@ -121,13 +131,14 @@ public final class ConfigRequest implements Parcelable { @Override public ConfigRequest createFromParcel(Parcel in) { boolean support5gBand = in.readInt() != 0; + boolean support6gBand = in.readInt() != 0; int masterPreference = in.readInt(); int clusterLow = in.readInt(); int clusterHigh = in.readInt(); int discoveryWindowInterval[] = in.createIntArray(); - return new ConfigRequest(support5gBand, masterPreference, clusterLow, clusterHigh, - discoveryWindowInterval); + return new ConfigRequest(support5gBand, support6gBand, masterPreference, clusterLow, + clusterHigh, discoveryWindowInterval); } }; @@ -143,7 +154,9 @@ public final class ConfigRequest implements Parcelable { ConfigRequest lhs = (ConfigRequest) o; - return mSupport5gBand == lhs.mSupport5gBand && mMasterPreference == lhs.mMasterPreference + return mSupport5gBand == lhs.mSupport5gBand + && mSupport6gBand == lhs.mSupport6gBand + && mMasterPreference == lhs.mMasterPreference && mClusterLow == lhs.mClusterLow && mClusterHigh == lhs.mClusterHigh && Arrays.equals(mDiscoveryWindowInterval, lhs.mDiscoveryWindowInterval); } @@ -153,6 +166,7 @@ public final class ConfigRequest implements Parcelable { int result = 17; result = 31 * result + (mSupport5gBand ? 1 : 0); + result = 31 * result + (mSupport6gBand ? 1 : 0); result = 31 * result + mMasterPreference; result = 31 * result + mClusterLow; result = 31 * result + mClusterHigh; @@ -190,9 +204,9 @@ public final class ConfigRequest implements Parcelable { throw new IllegalArgumentException( "Invalid argument combination - must have Cluster Low <= Cluster High"); } - if (mDiscoveryWindowInterval.length != 2) { + if (mDiscoveryWindowInterval.length != 3) { throw new IllegalArgumentException( - "Invalid discovery window interval: must have 2 elements (2.4 & 5"); + "Invalid discovery window interval: must have 3 elements (2.4 & 5 & 6"); } if (mDiscoveryWindowInterval[NAN_BAND_24GHZ] != DW_INTERVAL_NOT_INIT && (mDiscoveryWindowInterval[NAN_BAND_24GHZ] < 1 // valid for 2.4GHz: [1-5] @@ -206,7 +220,12 @@ public final class ConfigRequest implements Parcelable { throw new IllegalArgumentException( "Invalid discovery window interval for 5GHz: valid is UNSET or [0,5]"); } - + if (mDiscoveryWindowInterval[NAN_BAND_6GHZ] != DW_INTERVAL_NOT_INIT + && (mDiscoveryWindowInterval[NAN_BAND_6GHZ] < 0 // valid for 6GHz: [0-5] + || mDiscoveryWindowInterval[NAN_BAND_6GHZ] > 5)) { + throw new IllegalArgumentException( + "Invalid discovery window interval for 6GHz: valid is UNSET or [0,5]"); + } } /** @@ -214,10 +233,12 @@ public final class ConfigRequest implements Parcelable { */ public static final class Builder { private boolean mSupport5gBand = true; + private boolean mSupport6gBand = false; private int mMasterPreference = 0; private int mClusterLow = CLUSTER_ID_MIN; private int mClusterHigh = CLUSTER_ID_MAX; - private int mDiscoveryWindowInterval[] = {DW_INTERVAL_NOT_INIT, DW_INTERVAL_NOT_INIT}; + private int[] mDiscoveryWindowInterval = {DW_INTERVAL_NOT_INIT, DW_INTERVAL_NOT_INIT, + DW_INTERVAL_NOT_INIT}; /** * Specify whether 5G band support is required in this request. Disabled by default. @@ -233,6 +254,19 @@ public final class ConfigRequest implements Parcelable { } /** + * Specify whether 6G band support is required in this request. Disabled by default. + * + * @param support6gBand Support for 6G band is required. + * + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setSupport6gBand(boolean support6gBand) { + mSupport6gBand = support6gBand; + return this; + } + + /** * Specify the Master Preference requested. The permitted range is 0 (the default) to * 255 with 1 and 255 excluded (reserved). * @@ -310,7 +344,8 @@ public final class ConfigRequest implements Parcelable { * awake. The configuration enables trading off latency vs. power (higher interval means * higher discovery latency but lower power). * - * @param band Either {@link #NAN_BAND_24GHZ} or {@link #NAN_BAND_5GHZ}. + * @param band Either {@link #NAN_BAND_24GHZ} or {@link #NAN_BAND_5GHZ} or + * {@link #NAN_BAND_6GHZ}. * @param interval A value of 1, 2, 3, 4, or 5 indicating an interval of 2^(interval-1). For * the 5GHz band a value of 0 indicates that the device will not be awake * for any discovery windows. @@ -319,13 +354,14 @@ public final class ConfigRequest implements Parcelable { * {@code builder.setDiscoveryWindowInterval(...).setMasterPreference(...)}. */ public Builder setDiscoveryWindowInterval(int band, int interval) { - if (band != NAN_BAND_24GHZ && band != NAN_BAND_5GHZ) { + if (band != NAN_BAND_24GHZ && band != NAN_BAND_5GHZ && band != NAN_BAND_6GHZ) { throw new IllegalArgumentException("Invalid band value"); } if ((band == NAN_BAND_24GHZ && (interval < 1 || interval > 5)) - || (band == NAN_BAND_5GHZ && (interval < 0 || interval > 5))) { + || (band == NAN_BAND_5GHZ && (interval < 0 || interval > 5)) + || (band == NAN_BAND_6GHZ && (interval < 0 || interval > 5))) { throw new IllegalArgumentException( - "Invalid interval value: 2.4 GHz [1,5] or 5GHz [0,5]"); + "Invalid interval value: 2.4 GHz [1,5] or 5GHz/6GHz [0,5]"); } mDiscoveryWindowInterval[band] = interval; @@ -342,8 +378,8 @@ public final class ConfigRequest implements Parcelable { "Invalid argument combination - must have Cluster Low <= Cluster High"); } - return new ConfigRequest(mSupport5gBand, mMasterPreference, mClusterLow, mClusterHigh, - mDiscoveryWindowInterval); + return new ConfigRequest(mSupport5gBand, mSupport6gBand, mMasterPreference, mClusterLow, + mClusterHigh, mDiscoveryWindowInterval); } } } diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java index d97f6fb02ac6..4d92ae174e6d 100644 --- a/wifi/java/android/net/wifi/aware/DiscoverySession.java +++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java @@ -20,12 +20,12 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.NetworkSpecifier; +import android.util.CloseGuard; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import dalvik.system.CloseGuard; - +import java.lang.ref.Reference; import java.lang.ref.WeakReference; /** @@ -58,7 +58,7 @@ public class DiscoverySession implements AutoCloseable { /** @hide */ protected boolean mTerminated = false; - private final CloseGuard mCloseGuard = CloseGuard.get(); + private final CloseGuard mCloseGuard = new CloseGuard(); /** * Return the maximum permitted retry count when sending messages using @@ -108,6 +108,7 @@ public class DiscoverySession implements AutoCloseable { mTerminated = true; mMgr.clear(); mCloseGuard.close(); + Reference.reachabilityFence(this); } /** diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl index c4b24cfa2394..88f95ad4d495 100644 --- a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl +++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl @@ -38,14 +38,15 @@ interface IWifiAwareManager Characteristics getCharacteristics(); // client API - void connect(in IBinder binder, in String callingPackage, in IWifiAwareEventCallback callback, - in ConfigRequest configRequest, boolean notifyOnIdentityChanged); + void connect(in IBinder binder, in String callingPackage, in String callingFeatureId, + in IWifiAwareEventCallback callback, in ConfigRequest configRequest, + boolean notifyOnIdentityChanged); void disconnect(int clientId, in IBinder binder); - void publish(in String callingPackage, int clientId, in PublishConfig publishConfig, - in IWifiAwareDiscoverySessionCallback callback); - void subscribe(in String callingPackage, int clientId, in SubscribeConfig subscribeConfig, - in IWifiAwareDiscoverySessionCallback callback); + void publish(in String callingPackage, in String callingFeatureId, int clientId, + in PublishConfig publishConfig, in IWifiAwareDiscoverySessionCallback callback); + void subscribe(in String callingPackage, in String callingFeatureId, int clientId, + in SubscribeConfig subscribeConfig, in IWifiAwareDiscoverySessionCallback callback); // session API void updatePublish(int clientId, int discoverySessionId, in PublishConfig publishConfig); diff --git a/wifi/java/android/net/wifi/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java index 1886b7ef4c8d..a8844c1d3812 100644 --- a/wifi/java/android/net/wifi/aware/PublishConfig.java +++ b/wifi/java/android/net/wifi/aware/PublishConfig.java @@ -19,11 +19,10 @@ package android.net.wifi.aware; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.net.wifi.util.HexEncoding; import android.os.Parcel; import android.os.Parcelable; -import libcore.util.HexEncoding; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java index f0f758170bf2..76780f421af2 100644 --- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java +++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java @@ -19,11 +19,10 @@ package android.net.wifi.aware; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.net.wifi.util.HexEncoding; import android.os.Parcel; import android.os.Parcelable; -import libcore.util.HexEncoding; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; diff --git a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java index b3b5b2903471..2d3cc1eda643 100644 --- a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java +++ b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java @@ -18,8 +18,6 @@ package android.net.wifi.aware; import android.annotation.Nullable; -import libcore.io.Memory; - import java.nio.BufferOverflowException; import java.nio.ByteOrder; import java.util.ArrayList; @@ -266,7 +264,7 @@ public class TlvBufferUtils { public TlvConstructor putShort(int type, short data) { checkLength(2); addHeader(type, 2); - Memory.pokeShort(mArray, mPosition, data, mByteOrder); + pokeShort(mArray, mPosition, data, mByteOrder); mPosition += 2; return this; } @@ -284,7 +282,7 @@ public class TlvBufferUtils { public TlvConstructor putInt(int type, int data) { checkLength(4); addHeader(type, 4); - Memory.pokeInt(mArray, mPosition, data, mByteOrder); + pokeInt(mArray, mPosition, data, mByteOrder); mPosition += 4; return this; } @@ -349,14 +347,14 @@ public class TlvBufferUtils { if (mTypeSize == 1) { mArray[mPosition] = (byte) type; } else if (mTypeSize == 2) { - Memory.pokeShort(mArray, mPosition, (short) type, mByteOrder); + pokeShort(mArray, mPosition, (short) type, mByteOrder); } mPosition += mTypeSize; if (mLengthSize == 1) { mArray[mPosition] = (byte) length; } else if (mLengthSize == 2) { - Memory.pokeShort(mArray, mPosition, (short) length, mByteOrder); + pokeShort(mArray, mPosition, (short) length, mByteOrder); } mPosition += mLengthSize; } @@ -445,7 +443,7 @@ public class TlvBufferUtils { throw new IllegalArgumentException( "Accesing a short from a TLV element of length " + length); } - return Memory.peekShort(mRefArray, offset, byteOrder); + return peekShort(mRefArray, offset, byteOrder); } /** @@ -460,7 +458,7 @@ public class TlvBufferUtils { throw new IllegalArgumentException( "Accesing an int from a TLV element of length " + length); } - return Memory.peekInt(mRefArray, offset, byteOrder); + return peekInt(mRefArray, offset, byteOrder); } /** @@ -590,7 +588,7 @@ public class TlvBufferUtils { if (mTypeSize == 1) { type = mArray[mOffset]; } else if (mTypeSize == 2) { - type = Memory.peekShort(mArray, mOffset, mByteOrder); + type = peekShort(mArray, mOffset, mByteOrder); } mOffset += mTypeSize; @@ -598,7 +596,7 @@ public class TlvBufferUtils { if (mLengthSize == 1) { length = mArray[mOffset]; } else if (mLengthSize == 2) { - length = Memory.peekShort(mArray, mOffset, mByteOrder); + length = peekShort(mArray, mOffset, mByteOrder); } mOffset += mLengthSize; @@ -661,10 +659,56 @@ public class TlvBufferUtils { if (lengthSize == 1) { nextTlvIndex += lengthSize + array[nextTlvIndex]; } else { - nextTlvIndex += lengthSize + Memory.peekShort(array, nextTlvIndex, byteOrder); + nextTlvIndex += lengthSize + peekShort(array, nextTlvIndex, byteOrder); } } return nextTlvIndex == array.length; } + + private static void pokeShort(byte[] dst, int offset, short value, ByteOrder order) { + if (order == ByteOrder.BIG_ENDIAN) { + dst[offset++] = (byte) ((value >> 8) & 0xff); + dst[offset ] = (byte) ((value >> 0) & 0xff); + } else { + dst[offset++] = (byte) ((value >> 0) & 0xff); + dst[offset ] = (byte) ((value >> 8) & 0xff); + } + } + + private static void pokeInt(byte[] dst, int offset, int value, ByteOrder order) { + if (order == ByteOrder.BIG_ENDIAN) { + dst[offset++] = (byte) ((value >> 24) & 0xff); + dst[offset++] = (byte) ((value >> 16) & 0xff); + dst[offset++] = (byte) ((value >> 8) & 0xff); + dst[offset ] = (byte) ((value >> 0) & 0xff); + } else { + dst[offset++] = (byte) ((value >> 0) & 0xff); + dst[offset++] = (byte) ((value >> 8) & 0xff); + dst[offset++] = (byte) ((value >> 16) & 0xff); + dst[offset ] = (byte) ((value >> 24) & 0xff); + } + } + + private static short peekShort(byte[] src, int offset, ByteOrder order) { + if (order == ByteOrder.BIG_ENDIAN) { + return (short) ((src[offset] << 8) | (src[offset + 1] & 0xff)); + } else { + return (short) ((src[offset + 1] << 8) | (src[offset] & 0xff)); + } + } + + private static int peekInt(byte[] src, int offset, ByteOrder order) { + if (order == ByteOrder.BIG_ENDIAN) { + return ((src[offset++] & 0xff) << 24) + | ((src[offset++] & 0xff) << 16) + | ((src[offset++] & 0xff) << 8) + | ((src[offset ] & 0xff) << 0); + } else { + return ((src[offset++] & 0xff) << 0) + | ((src[offset++] & 0xff) << 8) + | ((src[offset++] & 0xff) << 16) + | ((src[offset ] & 0xff) << 24); + } + } } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java index 3b26c6e558b3..9ae3bd0c93c0 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java @@ -17,12 +17,11 @@ package android.net.wifi.aware; import android.net.NetworkSpecifier; +import android.net.wifi.util.HexEncoding; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; -import libcore.util.HexEncoding; - import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; @@ -201,14 +200,14 @@ public class WifiAwareAgentNetworkSpecifier extends NetworkSpecifier implements @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeBlob(mData); + dest.writeByteArray(mData); } public static final @android.annotation.NonNull Creator<ByteArrayWrapper> CREATOR = new Creator<ByteArrayWrapper>() { @Override public ByteArrayWrapper createFromParcel(Parcel in) { - return new ByteArrayWrapper(in.readBlob()); + return new ByteArrayWrapper(in.createByteArray()); } @Override diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index 41a412b1d134..c2ae17c4bdeb 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -19,6 +19,7 @@ package android.net.wifi.aware; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; @@ -26,18 +27,16 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkRequest; import android.net.NetworkSpecifier; +import android.net.wifi.util.HexEncoding; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.os.Process; import android.os.RemoteException; import android.util.Log; -import libcore.util.HexEncoding; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -158,7 +157,7 @@ public class WifiAwareManager { private final Object mLock = new Object(); // lock access to the following vars /** @hide */ - public WifiAwareManager(Context context, IWifiAwareManager service) { + public WifiAwareManager(@NonNull Context context, @NonNull IWifiAwareManager service) { mContext = context; mService = service; } @@ -268,7 +267,7 @@ public class WifiAwareManager { try { Binder binder = new Binder(); - mService.connect(binder, mContext.getOpPackageName(), + mService.connect(binder, mContext.getOpPackageName(), mContext.getAttributionTag(), new WifiAwareEventCallbackProxy(this, looper, binder, attachCallback, identityChangedListener), configRequest, identityChangedListener != null); @@ -299,7 +298,8 @@ public class WifiAwareManager { } try { - mService.publish(mContext.getOpPackageName(), clientId, publishConfig, + mService.publish(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId, + publishConfig, new WifiAwareDiscoverySessionCallbackProxy(this, looper, true, callback, clientId)); } catch (RemoteException e) { @@ -336,7 +336,8 @@ public class WifiAwareManager { } try { - mService.subscribe(mContext.getOpPackageName(), clientId, subscribeConfig, + mService.subscribe(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId, + subscribeConfig, new WifiAwareDiscoverySessionCallbackProxy(this, looper, false, callback, clientId)); } catch (RemoteException e) { @@ -395,6 +396,17 @@ public class WifiAwareManager { } /** @hide */ + @RequiresPermission(android.Manifest.permission.NETWORK_STACK) + public void requestMacAddresses(int uid, List<Integer> peerIds, + IWifiAwareMacAddressProvider callback) { + try { + mService.requestMacAddresses(uid, peerIds, callback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId, @NonNull PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) { if (VDBG) { @@ -434,8 +446,7 @@ public class WifiAwareManager { pmk, passphrase, 0, // no port info for deprecated IB APIs - -1, // no transport info for deprecated IB APIs - Process.myUid()); + -1); // no transport info for deprecated IB APIs } /** @hide */ @@ -475,8 +486,7 @@ public class WifiAwareManager { pmk, passphrase, 0, // no port info for OOB APIs - -1, // no transport protocol info for OOB APIs - Process.myUid()); + -1); // no transport protocol info for OOB APIs } private static class WifiAwareEventCallbackProxy extends IWifiAwareEventCallback.Stub { diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java index e9fb37ea0c29..60fe60438ce7 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java @@ -137,7 +137,7 @@ public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable { ipv6Addr = Inet6Address.getByAddress(null, addr, ni); } catch (UnknownHostException e) { e.printStackTrace(); - return null; + return new WifiAwareNetworkInfo(null); } return new WifiAwareNetworkInfo(ipv6Addr, port, transportProtocol); } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java index 487f3d8746cb..3547750896b3 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java @@ -20,11 +20,9 @@ import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_ import android.annotation.IntRange; import android.annotation.NonNull; -import android.annotation.SystemApi; import android.net.NetworkSpecifier; import android.os.Parcel; import android.os.Parcelable; -import android.os.Process; import android.text.TextUtils; import java.util.Arrays; @@ -145,19 +143,9 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements */ public final int transportProtocol; - /** - * The UID of the process initializing this network specifier. Validated by receiver using - * checkUidIfNecessary() and is used by satisfiedBy() to determine whether matches the - * offered network. - * - * @hide - */ - public final int requestorUid; - /** @hide */ public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId, - byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol, - int requestorUid) { + byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol) { this.type = type; this.role = role; this.clientId = clientId; @@ -168,7 +156,6 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements this.passphrase = passphrase; this.port = port; this.transportProtocol = transportProtocol; - this.requestorUid = requestorUid; } public static final @android.annotation.NonNull Creator<WifiAwareNetworkSpecifier> CREATOR = @@ -185,8 +172,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements in.createByteArray(), // pmk in.readString(), // passphrase in.readInt(), // port - in.readInt(), // transportProtocol - in.readInt()); // requestorUid + in.readInt()); // transportProtocol } @Override @@ -222,7 +208,6 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements dest.writeString(passphrase); dest.writeInt(port); dest.writeInt(transportProtocol); - dest.writeInt(requestorUid); } /** @hide */ @@ -239,7 +224,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements @Override public int hashCode() { return Objects.hash(type, role, clientId, sessionId, peerId, Arrays.hashCode(peerMac), - Arrays.hashCode(pmk), passphrase, port, transportProtocol, requestorUid); + Arrays.hashCode(pmk), passphrase, port, transportProtocol); } /** @hide */ @@ -264,8 +249,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements && Arrays.equals(pmk, lhs.pmk) && Objects.equals(passphrase, lhs.passphrase) && port == lhs.port - && transportProtocol == lhs.transportProtocol - && requestorUid == lhs.requestorUid; + && transportProtocol == lhs.transportProtocol; } /** @hide */ @@ -284,7 +268,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements // masking PII .append(", passphrase=").append((passphrase == null) ? "<null>" : "<non-null>") .append(", port=").append(port).append(", transportProtocol=") - .append(transportProtocol).append(", requestorUid=").append(requestorUid) + .append(transportProtocol) .append("]"); return sb.toString(); } @@ -329,7 +313,8 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements * Configure the PSK Passphrase for the Wi-Fi Aware connection being requested. This method * is optional - if not called, then an Open (unencrypted) connection will be created. * - * @param pskPassphrase The (optional) passphrase to be used to encrypt the link. + * @param pskPassphrase The (optional) passphrase to be used to encrypt the link. Use the + * {@link #setPmk(byte[])} to specify a PMK. * @return the current {@link Builder} builder, enabling chaining of builder * methods. */ @@ -350,9 +335,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements * specify a Passphrase. * @return the current {@link Builder} builder, enabling chaining of builder * methods. - * @hide */ - @SystemApi public @NonNull Builder setPmk(@NonNull byte[] pmk) { if (!WifiAwareUtils.validatePmk(pmk)) { throw new IllegalArgumentException("PMK must 32 bytes"); @@ -369,7 +352,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements * <ul> * <li>The server device must be the Publisher device! * <li>The port information can only be specified on secure links, specified using - * {@link #setPskPassphrase(String)}. + * {@link #setPskPassphrase(String)} or {@link #setPmk(byte[])}. * </ul> * * @param port A positive integer indicating the port to be used for communication. @@ -392,7 +375,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements * <ul> * <li>The server device must be the Publisher device! * <li>The transport protocol information can only be specified on secure links, - * specified using {@link #setPskPassphrase(String)}. + * specified using {@link #setPskPassphrase(String)} or {@link #setPmk(byte[])}. * </ul> * The transport protocol number is assigned by the Internet Assigned Numbers Authority * (IANA) https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml. @@ -418,7 +401,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> The default builder constructor will initialize a NetworkSpecifier which requests an * open (non-encrypted) link. To request an encrypted link use the - * {@link #setPskPassphrase(String)} builder method. + * {@link #setPskPassphrase(String)} or {@link #setPmk(byte[])} builder methods. * * @return A {@link NetworkSpecifier} to be used to construct * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass @@ -457,7 +440,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements return new WifiAwareNetworkSpecifier( WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, role, mDiscoverySession.mClientId, mDiscoverySession.mSessionId, mPeerHandle.peerId, - null, mPmk, mPskPassphrase, mPort, mTransportProtocol, Process.myUid()); + null, mPmk, mPskPassphrase, mPort, mTransportProtocol); } } } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java index 3c9781323c07..fe0872caf5f8 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java @@ -23,12 +23,12 @@ import android.net.NetworkSpecifier; import android.os.Binder; import android.os.Handler; import android.os.Looper; +import android.util.CloseGuard; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import dalvik.system.CloseGuard; - +import java.lang.ref.Reference; import java.lang.ref.WeakReference; /** @@ -45,7 +45,7 @@ public class WifiAwareSession implements AutoCloseable { private final int mClientId; private boolean mTerminated = true; - private final CloseGuard mCloseGuard = CloseGuard.get(); + private final CloseGuard mCloseGuard = new CloseGuard(); /** @hide */ public WifiAwareSession(WifiAwareManager manager, Binder binder, int clientId) { @@ -80,6 +80,7 @@ public class WifiAwareSession implements AutoCloseable { mTerminated = true; mMgr.clear(); mCloseGuard.close(); + Reference.reachabilityFence(this); } /** @hide */ diff --git a/wifi/java/android/net/wifi/hotspot2/ConfigParser.java b/wifi/java/android/net/wifi/hotspot2/ConfigParser.java index e8e873199e17..bb01365d6722 100644 --- a/wifi/java/android/net/wifi/hotspot2/ConfigParser.java +++ b/wifi/java/android/net/wifi/hotspot2/ConfigParser.java @@ -182,6 +182,12 @@ public final class ConfigParser { throw new IOException("Passpoint profile missing credential"); } + // Don't allow the installer to make changes to the update identifier. This is an + // indicator of an R2 (or newer) network. + if (config.getUpdateIdentifier() != Integer.MIN_VALUE) { + config.setUpdateIdentifier(Integer.MIN_VALUE); + } + // Parse CA (Certificate Authority) certificate. byte[] caCertData = mimeParts.get(TYPE_CA_CERT); if (caCertData != null) { diff --git a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java index a32bd547e1e2..f0a06076961c 100644 --- a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java +++ b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java @@ -19,7 +19,6 @@ package android.net.wifi.hotspot2; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.graphics.drawable.Icon; import android.net.Uri; import android.net.wifi.WifiSsid; import android.os.Bundle; @@ -27,6 +26,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -85,15 +85,16 @@ public final class OsuProvider implements Parcelable { */ private final List<Integer> mMethodList; - /** - * Icon data for the OSU (Online Sign-Up) provider. - */ - private final Icon mIcon; + /** @hide */ + public OsuProvider(String osuSsid, Map<String, String> friendlyNames, + String serviceDescription, Uri serverUri, String nai, List<Integer> methodList) { + this(WifiSsid.createFromByteArray(osuSsid.getBytes(StandardCharsets.UTF_8)), + friendlyNames, serviceDescription, serverUri, nai, methodList); + } /** @hide */ public OsuProvider(WifiSsid osuSsid, Map<String, String> friendlyNames, - String serviceDescription, Uri serverUri, String nai, List<Integer> methodList, - Icon icon) { + String serviceDescription, Uri serverUri, String nai, List<Integer> methodList) { mOsuSsid = osuSsid; mFriendlyNames = friendlyNames; mServiceDescription = serviceDescription; @@ -104,7 +105,6 @@ public final class OsuProvider implements Parcelable { } else { mMethodList = new ArrayList<>(methodList); } - mIcon = icon; } /** @@ -121,7 +121,6 @@ public final class OsuProvider implements Parcelable { mServerUri = null; mNetworkAccessIdentifier = null; mMethodList = new ArrayList<>(); - mIcon = null; return; } @@ -135,7 +134,6 @@ public final class OsuProvider implements Parcelable { } else { mMethodList = new ArrayList<>(source.mMethodList); } - mIcon = source.mIcon; } /** @hide */ @@ -196,11 +194,6 @@ public final class OsuProvider implements Parcelable { return mMethodList; } - /** @hide */ - public Icon getIcon() { - return mIcon; - } - @Override public int describeContents() { return 0; @@ -213,7 +206,6 @@ public final class OsuProvider implements Parcelable { dest.writeParcelable(mServerUri, flags); dest.writeString(mNetworkAccessIdentifier); dest.writeList(mMethodList); - dest.writeParcelable(mIcon, flags); Bundle bundle = new Bundle(); bundle.putSerializable("friendlyNameMap", (HashMap<String, String>) mFriendlyNames); dest.writeBundle(bundle); @@ -228,21 +220,16 @@ public final class OsuProvider implements Parcelable { return false; } OsuProvider that = (OsuProvider) thatObject; - return (mOsuSsid == null ? that.mOsuSsid == null : mOsuSsid.equals(that.mOsuSsid)) - && (mFriendlyNames == null) ? that.mFriendlyNames == null - : mFriendlyNames.equals(that.mFriendlyNames) + return Objects.equals(mOsuSsid, that.mOsuSsid) + && Objects.equals(mFriendlyNames, that.mFriendlyNames) && TextUtils.equals(mServiceDescription, that.mServiceDescription) - && (mServerUri == null ? that.mServerUri == null - : mServerUri.equals(that.mServerUri)) + && Objects.equals(mServerUri, that.mServerUri) && TextUtils.equals(mNetworkAccessIdentifier, that.mNetworkAccessIdentifier) - && (mMethodList == null ? that.mMethodList == null - : mMethodList.equals(that.mMethodList)) - && (mIcon == null ? that.mIcon == null : mIcon.sameAs(that.mIcon)); + && Objects.equals(mMethodList, that.mMethodList); } @Override public int hashCode() { - // mIcon is not hashable, skip the variable. return Objects.hash(mOsuSsid, mServiceDescription, mFriendlyNames, mServerUri, mNetworkAccessIdentifier, mMethodList); } @@ -255,8 +242,7 @@ public final class OsuProvider implements Parcelable { + " mServiceDescription=" + mServiceDescription + " mServerUri=" + mServerUri + " mNetworkAccessIdentifier=" + mNetworkAccessIdentifier - + " mMethodList=" + mMethodList - + " mIcon=" + mIcon; + + " mMethodList=" + mMethodList; } public static final @android.annotation.NonNull Creator<OsuProvider> CREATOR = @@ -269,12 +255,11 @@ public final class OsuProvider implements Parcelable { String nai = in.readString(); List<Integer> methodList = new ArrayList<>(); in.readList(methodList, null); - Icon icon = in.readParcelable(null); Bundle bundle = in.readBundle(); Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable( "friendlyNameMap"); return new OsuProvider(osuSsid, friendlyNamesMap, serviceDescription, - serverUri, nai, methodList, icon); + serverUri, nai, methodList); } @Override diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java index 9095b5d927a2..d1d1780a25fd 100644 --- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java +++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java @@ -16,6 +16,13 @@ package android.net.wifi.hotspot2; +import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE; +import static android.net.wifi.WifiConfiguration.MeteredOverride; + +import android.annotation.CurrentTimeMillisLong; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; import android.net.wifi.hotspot2.pps.Credential; import android.net.wifi.hotspot2.pps.HomeSp; import android.net.wifi.hotspot2.pps.Policy; @@ -23,6 +30,7 @@ import android.net.wifi.hotspot2.pps.UpdateParameter; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -64,6 +72,7 @@ public final class PasspointConfiguration implements Parcelable { * Configurations under HomeSp subtree. */ private HomeSp mHomeSp = null; + /** * Set the Home SP (Service Provider) information. * @@ -78,6 +87,29 @@ public final class PasspointConfiguration implements Parcelable { public HomeSp getHomeSp() { return mHomeSp; } /** + * Configurations under AAAServerTrustedNames subtree. + */ + private String[] mAaaServerTrustedNames = null; + /** + * Set the AAA server trusted names information. + * + * @param aaaServerTrustedNames The AAA server trusted names information to set to + * @hide + */ + public void setAaaServerTrustedNames(@Nullable String[] aaaServerTrustedNames) { + mAaaServerTrustedNames = aaaServerTrustedNames; + } + /** + * Get the AAA server trusted names information. + * + * @return AAA server trusted names information + * @hide + */ + public @Nullable String[] getAaaServerTrustedNames() { + return mAaaServerTrustedNames; + } + + /** * Configurations under Credential subtree. */ private Credential mCredential = null; @@ -216,18 +248,22 @@ public final class PasspointConfiguration implements Parcelable { * * Use Long.MIN_VALUE to indicate unset value. */ - private long mSubscriptionExpirationTimeInMillis = Long.MIN_VALUE; + private long mSubscriptionExpirationTimeMillis = Long.MIN_VALUE; /** * @hide */ public void setSubscriptionExpirationTimeInMillis(long subscriptionExpirationTimeInMillis) { - mSubscriptionExpirationTimeInMillis = subscriptionExpirationTimeInMillis; + mSubscriptionExpirationTimeMillis = subscriptionExpirationTimeInMillis; } /** - * @hide + * Utility method to get the time this subscription will expire. It is in the format of number + * of milliseconds since January 1, 1970, 00:00:00 GMT. + * + * @return The time this subscription will expire, or Long.MIN_VALUE to indicate unset value */ - public long getSubscriptionExpirationTimeInMillis() { - return mSubscriptionExpirationTimeInMillis; + @CurrentTimeMillisLong + public long getSubscriptionExpirationTimeMillis() { + return mSubscriptionExpirationTimeMillis; } /** @@ -370,6 +406,131 @@ public final class PasspointConfiguration implements Parcelable { } /** + * The carrier ID identifies the operator who provides this network configuration. + * see {@link TelephonyManager#getSimCarrierId()} + */ + private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; + + /** + * Set the carrier ID associated with current configuration. + * @param carrierId {@code mCarrierId} + * @hide + */ + public void setCarrierId(int carrierId) { + this.mCarrierId = carrierId; + } + + /** + * Get the carrier ID associated with current configuration. + * @return {@code mCarrierId} + * @hide + */ + public int getCarrierId() { + return mCarrierId; + } + + /** + * The auto-join configuration specifies whether or not the Passpoint Configuration is + * considered for auto-connection. If true then yes, if false then it isn't considered as part + * of auto-connection - but can still be manually connected to. + */ + private boolean mIsAutojoinEnabled = true; + + /** + * The mac randomization setting specifies whether a randomized or device MAC address will + * be used to connect to the passpoint network. If true, a randomized MAC will be used. + * Otherwise, the device MAC address will be used. + */ + private boolean mIsMacRandomizationEnabled = true; + + /** + * Indicates if the end user has expressed an explicit opinion about the + * meteredness of this network, such as through the Settings app. + * This value is one of {@link #METERED_OVERRIDE_NONE}, {@link #METERED_OVERRIDE_METERED}, + * or {@link #METERED_OVERRIDE_NOT_METERED}. + * <p> + * This should always override any values from {@link WifiInfo#getMeteredHint()}. + * + * By default this field is set to {@link #METERED_OVERRIDE_NONE}. + */ + private int mMeteredOverride = METERED_OVERRIDE_NONE; + + /** + * Configures the auto-association status of this Passpoint configuration. A value of true + * indicates that the configuration will be considered for auto-connection, a value of false + * indicates that only manual connection will work - the framework will not auto-associate to + * this Passpoint network. + * + * @param autojoinEnabled true to be considered for framework auto-connection, false otherwise. + * @hide + */ + public void setAutojoinEnabled(boolean autojoinEnabled) { + mIsAutojoinEnabled = autojoinEnabled; + } + + /** + * Configures the MAC randomization setting for this Passpoint configuration. + * If set to true, the framework will use a randomized MAC address to connect to this Passpoint + * network. Otherwise, the framework will use the device MAC address. + * + * @param enabled true to use randomized MAC address, false to use device MAC address. + * @hide + */ + public void setMacRandomizationEnabled(boolean enabled) { + mIsMacRandomizationEnabled = enabled; + } + + /** + * Sets the metered override setting for this Passpoint configuration. + * + * @param meteredOverride One of the values in {@link MeteredOverride} + * @hide + */ + public void setMeteredOverride(@MeteredOverride int meteredOverride) { + mMeteredOverride = meteredOverride; + } + + /** + * Indicates whether the Passpoint configuration may be auto-connected to by the framework. A + * value of true indicates that auto-connection can happen, a value of false indicates that it + * cannot. However, even when auto-connection is not possible manual connection by the user is + * possible. + * + * @return the auto-join configuration: true for auto-connection (or join) enabled, false + * otherwise. + * @hide + */ + @SystemApi + public boolean isAutojoinEnabled() { + return mIsAutojoinEnabled; + } + + /** + * Indicates whether the user chose this configuration to be treated as metered or not. + * + * @return One of the values in {@link MeteredOverride} + * @hide + */ + @SystemApi + @MeteredOverride + public int getMeteredOverride() { + return mMeteredOverride; + } + + /** + * Indicates whether a randomized MAC address or device MAC address will be used for + * connections to this Passpoint network. If true, a randomized MAC address will be used. + * Otherwise, the device MAC address will be used. + * + * @return true for MAC randomization enabled. False for disabled. + * @hide + */ + @SystemApi + public boolean isMacRandomizationEnabled() { + return mIsMacRandomizationEnabled; + } + + /** * Constructor for creating PasspointConfiguration with default values. */ public PasspointConfiguration() {} @@ -402,13 +563,18 @@ public final class PasspointConfiguration implements Parcelable { mUpdateIdentifier = source.mUpdateIdentifier; mCredentialPriority = source.mCredentialPriority; mSubscriptionCreationTimeInMillis = source.mSubscriptionCreationTimeInMillis; - mSubscriptionExpirationTimeInMillis = source.mSubscriptionExpirationTimeInMillis; + mSubscriptionExpirationTimeMillis = source.mSubscriptionExpirationTimeMillis; mSubscriptionType = source.mSubscriptionType; mUsageLimitDataLimit = source.mUsageLimitDataLimit; mUsageLimitStartTimeInMillis = source.mUsageLimitStartTimeInMillis; mUsageLimitTimeLimitInMinutes = source.mUsageLimitTimeLimitInMinutes; mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes; mServiceFriendlyNames = source.mServiceFriendlyNames; + mAaaServerTrustedNames = source.mAaaServerTrustedNames; + mCarrierId = source.mCarrierId; + mIsAutojoinEnabled = source.mIsAutojoinEnabled; + mIsMacRandomizationEnabled = source.mIsMacRandomizationEnabled; + mMeteredOverride = source.mMeteredOverride; } @Override @@ -426,16 +592,21 @@ public final class PasspointConfiguration implements Parcelable { dest.writeInt(mUpdateIdentifier); dest.writeInt(mCredentialPriority); dest.writeLong(mSubscriptionCreationTimeInMillis); - dest.writeLong(mSubscriptionExpirationTimeInMillis); + dest.writeLong(mSubscriptionExpirationTimeMillis); dest.writeString(mSubscriptionType); dest.writeLong(mUsageLimitUsageTimePeriodInMinutes); dest.writeLong(mUsageLimitStartTimeInMillis); dest.writeLong(mUsageLimitDataLimit); dest.writeLong(mUsageLimitTimeLimitInMinutes); + dest.writeStringArray(mAaaServerTrustedNames); Bundle bundle = new Bundle(); bundle.putSerializable("serviceFriendlyNames", (HashMap<String, String>) mServiceFriendlyNames); dest.writeBundle(bundle); + dest.writeInt(mCarrierId); + dest.writeBoolean(mIsAutojoinEnabled); + dest.writeBoolean(mIsMacRandomizationEnabled); + dest.writeInt(mMeteredOverride); } @Override @@ -448,6 +619,8 @@ public final class PasspointConfiguration implements Parcelable { } PasspointConfiguration that = (PasspointConfiguration) thatObject; return (mHomeSp == null ? that.mHomeSp == null : mHomeSp.equals(that.mHomeSp)) + && (mAaaServerTrustedNames == null ? that.mAaaServerTrustedNames == null + : Arrays.equals(mAaaServerTrustedNames, that.mAaaServerTrustedNames)) && (mCredential == null ? that.mCredential == null : mCredential.equals(that.mCredential)) && (mPolicy == null ? that.mPolicy == null : mPolicy.equals(that.mPolicy)) @@ -457,12 +630,16 @@ public final class PasspointConfiguration implements Parcelable { && mUpdateIdentifier == that.mUpdateIdentifier && mCredentialPriority == that.mCredentialPriority && mSubscriptionCreationTimeInMillis == that.mSubscriptionCreationTimeInMillis - && mSubscriptionExpirationTimeInMillis == that.mSubscriptionExpirationTimeInMillis + && mSubscriptionExpirationTimeMillis == that.mSubscriptionExpirationTimeMillis && TextUtils.equals(mSubscriptionType, that.mSubscriptionType) && mUsageLimitUsageTimePeriodInMinutes == that.mUsageLimitUsageTimePeriodInMinutes && mUsageLimitStartTimeInMillis == that.mUsageLimitStartTimeInMillis && mUsageLimitDataLimit == that.mUsageLimitDataLimit && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes + && mCarrierId == that.mCarrierId + && mIsAutojoinEnabled == that.mIsAutojoinEnabled + && mIsMacRandomizationEnabled == that.mIsMacRandomizationEnabled + && mMeteredOverride == that.mMeteredOverride && (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null : mServiceFriendlyNames.equals(that.mServiceFriendlyNames)); } @@ -471,9 +648,10 @@ public final class PasspointConfiguration implements Parcelable { public int hashCode() { return Objects.hash(mHomeSp, mCredential, mPolicy, mSubscriptionUpdate, mTrustRootCertList, mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis, - mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes, + mSubscriptionExpirationTimeMillis, mUsageLimitUsageTimePeriodInMinutes, mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes, - mServiceFriendlyNames); + mServiceFriendlyNames, mCarrierId, mIsAutojoinEnabled, mIsMacRandomizationEnabled, + mMeteredOverride); } @Override @@ -485,14 +663,16 @@ public final class PasspointConfiguration implements Parcelable { mSubscriptionCreationTimeInMillis != Long.MIN_VALUE ? new Date(mSubscriptionCreationTimeInMillis) : "Not specified").append("\n"); builder.append("SubscriptionExpirationTime: ").append( - mSubscriptionExpirationTimeInMillis != Long.MIN_VALUE - ? new Date(mSubscriptionExpirationTimeInMillis) : "Not specified").append("\n"); + mSubscriptionExpirationTimeMillis != Long.MIN_VALUE + ? new Date(mSubscriptionExpirationTimeMillis) : "Not specified").append("\n"); builder.append("UsageLimitStartTime: ").append(mUsageLimitStartTimeInMillis != Long.MIN_VALUE ? new Date(mUsageLimitStartTimeInMillis) : "Not specified").append("\n"); builder.append("UsageTimePeriod: ").append(mUsageLimitUsageTimePeriodInMinutes) .append("\n"); builder.append("UsageLimitDataLimit: ").append(mUsageLimitDataLimit).append("\n"); builder.append("UsageLimitTimeLimit: ").append(mUsageLimitTimeLimitInMinutes).append("\n"); + builder.append("Provisioned by a subscription server: ") + .append(isOsuProvisioned() ? "Yes" : "No").append("\n"); if (mHomeSp != null) { builder.append("HomeSP Begin ---\n"); builder.append(mHomeSp); @@ -517,9 +697,17 @@ public final class PasspointConfiguration implements Parcelable { builder.append("TrustRootCertServers: ").append(mTrustRootCertList.keySet()) .append("\n"); } + if (mAaaServerTrustedNames != null) { + builder.append("AAAServerTrustedNames: ") + .append(String.join(";", mAaaServerTrustedNames)).append("\n"); + } if (mServiceFriendlyNames != null) { builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames); } + builder.append("CarrierId:" + mCarrierId); + builder.append("IsAutojoinEnabled:" + mIsAutojoinEnabled); + builder.append("mIsMacRandomizationEnabled:" + mIsMacRandomizationEnabled); + builder.append("mMeteredOverride:" + mMeteredOverride); return builder.toString(); } @@ -534,7 +722,7 @@ public final class PasspointConfiguration implements Parcelable { if (mSubscriptionUpdate != null && !mSubscriptionUpdate.validate()) { return false; } - return validateForCommonR1andR2(true); + return validateForCommonR1andR2(); } /** @@ -553,17 +741,17 @@ public final class PasspointConfiguration implements Parcelable { if (mSubscriptionUpdate == null || !mSubscriptionUpdate.validate()) { return false; } - return validateForCommonR1andR2(false); + return validateForCommonR1andR2(); } - private boolean validateForCommonR1andR2(boolean isR1) { + private boolean validateForCommonR1andR2() { // Required: PerProviderSubscription/<X+>/HomeSP if (mHomeSp == null || !mHomeSp.validate()) { return false; } // Required: PerProviderSubscription/<X+>/Credential - if (mCredential == null || !mCredential.validate(isR1)) { + if (mCredential == null || !mCredential.validate()) { return false; } @@ -619,10 +807,15 @@ public final class PasspointConfiguration implements Parcelable { config.setUsageLimitStartTimeInMillis(in.readLong()); config.setUsageLimitDataLimit(in.readLong()); config.setUsageLimitTimeLimitInMinutes(in.readLong()); + config.setAaaServerTrustedNames(in.createStringArray()); Bundle bundle = in.readBundle(); Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable( "serviceFriendlyNames"); config.setServiceFriendlyNames(friendlyNamesMap); + config.mCarrierId = in.readInt(); + config.mIsAutojoinEnabled = in.readBoolean(); + config.mIsMacRandomizationEnabled = in.readBoolean(); + config.mMeteredOverride = in.readInt(); return config; } @@ -695,4 +888,34 @@ public final class PasspointConfiguration implements Parcelable { } return true; } + + /** + * Indicates if the Passpoint Configuration was provisioned by a subscription (OSU) server, + * which means that it's an R2 (or R3) profile. + * + * @return true if the Passpoint Configuration was provisioned by a subscription server. + */ + public boolean isOsuProvisioned() { + return getUpdateIdentifier() != Integer.MIN_VALUE; + } + + /** + * Get a unique identifier for a PasspointConfiguration object. The identifier depends on the + * configuration that identify the service provider under the HomeSp subtree, and on the + * credential configuration under the Credential subtree. + * The method throws an {@link IllegalStateException} if the configuration under HomeSp subtree + * or the configuration under Credential subtree are not initialized. + * + * @return A unique identifier + */ + public @NonNull String getUniqueId() { + if (mCredential == null || mHomeSp == null || TextUtils.isEmpty(mHomeSp.getFqdn())) { + throw new IllegalStateException("Credential or HomeSP are not initialized"); + } + + StringBuilder sb = new StringBuilder(); + sb.append(String.format("%s_%x%x", mHomeSp.getFqdn(), mHomeSp.getUniqueId(), + mCredential.getUniqueId())); + return sb.toString(); + } } diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java index 984cf7d62aa5..ae60ed47fd6a 100644 --- a/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java +++ b/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java @@ -25,6 +25,8 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import org.xml.sax.SAXException; + import java.io.IOException; import java.text.DateFormat; import java.text.ParseException; @@ -36,8 +38,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.xml.sax.SAXException; - /** * Utility class for converting OMA-DM (Open Mobile Alliance's Device Management) * PPS-MO (PerProviderSubscription Management Object) XML tree to a @@ -147,6 +147,47 @@ public final class PpsMoParser { private static final String NODE_EXTENSION = "Extension"; /** + * Fields under Extension/Android subtree. + */ + /* + * This node is used to put Android specific extension nodes and must be put + * under "Extension" node. Nodes with unknown names are allowed under this subtree. + * If there is any new node added in later release, it won't break older release parsing. + * <p> + * Ex: + * <Node> + * <NodeName>Extension</NodeName> + * <Node> + * <NodeName>Android</NodeName> + * <Node> + * <NodeName>AndroidSpecificAttribute</NodeName> + * <Value>AndroidSpecificValue</Value> + * </Node> + * <Node> + * <NodeName>AndroidSpecificAttribute2</NodeName> + * <Value>AndroidSpecificValue2</Value> + * </Node> + * </Node> + * </Node> + */ + private static final String NODE_VENDOR_ANDROID = "Android"; + /* + * This node describes AAA server trusted names. The trusted name must be put in + * a leaf named "FQDN". More than one trusted names can be provided by using + * semicolons to separate the strings (e.g., example.org;example.com). + * <p> + * Ex: + * <Node> + * <NodeName>AAAServerTrustedNames</NodeName> + * <Node> + * <NodeName>FQDN</NodeName> + * <Value>trusted.com;auth.net</Value> + * </Node> + * <Node> + */ + private static final String NODE_AAA_SERVER_TRUSTED_NAMES = "AAAServerTrustedNames"; + + /** * Fields under HomeSP subtree. */ private static final String NODE_HOMESP = "HomeSP"; @@ -633,7 +674,7 @@ public final class PpsMoParser { break; case NODE_EXTENSION: // All vendor specific information will be under this node. - Log.d(TAG, "Ignore Extension node for vendor specific information"); + parseExtension(child, config); break; default: throw new ParsingException("Unknown node: " + child.getName()); @@ -896,7 +937,7 @@ public final class PpsMoParser { */ private static Credential parseCredential(PPSNode node) throws ParsingException { if (node.isLeaf()) { - throw new ParsingException("Leaf node not expected for HomeSP"); + throw new ParsingException("Leaf node not expected for Credential"); } Credential credential = new Credential(); @@ -968,8 +1009,8 @@ public final class PpsMoParser { parseEAPMethod(child, userCred); break; default: - throw new ParsingException("Unknown node under UsernamPassword: " + - child.getName()); + throw new ParsingException("Unknown node under UsernamePassword: " + + child.getName()); } } return userCred; @@ -1024,7 +1065,7 @@ public final class PpsMoParser { private static Credential.CertificateCredential parseCertificateCredential(PPSNode node) throws ParsingException { if (node.isLeaf()) { - throw new ParsingException("Leaf node not expected for DigitalCertificate"); + throw new ParsingException("Leaf node not expected for CertificateCredential"); } Credential.CertificateCredential certCred = new Credential.CertificateCredential(); @@ -1037,8 +1078,8 @@ public final class PpsMoParser { certCred.setCertSha256Fingerprint(parseHexString(getPpsNodeValue(child))); break; default: - throw new ParsingException("Unknown node under DigitalCertificate: " + - child.getName()); + throw new ParsingException("Unknown node under CertificateCredential: " + + child.getName()); } } return certCred; @@ -1055,7 +1096,7 @@ public final class PpsMoParser { private static Credential.SimCredential parseSimCredential(PPSNode node) throws ParsingException { if (node.isLeaf()) { - throw new ParsingException("Leaf node not expected for SIM"); + throw new ParsingException("Leaf node not expected for SimCredential"); } Credential.SimCredential simCred = new Credential.SimCredential(); @@ -1068,7 +1109,8 @@ public final class PpsMoParser { simCred.setEapType(parseInteger(getPpsNodeValue(child))); break; default: - throw new ParsingException("Unknown node under SIM: " + child.getName()); + throw new ParsingException("Unknown node under SimCredential: " + + child.getName()); } } return simCred; @@ -1572,6 +1614,92 @@ public final class PpsMoParser { } /** + * Parse configurations under PerProviderSubscription/Extension/Android/AAAServerTrustedNames + * subtree. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Extension/Android/AAAServerTrustedNames subtree + * @return String[] list of trusted name + * @throws ParsingException + */ + private static String[] parseAaaServerTrustedNames(PPSNode node) throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for AAAServerTrustedNames instance"); + } + String fqdnListStr = null; + String[] fqdnListArray = null; + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_FQDN: + fqdnListStr = getPpsNodeValue(child); + fqdnListArray = fqdnListStr.split(";"); + break; + default: + throw new ParsingException( + "Unknown node under AAAServerTrustedNames instance: " + + child.getName()); + } + } + if (fqdnListArray == null) { + throw new ParsingException("AAAServerTrustedNames instance missing FQDN field"); + } + + return fqdnListArray; + } + + /** + * Parse configurations under PerProviderSubscription/Extension/Android subtree. + * + * @param node PPSNode representing the root of PerProviderSubscription/Extension + * subtree + * @param config Instance of {@link PasspointConfiguration} + * @throws ParsingException + */ + private static void parseVendorAndroidExtension(PPSNode node, PasspointConfiguration config) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for AndroidExtension"); + } + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_AAA_SERVER_TRUSTED_NAMES: + config.setAaaServerTrustedNames(parseAaaServerTrustedNames(child)); + break; + default: + // Don't raise an exception for unknown nodes to avoid breaking old release + Log.w(TAG, "Unknown node under Android Extension: " + child.getName()); + } + } + } + + /** + * Parse configurations under PerProviderSubscription/Extension subtree. + * + * @param node PPSNode representing the root of PerProviderSubscription/Extension + * subtree + * @param config Instance of {@link PasspointConfiguration} + * @throws ParsingException + */ + private static void parseExtension(PPSNode node, PasspointConfiguration config) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for Extension"); + } + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_VENDOR_ANDROID: + parseVendorAndroidExtension(child, config); + break; + default: + // Unknown nodes under Extension won't raise exception. + // This allows adding new nodes in the future and + // won't break older release. + Log.w(TAG, "Unknown node under Extension: " + child.getName()); + } + } + } + + /** * Convert a hex string to a byte array. * * @param str String containing hex values @@ -1580,7 +1708,8 @@ public final class PpsMoParser { */ private static byte[] parseHexString(String str) throws ParsingException { if ((str.length() & 1) == 1) { - throw new ParsingException("Odd length hex string: " + str.length()); + throw new ParsingException("Odd length hex string: " + str + ", length: " + + str.length()); } byte[] result = new byte[str.length() / 2]; diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java index 9409c03c614d..fa806e7797cd 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java @@ -738,7 +738,16 @@ public final class Credential implements Parcelable { @Override public String toString() { StringBuilder builder = new StringBuilder(); - builder.append("IMSI: ").append(mImsi).append("\n"); + String imsi; + if (mImsi != null) { + if (mImsi.length() > 6 && mImsi.charAt(6) != '*') { + // Truncate the full IMSI from the log + imsi = mImsi.substring(0, 6) + "****"; + } else { + imsi = mImsi; + } + builder.append("IMSI: ").append(imsi).append("\n"); + } builder.append("EAPType: ").append(mEapType).append("\n"); return builder.toString(); } @@ -1020,6 +1029,17 @@ public final class Credential implements Parcelable { Arrays.hashCode(mClientCertificateChain)); } + /** + * Get a unique identifier for Credential. This identifier depends only on items that remain + * constant throughout the lifetime of a subscription's credentials. + * + * @hide + * @return a Unique identifier for a Credential object + */ + public int getUniqueId() { + return Objects.hash(mUserCredential, mCertCredential, mSimCredential, mRealm); + } + @Override public String toString() { StringBuilder builder = new StringBuilder(); @@ -1050,11 +1070,10 @@ public final class Credential implements Parcelable { /** * Validate the configuration data. * - * @param isR1 {@code true} if the configuration is for R1 * @return true on success or false on failure * @hide */ - public boolean validate(boolean isR1) { + public boolean validate() { if (TextUtils.isEmpty(mRealm)) { Log.d(TAG, "Missing realm"); return false; @@ -1067,11 +1086,11 @@ public final class Credential implements Parcelable { // Verify the credential. if (mUserCredential != null) { - if (!verifyUserCredential(isR1)) { + if (!verifyUserCredential()) { return false; } } else if (mCertCredential != null) { - if (!verifyCertCredential(isR1)) { + if (!verifyCertCredential()) { return false; } } else if (mSimCredential != null) { @@ -1112,11 +1131,11 @@ public final class Credential implements Parcelable { /** * Verify user credential. + * If no CA certificate is provided, then the system uses the CAs in the trust store. * - * @param isR1 {@code true} if credential is for R1 * @return true if user credential is valid, false otherwise. */ - private boolean verifyUserCredential(boolean isR1) { + private boolean verifyUserCredential() { if (mUserCredential == null) { Log.d(TAG, "Missing user credential"); return false; @@ -1129,23 +1148,17 @@ public final class Credential implements Parcelable { return false; } - // CA certificate is required for R1 Passpoint profile. - // For R2, it is downloaded using cert URL provided in PPS MO after validation completes. - if (isR1 && mCaCertificates == null) { - Log.d(TAG, "Missing CA Certificate for user credential"); - return false; - } return true; } /** * Verify certificate credential, which is used for EAP-TLS. This will verify * that the necessary client key and certificates are provided. + * If no CA certificate is provided, then the system uses the CAs in the trust store. * - * @param isR1 {@code true} if credential is for R1 * @return true if certificate credential is valid, false otherwise. */ - private boolean verifyCertCredential(boolean isR1) { + private boolean verifyCertCredential() { if (mCertCredential == null) { Log.d(TAG, "Missing certificate credential"); return false; @@ -1159,13 +1172,6 @@ public final class Credential implements Parcelable { return false; } - // Verify required key and certificates for certificate credential. - // CA certificate is required for R1 Passpoint profile. - // For R2, it is downloaded using cert URL provided in PPS MO after validation completes. - if (isR1 && mCaCertificates == null) { - Log.d(TAG, "Missing CA Certificate for certificate credential"); - return false; - } if (mClientPrivateKey == null) { Log.d(TAG, "Missing client private key for certificate credential"); return false; diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java index 49a76c33d209..224c4bed9d5b 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java @@ -16,8 +16,8 @@ package android.net.wifi.hotspot2.pps; -import android.os.Parcelable; import android.os.Parcel; +import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; @@ -299,10 +299,26 @@ public final class HomeSp implements Parcelable { @Override public int hashCode() { - return Objects.hash(mFqdn, mFriendlyName, mIconUrl, mHomeNetworkIds, mMatchAllOis, - mMatchAnyOis, mOtherHomePartners, mRoamingConsortiumOis); + return Objects.hash(mFqdn, mFriendlyName, mIconUrl, + mHomeNetworkIds, Arrays.hashCode(mMatchAllOis), + Arrays.hashCode(mMatchAnyOis), Arrays.hashCode(mOtherHomePartners), + Arrays.hashCode(mRoamingConsortiumOis)); } + /** + * Get a unique identifier for HomeSp. This identifier depends only on items that remain + * constant throughout the lifetime of a subscription. + * + * @hide + * @return a Unique identifier for a HomeSp object + */ + public int getUniqueId() { + return Objects.hash(mFqdn, mFriendlyName, mHomeNetworkIds, Arrays.hashCode(mMatchAllOis), + Arrays.hashCode(mMatchAnyOis), Arrays.hashCode(mOtherHomePartners), + Arrays.hashCode(mRoamingConsortiumOis)); + } + + @Override public String toString() { StringBuilder builder = new StringBuilder(); diff --git a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl deleted file mode 100644 index 701db479076a..000000000000 --- a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2017, 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.hotspot2.pps; - -parcelable UpdateParameter; diff --git a/wifi/java/android/net/wifi/migration_samples/README.txt b/wifi/java/android/net/wifi/migration_samples/README.txt new file mode 100644 index 000000000000..264debaa51f9 --- /dev/null +++ b/wifi/java/android/net/wifi/migration_samples/README.txt @@ -0,0 +1,35 @@ +This folder contains sample files for each of the 4 XML Wi-Fi config store files in Android 11 AOSP. +OEMs can use these files as reference for converting their previous customized +formats into the AOSP format. The conversion logic needs to be written in +WifiMigration.java class, i.e each OEM needs to modify +WifiMigration.convertAndRetrieveSharedConfigStoreFile() and the +WifiMigration.convertAndRetrieveUserConfigStoreFile() methods. + +The 4 files are: + +Shared files +============ +1) WifiConfigStore.xml - General storage for shared configurations. Includes +user's saved Wi-Fi networks. +AOSP Path in Android 10: /data/misc/wifi/WifiConfigStore.xml +AOSP Path in Android 11: /data/misc/apexdata/com.android/wifi/WifiConfigStore.xml +Sample File (in this folder): Shared_WifiConfigStore.xml + +2) WifiConfigStoreSoftAp.xml - Storage for user's softap/tethering configuration. +AOSP Path in Android 10: /data/misc/wifi/softap.conf. +Note: Was key/value format in Android 10. Conversion to XML done in SoftApConfToXmlMigrationUtil.java. +AOSP Path in Android 11: /data/misc/apexdata/com.android/wifi/WifiConfigStore.xml +Sample File (in this folder): Shared_WifiConfigStoreSoftAp.xml + +User specific files +================== +3) WifiConfigStore.xml - General storage for user specific configurations. Includes +user's saved passpoint networks, Wi-Fi network request approvals, etc. +AOSP Path in Android 10: /data/misc_ce/<userId>/wifi/WifiConfigStore.xml +AOSP Path in Android 11: /data/misc_ce/<userId>/apexdata/com.android/wifi/WifiConfigStore.xml +Sample File (in this folder): User_WifiConfigStore.xml + +4) WifiConfigStoreNetworkSuggestions.xml - Storage for app installed network suggestions. +AOSP Path in Android 10: /data/misc_ce/<userId>/wifi/WifiConfigStoreNetworkSuggestions.xml +AOSP Path in Android 11: /data/misc_ce/<userId>/apexdata/com.android/wifi/WifiConfigStoreNetworkSuggestions.xml +Sample File (in this folder): User_WifiConfigStoreNetworkSuggestions.xml diff --git a/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStore.xml b/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStore.xml new file mode 100644 index 000000000000..3063276fae6a --- /dev/null +++ b/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStore.xml @@ -0,0 +1,200 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<WifiConfigStoreData> +<int name="Version" value="3" /> +<NetworkList> +<Network> +<WifiConfiguration> +<string name="ConfigKey">"OPEN_SSID"NONE</string> +<string name="SSID">"OPEN_SSID"</string> +<null name="PreSharedKey" /> +<null name="WEPKeys" /> +<int name="WEPTxKeyIndex" value="0" /> +<boolean name="HiddenSSID" value="false" /> +<boolean name="RequirePMF" value="false" /> +<byte-array name="AllowedKeyMgmt" num="1">01</byte-array> +<byte-array name="AllowedProtocols" num="1">03</byte-array> +<byte-array name="AllowedAuthAlgos" num="0"></byte-array> +<byte-array name="AllowedGroupCiphers" num="1">2f</byte-array> +<byte-array name="AllowedPairwiseCiphers" num="1">0e</byte-array> +<byte-array name="AllowedGroupMgmtCiphers" num="1">04</byte-array> +<byte-array name="AllowedSuiteBCiphers" num="0"></byte-array> +<boolean name="Shared" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="Trusted" value="true" /> +<null name="BSSID" /> +<int name="Status" value="2" /> +<null name="FQDN" /> +<null name="ProviderFriendlyName" /> +<null name="LinkedNetworksList" /> +<null name="DefaultGwMacAddress" /> +<boolean name="ValidatedInternetAccess" value="true" /> +<boolean name="NoInternetAccessExpected" value="false" /> +<boolean name="MeteredHint" value="false" /> +<int name="MeteredOverride" value="0" /> +<boolean name="UseExternalScores" value="false" /> +<int name="NumAssociation" value="3" /> +<int name="CreatorUid" value="1000" /> +<string name="CreatorName">android.uid.system:1000</string> +<int name="LastUpdateUid" value="1000" /> +<string name="LastUpdateName">android.uid.system:1000</string> +<int name="LastConnectUid" value="1000" /> +<boolean name="IsLegacyPasspointConfig" value="false" /> +<long-array name="RoamingConsortiumOIs" num="0" /> +<string name="RandomizedMacAddress">ce:b1:36:bb:71:ac</string> +<int name="MacRandomizationSetting" value="1" /> +<int name="CarrierId" value="-1" /> +</WifiConfiguration> +<NetworkStatus> +<string name="SelectionStatus">NETWORK_SELECTION_ENABLED</string> +<string name="DisableReason">NETWORK_SELECTION_ENABLE</string> +<string name="ConnectChoice">"ENTERPRISE_SSID"WPA_EAP</string> +<boolean name="HasEverConnected" value="true" /> +</NetworkStatus> +<IpConfiguration> +<string name="IpAssignment">DHCP</string> +<string name="ProxySettings">NONE</string> +</IpConfiguration> +</Network> +<Network> +<WifiConfiguration> +<string name="ConfigKey">"ENTERPRISE_SSID"WPA_EAP</string> +<string name="SSID">"ENTERPRISE_SSID"</string> +<null name="PreSharedKey" /> +<null name="WEPKeys" /> +<int name="WEPTxKeyIndex" value="0" /> +<boolean name="HiddenSSID" value="false" /> +<boolean name="RequirePMF" value="false" /> +<byte-array name="AllowedKeyMgmt" num="1">0c</byte-array> +<byte-array name="AllowedProtocols" num="1">03</byte-array> +<byte-array name="AllowedAuthAlgos" num="0"></byte-array> +<byte-array name="AllowedGroupCiphers" num="1">2f</byte-array> +<byte-array name="AllowedPairwiseCiphers" num="1">0e</byte-array> +<byte-array name="AllowedGroupMgmtCiphers" num="1">04</byte-array> +<byte-array name="AllowedSuiteBCiphers" num="0"></byte-array> +<boolean name="Shared" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="Trusted" value="true" /> +<null name="BSSID" /> +<int name="Status" value="2" /> +<null name="FQDN" /> +<null name="ProviderFriendlyName" /> +<null name="LinkedNetworksList" /> +<null name="DefaultGwMacAddress" /> +<boolean name="ValidatedInternetAccess" value="false" /> +<boolean name="NoInternetAccessExpected" value="false" /> +<boolean name="MeteredHint" value="false" /> +<int name="MeteredOverride" value="0" /> +<boolean name="UseExternalScores" value="false" /> +<int name="NumAssociation" value="0" /> +<int name="CreatorUid" value="1000" /> +<string name="CreatorName">android.uid.system:1000</string> +<int name="LastUpdateUid" value="1000" /> +<string name="LastUpdateName">android.uid.system:1000</string> +<int name="LastConnectUid" value="1000" /> +<boolean name="IsLegacyPasspointConfig" value="false" /> +<long-array name="RoamingConsortiumOIs" num="0" /> +<string name="RandomizedMacAddress">f6:b3:94:44:40:87</string> +<int name="MacRandomizationSetting" value="1" /> +<int name="CarrierId" value="-1" /> +</WifiConfiguration> +<NetworkStatus> +<string name="SelectionStatus">NETWORK_SELECTION_TEMPORARY_DISABLED</string> +<string name="DisableReason">NETWORK_SELECTION_DISABLED_AUTHENTICATION_FAILURE</string> +<null name="ConnectChoice" /> +<boolean name="HasEverConnected" value="false" /> +</NetworkStatus> +<IpConfiguration> +<string name="IpAssignment">DHCP</string> +<string name="ProxySettings">NONE</string> +</IpConfiguration> +<WifiEnterpriseConfiguration> +<string name="Identity">adadadasdaddsa</string> +<string name="AnonIdentity">asdadaddadasd</string> +<string name="Password">adasdadadad</string> +<string name="ClientCert"></string> +<string name="CaCert"></string> +<string name="SubjectMatch"></string> +<string name="Engine">0</string> +<string name="EngineId"></string> +<string name="PrivateKeyId"></string> +<string name="AltSubjectMatch"></string> +<string name="DomSuffixMatch">adsad</string> +<string name="CaPath">/system/etc/security/cacerts</string> +<int name="EapMethod" value="0" /> +<int name="Phase2Method" value="3" /> +<string name="PLMN"></string> +<string name="Realm"></string> +<int name="Ocsp" value="0" /> +<string name="WapiCertSuite"></string> +</WifiEnterpriseConfiguration> +</Network> +<Network> +<WifiConfiguration> +<string name="ConfigKey">"WPA3_SSID"SAE</string> +<string name="SSID">"WPA3_SSID"</string> +<string name="PreSharedKey">"sfsdfsfdsfsdf"</string> +<null name="WEPKeys" /> +<int name="WEPTxKeyIndex" value="0" /> +<boolean name="HiddenSSID" value="false" /> +<boolean name="RequirePMF" value="true" /> +<byte-array name="AllowedKeyMgmt" num="2">0001</byte-array> +<byte-array name="AllowedProtocols" num="1">02</byte-array> +<byte-array name="AllowedAuthAlgos" num="0"></byte-array> +<byte-array name="AllowedGroupCiphers" num="1">28</byte-array> +<byte-array name="AllowedPairwiseCiphers" num="1">0c</byte-array> +<byte-array name="AllowedGroupMgmtCiphers" num="1">04</byte-array> +<byte-array name="AllowedSuiteBCiphers" num="0"></byte-array> +<boolean name="Shared" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="Trusted" value="true" /> +<null name="BSSID" /> +<int name="Status" value="1" /> +<null name="FQDN" /> +<null name="ProviderFriendlyName" /> +<null name="LinkedNetworksList" /> +<null name="DefaultGwMacAddress" /> +<boolean name="ValidatedInternetAccess" value="false" /> +<boolean name="NoInternetAccessExpected" value="false" /> +<boolean name="MeteredHint" value="false" /> +<int name="MeteredOverride" value="0" /> +<boolean name="UseExternalScores" value="false" /> +<int name="NumAssociation" value="0" /> +<int name="CreatorUid" value="1000" /> +<string name="CreatorName">android.uid.system:1000</string> +<int name="LastUpdateUid" value="1000" /> +<string name="LastUpdateName">android.uid.system:1000</string> +<int name="LastConnectUid" value="1000" /> +<boolean name="IsLegacyPasspointConfig" value="false" /> +<long-array name="RoamingConsortiumOIs" num="0" /> +<string name="RandomizedMacAddress">a6:3d:b0:13:ed:41</string> +<int name="MacRandomizationSetting" value="1" /> +<int name="CarrierId" value="-1" /> +</WifiConfiguration> +<NetworkStatus> +<string name="SelectionStatus">NETWORK_SELECTION_PERMANENTLY_DISABLED</string> +<string name="DisableReason">NETWORK_SELECTION_DISABLED_BY_WRONG_PASSWORD</string> +<null name="ConnectChoice" /> +<boolean name="HasEverConnected" value="false" /> +</NetworkStatus> +<IpConfiguration> +<string name="IpAssignment">DHCP</string> +<string name="ProxySettings">NONE</string> +</IpConfiguration> +</Network> +</NetworkList> +<MacAddressMap> +<map name="MacMapEntry" /> +</MacAddressMap> +<Settings> +<map name="Values"> +<boolean name="wifi_p2p_pending_factory_reset" value="false" /> +<boolean name="wifi_scan_throttle_enabled" value="true" /> +<null name="wifi_p2p_device_name" /> +<boolean name="wifi_scan_always_enabled" value="false" /> +<boolean name="wifi_verbose_logging_enabled" value="true" /> +</map> +</Settings> +<PasspointConfigData> +<long name="ProviderIndex" value="0" /> +</PasspointConfigData> +</WifiConfigStoreData> diff --git a/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml b/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml new file mode 100644 index 000000000000..fd99dd3df8b2 --- /dev/null +++ b/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml @@ -0,0 +1,22 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<WifiConfigStoreData> +<int name="Version" value="3" /> +<SoftAp> +<string name="SSID">HOTSPOT_SSID</string> +<int name="ApBand" value="1" /> +<int name="Channel" value="0" /> +<boolean name="HiddenSSID" value="false" /> +<int name="SecurityType" value="1" /> +<string name="Passphrase">blahblahblah</string> +<int name="MaxNumberOfClients" value="0" /> +<boolean name="ClientControlByUser" value="false" /> +<boolean name="AutoShutdownEnabled" value="true" /> +<long name="ShutdownTimeoutMillis" value="0" /> +<BlockedClientList> +<string name="ClientMacAddress">00:11:22:33:44:55</string> +</BlockedClientList> +<AllowedClientList> +<string name="ClientMacAddress">aa:bb:cc:dd:ee:ff</string> +</AllowedClientList> +</SoftAp> +</WifiConfigStoreData> diff --git a/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStore.xml b/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStore.xml new file mode 100644 index 000000000000..67d5aab215f2 --- /dev/null +++ b/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStore.xml @@ -0,0 +1,81 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<WifiConfigStoreData> +<int name="Version" value="3" /> +<NetworkList /> +<PasspointConfigData> +<ProviderList> +<Provider> +<long name="ProviderID" value="0" /> +<int name="CreatorUID" value="10085" /> +<string name="PackageName">com.android.certinstaller</string> +<list name="CaCertificateAliases"> +<string>HS2_0_0</string> +</list> +<null name="ClientPrivateKeyAlias" /> +<boolean name="HasEverConnected" value="false" /> +<boolean name="IsFromSuggestion" value="false" /> +<boolean name="IsTrusted" value="true" /> +<Configuration> +<int name="UpdateIdentifier" value="-2147483648" /> +<int name="CredentialPriority" value="-2147483648" /> +<null name="TrustRootCertList" /> +<long name="SubscriptionCreationTime" value="-9223372036854775808" /> +<long name="SubscriptionExpirationTime" value="-9223372036854775808" /> +<null name="SubscriptionType" /> +<long name="UsageLimitTimePeriod" value="-9223372036854775808" /> +<long name="UsageLimitStartTime" value="-9223372036854775808" /> +<long name="UsageLimitDataLimit" value="-9223372036854775808" /> +<long name="UsageLimitTimeLimit" value="-9223372036854775808" /> +<HomeSP> +<string name="FQDN">Passpoint.net</string> +<string name="FriendlyName">Passpoint Friendly Name</string> +<null name="IconURL" /> +<null name="HomeNetworkIDs" /> +<null name="MatchAllOIs" /> +<null name="MatchAnyOIs" /> +<null name="OtherHomePartners" /> +<null name="RoamingConsortiumOIs" /> +</HomeSP> +<Credential> +<long name="CreationTime" value="-9223372036854775808" /> +<long name="ExpirationTime" value="-9223372036854775808" /> +<string name="Realm">passpoint.com</string> +<boolean name="CheckAAAServerCertStatus" value="false" /> +<UserCredential> +<string name="Username">blahblahblah</string> +<string name="Password">doubleblahlah</string> +<boolean name="MachineManaged" value="true" /> +<null name="SoftTokenApp" /> +<boolean name="AbleToShare" value="false" /> +<int name="EAPType" value="21" /> +<string name="NonEAPInnerMethod">PAP</string> +</UserCredential> +</Credential> +<int name="CarrierId" value="-1" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="IsMacRandomizationEnabled" value="true" /> +<int name="MeteredOverride" value="0" /> +</Configuration> +<null name="RemediationCaCertificateAlias" /> +</Provider> +</ProviderList> +</PasspointConfigData> +<OpenNetworkNotifierBlacklistConfigData /> +<NetworkRequestMap> +<ApprovedAccessPointsPerApp> +<string name="RequestorPackageName">com.android.cts.verifier</string> +<AccessPoint> +<string name="SSID">OPEN_SSID</string> +<string name="BSSID">00:11:22:33:44:55</string> +<int name="NetworkType" value="0" /> +</AccessPoint> +</ApprovedAccessPointsPerApp> +</NetworkRequestMap> +<WakeupConfigStoreData> +<FeatureState> +<boolean name="IsActive" value="false" /> +<boolean name="IsOnboarded" value="false" /> +<int name="NotificationsShown" value="1" /> +</FeatureState> +</WakeupConfigStoreData> +</WifiConfigStoreData> diff --git a/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml b/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml new file mode 100644 index 000000000000..4ecdd29709b4 --- /dev/null +++ b/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml @@ -0,0 +1,155 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<WifiConfigStoreData> +<int name="Version" value="3" /> +<NetworkSuggestionMap> +<NetworkSuggestionPerApp> +<string name="SuggestorPackageName">com.android.cts.verifier</string> +<null name="SuggestorFeatureId" /> +<boolean name="SuggestorHasUserApproved" value="true" /> +<int name="SuggestorMaxSize" value="1" /> +<int name="SuggestorUid" value="10228" /> +<int name="SuggestorCarrierId" value="-1" /> +<NetworkSuggestion> +<WifiConfiguration> +<string name="ConfigKey">"OPEN_SSID"NONE</string> +<string name="SSID">"OPEN_SSID"</string> +<null name="PreSharedKey" /> +<null name="WEPKeys" /> +<int name="WEPTxKeyIndex" value="0" /> +<boolean name="HiddenSSID" value="false" /> +<boolean name="RequirePMF" value="false" /> +<byte-array name="AllowedKeyMgmt" num="1">01</byte-array> +<byte-array name="AllowedProtocols" num="0"></byte-array> +<byte-array name="AllowedAuthAlgos" num="0"></byte-array> +<byte-array name="AllowedGroupCiphers" num="0"></byte-array> +<byte-array name="AllowedPairwiseCiphers" num="0"></byte-array> +<byte-array name="AllowedGroupMgmtCiphers" num="0"></byte-array> +<byte-array name="AllowedSuiteBCiphers" num="0"></byte-array> +<boolean name="Shared" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="Trusted" value="true" /> +<null name="BSSID" /> +<int name="Status" value="0" /> +<null name="FQDN" /> +<null name="ProviderFriendlyName" /> +<null name="LinkedNetworksList" /> +<null name="DefaultGwMacAddress" /> +<boolean name="ValidatedInternetAccess" value="false" /> +<boolean name="NoInternetAccessExpected" value="false" /> +<boolean name="MeteredHint" value="false" /> +<int name="MeteredOverride" value="1" /> +<boolean name="UseExternalScores" value="false" /> +<int name="NumAssociation" value="0" /> +<int name="CreatorUid" value="10228" /> +<string name="CreatorName">com.android.cts.verifier</string> +<int name="LastUpdateUid" value="-1" /> +<null name="LastUpdateName" /> +<int name="LastConnectUid" value="0" /> +<boolean name="IsLegacyPasspointConfig" value="false" /> +<long-array name="RoamingConsortiumOIs" num="0" /> +<string name="RandomizedMacAddress">02:00:00:00:00:00</string> +<int name="MacRandomizationSetting" value="1" /> +<int name="CarrierId" value="-1" /> +</WifiConfiguration> +<boolean name="IsAppInteractionRequired" value="false" /> +<boolean name="IsUserInteractionRequired" value="false" /> +<boolean name="IsUserAllowedToManuallyConnect" value="false" /> +<boolean name="InitializedAutoJoinEnabled" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +</NetworkSuggestion> +<NetworkSuggestion> +<WifiConfiguration> +<string name="ConfigKey">passpoint.net</string> +<null name="SSID" /> +<null name="PreSharedKey" /> +<null name="WEPKeys" /> +<int name="WEPTxKeyIndex" value="0" /> +<boolean name="HiddenSSID" value="false" /> +<boolean name="RequirePMF" value="false" /> +<byte-array name="AllowedKeyMgmt" num="0"></byte-array> +<byte-array name="AllowedProtocols" num="0"></byte-array> +<byte-array name="AllowedAuthAlgos" num="0"></byte-array> +<byte-array name="AllowedGroupCiphers" num="0"></byte-array> +<byte-array name="AllowedPairwiseCiphers" num="0"></byte-array> +<byte-array name="AllowedGroupMgmtCiphers" num="0"></byte-array> +<byte-array name="AllowedSuiteBCiphers" num="0"></byte-array> +<boolean name="Shared" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="Trusted" value="true" /> +<null name="BSSID" /> +<int name="Status" value="0" /> +<string name="FQDN">passpoint.net</string> +<null name="ProviderFriendlyName" /> +<null name="LinkedNetworksList" /> +<null name="DefaultGwMacAddress" /> +<boolean name="ValidatedInternetAccess" value="false" /> +<boolean name="NoInternetAccessExpected" value="false" /> +<boolean name="MeteredHint" value="false" /> +<int name="MeteredOverride" value="0" /> +<boolean name="UseExternalScores" value="false" /> +<int name="NumAssociation" value="0" /> +<int name="CreatorUid" value="1000" /> +<string name="CreatorName">com.android.cts.verifier</string> +<int name="LastUpdateUid" value="-1" /> +<null name="LastUpdateName" /> +<int name="LastConnectUid" value="0" /> +<boolean name="IsLegacyPasspointConfig" value="false" /> +<long-array name="RoamingConsortiumOIs" num="0" /> +<string name="RandomizedMacAddress">02:00:00:00:00:00</string> +<int name="MacRandomizationSetting" value="1" /> +<int name="CarrierId" value="-1" /> +<boolean name="IsMostRecentlyConnected" value="false" /> +</WifiConfiguration> +<PasspointConfiguration> +<int name="UpdateIdentifier" value="-2147483648" /> +<int name="CredentialPriority" value="-2147483648" /> +<null name="TrustRootCertList" /> +<long name="SubscriptionCreationTime" value="-9223372036854775808" /> +<long name="SubscriptionExpirationTime" value="-9223372036854775808" /> +<null name="SubscriptionType" /> +<long name="UsageLimitTimePeriod" value="-9223372036854775808" /> +<long name="UsageLimitStartTime" value="-9223372036854775808" /> +<long name="UsageLimitDataLimit" value="-9223372036854775808" /> +<long name="UsageLimitTimeLimit" value="-9223372036854775808" /> +<HomeSP> +<string name="FQDN">passpoint.net</string> +<string name="FriendlyName">Passpoint Friendly Name</string> +<null name="IconURL" /> +<null name="HomeNetworkIDs" /> +<null name="MatchAllOIs" /> +<null name="MatchAnyOIs" /> +<null name="OtherHomePartners" /> +<null name="RoamingConsortiumOIs" /> +</HomeSP> +<Credential> +<long name="CreationTime" value="-9223372036854775808" /> +<long name="ExpirationTime" value="-9223372036854775808" /> +<string name="Realm">passpoint.com</string> +<boolean name="CheckAAAServerCertStatus" value="false" /> +<UserCredential> +<string name="Username">blahblahblah</string> +<string name="Password">doubleblahblah</string> +<boolean name="MachineManaged" value="false" /> +<null name="SoftTokenApp" /> +<boolean name="AbleToShare" value="false" /> +<int name="EAPType" value="21" /> +<string name="NonEAPInnerMethod">PAP</string> +</UserCredential> +</Credential> +<int name="CarrierId" value="-1" /> +<boolean name="AutoJoinEnabled" value="true" /> +<boolean name="IsMacRandomizationEnabled" value="true" /> +<int name="MeteredOverride" value="0" /> +</PasspointConfiguration> +<boolean name="IsAppInteractionRequired" value="false" /> +<boolean name="IsUserInteractionRequired" value="false" /> +<boolean name="IsUserAllowedToManuallyConnect" value="true" /> +<boolean name="InitializedAutoJoinEnabled" value="true" /> +<boolean name="AutoJoinEnabled" value="true" /> +</NetworkSuggestion> +</NetworkSuggestionPerApp> +</NetworkSuggestionMap> +<ImsiPrivacyProtectionExemptionMap> +<map name="CarrierExemptionMap" /> +</ImsiPrivacyProtectionExemptionMap> +</WifiConfigStoreData> diff --git a/wifi/java/android/net/wifi/nl80211/ChannelSettings.java b/wifi/java/android/net/wifi/nl80211/ChannelSettings.java new file mode 100644 index 000000000000..4c14fd499c28 --- /dev/null +++ b/wifi/java/android/net/wifi/nl80211/ChannelSettings.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016 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.nl80211; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.util.Objects; + +/** + * ChannelSettings for wificond + * + * @hide + */ +public class ChannelSettings implements Parcelable { + private static final String TAG = "ChannelSettings"; + + public int frequency; + + /** public constructor */ + public ChannelSettings() { } + + /** override comparator */ + @Override + public boolean equals(Object rhs) { + if (this == rhs) return true; + if (!(rhs instanceof ChannelSettings)) { + return false; + } + ChannelSettings channel = (ChannelSettings) rhs; + if (channel == null) { + return false; + } + return frequency == channel.frequency; + } + + /** override hash code */ + @Override + public int hashCode() { + return Objects.hash(frequency); + } + + /** implement Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** + * implement Parcelable interface + * |flags| is ignored. + **/ + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(frequency); + } + + /** implement Parcelable interface */ + public static final Parcelable.Creator<ChannelSettings> CREATOR = + new Parcelable.Creator<ChannelSettings>() { + /** + * Caller is responsible for providing a valid parcel. + */ + @Override + public ChannelSettings createFromParcel(Parcel in) { + ChannelSettings result = new ChannelSettings(); + result.frequency = in.readInt(); + if (in.dataAvail() != 0) { + Log.e(TAG, "Found trailing data after parcel parsing."); + } + + return result; + } + + @Override + public ChannelSettings[] newArray(int size) { + return new ChannelSettings[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java b/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java new file mode 100644 index 000000000000..bb0cc975a3db --- /dev/null +++ b/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.nl80211; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiAnnotations.ChannelWidth; +import android.net.wifi.WifiAnnotations.WifiStandard; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.util.Objects; + +/** + * DeviceWiphyCapabilities for wificond + * + * Contains the WiFi physical layer attributes and capabilities of the device. + * It is used to collect these attributes from the device driver via wificond. + * + * @hide + */ +@SystemApi +public final class DeviceWiphyCapabilities implements Parcelable { + private static final String TAG = "DeviceWiphyCapabilities"; + + private boolean m80211nSupported; + private boolean m80211acSupported; + private boolean m80211axSupported; + private boolean mChannelWidth160MhzSupported; + private boolean mChannelWidth80p80MhzSupported; + private int mMaxNumberTxSpatialStreams; + private int mMaxNumberRxSpatialStreams; + + + /** public constructor */ + public DeviceWiphyCapabilities() { + m80211nSupported = false; + m80211acSupported = false; + m80211axSupported = false; + mChannelWidth160MhzSupported = false; + mChannelWidth80p80MhzSupported = false; + mMaxNumberTxSpatialStreams = 1; + mMaxNumberRxSpatialStreams = 1; + } + + /** + * Get the IEEE 802.11 standard support + * + * @param standard the IEEE 802.11 standard to check on its support. + * valid values from {@link ScanResult}'s {@code WIFI_STANDARD_} + * @return {@code true} if supported, {@code false} otherwise. + */ + public boolean isWifiStandardSupported(@WifiStandard int standard) { + switch (standard) { + case ScanResult.WIFI_STANDARD_LEGACY: + return true; + case ScanResult.WIFI_STANDARD_11N: + return m80211nSupported; + case ScanResult.WIFI_STANDARD_11AC: + return m80211acSupported; + case ScanResult.WIFI_STANDARD_11AX: + return m80211axSupported; + default: + Log.e(TAG, "isWifiStandardSupported called with invalid standard: " + standard); + return false; + } + } + + /** + * Set the IEEE 802.11 standard support + * + * @param standard the IEEE 802.11 standard to set its support. + * valid values from {@link ScanResult}'s {@code WIFI_STANDARD_} + * @param support {@code true} if supported, {@code false} otherwise. + */ + public void setWifiStandardSupport(@WifiStandard int standard, boolean support) { + switch (standard) { + case ScanResult.WIFI_STANDARD_11N: + m80211nSupported = support; + break; + case ScanResult.WIFI_STANDARD_11AC: + m80211acSupported = support; + break; + case ScanResult.WIFI_STANDARD_11AX: + m80211axSupported = support; + break; + default: + Log.e(TAG, "setWifiStandardSupport called with invalid standard: " + standard); + } + } + + /** + * Get the support for channel bandwidth + * + * @param chWidth valid values from {@link ScanResult}'s {@code CHANNEL_WIDTH_} + * + * @return {@code true} if supported, {@code false} otherwise. + */ + public boolean isChannelWidthSupported(@ChannelWidth int chWidth) { + switch (chWidth) { + case ScanResult.CHANNEL_WIDTH_20MHZ: + return true; + case ScanResult.CHANNEL_WIDTH_40MHZ: + return (m80211nSupported || m80211acSupported || m80211axSupported); + case ScanResult.CHANNEL_WIDTH_80MHZ: + return (m80211acSupported || m80211axSupported); + case ScanResult.CHANNEL_WIDTH_160MHZ: + return mChannelWidth160MhzSupported; + case ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ: + return mChannelWidth80p80MhzSupported; + default: + Log.e(TAG, "isChannelWidthSupported called with invalid channel width: " + chWidth); + } + return false; + } + + /** + * Set support for channel bandwidth + * + * @param chWidth valid values are {@link ScanResult#CHANNEL_WIDTH_160MHZ} and + * {@link ScanResult#CHANNEL_WIDTH_80MHZ_PLUS_MHZ} + * @param support {@code true} if supported, {@code false} otherwise. + * + * @hide + */ + public void setChannelWidthSupported(@ChannelWidth int chWidth, boolean support) { + switch (chWidth) { + case ScanResult.CHANNEL_WIDTH_160MHZ: + mChannelWidth160MhzSupported = support; + break; + case ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ: + mChannelWidth80p80MhzSupported = support; + break; + default: + Log.e(TAG, "setChannelWidthSupported called with Invalid channel width: " + + chWidth); + } + } + + /** + * Get maximum number of transmit spatial streams + * + * @return number of spatial streams + */ + public int getMaxNumberTxSpatialStreams() { + return mMaxNumberTxSpatialStreams; + } + + /** + * Set maximum number of transmit spatial streams + * + * @param streams number of spatial streams + * + * @hide + */ + public void setMaxNumberTxSpatialStreams(int streams) { + mMaxNumberTxSpatialStreams = streams; + } + + /** + * Get maximum number of receive spatial streams + * + * @return number of streams + */ + public int getMaxNumberRxSpatialStreams() { + return mMaxNumberRxSpatialStreams; + } + + /** + * Set maximum number of receive spatial streams + * + * @param streams number of streams + * + * @hide + */ + public void setMaxNumberRxSpatialStreams(int streams) { + mMaxNumberRxSpatialStreams = streams; + } + + /** override comparator */ + @Override + public boolean equals(Object rhs) { + if (this == rhs) return true; + if (!(rhs instanceof DeviceWiphyCapabilities)) { + return false; + } + DeviceWiphyCapabilities capa = (DeviceWiphyCapabilities) rhs; + + return m80211nSupported == capa.m80211nSupported + && m80211acSupported == capa.m80211acSupported + && m80211axSupported == capa.m80211axSupported + && mChannelWidth160MhzSupported == capa.mChannelWidth160MhzSupported + && mChannelWidth80p80MhzSupported == capa.mChannelWidth80p80MhzSupported + && mMaxNumberTxSpatialStreams == capa.mMaxNumberTxSpatialStreams + && mMaxNumberRxSpatialStreams == capa.mMaxNumberRxSpatialStreams; + } + + /** override hash code */ + @Override + public int hashCode() { + return Objects.hash(m80211nSupported, m80211acSupported, m80211axSupported, + mChannelWidth160MhzSupported, mChannelWidth80p80MhzSupported, + mMaxNumberTxSpatialStreams, mMaxNumberRxSpatialStreams); + } + + /** implement Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** + * implement Parcelable interface + * |flags| is ignored. + */ + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeBoolean(m80211nSupported); + out.writeBoolean(m80211acSupported); + out.writeBoolean(m80211axSupported); + out.writeBoolean(mChannelWidth160MhzSupported); + out.writeBoolean(mChannelWidth80p80MhzSupported); + out.writeInt(mMaxNumberTxSpatialStreams); + out.writeInt(mMaxNumberRxSpatialStreams); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("m80211nSupported:").append(m80211nSupported ? "Yes" : "No"); + sb.append("m80211acSupported:").append(m80211acSupported ? "Yes" : "No"); + sb.append("m80211axSupported:").append(m80211axSupported ? "Yes" : "No"); + sb.append("mChannelWidth160MhzSupported: ") + .append(mChannelWidth160MhzSupported ? "Yes" : "No"); + sb.append("mChannelWidth80p80MhzSupported: ") + .append(mChannelWidth80p80MhzSupported ? "Yes" : "No"); + sb.append("mMaxNumberTxSpatialStreams: ").append(mMaxNumberTxSpatialStreams); + sb.append("mMaxNumberRxSpatialStreams: ").append(mMaxNumberRxSpatialStreams); + + return sb.toString(); + } + + /** implement Parcelable interface */ + public static final @NonNull Parcelable.Creator<DeviceWiphyCapabilities> CREATOR = + new Parcelable.Creator<DeviceWiphyCapabilities>() { + /** + * Caller is responsible for providing a valid parcel. + */ + @Override + public DeviceWiphyCapabilities createFromParcel(Parcel in) { + DeviceWiphyCapabilities capabilities = new DeviceWiphyCapabilities(); + capabilities.m80211nSupported = in.readBoolean(); + capabilities.m80211acSupported = in.readBoolean(); + capabilities.m80211axSupported = in.readBoolean(); + capabilities.mChannelWidth160MhzSupported = in.readBoolean(); + capabilities.mChannelWidth80p80MhzSupported = in.readBoolean(); + capabilities.mMaxNumberTxSpatialStreams = in.readInt(); + capabilities.mMaxNumberRxSpatialStreams = in.readInt(); + return capabilities; + } + + @Override + public DeviceWiphyCapabilities[] newArray(int size) { + return new DeviceWiphyCapabilities[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/nl80211/HiddenNetwork.java b/wifi/java/android/net/wifi/nl80211/HiddenNetwork.java new file mode 100644 index 000000000000..b1475b2c7b43 --- /dev/null +++ b/wifi/java/android/net/wifi/nl80211/HiddenNetwork.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 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.nl80211; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; + +/** + * HiddenNetwork for wificond + * + * @hide + */ +public class HiddenNetwork implements Parcelable { + private static final String TAG = "HiddenNetwork"; + + public byte[] ssid; + + /** public constructor */ + public HiddenNetwork() { } + + /** override comparator */ + @Override + public boolean equals(Object rhs) { + if (this == rhs) return true; + if (!(rhs instanceof HiddenNetwork)) { + return false; + } + HiddenNetwork network = (HiddenNetwork) rhs; + return Arrays.equals(ssid, network.ssid); + } + + /** override hash code */ + @Override + public int hashCode() { + return Arrays.hashCode(ssid); + } + + /** implement Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** + * implement Parcelable interface + * |flags| is ignored. + */ + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeByteArray(ssid); + } + + /** implement Parcelable interface */ + public static final Parcelable.Creator<HiddenNetwork> CREATOR = + new Parcelable.Creator<HiddenNetwork>() { + /** + * Caller is responsible for providing a valid parcel. + */ + @Override + public HiddenNetwork createFromParcel(Parcel in) { + HiddenNetwork result = new HiddenNetwork(); + result.ssid = in.createByteArray(); + return result; + } + + @Override + public HiddenNetwork[] newArray(int size) { + return new HiddenNetwork[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/nl80211/NativeScanResult.java b/wifi/java/android/net/wifi/nl80211/NativeScanResult.java new file mode 100644 index 000000000000..a8e999973fe8 --- /dev/null +++ b/wifi/java/android/net/wifi/nl80211/NativeScanResult.java @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2016 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.nl80211; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.net.MacAddress; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Raw scan result data from the wificond daemon. + * + * @hide + */ +@SystemApi +public final class NativeScanResult implements Parcelable { + private static final String TAG = "NativeScanResult"; + + /** @hide */ + @VisibleForTesting + public byte[] ssid; + /** @hide */ + @VisibleForTesting + public byte[] bssid; + /** @hide */ + @VisibleForTesting + public byte[] infoElement; + /** @hide */ + @VisibleForTesting + public int frequency; + /** @hide */ + @VisibleForTesting + public int signalMbm; + /** @hide */ + @VisibleForTesting + public long tsf; + /** @hide */ + @VisibleForTesting + @BssCapabilityBits public int capability; + /** @hide */ + @VisibleForTesting + public boolean associated; + /** @hide */ + @VisibleForTesting + public List<RadioChainInfo> radioChainInfos; + + /** + * Returns the SSID raw byte array of the AP represented by this scan result. + * + * @return A byte array. + */ + @NonNull public byte[] getSsid() { + return ssid; + } + + /** + * Returns the MAC address (BSSID) of the AP represented by this scan result. + * + * @return a MacAddress or null on error. + */ + @Nullable public MacAddress getBssid() { + try { + return MacAddress.fromBytes(bssid); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Illegal argument " + Arrays.toString(bssid), e); + return null; + } + } + + /** + * Returns the raw bytes of the information element advertised by the AP represented by this + * scan result. + * + * @return A byte array, possibly null or containing an invalid TLV configuration. + */ + @NonNull public byte[] getInformationElements() { + return infoElement; + } + + /** + * Returns the frequency (in MHz) on which the AP represented by this scan result was observed. + * + * @return The frequency in MHz. + */ + public int getFrequencyMhz() { + return frequency; + } + + /** + * Return the signal strength of probe response/beacon in (100 * dBm). + * + * @return Signal strenght in (100 * dBm). + */ + public int getSignalMbm() { + return signalMbm; + } + + /** + * Return the TSF (Timing Synchronization Function) of the received probe response/beacon. + * @return + */ + public long getTsf() { + return tsf; + } + + /** + * Return a boolean indicating whether or not we're associated to the AP represented by this + * scan result. + * + * @return A boolean indicating association. + */ + public boolean isAssociated() { + return associated; + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = {"BSS_CAPABILITY_"}, + value = {BSS_CAPABILITY_ESS, + BSS_CAPABILITY_IBSS, + BSS_CAPABILITY_CF_POLLABLE, + BSS_CAPABILITY_CF_POLL_REQUEST, + BSS_CAPABILITY_PRIVACY, + BSS_CAPABILITY_SHORT_PREAMBLE, + BSS_CAPABILITY_PBCC, + BSS_CAPABILITY_CHANNEL_AGILITY, + BSS_CAPABILITY_SPECTRUM_MANAGEMENT, + BSS_CAPABILITY_QOS, + BSS_CAPABILITY_SHORT_SLOT_TIME, + BSS_CAPABILITY_APSD, + BSS_CAPABILITY_RADIO_MANAGEMENT, + BSS_CAPABILITY_DSSS_OFDM, + BSS_CAPABILITY_DELAYED_BLOCK_ACK, + BSS_CAPABILITY_IMMEDIATE_BLOCK_ACK + }) + public @interface BssCapabilityBits { } + + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): ESS. + */ + public static final int BSS_CAPABILITY_ESS = 0x1 << 0; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): IBSS. + */ + public static final int BSS_CAPABILITY_IBSS = 0x1 << 1; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): CF Pollable. + */ + public static final int BSS_CAPABILITY_CF_POLLABLE = 0x1 << 2; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): CF-Poll Request. + */ + public static final int BSS_CAPABILITY_CF_POLL_REQUEST = 0x1 << 3; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Privacy. + */ + public static final int BSS_CAPABILITY_PRIVACY = 0x1 << 4; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Short Preamble. + */ + public static final int BSS_CAPABILITY_SHORT_PREAMBLE = 0x1 << 5; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): PBCC. + */ + public static final int BSS_CAPABILITY_PBCC = 0x1 << 6; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Channel Agility. + */ + public static final int BSS_CAPABILITY_CHANNEL_AGILITY = 0x1 << 7; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Spectrum Management. + */ + public static final int BSS_CAPABILITY_SPECTRUM_MANAGEMENT = 0x1 << 8; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): QoS. + */ + public static final int BSS_CAPABILITY_QOS = 0x1 << 9; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Short Slot Time. + */ + public static final int BSS_CAPABILITY_SHORT_SLOT_TIME = 0x1 << 10; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): APSD. + */ + public static final int BSS_CAPABILITY_APSD = 0x1 << 11; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Radio Management. + */ + public static final int BSS_CAPABILITY_RADIO_MANAGEMENT = 0x1 << 12; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): DSSS-OFDM. + */ + public static final int BSS_CAPABILITY_DSSS_OFDM = 0x1 << 13; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Delayed Block Ack. + */ + public static final int BSS_CAPABILITY_DELAYED_BLOCK_ACK = 0x1 << 14; + /** + * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Immediate Block Ack. + */ + public static final int BSS_CAPABILITY_IMMEDIATE_BLOCK_ACK = 0x1 << 15; + + /** + * Returns the capabilities of the AP repseresented by this scan result as advertised in the + * received probe response or beacon. + * + * This is a bit mask describing the capabilities of a BSS. See IEEE Std 802.11: 9.4.1.4: one + * of the {@code BSS_CAPABILITY_*} flags. + * + * @return a bit mask of capabilities. + */ + @BssCapabilityBits public int getCapabilities() { + return capability; + } + + /** + * Returns details of the signal received on each radio chain for the AP represented by this + * scan result in a list of {@link RadioChainInfo} elements. + * + * @return A list of {@link RadioChainInfo} - possibly empty in case of error. + */ + @NonNull public List<RadioChainInfo> getRadioChainInfos() { + return radioChainInfos; + } + + /** + * Construct an empty native scan result. + */ + public NativeScanResult() { } + + /** implement Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** implement Parcelable interface */ + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeByteArray(ssid); + out.writeByteArray(bssid); + out.writeByteArray(infoElement); + out.writeInt(frequency); + out.writeInt(signalMbm); + out.writeLong(tsf); + out.writeInt(capability); + out.writeInt(associated ? 1 : 0); + out.writeTypedList(radioChainInfos); + } + + /** implement Parcelable interface */ + @NonNull public static final Parcelable.Creator<NativeScanResult> CREATOR = + new Parcelable.Creator<NativeScanResult>() { + @Override + public NativeScanResult createFromParcel(Parcel in) { + NativeScanResult result = new NativeScanResult(); + result.ssid = in.createByteArray(); + if (result.ssid == null) { + result.ssid = new byte[0]; + } + result.bssid = in.createByteArray(); + if (result.bssid == null) { + result.bssid = new byte[0]; + } + result.infoElement = in.createByteArray(); + if (result.infoElement == null) { + result.infoElement = new byte[0]; + } + result.frequency = in.readInt(); + result.signalMbm = in.readInt(); + result.tsf = in.readLong(); + result.capability = in.readInt(); + result.associated = (in.readInt() != 0); + result.radioChainInfos = new ArrayList<>(); + in.readTypedList(result.radioChainInfos, RadioChainInfo.CREATOR); + return result; + } + + @Override + public NativeScanResult[] newArray(int size) { + return new NativeScanResult[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/nl80211/NativeWifiClient.java b/wifi/java/android/net/wifi/nl80211/NativeWifiClient.java new file mode 100644 index 000000000000..984d7d034302 --- /dev/null +++ b/wifi/java/android/net/wifi/nl80211/NativeWifiClient.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 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.nl80211; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.net.MacAddress; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Structure providing information about clients (STAs) associated with a SoftAp. + * + * @hide + */ +@SystemApi +public final class NativeWifiClient implements Parcelable { + private final MacAddress mMacAddress; + + /** + * The MAC address of the client (STA) represented by this object. The MAC address may be null + * in case of an error. + */ + @Nullable public MacAddress getMacAddress() { + return mMacAddress; + } + + /** + * Construct a native Wi-Fi client. + */ + public NativeWifiClient(@Nullable MacAddress macAddress) { + this.mMacAddress = macAddress; + } + + /** override comparator */ + @Override + public boolean equals(Object rhs) { + if (this == rhs) return true; + if (!(rhs instanceof NativeWifiClient)) { + return false; + } + NativeWifiClient other = (NativeWifiClient) rhs; + return Objects.equals(mMacAddress, other.mMacAddress); + } + + /** override hash code */ + @Override + public int hashCode() { + return mMacAddress.hashCode(); + } + + /** implement Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** + * implement Parcelable interface + * |flag| is ignored. + */ + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeByteArray(mMacAddress.toByteArray()); + } + + /** implement Parcelable interface */ + @NonNull public static final Parcelable.Creator<NativeWifiClient> CREATOR = + new Parcelable.Creator<NativeWifiClient>() { + @Override + public NativeWifiClient createFromParcel(Parcel in) { + MacAddress macAddress; + try { + macAddress = MacAddress.fromBytes(in.createByteArray()); + } catch (IllegalArgumentException e) { + macAddress = null; + } + return new NativeWifiClient(macAddress); + } + + @Override + public NativeWifiClient[] newArray(int size) { + return new NativeWifiClient[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/nl80211/PnoNetwork.java b/wifi/java/android/net/wifi/nl80211/PnoNetwork.java new file mode 100644 index 000000000000..e8eff09583b9 --- /dev/null +++ b/wifi/java/android/net/wifi/nl80211/PnoNetwork.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2016 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.nl80211; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Configuration for a PNO (preferred network offload) network used in {@link PnoSettings}. A PNO + * network allows configuration of a specific network to search for. + * + * @hide + */ +@SystemApi +public final class PnoNetwork implements Parcelable { + private boolean mIsHidden; + private byte[] mSsid; + private int[] mFrequencies; + + /** + * Indicates whether the PNO network configuration is for a hidden SSID - i.e. a network which + * does not broadcast its SSID and must be queried explicitly. + * + * @return True if the configuration is for a hidden network, false otherwise. + */ + public boolean isHidden() { + return mIsHidden; + } + + /** + * Configure whether the PNO network configuration is for a hidden SSID - i.e. a network which + * does not broadcast its SSID and must be queried explicitly. + * + * @param isHidden True if the configuration is for a hidden network, false otherwise. + */ + public void setHidden(boolean isHidden) { + mIsHidden = isHidden; + } + + /** + * Get the raw bytes for the SSID of the PNO network being scanned for. + * + * @return A byte array. + */ + @NonNull public byte[] getSsid() { + return mSsid; + } + + /** + * Set the raw bytes for the SSID of the PNO network being scanned for. + * + * @param ssid A byte array. + */ + public void setSsid(@NonNull byte[] ssid) { + if (ssid == null) { + throw new IllegalArgumentException("null argument"); + } + this.mSsid = ssid; + } + + /** + * Get the frequencies (in MHz) on which to PNO scan for the current network is being searched + * for. A null return (i.e. no frequencies configured) indicates that the network is search for + * on all supported frequencies. + * + * @return A array of frequencies (in MHz), a null indicates no configured frequencies. + */ + @NonNull public int[] getFrequenciesMhz() { + return mFrequencies; + } + + /** + * Set the frequencies (in MHz) on which to PNO scan for the current network is being searched + * for. A null configuration (i.e. no frequencies configured) indicates that the network is + * search for on all supported frequencies. + * + * @param frequenciesMhz an array of frequencies (in MHz), null indicating no configured + * frequencies. + */ + public void setFrequenciesMhz(@NonNull int[] frequenciesMhz) { + if (frequenciesMhz == null) { + throw new IllegalArgumentException("null argument"); + } + this.mFrequencies = frequenciesMhz; + } + + /** Construct an uninitialized PnoNetwork object */ + public PnoNetwork() { } + + /** override comparator */ + @Override + public boolean equals(Object rhs) { + if (this == rhs) return true; + if (!(rhs instanceof PnoNetwork)) { + return false; + } + PnoNetwork network = (PnoNetwork) rhs; + return Arrays.equals(mSsid, network.mSsid) + && Arrays.equals(mFrequencies, network.mFrequencies) + && mIsHidden == network.mIsHidden; + } + + /** override hash code */ + @Override + public int hashCode() { + return Objects.hash( + mIsHidden, + Arrays.hashCode(mSsid), + Arrays.hashCode(mFrequencies)); + } + + /** implement Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** + * implement Parcelable interface + * |flag| is ignored. + */ + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(mIsHidden ? 1 : 0); + out.writeByteArray(mSsid); + out.writeIntArray(mFrequencies); + } + + /** implement Parcelable interface */ + @NonNull public static final Parcelable.Creator<PnoNetwork> CREATOR = + new Parcelable.Creator<PnoNetwork>() { + @Override + public PnoNetwork createFromParcel(Parcel in) { + PnoNetwork result = new PnoNetwork(); + result.mIsHidden = in.readInt() != 0 ? true : false; + result.mSsid = in.createByteArray(); + if (result.mSsid == null) { + result.mSsid = new byte[0]; + } + result.mFrequencies = in.createIntArray(); + if (result.mFrequencies == null) { + result.mFrequencies = new int[0]; + } + return result; + } + + @Override + public PnoNetwork[] newArray(int size) { + return new PnoNetwork[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/nl80211/PnoSettings.java b/wifi/java/android/net/wifi/nl80211/PnoSettings.java new file mode 100644 index 000000000000..00ebe624ba0d --- /dev/null +++ b/wifi/java/android/net/wifi/nl80211/PnoSettings.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2016 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.nl80211; + +import android.annotation.DurationMillisLong; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Configuration for a PNO (preferred network offload). A mechanism by which scans are offloaded + * from the host device to the Wi-Fi chip. + * + * @hide + */ +@SystemApi +public final class PnoSettings implements Parcelable { + private long mIntervalMs; + private int mMin2gRssi; + private int mMin5gRssi; + private int mMin6gRssi; + private List<PnoNetwork> mPnoNetworks; + + /** Construct an uninitialized PnoSettings object */ + public PnoSettings() { } + + /** + * Get the requested PNO scan interval in milliseconds. + * + * @return An interval in milliseconds. + */ + public @DurationMillisLong long getIntervalMillis() { + return mIntervalMs; + } + + /** + * Set the requested PNO scan interval in milliseconds. + * + * @param intervalMillis An interval in milliseconds. + */ + public void setIntervalMillis(@DurationMillisLong long intervalMillis) { + this.mIntervalMs = intervalMillis; + } + + /** + * Get the requested minimum RSSI threshold (in dBm) for APs to report in scan results in the + * 2.4GHz band. + * + * @return An RSSI value in dBm. + */ + public int getMin2gRssiDbm() { + return mMin2gRssi; + } + + /** + * Set the requested minimum RSSI threshold (in dBm) for APs to report in scan scan results in + * the 2.4GHz band. + * + * @param min2gRssiDbm An RSSI value in dBm. + */ + public void setMin2gRssiDbm(int min2gRssiDbm) { + this.mMin2gRssi = min2gRssiDbm; + } + + /** + * Get the requested minimum RSSI threshold (in dBm) for APs to report in scan results in the + * 5GHz band. + * + * @return An RSSI value in dBm. + */ + public int getMin5gRssiDbm() { + return mMin5gRssi; + } + + /** + * Set the requested minimum RSSI threshold (in dBm) for APs to report in scan scan results in + * the 5GHz band. + * + * @param min5gRssiDbm An RSSI value in dBm. + */ + public void setMin5gRssiDbm(int min5gRssiDbm) { + this.mMin5gRssi = min5gRssiDbm; + } + + /** + * Get the requested minimum RSSI threshold (in dBm) for APs to report in scan results in the + * 6GHz band. + * + * @return An RSSI value in dBm. + */ + public int getMin6gRssiDbm() { + return mMin6gRssi; + } + + /** + * Set the requested minimum RSSI threshold (in dBm) for APs to report in scan scan results in + * the 6GHz band. + * + * @param min6gRssiDbm An RSSI value in dBm. + */ + public void setMin6gRssiDbm(int min6gRssiDbm) { + this.mMin6gRssi = min6gRssiDbm; + } + + /** + * Return the configured list of specific networks to search for in a PNO scan. + * + * @return A list of {@link PnoNetwork} objects, possibly empty if non configured. + */ + @NonNull public List<PnoNetwork> getPnoNetworks() { + return mPnoNetworks; + } + + /** + * Set the list of specified networks to scan for in a PNO scan. The networks (APs) are + * specified using {@link PnoNetwork}s. An empty list indicates that all networks are scanned + * for. + * + * @param pnoNetworks A (possibly empty) list of {@link PnoNetwork} objects. + */ + public void setPnoNetworks(@NonNull List<PnoNetwork> pnoNetworks) { + this.mPnoNetworks = pnoNetworks; + } + + /** override comparator */ + @Override + public boolean equals(Object rhs) { + if (this == rhs) return true; + if (!(rhs instanceof PnoSettings)) { + return false; + } + PnoSettings settings = (PnoSettings) rhs; + if (settings == null) { + return false; + } + return mIntervalMs == settings.mIntervalMs + && mMin2gRssi == settings.mMin2gRssi + && mMin5gRssi == settings.mMin5gRssi + && mMin6gRssi == settings.mMin6gRssi + && mPnoNetworks.equals(settings.mPnoNetworks); + } + + /** override hash code */ + @Override + public int hashCode() { + return Objects.hash(mIntervalMs, mMin2gRssi, mMin5gRssi, mMin6gRssi, mPnoNetworks); + } + + /** implement Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** + * implement Parcelable interface + * |flag| is ignored. + **/ + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeLong(mIntervalMs); + out.writeInt(mMin2gRssi); + out.writeInt(mMin5gRssi); + out.writeInt(mMin6gRssi); + out.writeTypedList(mPnoNetworks); + } + + /** implement Parcelable interface */ + @NonNull public static final Parcelable.Creator<PnoSettings> CREATOR = + new Parcelable.Creator<PnoSettings>() { + @Override + public PnoSettings createFromParcel(Parcel in) { + PnoSettings result = new PnoSettings(); + result.mIntervalMs = in.readLong(); + result.mMin2gRssi = in.readInt(); + result.mMin5gRssi = in.readInt(); + result.mMin6gRssi = in.readInt(); + + result.mPnoNetworks = new ArrayList<>(); + in.readTypedList(result.mPnoNetworks, PnoNetwork.CREATOR); + + return result; + } + + @Override + public PnoSettings[] newArray(int size) { + return new PnoSettings[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/nl80211/RadioChainInfo.java b/wifi/java/android/net/wifi/nl80211/RadioChainInfo.java new file mode 100644 index 000000000000..2c12163dc9a1 --- /dev/null +++ b/wifi/java/android/net/wifi/nl80211/RadioChainInfo.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2016 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.nl80211; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Objects; + +/** + * A class representing the radio chains of the Wi-Fi modems. Use to provide raw information about + * signals received on different radio chains. + * + * @hide + */ +@SystemApi +public final class RadioChainInfo implements Parcelable { + private static final String TAG = "RadioChainInfo"; + + /** @hide */ + @VisibleForTesting + public int chainId; + /** @hide */ + @VisibleForTesting + public int level; + + /** + * Return an identifier for this radio chain. This is an arbitrary ID which is consistent for + * the same device. + * + * @return The radio chain ID. + */ + public int getChainId() { + return chainId; + } + + /** + * Returns the detected signal level on this radio chain in dBm (aka RSSI). + * + * @return A signal level in dBm. + */ + public int getLevelDbm() { + return level; + } + + /** + * Construct a RadioChainInfo. + */ + public RadioChainInfo(int chainId, int level) { + this.chainId = chainId; + this.level = level; + } + + /** override comparator */ + @Override + public boolean equals(Object rhs) { + if (this == rhs) return true; + if (!(rhs instanceof RadioChainInfo)) { + return false; + } + RadioChainInfo chainInfo = (RadioChainInfo) rhs; + if (chainInfo == null) { + return false; + } + return chainId == chainInfo.chainId && level == chainInfo.level; + } + + /** override hash code */ + @Override + public int hashCode() { + return Objects.hash(chainId, level); + } + + + /** implement Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** + * implement Parcelable interface + * |flags| is ignored. + */ + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(chainId); + out.writeInt(level); + } + + /** implement Parcelable interface */ + @NonNull public static final Parcelable.Creator<RadioChainInfo> CREATOR = + new Parcelable.Creator<RadioChainInfo>() { + /** + * Caller is responsible for providing a valid parcel. + */ + @Override + public RadioChainInfo createFromParcel(Parcel in) { + return new RadioChainInfo(in.readInt(), in.readInt()); + } + + @Override + public RadioChainInfo[] newArray(int size) { + return new RadioChainInfo[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/nl80211/SingleScanSettings.java b/wifi/java/android/net/wifi/nl80211/SingleScanSettings.java new file mode 100644 index 000000000000..24b1854fbf6c --- /dev/null +++ b/wifi/java/android/net/wifi/nl80211/SingleScanSettings.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 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.nl80211; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Objects; + +/** + * SingleScanSettings for wificond + * + * @hide + */ +public class SingleScanSettings implements Parcelable { + private static final String TAG = "SingleScanSettings"; + + public int scanType; + public ArrayList<ChannelSettings> channelSettings; + public ArrayList<HiddenNetwork> hiddenNetworks; + + /** public constructor */ + public SingleScanSettings() { } + + /** override comparator */ + @Override + public boolean equals(Object rhs) { + if (this == rhs) return true; + if (!(rhs instanceof SingleScanSettings)) { + return false; + } + SingleScanSettings settings = (SingleScanSettings) rhs; + if (settings == null) { + return false; + } + return scanType == settings.scanType + && channelSettings.equals(settings.channelSettings) + && hiddenNetworks.equals(settings.hiddenNetworks); + } + + /** override hash code */ + @Override + public int hashCode() { + return Objects.hash(scanType, channelSettings, hiddenNetworks); + } + + + /** implement Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + private static boolean isValidScanType(int scanType) { + return scanType == IWifiScannerImpl.SCAN_TYPE_LOW_SPAN + || scanType == IWifiScannerImpl.SCAN_TYPE_LOW_POWER + || scanType == IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY; + } + + /** + * implement Parcelable interface + * |flags| is ignored. + */ + @Override + public void writeToParcel(Parcel out, int flags) { + if (!isValidScanType(scanType)) { + Log.wtf(TAG, "Invalid scan type " + scanType); + } + out.writeInt(scanType); + out.writeTypedList(channelSettings); + out.writeTypedList(hiddenNetworks); + } + + /** implement Parcelable interface */ + public static final Parcelable.Creator<SingleScanSettings> CREATOR = + new Parcelable.Creator<SingleScanSettings>() { + /** + * Caller is responsible for providing a valid parcel. + */ + @Override + public SingleScanSettings createFromParcel(Parcel in) { + SingleScanSettings result = new SingleScanSettings(); + result.scanType = in.readInt(); + if (!isValidScanType(result.scanType)) { + Log.wtf(TAG, "Invalid scan type " + result.scanType); + } + result.channelSettings = new ArrayList<ChannelSettings>(); + in.readTypedList(result.channelSettings, ChannelSettings.CREATOR); + result.hiddenNetworks = new ArrayList<HiddenNetwork>(); + in.readTypedList(result.hiddenNetworks, HiddenNetwork.CREATOR); + if (in.dataAvail() != 0) { + Log.e(TAG, "Found trailing data after parcel parsing."); + } + return result; + } + + @Override + public SingleScanSettings[] newArray(int size) { + return new SingleScanSettings[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java new file mode 100644 index 000000000000..4116234c4c8d --- /dev/null +++ b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java @@ -0,0 +1,1287 @@ +/* + * Copyright (C) 2017 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.nl80211; + +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.app.AlarmManager; +import android.content.Context; +import android.net.wifi.SoftApInfo; +import android.net.wifi.WifiAnnotations; +import android.net.wifi.WifiScanner; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * This class encapsulates the interface the wificond daemon presents to the Wi-Fi framework - used + * to encapsulate the Wi-Fi 80211nl management interface. The + * interface is only for use by the Wi-Fi framework and access is protected by SELinux permissions. + * + * @hide + */ +@SystemApi +@SystemService(Context.WIFI_NL80211_SERVICE) +public class WifiNl80211Manager { + private static final String TAG = "WifiNl80211Manager"; + private boolean mVerboseLoggingEnabled = false; + + /** + * The {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)} + * timeout, in milliseconds, after which + * {@link SendMgmtFrameCallback#onFailure(int)} will be called with reason + * {@link #SEND_MGMT_FRAME_ERROR_TIMEOUT}. + */ + private static final int SEND_MGMT_FRAME_TIMEOUT_MS = 1000; + + private static final String TIMEOUT_ALARM_TAG = TAG + " Send Management Frame Timeout"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SCAN_TYPE_"}, + value = {SCAN_TYPE_SINGLE_SCAN, + SCAN_TYPE_PNO_SCAN}) + public @interface ScanResultType {} + + /** + * Specifies a scan type: single scan initiated by the framework. Can be used in + * {@link #getScanResults(String, int)} to specify the type of scan result to fetch. + */ + public static final int SCAN_TYPE_SINGLE_SCAN = 0; + + /** + * Specifies a scan type: PNO scan. Can be used in {@link #getScanResults(String, int)} to + * specify the type of scan result to fetch. + */ + public static final int SCAN_TYPE_PNO_SCAN = 1; + + private AlarmManager mAlarmManager; + private Handler mEventHandler; + + // Cached wificond binder handlers. + private IWificond mWificond; + private HashMap<String, IClientInterface> mClientInterfaces = new HashMap<>(); + private HashMap<String, IApInterface> mApInterfaces = new HashMap<>(); + private HashMap<String, IWifiScannerImpl> mWificondScanners = new HashMap<>(); + private HashMap<String, IScanEvent> mScanEventHandlers = new HashMap<>(); + private HashMap<String, IPnoScanEvent> mPnoScanEventHandlers = new HashMap<>(); + private HashMap<String, IApInterfaceEventCallback> mApInterfaceListeners = new HashMap<>(); + private Runnable mDeathEventHandler; + /** + * Ensures that no more than one sendMgmtFrame operation runs concurrently. + */ + private AtomicBoolean mSendMgmtFrameInProgress = new AtomicBoolean(false); + + /** + * Interface used when waiting for scans to be completed (with results). + */ + public interface ScanEventCallback { + /** + * Called when scan results are available. Scans results should then be obtained from + * {@link #getScanResults(String, int)}. + */ + void onScanResultReady(); + + /** + * Called when a scan has failed. + */ + void onScanFailed(); + } + + /** + * Interface for a callback to provide information about PNO scan request requested with + * {@link #startPnoScan(String, PnoSettings, Executor, PnoScanRequestCallback)}. Note that the + * callback are for the status of the request - not the scan itself. The results of the scan + * are returned with {@link ScanEventCallback}. + */ + public interface PnoScanRequestCallback { + /** + * Called when a PNO scan request has been successfully submitted. + */ + void onPnoRequestSucceeded(); + + /** + * Called when a PNO scan request fails. + */ + void onPnoRequestFailed(); + } + + private class ScanEventHandler extends IScanEvent.Stub { + private Executor mExecutor; + private ScanEventCallback mCallback; + + ScanEventHandler(@NonNull Executor executor, @NonNull ScanEventCallback callback) { + mExecutor = executor; + mCallback = callback; + } + + @Override + public void OnScanResultReady() { + Log.d(TAG, "Scan result ready event"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onScanResultReady()); + } + + @Override + public void OnScanFailed() { + Log.d(TAG, "Scan failed event"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onScanFailed()); + } + } + + /** + * Result of a signal poll requested using {@link #signalPoll(String)}. + */ + public static class SignalPollResult { + /** @hide */ + public SignalPollResult(int currentRssiDbm, int txBitrateMbps, int rxBitrateMbps, + int associationFrequencyMHz) { + this.currentRssiDbm = currentRssiDbm; + this.txBitrateMbps = txBitrateMbps; + this.rxBitrateMbps = rxBitrateMbps; + this.associationFrequencyMHz = associationFrequencyMHz; + } + + /** + * RSSI value in dBM. + */ + public final int currentRssiDbm; + + /** + * Transmission bit rate in Mbps. + */ + public final int txBitrateMbps; + + /** + * Last received packet bit rate in Mbps. + */ + public final int rxBitrateMbps; + + /** + * Association frequency in MHz. + */ + public final int associationFrequencyMHz; + } + + /** + * Transmission counters obtained using {@link #getTxPacketCounters(String)}. + */ + public static class TxPacketCounters { + /** @hide */ + public TxPacketCounters(int txPacketSucceeded, int txPacketFailed) { + this.txPacketSucceeded = txPacketSucceeded; + this.txPacketFailed = txPacketFailed; + } + + /** + * Number of successfully transmitted packets. + */ + public final int txPacketSucceeded; + + /** + * Number of packet transmission failures. + */ + public final int txPacketFailed; + } + + /** + * Callbacks for SoftAp interface registered using + * {@link #registerApCallback(String, Executor, SoftApCallback)}. + */ + public interface SoftApCallback { + /** + * Invoked when there is a fatal failure and the SoftAp is shutdown. + */ + void onFailure(); + + /** + * Invoked when there is a change in the associated station (STA). + * @param client Information about the client whose status has changed. + * @param isConnected Indication as to whether the client is connected (true), or + * disconnected (false). + */ + void onConnectedClientsChanged(@NonNull NativeWifiClient client, boolean isConnected); + + /** + * Invoked when a channel switch event happens - i.e. the SoftAp is moved to a different + * channel. Also called on initial registration. + * @param frequencyMhz The new frequency of the SoftAp. A value of 0 is invalid and is an + * indication that the SoftAp is not enabled. + * @param bandwidth The new bandwidth of the SoftAp. + */ + void onSoftApChannelSwitched(int frequencyMhz, @WifiAnnotations.Bandwidth int bandwidth); + } + + /** + * Callback to notify the results of a + * {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)} call. + * Note: no callbacks will be triggered if the interface dies while sending a frame. + */ + public interface SendMgmtFrameCallback { + /** + * Called when the management frame was successfully sent and ACKed by the recipient. + * @param elapsedTimeMs The elapsed time between when the management frame was sent and when + * the ACK was processed, in milliseconds, as measured by wificond. + * This includes the time that the send frame spent queuing before it + * was sent, any firmware retries, and the time the received ACK spent + * queuing before it was processed. + */ + void onAck(int elapsedTimeMs); + + /** + * Called when the send failed. + * @param reason The error code for the failure. + */ + void onFailure(@SendMgmtFrameError int reason); + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SEND_MGMT_FRAME_ERROR_"}, + value = {SEND_MGMT_FRAME_ERROR_UNKNOWN, + SEND_MGMT_FRAME_ERROR_MCS_UNSUPPORTED, + SEND_MGMT_FRAME_ERROR_NO_ACK, + SEND_MGMT_FRAME_ERROR_TIMEOUT, + SEND_MGMT_FRAME_ERROR_ALREADY_STARTED}) + public @interface SendMgmtFrameError {} + + // Send management frame error codes + + /** + * Unknown error occurred during call to + * {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)}. + */ + public static final int SEND_MGMT_FRAME_ERROR_UNKNOWN = 1; + + /** + * Specifying the MCS rate in + * {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)} is not + * supported by this device. + */ + public static final int SEND_MGMT_FRAME_ERROR_MCS_UNSUPPORTED = 2; + + /** + * Driver reported that no ACK was received for the frame transmitted using + * {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)}. + */ + public static final int SEND_MGMT_FRAME_ERROR_NO_ACK = 3; + + /** + * Error code for when the driver fails to report on the status of the frame sent by + * {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)} + * after {@link #SEND_MGMT_FRAME_TIMEOUT_MS} milliseconds. + */ + public static final int SEND_MGMT_FRAME_ERROR_TIMEOUT = 4; + + /** + * An existing call to + * {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)} + * is in progress. Another frame cannot be sent until the first call completes. + */ + public static final int SEND_MGMT_FRAME_ERROR_ALREADY_STARTED = 5; + + /** @hide */ + public WifiNl80211Manager(Context context) { + mAlarmManager = context.getSystemService(AlarmManager.class); + mEventHandler = new Handler(context.getMainLooper()); + } + + /** @hide */ + @VisibleForTesting + public WifiNl80211Manager(Context context, IWificond wificond) { + this(context); + mWificond = wificond; + } + + private class PnoScanEventHandler extends IPnoScanEvent.Stub { + private Executor mExecutor; + private ScanEventCallback mCallback; + + PnoScanEventHandler(@NonNull Executor executor, @NonNull ScanEventCallback callback) { + mExecutor = executor; + mCallback = callback; + } + + @Override + public void OnPnoNetworkFound() { + Log.d(TAG, "Pno scan result event"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onScanResultReady()); + } + + @Override + public void OnPnoScanFailed() { + Log.d(TAG, "Pno Scan failed event"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onScanFailed()); + } + } + + /** + * Listener for AP Interface events. + */ + private class ApInterfaceEventCallback extends IApInterfaceEventCallback.Stub { + private Executor mExecutor; + private SoftApCallback mSoftApListener; + + ApInterfaceEventCallback(Executor executor, SoftApCallback listener) { + mExecutor = executor; + mSoftApListener = listener; + } + + @Override + public void onConnectedClientsChanged(NativeWifiClient client, boolean isConnected) { + if (mVerboseLoggingEnabled) { + Log.d(TAG, "onConnectedClientsChanged called with " + + client.getMacAddress() + " isConnected: " + isConnected); + } + + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mSoftApListener.onConnectedClientsChanged(client, isConnected)); + } + + @Override + public void onSoftApChannelSwitched(int frequency, int bandwidth) { + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mSoftApListener.onSoftApChannelSwitched(frequency, + toFrameworkBandwidth(bandwidth))); + } + + private @WifiAnnotations.Bandwidth int toFrameworkBandwidth(int bandwidth) { + switch(bandwidth) { + case IApInterfaceEventCallback.BANDWIDTH_INVALID: + return SoftApInfo.CHANNEL_WIDTH_INVALID; + case IApInterfaceEventCallback.BANDWIDTH_20_NOHT: + return SoftApInfo.CHANNEL_WIDTH_20MHZ_NOHT; + case IApInterfaceEventCallback.BANDWIDTH_20: + return SoftApInfo.CHANNEL_WIDTH_20MHZ; + case IApInterfaceEventCallback.BANDWIDTH_40: + return SoftApInfo.CHANNEL_WIDTH_40MHZ; + case IApInterfaceEventCallback.BANDWIDTH_80: + return SoftApInfo.CHANNEL_WIDTH_80MHZ; + case IApInterfaceEventCallback.BANDWIDTH_80P80: + return SoftApInfo.CHANNEL_WIDTH_80MHZ_PLUS_MHZ; + case IApInterfaceEventCallback.BANDWIDTH_160: + return SoftApInfo.CHANNEL_WIDTH_160MHZ; + default: + return SoftApInfo.CHANNEL_WIDTH_INVALID; + } + } + } + + /** + * Callback triggered by wificond. + */ + private class SendMgmtFrameEvent extends ISendMgmtFrameEvent.Stub { + private Executor mExecutor; + private SendMgmtFrameCallback mCallback; + private AlarmManager.OnAlarmListener mTimeoutCallback; + /** + * ensures that mCallback is only called once + */ + private boolean mWasCalled; + + private void runIfFirstCall(Runnable r) { + if (mWasCalled) return; + mWasCalled = true; + + mSendMgmtFrameInProgress.set(false); + r.run(); + } + + SendMgmtFrameEvent(@NonNull Executor executor, @NonNull SendMgmtFrameCallback callback) { + mExecutor = executor; + mCallback = callback; + // called in main thread + mTimeoutCallback = () -> runIfFirstCall(() -> { + if (mVerboseLoggingEnabled) { + Log.e(TAG, "Timed out waiting for ACK"); + } + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onFailure(SEND_MGMT_FRAME_ERROR_TIMEOUT)); + }); + mWasCalled = false; + + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + SEND_MGMT_FRAME_TIMEOUT_MS, + TIMEOUT_ALARM_TAG, mTimeoutCallback, mEventHandler); + } + + // called in binder thread + @Override + public void OnAck(int elapsedTimeMs) { + // post to main thread + mEventHandler.post(() -> runIfFirstCall(() -> { + mAlarmManager.cancel(mTimeoutCallback); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onAck(elapsedTimeMs)); + })); + } + + // called in binder thread + @Override + public void OnFailure(int reason) { + // post to main thread + mEventHandler.post(() -> runIfFirstCall(() -> { + mAlarmManager.cancel(mTimeoutCallback); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onFailure(reason)); + })); + } + } + + /** + * Called by the binder subsystem upon remote object death. + * Invoke all the register death handlers and clear state. + * @hide + */ + @VisibleForTesting + public void binderDied() { + mEventHandler.post(() -> { + Log.e(TAG, "Wificond died!"); + clearState(); + // Invalidate the global wificond handle on death. Will be refreshed + // on the next setup call. + mWificond = null; + if (mDeathEventHandler != null) { + mDeathEventHandler.run(); + } + }); + } + + /** + * Enable or disable verbose logging of the WifiNl80211Manager module. + * @param enable True to enable verbose logging. False to disable verbose logging. + */ + public void enableVerboseLogging(boolean enable) { + mVerboseLoggingEnabled = enable; + } + + /** + * Register a death notification for the WifiNl80211Manager which acts as a proxy for the + * wificond daemon (i.e. the death listener will be called when and if the wificond daemon + * dies). + * + * @param deathEventHandler A {@link Runnable} to be called whenever the wificond daemon dies. + */ + public void setOnServiceDeadCallback(@NonNull Runnable deathEventHandler) { + if (mDeathEventHandler != null) { + Log.e(TAG, "Death handler already present"); + } + mDeathEventHandler = deathEventHandler; + } + + /** + * Helper method to retrieve the global wificond handle and register for + * death notifications. + */ + private boolean retrieveWificondAndRegisterForDeath() { + if (mWificond != null) { + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Wificond handle already retrieved"); + } + // We already have a wificond handle. + return true; + } + IBinder binder = ServiceManager.getService(Context.WIFI_NL80211_SERVICE); + mWificond = IWificond.Stub.asInterface(binder); + if (mWificond == null) { + Log.e(TAG, "Failed to get reference to wificond"); + return false; + } + try { + mWificond.asBinder().linkToDeath(() -> binderDied(), 0); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register death notification for wificond"); + // The remote has already died. + return false; + } + return true; + } + + /** + * Set up an interface for client (STA) mode. + * + * @param ifaceName Name of the interface to configure. + * @param executor The Executor on which to execute the callbacks. + * @param scanCallback A callback for framework initiated scans. + * @param pnoScanCallback A callback for PNO (offloaded) scans. + * @return true on success. + */ + public boolean setupInterfaceForClientMode(@NonNull String ifaceName, + @NonNull @CallbackExecutor Executor executor, + @NonNull ScanEventCallback scanCallback, @NonNull ScanEventCallback pnoScanCallback) { + Log.d(TAG, "Setting up interface for client mode"); + if (!retrieveWificondAndRegisterForDeath()) { + return false; + } + + if (scanCallback == null || pnoScanCallback == null || executor == null) { + Log.e(TAG, "setupInterfaceForClientMode invoked with null callbacks"); + return false; + } + + IClientInterface clientInterface = null; + try { + clientInterface = mWificond.createClientInterface(ifaceName); + } catch (RemoteException e1) { + Log.e(TAG, "Failed to get IClientInterface due to remote exception"); + return false; + } + + if (clientInterface == null) { + Log.e(TAG, "Could not get IClientInterface instance from wificond"); + return false; + } + Binder.allowBlocking(clientInterface.asBinder()); + + // Refresh Handlers + mClientInterfaces.put(ifaceName, clientInterface); + try { + IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl(); + if (wificondScanner == null) { + Log.e(TAG, "Failed to get WificondScannerImpl"); + return false; + } + mWificondScanners.put(ifaceName, wificondScanner); + Binder.allowBlocking(wificondScanner.asBinder()); + ScanEventHandler scanEventHandler = new ScanEventHandler(executor, scanCallback); + mScanEventHandlers.put(ifaceName, scanEventHandler); + wificondScanner.subscribeScanEvents(scanEventHandler); + PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(executor, + pnoScanCallback); + mPnoScanEventHandlers.put(ifaceName, pnoScanEventHandler); + wificondScanner.subscribePnoScanEvents(pnoScanEventHandler); + } catch (RemoteException e) { + Log.e(TAG, "Failed to refresh wificond scanner due to remote exception"); + } + + return true; + } + + /** + * Tear down a specific client (STA) interface configured using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}. + * + * @param ifaceName Name of the interface to tear down. + * @return Returns true on success, false on failure (e.g. when called before an interface was + * set up). + */ + public boolean tearDownClientInterface(@NonNull String ifaceName) { + if (getClientInterface(ifaceName) == null) { + Log.e(TAG, "No valid wificond client interface handler"); + return false; + } + try { + IWifiScannerImpl scannerImpl = mWificondScanners.get(ifaceName); + if (scannerImpl != null) { + scannerImpl.unsubscribeScanEvents(); + scannerImpl.unsubscribePnoScanEvents(); + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to unsubscribe wificond scanner due to remote exception"); + return false; + } + + if (mWificond == null) { + Log.e(TAG, "Reference to wifiCond is null"); + return false; + } + + boolean success; + try { + success = mWificond.tearDownClientInterface(ifaceName); + } catch (RemoteException e1) { + Log.e(TAG, "Failed to teardown client interface due to remote exception"); + return false; + } + if (!success) { + Log.e(TAG, "Failed to teardown client interface"); + return false; + } + + mClientInterfaces.remove(ifaceName); + mWificondScanners.remove(ifaceName); + mScanEventHandlers.remove(ifaceName); + mPnoScanEventHandlers.remove(ifaceName); + return true; + } + + /** + * Set up interface as a Soft AP. + * + * @param ifaceName Name of the interface to configure. + * @return true on success. + */ + public boolean setupInterfaceForSoftApMode(@NonNull String ifaceName) { + Log.d(TAG, "Setting up interface for soft ap mode"); + if (!retrieveWificondAndRegisterForDeath()) { + return false; + } + + IApInterface apInterface = null; + try { + apInterface = mWificond.createApInterface(ifaceName); + } catch (RemoteException e1) { + Log.e(TAG, "Failed to get IApInterface due to remote exception"); + return false; + } + + if (apInterface == null) { + Log.e(TAG, "Could not get IApInterface instance from wificond"); + return false; + } + Binder.allowBlocking(apInterface.asBinder()); + + // Refresh Handlers + mApInterfaces.put(ifaceName, apInterface); + return true; + } + + /** + * Tear down a Soft AP interface configured using + * {@link #setupInterfaceForSoftApMode(String)}. + * + * @param ifaceName Name of the interface to tear down. + * @return Returns true on success, false on failure (e.g. when called before an interface was + * set up). + */ + public boolean tearDownSoftApInterface(@NonNull String ifaceName) { + if (getApInterface(ifaceName) == null) { + Log.e(TAG, "No valid wificond ap interface handler"); + return false; + } + + if (mWificond == null) { + Log.e(TAG, "Reference to wifiCond is null"); + return false; + } + + boolean success; + try { + success = mWificond.tearDownApInterface(ifaceName); + } catch (RemoteException e1) { + Log.e(TAG, "Failed to teardown AP interface due to remote exception"); + return false; + } + if (!success) { + Log.e(TAG, "Failed to teardown AP interface"); + return false; + } + mApInterfaces.remove(ifaceName); + mApInterfaceListeners.remove(ifaceName); + return true; + } + + /** + * Tear down all interfaces, whether clients (STA) or Soft AP. + * + * @return Returns true on success. + */ + public boolean tearDownInterfaces() { + Log.d(TAG, "tearing down interfaces in wificond"); + // Explicitly refresh the wificodn handler because |tearDownInterfaces()| + // could be used to cleanup before we setup any interfaces. + if (!retrieveWificondAndRegisterForDeath()) { + return false; + } + + try { + for (Map.Entry<String, IWifiScannerImpl> entry : mWificondScanners.entrySet()) { + entry.getValue().unsubscribeScanEvents(); + entry.getValue().unsubscribePnoScanEvents(); + } + mWificond.tearDownInterfaces(); + clearState(); + return true; + } catch (RemoteException e) { + Log.e(TAG, "Failed to tear down interfaces due to remote exception"); + } + + return false; + } + + /** Helper function to look up the interface handle using name */ + private IClientInterface getClientInterface(@NonNull String ifaceName) { + return mClientInterfaces.get(ifaceName); + } + + /** + * Request signal polling. + * + * @param ifaceName Name of the interface on which to poll. The interface must have been + * already set up using + *{@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * + * @return A {@link SignalPollResult} object containing interface statistics, or a null on + * error (e.g. the interface hasn't been set up yet). + */ + @Nullable public SignalPollResult signalPoll(@NonNull String ifaceName) { + IClientInterface iface = getClientInterface(ifaceName); + if (iface == null) { + Log.e(TAG, "No valid wificond client interface handler"); + return null; + } + + int[] resultArray; + try { + resultArray = iface.signalPoll(); + if (resultArray == null || resultArray.length != 4) { + Log.e(TAG, "Invalid signal poll result from wificond"); + return null; + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to do signal polling due to remote exception"); + return null; + } + return new SignalPollResult(resultArray[0], resultArray[1], resultArray[3], resultArray[2]); + } + + /** + * Get current transmit (Tx) packet counters of the specified interface. The interface must + * have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * + * @param ifaceName Name of the interface. + * @return {@link TxPacketCounters} of the current interface or null on error (e.g. when + * called before the interface has been set up). + */ + @Nullable public TxPacketCounters getTxPacketCounters(@NonNull String ifaceName) { + IClientInterface iface = getClientInterface(ifaceName); + if (iface == null) { + Log.e(TAG, "No valid wificond client interface handler"); + return null; + } + + int[] resultArray; + try { + resultArray = iface.getPacketCounters(); + if (resultArray == null || resultArray.length != 2) { + Log.e(TAG, "Invalid signal poll result from wificond"); + return null; + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to do signal polling due to remote exception"); + return null; + } + return new TxPacketCounters(resultArray[0], resultArray[1]); + } + + /** Helper function to look up the scanner impl handle using name */ + private IWifiScannerImpl getScannerImpl(@NonNull String ifaceName) { + return mWificondScanners.get(ifaceName); + } + + /** + * Fetch the latest scan results of the indicated type for the specified interface. Note that + * this method fetches the latest results - it does not initiate a scan. Initiating a scan can + * be done using {@link #startScan(String, int, Set, List)} or + * {@link #startPnoScan(String, PnoSettings, Executor, PnoScanRequestCallback)}. + * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * + * @param ifaceName Name of the interface. + * @param scanType The type of scan result to be returned, can be + * {@link #SCAN_TYPE_SINGLE_SCAN} or {@link #SCAN_TYPE_PNO_SCAN}. + * @return Returns an array of {@link NativeScanResult} or an empty array on failure (e.g. when + * called before the interface has been set up). + */ + @NonNull public List<NativeScanResult> getScanResults(@NonNull String ifaceName, + @ScanResultType int scanType) { + IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName); + if (scannerImpl == null) { + Log.e(TAG, "No valid wificond scanner interface handler"); + return new ArrayList<>(); + } + List<NativeScanResult> results = null; + try { + if (scanType == SCAN_TYPE_SINGLE_SCAN) { + results = Arrays.asList(scannerImpl.getScanResults()); + } else { + results = Arrays.asList(scannerImpl.getPnoScanResults()); + } + } catch (RemoteException e1) { + Log.e(TAG, "Failed to create ScanDetail ArrayList"); + } + if (results == null) { + results = new ArrayList<>(); + } + if (mVerboseLoggingEnabled) { + Log.d(TAG, "get " + results.size() + " scan results from wificond"); + } + + return results; + } + + /** + * Return scan type for the parcelable {@link SingleScanSettings} + */ + private static int getScanType(@WifiAnnotations.ScanType int scanType) { + switch (scanType) { + case WifiScanner.SCAN_TYPE_LOW_LATENCY: + return IWifiScannerImpl.SCAN_TYPE_LOW_SPAN; + case WifiScanner.SCAN_TYPE_LOW_POWER: + return IWifiScannerImpl.SCAN_TYPE_LOW_POWER; + case WifiScanner.SCAN_TYPE_HIGH_ACCURACY: + return IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY; + default: + throw new IllegalArgumentException("Invalid scan type " + scanType); + } + } + + /** + * Start a scan using the specified parameters. A scan is an asynchronous operation. The + * result of the operation is returned in the {@link ScanEventCallback} registered when + * setting up an interface using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}. + * The latest scans can be obtained using {@link #getScanResults(String, int)} and using a + * {@link #SCAN_TYPE_SINGLE_SCAN} for the {@code scanType}. + * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * + * @param ifaceName Name of the interface on which to initiate the scan. + * @param scanType Type of scan to perform, can be any of + * {@link WifiScanner#SCAN_TYPE_HIGH_ACCURACY}, {@link WifiScanner#SCAN_TYPE_LOW_POWER}, or + * {@link WifiScanner#SCAN_TYPE_LOW_LATENCY}. + * @param freqs list of frequencies to scan for, if null scan all supported channels. + * @param hiddenNetworkSSIDs List of hidden networks to be scanned for, a null indicates that + * no hidden frequencies will be scanned for. + * @return Returns true on success, false on failure (e.g. when called before the interface + * has been set up). + */ + public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType, + @Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs) { + IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName); + if (scannerImpl == null) { + Log.e(TAG, "No valid wificond scanner interface handler"); + return false; + } + SingleScanSettings settings = new SingleScanSettings(); + try { + settings.scanType = getScanType(scanType); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Invalid scan type ", e); + return false; + } + settings.channelSettings = new ArrayList<>(); + settings.hiddenNetworks = new ArrayList<>(); + + if (freqs != null) { + for (Integer freq : freqs) { + ChannelSettings channel = new ChannelSettings(); + channel.frequency = freq; + settings.channelSettings.add(channel); + } + } + if (hiddenNetworkSSIDs != null) { + for (byte[] ssid : hiddenNetworkSSIDs) { + HiddenNetwork network = new HiddenNetwork(); + network.ssid = ssid; + + // settings.hiddenNetworks is expected to be very small, so this shouldn't cause + // any performance issues. + if (!settings.hiddenNetworks.contains(network)) { + settings.hiddenNetworks.add(network); + } + } + } + + try { + return scannerImpl.scan(settings); + } catch (RemoteException e1) { + Log.e(TAG, "Failed to request scan due to remote exception"); + } + return false; + } + + /** + * Request a PNO (Preferred Network Offload). The offload request and the scans are asynchronous + * operations. The result of the request are returned in the {@code callback} parameter which + * is an {@link PnoScanRequestCallback}. The scan results are are return in the + * {@link ScanEventCallback} which is registered when setting up an interface using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}. + * The latest PNO scans can be obtained using {@link #getScanResults(String, int)} with the + * {@code scanType} set to {@link #SCAN_TYPE_PNO_SCAN}. + * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * + * @param ifaceName Name of the interface on which to request a PNO. + * @param pnoSettings PNO scan configuration. + * @param executor The Executor on which to execute the callback. + * @param callback Callback for the results of the offload request. + * @return true on success, false on failure (e.g. when called before the interface has been set + * up). + */ + public boolean startPnoScan(@NonNull String ifaceName, @NonNull PnoSettings pnoSettings, + @NonNull @CallbackExecutor Executor executor, + @NonNull PnoScanRequestCallback callback) { + IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName); + if (scannerImpl == null) { + Log.e(TAG, "No valid wificond scanner interface handler"); + return false; + } + + if (callback == null || executor == null) { + Log.e(TAG, "startPnoScan called with a null callback"); + return false; + } + + try { + boolean success = scannerImpl.startPnoScan(pnoSettings); + if (success) { + executor.execute(callback::onPnoRequestSucceeded); + } else { + executor.execute(callback::onPnoRequestFailed); + } + return success; + } catch (RemoteException e1) { + Log.e(TAG, "Failed to start pno scan due to remote exception"); + } + return false; + } + + /** + * Stop PNO scan configured with + * {@link #startPnoScan(String, PnoSettings, Executor, PnoScanRequestCallback)}. + * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * + * @param ifaceName Name of the interface on which the PNO scan was configured. + * @return true on success, false on failure (e.g. when called before the interface has been + * set up). + */ + public boolean stopPnoScan(@NonNull String ifaceName) { + IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName); + if (scannerImpl == null) { + Log.e(TAG, "No valid wificond scanner interface handler"); + return false; + } + try { + return scannerImpl.stopPnoScan(); + } catch (RemoteException e1) { + Log.e(TAG, "Failed to stop pno scan due to remote exception"); + } + return false; + } + + /** + * Abort ongoing single scan started with {@link #startScan(String, int, Set, List)}. No failure + * callback, e.g. {@link ScanEventCallback#onScanFailed()}, is triggered by this operation. + * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. If the interface has not been set up then + * this method has no impact. + * + * @param ifaceName Name of the interface on which the scan was started. + */ + public void abortScan(@NonNull String ifaceName) { + IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName); + if (scannerImpl == null) { + Log.e(TAG, "No valid wificond scanner interface handler"); + return; + } + try { + scannerImpl.abortScan(); + } catch (RemoteException e1) { + Log.e(TAG, "Failed to request abortScan due to remote exception"); + } + } + + /** + * Query the list of valid frequencies (in MHz) for the provided band. + * The result depends on the on the country code that has been set. + * + * @param band as specified by one of the WifiScanner.WIFI_BAND_* constants. + * The following bands are supported: + * {@link WifiScanner#WIFI_BAND_24_GHZ}, + * {@link WifiScanner#WIFI_BAND_5_GHZ}, + * {@link WifiScanner#WIFI_BAND_5_GHZ_DFS_ONLY}, + * {@link WifiScanner#WIFI_BAND_6_GHZ} + * @return frequencies vector of valid frequencies (MHz), or an empty array for error. + * @throws IllegalArgumentException if band is not recognized. + */ + public @NonNull int[] getChannelsMhzForBand(@WifiAnnotations.WifiBandBasic int band) { + if (mWificond == null) { + Log.e(TAG, "No valid wificond scanner interface handler"); + return new int[0]; + } + int[] result = null; + try { + switch (band) { + case WifiScanner.WIFI_BAND_24_GHZ: + result = mWificond.getAvailable2gChannels(); + break; + case WifiScanner.WIFI_BAND_5_GHZ: + result = mWificond.getAvailable5gNonDFSChannels(); + break; + case WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY: + result = mWificond.getAvailableDFSChannels(); + break; + case WifiScanner.WIFI_BAND_6_GHZ: + result = mWificond.getAvailable6gChannels(); + break; + default: + throw new IllegalArgumentException("unsupported band " + band); + } + } catch (RemoteException e1) { + Log.e(TAG, "Failed to request getChannelsForBand due to remote exception"); + } + if (result == null) { + result = new int[0]; + } + return result; + } + + /** Helper function to look up the interface handle using name */ + private IApInterface getApInterface(@NonNull String ifaceName) { + return mApInterfaces.get(ifaceName); + } + + /** + * Get the device phy capabilities for a given interface. + * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * + * @return DeviceWiphyCapabilities or null on error (e.g. when called on an interface which has + * not been set up). + */ + @Nullable public DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String ifaceName) { + if (mWificond == null) { + Log.e(TAG, "Can not query for device wiphy capabilities at this time"); + return null; + } + + try { + return mWificond.getDeviceWiphyCapabilities(ifaceName); + } catch (RemoteException e) { + return null; + } + } + + /** + * Register the provided callback handler for SoftAp events. The interface must first be created + * using {@link #setupInterfaceForSoftApMode(String)}. The callback registration is valid until + * the interface is deleted using {@link #tearDownSoftApInterface(String)} (no deregistration + * method is provided). + * <p> + * Note that only one callback can be registered at a time - any registration overrides previous + * registrations. + * + * @param ifaceName Name of the interface on which to register the callback. + * @param executor The Executor on which to execute the callbacks. + * @param callback Callback for AP events. + * @return true on success, false on failure (e.g. when called on an interface which has not + * been set up). + */ + public boolean registerApCallback(@NonNull String ifaceName, + @NonNull @CallbackExecutor Executor executor, + @NonNull SoftApCallback callback) { + IApInterface iface = getApInterface(ifaceName); + if (iface == null) { + Log.e(TAG, "No valid ap interface handler"); + return false; + } + + if (callback == null || executor == null) { + Log.e(TAG, "registerApCallback called with a null callback"); + return false; + } + + try { + IApInterfaceEventCallback wificondCallback = new ApInterfaceEventCallback(executor, + callback); + mApInterfaceListeners.put(ifaceName, wificondCallback); + boolean success = iface.registerCallback(wificondCallback); + if (!success) { + Log.e(TAG, "Failed to register ap callback."); + return false; + } + } catch (RemoteException e) { + Log.e(TAG, "Exception in registering AP callback: " + e); + return false; + } + return true; + } + + /** + * Send a management frame on the specified interface at the specified rate. Useful for probing + * the link with arbitrary frames. + * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * + * @param ifaceName The interface on which to send the frame. + * @param frame The raw byte array of the management frame to tramit. + * @param mcs The MCS (modulation and coding scheme), i.e. rate, at which to transmit the + * frame. Specified per IEEE 802.11. + * @param executor The Executor on which to execute the callbacks. + * @param callback A {@link SendMgmtFrameCallback} callback for results of the operation. + */ + public void sendMgmtFrame(@NonNull String ifaceName, @NonNull byte[] frame, int mcs, + @NonNull @CallbackExecutor Executor executor, + @NonNull SendMgmtFrameCallback callback) { + + if (callback == null || executor == null) { + Log.e(TAG, "callback cannot be null!"); + return; + } + + if (frame == null) { + Log.e(TAG, "frame cannot be null!"); + executor.execute(() -> callback.onFailure(SEND_MGMT_FRAME_ERROR_UNKNOWN)); + return; + } + + // TODO (b/112029045) validate mcs + IClientInterface clientInterface = getClientInterface(ifaceName); + if (clientInterface == null) { + Log.e(TAG, "No valid wificond client interface handler"); + executor.execute(() -> callback.onFailure(SEND_MGMT_FRAME_ERROR_UNKNOWN)); + return; + } + + if (!mSendMgmtFrameInProgress.compareAndSet(false, true)) { + Log.e(TAG, "An existing management frame transmission is in progress!"); + executor.execute(() -> callback.onFailure(SEND_MGMT_FRAME_ERROR_ALREADY_STARTED)); + return; + } + + SendMgmtFrameEvent sendMgmtFrameEvent = new SendMgmtFrameEvent(executor, callback); + try { + clientInterface.SendMgmtFrame(frame, sendMgmtFrameEvent, mcs); + } catch (RemoteException e) { + Log.e(TAG, "Exception while starting link probe: " + e); + // Call sendMgmtFrameEvent.OnFailure() instead of callback.onFailure() so that + // sendMgmtFrameEvent can clean up internal state, such as cancelling the timer. + sendMgmtFrameEvent.OnFailure(SEND_MGMT_FRAME_ERROR_UNKNOWN); + } + } + + /** + * Clear all internal handles. + */ + private void clearState() { + // Refresh handlers + mClientInterfaces.clear(); + mWificondScanners.clear(); + mPnoScanEventHandlers.clear(); + mScanEventHandlers.clear(); + mApInterfaces.clear(); + mApInterfaceListeners.clear(); + mSendMgmtFrameInProgress.set(false); + } + + /** + * OEM parsed security type + */ + public static class OemSecurityType { + /** The protocol defined in {@link android.net.wifi.WifiAnnotations.Protocol}. */ + public final @WifiAnnotations.Protocol int protocol; + /** + * Supported key management types defined + * in {@link android.net.wifi.WifiAnnotations.KeyMgmt}. + */ + @NonNull public final List<Integer> keyManagement; + /** + * Supported pairwise cipher types defined + * in {@link android.net.wifi.WifiAnnotations.Cipher}. + */ + @NonNull public final List<Integer> pairwiseCipher; + /** The group cipher type defined in {@link android.net.wifi.WifiAnnotations.Cipher}. */ + public final @WifiAnnotations.Cipher int groupCipher; + /** + * Default constructor for OemSecurityType + * + * @param protocol The protocol defined in + * {@link android.net.wifi.WifiAnnotations.Protocol}. + * @param keyManagement Supported key management types defined + * in {@link android.net.wifi.WifiAnnotations.KeyMgmt}. + * @param pairwiseCipher Supported pairwise cipher types defined + * in {@link android.net.wifi.WifiAnnotations.Cipher}. + * @param groupCipher The group cipher type defined + * in {@link android.net.wifi.WifiAnnotations.Cipher}. + */ + public OemSecurityType( + @WifiAnnotations.Protocol int protocol, + @NonNull List<Integer> keyManagement, + @NonNull List<Integer> pairwiseCipher, + @WifiAnnotations.Cipher int groupCipher) { + this.protocol = protocol; + this.keyManagement = (keyManagement != null) + ? keyManagement : new ArrayList<Integer>(); + this.pairwiseCipher = (pairwiseCipher != null) + ? pairwiseCipher : new ArrayList<Integer>(); + this.groupCipher = groupCipher; + } + } + + /** + * OEM information element parser for security types not parsed by the framework. + * + * The OEM method should use the method inputs {@code id}, {@code idExt}, and {@code bytes} + * to perform the parsing. The method should place the results in an OemSecurityType objct. + * + * @param id The information element id. + * @param idExt The information element extension id. This is valid only when id is + * the extension id, {@code 255}. + * @param bytes The raw bytes of information element data, 'Element ID' and 'Length' are + * stripped off already. + * @return an OemSecurityType object if this IE is parsed successfully, null otherwise. + */ + @Nullable public static OemSecurityType parseOemSecurityTypeElement( + int id, + int idExt, + @NonNull byte[] bytes) { + return null; + } +} diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java index a59799b3fc54..d47989235f0b 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java @@ -17,6 +17,7 @@ package android.net.wifi.p2p; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; @@ -48,29 +49,39 @@ public class WifiP2pConfig implements Parcelable { */ public WpsInfo wps; - /** - * The network name of a group, should be configured by helper method - */ + /** Get the network name of this P2P configuration, or null if unset. */ + @Nullable + public String getNetworkName() { + return networkName; + } + /** @hide */ public String networkName = ""; - /** - * The passphrase of a group, should be configured by helper method - */ + /** Get the passphrase of this P2P configuration, or null if unset. */ + @Nullable + public String getPassphrase() { + return passphrase; + } + /** @hide */ public String passphrase = ""; /** - * The required band for Group Owner + * Get the required band for the group owner. + * The result will be one of the following: + * {@link #GROUP_OWNER_BAND_AUTO}, + * {@link #GROUP_OWNER_BAND_2GHZ}, + * {@link #GROUP_OWNER_BAND_5GHZ} */ - /** @hide */ - public int groupOwnerBand = GROUP_OWNER_BAND_AUTO; + @GroupOperatingBandType + public int getGroupOwnerBand() { + return groupOwnerBand; + } /** @hide */ - public static final int MAX_GROUP_OWNER_INTENT = 15; - /** @hide */ - @UnsupportedAppUsage - public static final int MIN_GROUP_OWNER_INTENT = 0; + @GroupOperatingBandType + public int groupOwnerBand = GROUP_OWNER_BAND_AUTO; /** @hide */ @IntDef(flag = false, prefix = { "GROUP_OWNER_BAND_" }, value = { @@ -95,17 +106,49 @@ public class WifiP2pConfig implements Parcelable { public static final int GROUP_OWNER_BAND_5GHZ = 2; /** - * This is an integer value between 0 and 15 where 0 indicates the least - * inclination to be a group owner and 15 indicates the highest inclination - * to be a group owner. + * The least inclination to be a group owner, to be filled in the field + * {@link #groupOwnerIntent}. + */ + public static final int GROUP_OWNER_INTENT_MIN = 0; + + /** + * The most inclination to be a group owner, to be filled in the field + * {@link #groupOwnerIntent}. + */ + public static final int GROUP_OWNER_INTENT_MAX = 15; + + /** + * The system can choose an appropriate owner intent value, to be filled in the field + * {@link #groupOwnerIntent}. + */ + public static final int GROUP_OWNER_INTENT_AUTO = -1; + + /** + * This is an integer value between {@link #GROUP_OWNER_INTENT_MIN} and + * {@link #GROUP_OWNER_INTENT_MAX} where + * {@link #GROUP_OWNER_INTENT_MIN} indicates the least inclination to be a group owner and + * {@link #GROUP_OWNER_INTENT_MAX} indicates the highest inclination to be a group owner. * - * A value of -1 indicates the system can choose an appropriate value. + * A value of {@link #GROUP_OWNER_INTENT_AUTO} indicates the system can choose an appropriate + * value. + * + * By default this field is set to {@link #GROUP_OWNER_INTENT_AUTO}. */ - public int groupOwnerIntent = -1; + @IntRange(from = 0, to = 15) + public int groupOwnerIntent = GROUP_OWNER_INTENT_AUTO; /** @hide */ @UnsupportedAppUsage - public int netId = WifiP2pGroup.PERSISTENT_NET_ID; + public int netId = WifiP2pGroup.NETWORK_ID_PERSISTENT; + + /** + * Get the network ID of this P2P configuration. + * @return either a non-negative network ID, or one of + * {@link WifiP2pGroup#NETWORK_ID_PERSISTENT} or {@link WifiP2pGroup#NETWORK_ID_TEMPORARY}. + */ + public int getNetworkId() { + return netId; + } public WifiP2pConfig() { //set defaults @@ -239,7 +282,7 @@ public class WifiP2pConfig implements Parcelable { private String mPassphrase = ""; private int mGroupOperatingBand = GROUP_OWNER_BAND_AUTO; private int mGroupOperatingFrequency = GROUP_OWNER_BAND_AUTO; - private int mNetId = WifiP2pGroup.TEMPORARY_NET_ID; + private int mNetId = WifiP2pGroup.NETWORK_ID_TEMPORARY; /** * Specify the peer's MAC address. If not set, the device will @@ -419,9 +462,9 @@ public class WifiP2pConfig implements Parcelable { */ public @NonNull Builder enablePersistentMode(boolean persistent) { if (persistent) { - mNetId = WifiP2pGroup.PERSISTENT_NET_ID; + mNetId = WifiP2pGroup.NETWORK_ID_PERSISTENT; } else { - mNetId = WifiP2pGroup.TEMPORARY_NET_ID; + mNetId = WifiP2pGroup.NETWORK_ID_TEMPORARY; } return this; } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java index 69aefcce4f5b..710175fee747 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java @@ -16,6 +16,8 @@ package android.net.wifi.p2p; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -237,6 +239,12 @@ public class WifiP2pDevice implements Parcelable { } } + /** The Wifi Display information for this device, or null if unavailable. */ + @Nullable + public WifiP2pWfdInfo getWfdInfo() { + return wfdInfo; + } + /** Returns true if WPS push button configuration is supported */ public boolean wpsPbcSupported() { return (wpsConfigMethodsSupported & WPS_CONFIG_PUSHBUTTON) != 0; @@ -278,14 +286,13 @@ public class WifiP2pDevice implements Parcelable { } /** - * Update device details. This will be throw an exception if the device address - * does not match. - * @param device to be updated - * @throws IllegalArgumentException if the device is null or device address does not match - * @hide + * Update this device's details using another {@link WifiP2pDevice} instance. + * This will throw an exception if the device address does not match. + * + * @param device another instance of {@link WifiP2pDevice} used to update this instance. + * @throws IllegalArgumentException if the device is null or the device address does not match */ - @UnsupportedAppUsage - public void update(WifiP2pDevice device) { + public void update(@NonNull WifiP2pDevice device) { updateSupplicantDetails(device); status = device.status; } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java index b292da193a89..e497b22d7769 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java @@ -16,6 +16,7 @@ package android.net.wifi.p2p; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -36,16 +37,27 @@ import java.util.regex.Pattern; */ public class WifiP2pGroup implements Parcelable { - /** The temporary network id. - * {@hide} */ + /** + * The temporary network id. + * @see #getNetworkId() + */ + public static final int NETWORK_ID_TEMPORARY = -1; + + /** + * The temporary network id. + * + * @hide + */ @UnsupportedAppUsage - public static final int TEMPORARY_NET_ID = -1; + public static final int TEMPORARY_NET_ID = NETWORK_ID_TEMPORARY; - /** The persistent network id. + /** + * The persistent network id. * If a matching persistent profile is found, use it. * Otherwise, create a new persistent profile. - * {@hide} */ - public static final int PERSISTENT_NET_ID = -2; + * @see #getNetworkId() + */ + public static final int NETWORK_ID_PERSISTENT = -2; /** The network name */ private String mNetworkName; @@ -64,7 +76,7 @@ public class WifiP2pGroup implements Parcelable { private String mInterface; - /** The network id in the wpa_supplicant */ + /** The network ID in wpa_supplicant */ private int mNetId; /** The frequency (in MHz) used by this group */ @@ -126,13 +138,13 @@ public class WifiP2pGroup implements Parcelable { mPassphrase = match.group(4); mOwner = new WifiP2pDevice(match.group(5)); if (match.group(6) != null) { - mNetId = PERSISTENT_NET_ID; + mNetId = NETWORK_ID_PERSISTENT; } else { - mNetId = TEMPORARY_NET_ID; + mNetId = NETWORK_ID_TEMPORARY; } } else if (tokens[0].equals("P2P-INVITATION-RECEIVED")) { String sa = null; - mNetId = PERSISTENT_NET_ID; + mNetId = NETWORK_ID_PERSISTENT; for (String token : tokens) { String[] nameValue = token.split("="); if (nameValue.length != 2) continue; @@ -225,10 +237,13 @@ public class WifiP2pGroup implements Parcelable { return mClients.size() == 0; } - /** @hide Returns {@code true} if the device is part of the group */ - public boolean contains(WifiP2pDevice device) { - if (mOwner.equals(device) || mClients.contains(device)) return true; - return false; + /** + * Returns {@code true} if the device is part of the group, {@code false} otherwise. + * + * @hide + */ + public boolean contains(@Nullable WifiP2pDevice device) { + return mOwner.equals(device) || mClients.contains(device); } /** Get the list of clients currently part of the p2p group */ @@ -261,8 +276,7 @@ public class WifiP2pGroup implements Parcelable { return mInterface; } - /** @hide */ - @UnsupportedAppUsage + /** The network ID of the P2P group in wpa_supplicant. */ public int getNetworkId() { return mNetId; } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.aidl deleted file mode 100644 index 3d8a47682f6e..000000000000 --- a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2012, 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.p2p; - -parcelable WifiP2pGroupList;
\ No newline at end of file diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java index 6068d3a2f697..8a86311defca 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java @@ -15,12 +15,16 @@ */ package android.net.wifi.p2p; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.util.LruCache; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; @@ -30,7 +34,8 @@ import java.util.Map; * {@see WifiP2pManager} * @hide */ -public class WifiP2pGroupList implements Parcelable { +@SystemApi +public final class WifiP2pGroupList implements Parcelable { private static final int CREDENTIAL_MAX_NUM = 32; @@ -40,6 +45,7 @@ public class WifiP2pGroupList implements Parcelable { private boolean isClearCalled = false; + /** @hide */ public interface GroupDeleteListener { public void onDeleteGroup(int netId); } @@ -71,13 +77,11 @@ public class WifiP2pGroupList implements Parcelable { } /** - * Return the list of p2p group. - * - * @return the list of p2p group. + * Get the list of P2P groups. */ - @UnsupportedAppUsage - public Collection<WifiP2pGroup> getGroupList() { - return mGroups.snapshot().values(); + @NonNull + public List<WifiP2pGroup> getGroupList() { + return new ArrayList<>(mGroups.snapshot().values()); } /** @@ -206,6 +210,7 @@ public class WifiP2pGroupList implements Parcelable { return false; } + @Override public String toString() { StringBuffer sbuf = new StringBuffer(); @@ -217,12 +222,14 @@ public class WifiP2pGroupList implements Parcelable { } /** Implement the Parcelable interface */ + @Override public int describeContents() { return 0; } /** Implement the Parcelable interface */ - public void writeToParcel(Parcel dest, int flags) { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { final Collection<WifiP2pGroup> groups = mGroups.snapshot().values(); dest.writeInt(groups.size()); for(WifiP2pGroup group : groups) { @@ -231,7 +238,7 @@ public class WifiP2pGroupList implements Parcelable { } /** Implement the Parcelable interface */ - public static final @android.annotation.NonNull Creator<WifiP2pGroupList> CREATOR = + public static final @NonNull Creator<WifiP2pGroupList> CREATOR = new Creator<WifiP2pGroupList>() { public WifiP2pGroupList createFromParcel(Parcel in) { WifiP2pGroupList grpList = new WifiP2pGroupList(); diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java index eec4a0007d8e..724ccf0d7c45 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -43,15 +44,15 @@ import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.text.TextUtils; +import android.util.CloseGuard; import android.util.Log; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; -import dalvik.system.CloseGuard; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.Reference; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -325,10 +326,17 @@ public class WifiP2pManager { /** * Broadcast intent action indicating that remembered persistent groups have changed. + * + * You can <em>not</em> receive this through components declared + * in manifests, only by explicitly registering for it with + * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver, + * android.content.IntentFilter) Context.registerReceiver()}. + * * @hide */ - public static final String WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION = - "android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED"; + @SystemApi + public static final String ACTION_WIFI_P2P_PERSISTENT_GROUPS_CHANGED = + "android.net.wifi.p2p.action.WIFI_P2P_PERSISTENT_GROUPS_CHANGED"; /** * The lookup key for a handover message returned by the WifiP2pService. @@ -345,6 +353,13 @@ public class WifiP2pManager { "android.net.wifi.p2p.CALLING_PACKAGE"; /** + * The lookup key for a calling feature id from WifiP2pManager + * @hide + */ + public static final String CALLING_FEATURE_ID = + "android.net.wifi.p2p.CALLING_FEATURE_ID"; + + /** * The lookup key for a calling package binder from WifiP2pManager * @hide */ @@ -749,13 +764,18 @@ public class WifiP2pManager { } - /** Interface for callback invocation when stored group info list is available {@hide}*/ + /** + * Interface for callback invocation when stored group info list is available + * + * @hide + */ + @SystemApi public interface PersistentGroupInfoListener { /** * The requested stored p2p group info list is available * @param groups Wi-Fi p2p group info list */ - public void onPersistentGroupInfoAvailable(WifiP2pGroupList groups); + void onPersistentGroupInfoAvailable(@NonNull WifiP2pGroupList groups); } /** @@ -855,7 +875,7 @@ public class WifiP2pManager { private final Object mListenerMapLock = new Object(); private int mListenerKey = 0; - private final CloseGuard mCloseGuard = CloseGuard.get(); + private final CloseGuard mCloseGuard = new CloseGuard(); /** * Close the current P2P connection and indicate to the P2P service that connections @@ -874,6 +894,7 @@ public class WifiP2pManager { mAsyncChannel.disconnect(); mCloseGuard.close(); + Reference.reachabilityFence(this); } /** @hide */ @@ -1159,6 +1180,7 @@ public class WifiP2pManager { == AsyncChannel.STATUS_SUCCESSFUL) { Bundle bundle = new Bundle(); bundle.putString(CALLING_PACKAGE, c.mContext.getOpPackageName()); + bundle.putString(CALLING_FEATURE_ID, c.mContext.getAttributionTag()); bundle.putBinder(CALLING_BINDER, binder); c.mAsyncChannel.sendMessage(UPDATE_CHANNEL_INFO, 0, c.putListener(null), bundle); @@ -1194,7 +1216,7 @@ public class WifiP2pManager { c.mAsyncChannel.sendMessage(DISCOVER_PEERS, 0, c.putListener(listener)); } - /** + /** * Stop an ongoing peer discovery * * <p> The function call immediately returns after sending a stop request @@ -1277,7 +1299,7 @@ public class WifiP2pManager { @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void createGroup(Channel c, ActionListener listener) { checkChannel(c); - c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.PERSISTENT_NET_ID, + c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.NETWORK_ID_PERSISTENT, c.putListener(listener)); } @@ -1331,28 +1353,57 @@ public class WifiP2pManager { } /** - * Force p2p to enter or exit listen state + * Force p2p to enter listen state * * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)} - * @param enable enables or disables listening * @param listener for callbacks on success or failure. Can be null. * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) - public void listen(Channel c, boolean enable, ActionListener listener) { + public void startListening(@NonNull Channel c, @Nullable ActionListener listener) { checkChannel(c); - c.mAsyncChannel.sendMessage(enable ? START_LISTEN : STOP_LISTEN, - 0, c.putListener(listener)); + c.mAsyncChannel.sendMessage(START_LISTEN, 0, c.putListener(listener)); } - /** @hide */ - @UnsupportedAppUsage - public void setWifiP2pChannels(Channel c, int lc, int oc, ActionListener listener) { + /** + * Force p2p to exit listen state + * + * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)} + * @param listener for callbacks on success or failure. Can be null. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void stopListening(@NonNull Channel c, @Nullable ActionListener listener) { + checkChannel(c); + c.mAsyncChannel.sendMessage(STOP_LISTEN, 0, c.putListener(listener)); + } + + /** + * Set P2P listening and operating channel. + * + * @param c is the channel created at {@link #initialize} + * @param listeningChannel the listening channel's Wifi channel number. e.g. 1, 6, 11. + * @param operatingChannel the operating channel's Wifi channel number. e.g. 1, 6, 11. + * @param listener for callbacks on success or failure. Can be null. + * + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_STACK, + android.Manifest.permission.OVERRIDE_WIFI_CONFIG + }) + public void setWifiP2pChannels(@NonNull Channel c, int listeningChannel, int operatingChannel, + @Nullable ActionListener listener) { checkChannel(c); Bundle p2pChannels = new Bundle(); - p2pChannels.putInt("lc", lc); - p2pChannels.putInt("oc", oc); + p2pChannels.putInt("lc", listeningChannel); + p2pChannels.putInt("oc", operatingChannel); c.mAsyncChannel.sendMessage(SET_CHANNEL, 0, c.putListener(listener), p2pChannels); } @@ -1610,23 +1661,47 @@ public class WifiP2pManager { /** * Set p2p device name. - * @hide + * * @param c is the channel created at {@link #initialize} * @param listener for callback when group info is available. Can be null. + * + * @hide */ - @UnsupportedAppUsage - public void setDeviceName(Channel c, String devName, ActionListener listener) { + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_STACK, + android.Manifest.permission.OVERRIDE_WIFI_CONFIG + }) + public void setDeviceName(@NonNull Channel c, @NonNull String devName, + @Nullable ActionListener listener) { checkChannel(c); WifiP2pDevice d = new WifiP2pDevice(); d.deviceName = devName; c.mAsyncChannel.sendMessage(SET_DEVICE_NAME, 0, c.putListener(listener), d); } + /** + * Set Wifi Display information. + * + * @param c is the channel created at {@link #initialize} + * @param wfdInfo the Wifi Display information to set + * @param listener for callbacks on success or failure. Can be null. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) + public void setWfdInfo(@NonNull Channel c, @NonNull WifiP2pWfdInfo wfdInfo, + @Nullable ActionListener listener) { + setWFDInfo(c, wfdInfo, listener); + } + /** @hide */ @UnsupportedAppUsage - public void setWFDInfo( - Channel c, WifiP2pWfdInfo wfdInfo, - ActionListener listener) { + @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) + public void setWFDInfo(@NonNull Channel c, @NonNull WifiP2pWfdInfo wfdInfo, + @Nullable ActionListener listener) { checkChannel(c); try { mService.checkConfigureWifiDisplayPermission(); @@ -1650,12 +1725,19 @@ public class WifiP2pManager { * a network id can be obtained by {@link WifiP2pGroup#getNetworkId()}. * * @param c is the channel created at {@link #initialize} - * @param netId he network id of the p2p group. + * @param netId the network id of the p2p group. * @param listener for callbacks on success or failure. Can be null. + * * @hide */ - @UnsupportedAppUsage - public void deletePersistentGroup(Channel c, int netId, ActionListener listener) { + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_STACK, + android.Manifest.permission.OVERRIDE_WIFI_CONFIG + }) + public void deletePersistentGroup(@NonNull Channel c, int netId, + @Nullable ActionListener listener) { checkChannel(c); c.mAsyncChannel.sendMessage(DELETE_PERSISTENT_GROUP, netId, c.putListener(listener)); } @@ -1665,23 +1747,66 @@ public class WifiP2pManager { * * @param c is the channel created at {@link #initialize} * @param listener for callback when persistent group info list is available. Can be null. + * * @hide */ - @UnsupportedAppUsage - public void requestPersistentGroupInfo(Channel c, PersistentGroupInfoListener listener) { + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_STACK, + android.Manifest.permission.READ_WIFI_CREDENTIAL + }) + public void requestPersistentGroupInfo(@NonNull Channel c, + @Nullable PersistentGroupInfoListener listener) { checkChannel(c); c.mAsyncChannel.sendMessage(REQUEST_PERSISTENT_GROUP_INFO, 0, c.putListener(listener)); } /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"MIRACAST_"}, value = { + MIRACAST_DISABLED, + MIRACAST_SOURCE, + MIRACAST_SINK}) + public @interface MiracastMode {} + + /** + * Miracast is disabled. + * @hide + */ + @SystemApi public static final int MIRACAST_DISABLED = 0; - /** @hide */ + /** + * Device acts as a Miracast source. + * @hide + */ + @SystemApi public static final int MIRACAST_SOURCE = 1; - /** @hide */ + /** + * Device acts as a Miracast sink. + * @hide + */ + @SystemApi public static final int MIRACAST_SINK = 2; - /** Internal use only @hide */ - @UnsupportedAppUsage - public void setMiracastMode(int mode) { + + /** + * This is used to provide information to drivers to optimize performance depending + * on the current mode of operation. + * {@link #MIRACAST_DISABLED} - disabled + * {@link #MIRACAST_SOURCE} - source operation + * {@link #MIRACAST_SINK} - sink operation + * + * As an example, the driver could reduce the channel dwell time during scanning + * when acting as a source or sink to minimize impact on Miracast. + * + * @param mode mode of operation. One of {@link #MIRACAST_DISABLED}, {@link #MIRACAST_SOURCE}, + * or {@link #MIRACAST_SINK} + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) + public void setMiracastMode(@MiracastMode int mode) { try { mService.setMiracastMode(mode); } catch (RemoteException e) { @@ -1770,8 +1895,10 @@ public class WifiP2pManager { * * @param c is the channel created at {@link #initialize}. * @param listener for callback on success or failure. Can be null. + * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(@NonNull Channel c, @Nullable ActionListener listener) { checkChannel(c); diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java index 7a5f909f93ae..e399b5b9afa6 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java @@ -16,75 +16,116 @@ package android.net.wifi.p2p; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Locale; /** - * A class representing Wifi Display information for a device - * @hide + * A class representing Wifi Display information for a device. + * + * See Wifi Display technical specification v1.0.0, section 5.1.2. */ -public class WifiP2pWfdInfo implements Parcelable { - - private static final String TAG = "WifiP2pWfdInfo"; +public final class WifiP2pWfdInfo implements Parcelable { - private boolean mWfdEnabled; + private boolean mEnabled; + /** Device information bitmap */ private int mDeviceInfo; - public static final int WFD_SOURCE = 0; - public static final int PRIMARY_SINK = 1; - public static final int SECONDARY_SINK = 2; - public static final int SOURCE_OR_PRIMARY_SINK = 3; - - /* Device information bitmap */ - /** One of {@link #WFD_SOURCE}, {@link #PRIMARY_SINK}, {@link #SECONDARY_SINK} - * or {@link #SOURCE_OR_PRIMARY_SINK} + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "DEVICE_TYPE_" }, value = { + DEVICE_TYPE_WFD_SOURCE, + DEVICE_TYPE_PRIMARY_SINK, + DEVICE_TYPE_SECONDARY_SINK, + DEVICE_TYPE_SOURCE_OR_PRIMARY_SINK}) + public @interface DeviceType {} + + /** The device is a Wifi Display Source. */ + public static final int DEVICE_TYPE_WFD_SOURCE = 0; + /** The device is a primary sink. */ + public static final int DEVICE_TYPE_PRIMARY_SINK = 1; + /** The device is a secondary sink. */ + public static final int DEVICE_TYPE_SECONDARY_SINK = 2; + /** The device is dual-role capable i.e. either a WFD source or a primary sink. */ + public static final int DEVICE_TYPE_SOURCE_OR_PRIMARY_SINK = 3; + + /** + * {@link #mDeviceInfo} & {@link #DEVICE_TYPE} is one of {@link #DEVICE_TYPE_WFD_SOURCE}, + * {@link #DEVICE_TYPE_PRIMARY_SINK}, {@link #DEVICE_TYPE_SECONDARY_SINK} or + * {@link #DEVICE_TYPE_SOURCE_OR_PRIMARY_SINK}. + * + * The bit definition is listed in 5.1.2 WFD Device Information Subelement in + * Wi-Fi Display Technical Specification. */ - private static final int DEVICE_TYPE = 0x3; - private static final int COUPLED_SINK_SUPPORT_AT_SOURCE = 0x4; - private static final int COUPLED_SINK_SUPPORT_AT_SINK = 0x8; - private static final int SESSION_AVAILABLE = 0x30; - private static final int SESSION_AVAILABLE_BIT1 = 0x10; - private static final int SESSION_AVAILABLE_BIT2 = 0x20; + private static final int DEVICE_TYPE = 1 << 1 | 1 << 0; + private static final int COUPLED_SINK_SUPPORT_AT_SOURCE = 1 << 2; + private static final int COUPLED_SINK_SUPPORT_AT_SINK = 1 << 3; + private static final int SESSION_AVAILABLE_BIT1 = 1 << 4; + private static final int SESSION_AVAILABLE_BIT2 = 1 << 5; + private static final int SESSION_AVAILABLE = + SESSION_AVAILABLE_BIT2 | SESSION_AVAILABLE_BIT1; + /* The support of Content Protection using the HDCP system 2.0/2.1. */ + private static final int CONTENT_PROTECTION_SUPPORT = 1 << 8; private int mCtrlPort; private int mMaxThroughput; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public WifiP2pWfdInfo() { - } + /** Default constructor. */ + public WifiP2pWfdInfo() {} + /** @hide */ @UnsupportedAppUsage public WifiP2pWfdInfo(int devInfo, int ctrlPort, int maxTput) { - mWfdEnabled = true; + mEnabled = true; mDeviceInfo = devInfo; mCtrlPort = ctrlPort; mMaxThroughput = maxTput; } - @UnsupportedAppUsage - public boolean isWfdEnabled() { - return mWfdEnabled; + /** Returns true is Wifi Display is enabled, false otherwise. */ + public boolean isEnabled() { + return mEnabled; } - @UnsupportedAppUsage - public void setWfdEnabled(boolean enabled) { - mWfdEnabled = enabled; + /** + * Sets whether Wifi Display should be enabled. + * + * @param enabled true to enable Wifi Display, false to disable + */ + public void setEnabled(boolean enabled) { + mEnabled = enabled; } - @UnsupportedAppUsage + /** + * Get the type of the device. + * One of {@link #DEVICE_TYPE_WFD_SOURCE}, {@link #DEVICE_TYPE_PRIMARY_SINK}, + * {@link #DEVICE_TYPE_SECONDARY_SINK}, {@link #DEVICE_TYPE_SOURCE_OR_PRIMARY_SINK} + */ + @DeviceType public int getDeviceType() { - return (mDeviceInfo & DEVICE_TYPE); + return mDeviceInfo & DEVICE_TYPE; } - @UnsupportedAppUsage - public boolean setDeviceType(int deviceType) { - if (deviceType >= WFD_SOURCE && deviceType <= SOURCE_OR_PRIMARY_SINK) { + /** + * Sets the type of the device. + * + * @param deviceType One of {@link #DEVICE_TYPE_WFD_SOURCE}, {@link #DEVICE_TYPE_PRIMARY_SINK}, + * {@link #DEVICE_TYPE_SECONDARY_SINK}, {@link #DEVICE_TYPE_SOURCE_OR_PRIMARY_SINK} + * @return true if the device type was successfully set, false otherwise + */ + public boolean setDeviceType(@DeviceType int deviceType) { + if (DEVICE_TYPE_WFD_SOURCE <= deviceType + && deviceType <= DEVICE_TYPE_SOURCE_OR_PRIMARY_SINK) { mDeviceInfo &= ~DEVICE_TYPE; mDeviceInfo |= deviceType; return true; @@ -92,35 +133,16 @@ public class WifiP2pWfdInfo implements Parcelable { return false; } - public boolean isCoupledSinkSupportedAtSource() { - return (mDeviceInfo & COUPLED_SINK_SUPPORT_AT_SINK) != 0; - } - - public void setCoupledSinkSupportAtSource(boolean enabled) { - if (enabled ) { - mDeviceInfo |= COUPLED_SINK_SUPPORT_AT_SINK; - } else { - mDeviceInfo &= ~COUPLED_SINK_SUPPORT_AT_SINK; - } - } - - public boolean isCoupledSinkSupportedAtSink() { - return (mDeviceInfo & COUPLED_SINK_SUPPORT_AT_SINK) != 0; - } - - public void setCoupledSinkSupportAtSink(boolean enabled) { - if (enabled ) { - mDeviceInfo |= COUPLED_SINK_SUPPORT_AT_SINK; - } else { - mDeviceInfo &= ~COUPLED_SINK_SUPPORT_AT_SINK; - } - } - + /** Returns true if a session is available, false otherwise. */ public boolean isSessionAvailable() { return (mDeviceInfo & SESSION_AVAILABLE) != 0; } - @UnsupportedAppUsage + /** + * Sets whether a session is available. + * + * @param enabled true to indicate that a session is available, false otherwise. + */ public void setSessionAvailable(boolean enabled) { if (enabled) { mDeviceInfo |= SESSION_AVAILABLE_BIT1; @@ -130,32 +152,56 @@ public class WifiP2pWfdInfo implements Parcelable { } } + /** + * @return true if Content Protection using the HDCP system 2.0/2.1 is supported. + */ + public boolean isContentProtectionSupported() { + return (mDeviceInfo & CONTENT_PROTECTION_SUPPORT) != 0; + } + + /** + * Sets whether Content Protection using the HDCP system 2.0/2.1 is supported. + * + * @param enabled true to indicate that Content Protection is supported, false otherwise. + */ + public void setContentProtectionSupported(boolean enabled) { + if (enabled) { + mDeviceInfo |= CONTENT_PROTECTION_SUPPORT; + } else { + mDeviceInfo &= ~CONTENT_PROTECTION_SUPPORT; + } + } + + /** Returns the TCP port at which the WFD Device listens for RTSP messages. */ public int getControlPort() { return mCtrlPort; } - @UnsupportedAppUsage - public void setControlPort(int port) { + /** Sets the TCP port at which the WFD Device listens for RTSP messages. */ + public void setControlPort(@IntRange(from = 0) int port) { mCtrlPort = port; } - @UnsupportedAppUsage - public void setMaxThroughput(int maxThroughput) { + /** Sets the maximum average throughput capability of the WFD Device, in megabits/second. */ + public void setMaxThroughput(@IntRange(from = 0) int maxThroughput) { mMaxThroughput = maxThroughput; } + /** Returns the maximum average throughput capability of the WFD Device, in megabits/second. */ public int getMaxThroughput() { return mMaxThroughput; } + /** @hide */ public String getDeviceInfoHex() { return String.format( Locale.US, "%04x%04x%04x", mDeviceInfo, mCtrlPort, mMaxThroughput); } + @Override public String toString() { StringBuffer sbuf = new StringBuffer(); - sbuf.append("WFD enabled: ").append(mWfdEnabled); + sbuf.append("WFD enabled: ").append(mEnabled); sbuf.append("WFD DeviceInfo: ").append(mDeviceInfo); sbuf.append("\n WFD CtrlPort: ").append(mCtrlPort); sbuf.append("\n WFD MaxThroughput: ").append(mMaxThroughput); @@ -167,11 +213,10 @@ public class WifiP2pWfdInfo implements Parcelable { return 0; } - /** copy constructor */ - @UnsupportedAppUsage - public WifiP2pWfdInfo(WifiP2pWfdInfo source) { + /** Copy constructor. */ + public WifiP2pWfdInfo(@Nullable WifiP2pWfdInfo source) { if (source != null) { - mWfdEnabled = source.mWfdEnabled; + mEnabled = source.mEnabled; mDeviceInfo = source.mDeviceInfo; mCtrlPort = source.mCtrlPort; mMaxThroughput = source.mMaxThroughput; @@ -179,23 +224,23 @@ public class WifiP2pWfdInfo implements Parcelable { } /** Implement the Parcelable interface */ - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mWfdEnabled ? 1 : 0); + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mEnabled ? 1 : 0); dest.writeInt(mDeviceInfo); dest.writeInt(mCtrlPort); dest.writeInt(mMaxThroughput); } - public void readFromParcel(Parcel in) { - mWfdEnabled = (in.readInt() == 1); + private void readFromParcel(Parcel in) { + mEnabled = (in.readInt() == 1); mDeviceInfo = in.readInt(); mCtrlPort = in.readInt(); mMaxThroughput = in.readInt(); } /** Implement the Parcelable interface */ - @UnsupportedAppUsage - public static final @android.annotation.NonNull Creator<WifiP2pWfdInfo> CREATOR = + public static final @NonNull Creator<WifiP2pWfdInfo> CREATOR = new Creator<WifiP2pWfdInfo>() { public WifiP2pWfdInfo createFromParcel(Parcel in) { WifiP2pWfdInfo device = new WifiP2pWfdInfo(); diff --git a/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl b/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl index 3e37af06ab27..7c92a6b6bdba 100644 --- a/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl +++ b/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl @@ -27,7 +27,7 @@ import android.net.wifi.rtt.RangingRequest; interface IWifiRttManager { boolean isAvailable(); - void startRanging(in IBinder binder, in String callingPackage, in WorkSource workSource, - in RangingRequest request, in IRttCallback callback); + void startRanging(in IBinder binder, in String callingPackage, in String callingFeatureId, + in WorkSource workSource, in RangingRequest request, in IRttCallback callback); void cancelRanging(in WorkSource workSource); } diff --git a/wifi/java/android/net/wifi/rtt/ResponderConfig.java b/wifi/java/android/net/wifi/rtt/ResponderConfig.java index 64dfc3499aaf..be4eeccd0c31 100644 --- a/wifi/java/android/net/wifi/rtt/ResponderConfig.java +++ b/wifi/java/android/net/wifi/rtt/ResponderConfig.java @@ -16,6 +16,8 @@ package android.net.wifi.rtt; +import static android.net.wifi.ScanResult.InformationElement.EID_EXTENSION_PRESENT; +import static android.net.wifi.ScanResult.InformationElement.EID_EXT_HE_CAPABILITIES; import static android.net.wifi.ScanResult.InformationElement.EID_HT_CAPABILITIES; import static android.net.wifi.ScanResult.InformationElement.EID_VHT_CAPABILITIES; @@ -106,7 +108,7 @@ public final class ResponderConfig implements Parcelable { public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4; /** @hide */ - @IntDef({PREAMBLE_LEGACY, PREAMBLE_HT, PREAMBLE_VHT}) + @IntDef({PREAMBLE_LEGACY, PREAMBLE_HT, PREAMBLE_VHT, PREAMBLE_HE}) @Retention(RetentionPolicy.SOURCE) public @interface PreambleType { } @@ -126,6 +128,10 @@ public final class ResponderConfig implements Parcelable { */ public static final int PREAMBLE_VHT = 2; + /** + * Preamble type: HE. + */ + public static final int PREAMBLE_HE = 3; /** * The MAC address of the Responder. Will be null if a Wi-Fi Aware peer identifier (the @@ -307,14 +313,21 @@ public final class ResponderConfig implements Parcelable { if (scanResult.informationElements != null && scanResult.informationElements.length != 0) { boolean htCapabilitiesPresent = false; boolean vhtCapabilitiesPresent = false; + boolean heCapabilitiesPresent = false; + for (ScanResult.InformationElement ie : scanResult.informationElements) { if (ie.id == EID_HT_CAPABILITIES) { htCapabilitiesPresent = true; } else if (ie.id == EID_VHT_CAPABILITIES) { vhtCapabilitiesPresent = true; + } else if (ie.id == EID_EXTENSION_PRESENT && ie.idExt == EID_EXT_HE_CAPABILITIES) { + heCapabilitiesPresent = true; } } - if (vhtCapabilitiesPresent) { + + if (heCapabilitiesPresent) { + preamble = PREAMBLE_HE; + } else if (vhtCapabilitiesPresent) { preamble = PREAMBLE_VHT; } else if (htCapabilitiesPresent) { preamble = PREAMBLE_HT; diff --git a/wifi/java/android/net/wifi/rtt/ResponderLocation.java b/wifi/java/android/net/wifi/rtt/ResponderLocation.java index 970a75d7c418..218b2dcae71d 100644 --- a/wifi/java/android/net/wifi/rtt/ResponderLocation.java +++ b/wifi/java/android/net/wifi/rtt/ResponderLocation.java @@ -21,6 +21,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.location.Address; import android.location.Location; @@ -1367,7 +1368,8 @@ public final class ResponderLocation implements Parcelable { * */ @Nullable - public SparseArray toCivicLocationSparseArray() { + @SuppressLint("ChangedType") + public SparseArray<String> toCivicLocationSparseArray() { if (mCivicLocation != null && mCivicLocation.isValid()) { return mCivicLocation.toSparseArray(); } else { diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java index 457e904126a2..865702af695c 100644 --- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java +++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java @@ -77,7 +77,7 @@ public class WifiRttManager { "android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED"; /** @hide */ - public WifiRttManager(Context context, IWifiRttManager service) { + public WifiRttManager(@NonNull Context context, @NonNull IWifiRttManager service) { mContext = context; mService = service; } @@ -146,8 +146,8 @@ public class WifiRttManager { Binder binder = new Binder(); try { - mService.startRanging(binder, mContext.getOpPackageName(), workSource, request, - new IRttCallback.Stub() { + mService.startRanging(binder, mContext.getOpPackageName(), + mContext.getAttributionTag(), workSource, request, new IRttCallback.Stub() { @Override public void onRangingFailure(int status) throws RemoteException { clearCallingIdentity(); diff --git a/wifi/java/android/net/wifi/util/HexEncoding.java b/wifi/java/android/net/wifi/util/HexEncoding.java new file mode 100644 index 000000000000..9ebf947e2dc3 --- /dev/null +++ b/wifi/java/android/net/wifi/util/HexEncoding.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2014 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.util; + +/** + * Hexadecimal encoding where each byte is represented by two hexadecimal digits. + * + * Note: this is copied from {@link libcore.util.HexEncoding}. + * + * @hide + */ +public class HexEncoding { + + private static final char[] LOWER_CASE_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + private static final char[] UPPER_CASE_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + /** Hidden constructor to prevent instantiation. */ + private HexEncoding() {} + + /** + * Encodes the provided byte as a two-digit hexadecimal String value. + */ + public static String encodeToString(byte b, boolean upperCase) { + char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; + char[] buf = new char[2]; // We always want two digits. + buf[0] = digits[(b >> 4) & 0xf]; + buf[1] = digits[b & 0xf]; + return new String(buf, 0, 2); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + */ + public static char[] encode(byte[] data) { + return encode(data, 0, data.length, true /* upperCase */); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + */ + public static char[] encode(byte[] data, boolean upperCase) { + return encode(data, 0, data.length, upperCase); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + */ + public static char[] encode(byte[] data, int offset, int len) { + return encode(data, offset, len, true /* upperCase */); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + */ + private static char[] encode(byte[] data, int offset, int len, boolean upperCase) { + char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; + char[] result = new char[len * 2]; + for (int i = 0; i < len; i++) { + byte b = data[offset + i]; + int resultIndex = 2 * i; + result[resultIndex] = (digits[(b >> 4) & 0x0f]); + result[resultIndex + 1] = (digits[b & 0x0f]); + } + + return result; + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + */ + public static String encodeToString(byte[] data) { + return encodeToString(data, true /* upperCase */); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + */ + public static String encodeToString(byte[] data, boolean upperCase) { + return new String(encode(data, upperCase)); + } + + /** + * Decodes the provided hexadecimal string into a byte array. Odd-length inputs + * are not allowed. + * + * Throws an {@code IllegalArgumentException} if the input is malformed. + */ + public static byte[] decode(String encoded) throws IllegalArgumentException { + return decode(encoded.toCharArray()); + } + + /** + * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar} + * is {@code true} odd-length inputs are allowed and the first character is interpreted + * as the lower bits of the first result byte. + * + * Throws an {@code IllegalArgumentException} if the input is malformed. + */ + public static byte[] decode(String encoded, boolean allowSingleChar) + throws IllegalArgumentException { + return decode(encoded.toCharArray(), allowSingleChar); + } + + /** + * Decodes the provided hexadecimal string into a byte array. Odd-length inputs + * are not allowed. + * + * Throws an {@code IllegalArgumentException} if the input is malformed. + */ + public static byte[] decode(char[] encoded) throws IllegalArgumentException { + return decode(encoded, false); + } + + /** + * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar} + * is {@code true} odd-length inputs are allowed and the first character is interpreted + * as the lower bits of the first result byte. + * + * Throws an {@code IllegalArgumentException} if the input is malformed. + */ + public static byte[] decode(char[] encoded, boolean allowSingleChar) + throws IllegalArgumentException { + int encodedLength = encoded.length; + int resultLengthBytes = (encodedLength + 1) / 2; + byte[] result = new byte[resultLengthBytes]; + + int resultOffset = 0; + int i = 0; + if (allowSingleChar) { + if ((encodedLength % 2) != 0) { + // Odd number of digits -- the first digit is the lower 4 bits of the first result + // byte. + result[resultOffset++] = (byte) toDigit(encoded, i); + i++; + } + } else { + if ((encodedLength % 2) != 0) { + throw new IllegalArgumentException("Invalid input length: " + encodedLength); + } + } + + for (; i < encodedLength; i += 2) { + result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1)); + } + + return result; + } + + private static int toDigit(char[] str, int offset) throws IllegalArgumentException { + // NOTE: that this isn't really a code point in the traditional sense, since we're + // just rejecting surrogate pairs outright. + int pseudoCodePoint = str[offset]; + + if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') { + return pseudoCodePoint - '0'; + } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') { + return 10 + (pseudoCodePoint - 'a'); + } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') { + return 10 + (pseudoCodePoint - 'A'); + } + + throw new IllegalArgumentException("Illegal char: " + str[offset] + " at offset " + offset); + } +} diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java deleted file mode 100644 index fcf8bd5eaa3e..000000000000 --- a/wifi/java/com/android/server/wifi/BaseWifiService.java +++ /dev/null @@ -1,484 +0,0 @@ -/** - * Copyright (c) 2018, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License") { - * throw new UnsupportedOperationException(); - } - * 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 com.android.server.wifi; - -import android.content.pm.ParceledListSlice; -import android.net.DhcpInfo; -import android.net.Network; -import android.net.wifi.IDppCallback; -import android.net.wifi.INetworkRequestMatchCallback; -import android.net.wifi.IOnWifiUsabilityStatsListener; -import android.net.wifi.ISoftApCallback; -import android.net.wifi.ITrafficStateCallback; -import android.net.wifi.IWifiManager; -import android.net.wifi.ScanResult; -import android.net.wifi.WifiActivityEnergyInfo; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiNetworkSuggestion; -import android.net.wifi.hotspot2.IProvisioningCallback; -import android.net.wifi.hotspot2.OsuProvider; -import android.net.wifi.hotspot2.PasspointConfiguration; -import android.os.IBinder; -import android.os.Messenger; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.os.WorkSource; - -import java.util.List; -import java.util.Map; - -/** - * Empty concrete class implementing IWifiManager with stub methods throwing runtime exceptions. - * - * This class is meant to be extended by real implementations of IWifiManager in order to facilitate - * cross-repo changes to WiFi internal APIs, including the introduction of new APIs, the removal of - * deprecated APIs, or the migration of existing API signatures. - * - * When an existing API is scheduled for removal, it can be removed from IWifiManager.aidl - * immediately and marked as @Deprecated first in this class. Children inheriting this class are - * then given a short grace period to update themselves before the @Deprecated stub is removed for - * good. If the API scheduled for removal has a replacement or an overload (signature change), - * these should be introduced before the stub is removed to allow children to migrate. - * - * When a new API is added to IWifiManager.aidl, a stub should be added in BaseWifiService as - * well otherwise compilation will fail. - */ -public class BaseWifiService extends IWifiManager.Stub { - - private static final String TAG = BaseWifiService.class.getSimpleName(); - - @Override - public long getSupportedFeatures() { - throw new UnsupportedOperationException(); - } - - @Override - public WifiActivityEnergyInfo reportActivityInfo() { - throw new UnsupportedOperationException(); - } - - @Override - public void requestActivityInfo(ResultReceiver result) { - throw new UnsupportedOperationException(); - } - - @Override - public ParceledListSlice getConfiguredNetworks(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public ParceledListSlice getPrivilegedConfiguredNetworks(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public Map<String, Map<Integer, List<ScanResult>>> getAllMatchingFqdnsForScanResults( - List<ScanResult> scanResults) { - throw new UnsupportedOperationException(); - } - - @Override - public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders( - List<ScanResult> scanResults) { - throw new UnsupportedOperationException(); - } - - @Override - public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders( - List<OsuProvider> osuProviders) { - throw new UnsupportedOperationException(); - } - - @Override - public int addOrUpdateNetwork(WifiConfiguration config, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addOrUpdatePasspointConfiguration( - PasspointConfiguration config, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removePasspointConfiguration(String fqdn, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public List<PasspointConfiguration> getPasspointConfigurations(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public List<WifiConfiguration> getWifiConfigsForPasspointProfiles(List<String> fqdnList) { - throw new UnsupportedOperationException(); - } - - @Override - public void queryPasspointIcon(long bssid, String fileName) { - throw new UnsupportedOperationException(); - } - - @Override - public int matchProviderWithCurrentNetwork(String fqdn) { - throw new UnsupportedOperationException(); - } - - @Override - public void deauthenticateNetwork(long holdoff, boolean ess) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeNetwork(int netId, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean enableNetwork(int netId, boolean disableOthers, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean disableNetwork(int netId, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean startScan(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public List<ScanResult> getScanResults(String callingPackage) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean disconnect(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean reconnect(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean reassociate(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public WifiInfo getConnectionInfo(String callingPackage) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean setWifiEnabled(String packageName, boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public int getWifiEnabledState() { - throw new UnsupportedOperationException(); - } - - @Override - public void setCountryCode(String country) { - throw new UnsupportedOperationException(); - } - - @Override - public String getCountryCode() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isDualBandSupported() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean needs5GHzToAnyApBandConversion() { - throw new UnsupportedOperationException(); - } - - @Override - public DhcpInfo getDhcpInfo() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isScanAlwaysAvailable() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean acquireWifiLock(IBinder lock, int lockType, String tag, WorkSource ws) { - throw new UnsupportedOperationException(); - } - - @Override - public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean releaseWifiLock(IBinder lock) { - throw new UnsupportedOperationException(); - } - - @Override - public void initializeMulticastFiltering() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isMulticastEnabled() { - throw new UnsupportedOperationException(); - } - - @Override - public void acquireMulticastLock(IBinder binder, String tag) { - throw new UnsupportedOperationException(); - } - - @Override - public void releaseMulticastLock(String tag) { - throw new UnsupportedOperationException(); - } - - @Override - public void updateInterfaceIpState(String ifaceName, int mode) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean startSoftAp(WifiConfiguration wifiConfig) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean stopSoftAp() { - throw new UnsupportedOperationException(); - } - - @Override - public int startLocalOnlyHotspot(Messenger messenger, IBinder binder, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void stopLocalOnlyHotspot() { - throw new UnsupportedOperationException(); - } - - @Override - public void startWatchLocalOnlyHotspot(Messenger messenger, IBinder binder) { - throw new UnsupportedOperationException(); - } - - @Override - public void stopWatchLocalOnlyHotspot() { - throw new UnsupportedOperationException(); - } - - @Override - public int getWifiApEnabledState() { - throw new UnsupportedOperationException(); - } - - @Override - public WifiConfiguration getWifiApConfiguration() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean setWifiApConfiguration(WifiConfiguration wifiConfig, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void notifyUserOfApBandConversion(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public Messenger getWifiServiceMessenger(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void enableTdls(String remoteIPAddress, boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public String getCurrentNetworkWpsNfcConfigurationToken() { - throw new UnsupportedOperationException(); - } - - @Override - public void enableVerboseLogging(int verbose) { - throw new UnsupportedOperationException(); - } - - @Override - public int getVerboseLoggingLevel() { - throw new UnsupportedOperationException(); - } - - @Override - public void enableWifiConnectivityManager(boolean enabled) { - throw new UnsupportedOperationException(); - } - - @Override - public void disableEphemeralNetwork(String SSID, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void factoryReset(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public Network getCurrentNetwork() { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] retrieveBackupData() { - throw new UnsupportedOperationException(); - } - - @Override - public void restoreBackupData(byte[] data) { - throw new UnsupportedOperationException(); - } - - @Override - public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) { - throw new UnsupportedOperationException(); - } - - @Override - public void startSubscriptionProvisioning( - OsuProvider provider, IProvisioningCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerSoftApCallback( - IBinder binder, ISoftApCallback callback, int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterSoftApCallback(int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerTrafficStateCallback( - IBinder binder, ITrafficStateCallback callback, int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterTrafficStateCallback(int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerNetworkRequestMatchCallback( - IBinder binder, INetworkRequestMatchCallback callback, int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterNetworkRequestMatchCallback(int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public int addNetworkSuggestions( - List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) { - throw new UnsupportedOperationException(); - } - - @Override - public int removeNetworkSuggestions( - List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) { - throw new UnsupportedOperationException(); - } - - @Override - public String[] getFactoryMacAddresses() { - throw new UnsupportedOperationException(); - } - - @Override - public void setDeviceMobilityState(int state) { - throw new UnsupportedOperationException(); - } - - @Override - public void startDppAsConfiguratorInitiator(IBinder binder, String enrolleeUri, - int selectedNetworkId, int netRole, IDppCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void startDppAsEnrolleeInitiator(IBinder binder, String configuratorUri, - IDppCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void stopDppSession() throws RemoteException { - throw new UnsupportedOperationException(); - } - - @Override - public void addOnWifiUsabilityStatsListener( - IBinder binder, IOnWifiUsabilityStatsListener listener, int listenerIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeOnWifiUsabilityStatsListener(int listenerIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec) { - throw new UnsupportedOperationException(); - } -} diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp new file mode 100644 index 000000000000..6a39959e8cfd --- /dev/null +++ b/wifi/tests/Android.bp @@ -0,0 +1,50 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Make test APK +// ============================================================ + +android_test { + name: "FrameworksWifiApiTests", + + defaults: ["framework-wifi-test-defaults"], + + srcs: ["**/*.java"], + + jacoco: { + include_filter: ["android.net.wifi.*"], + // TODO(b/147521214) need to exclude test classes + exclude_filter: [], + }, + + static_libs: [ + "androidx.test.rules", + "core-test-rules", + "guava", + "mockito-target-minus-junit4", + "net-tests-utils", + "frameworks-base-testutils", + "truth-prebuilt", + ], + + libs: [ + "android.test.runner", + "android.test.base", + ], + + test_suites: [ + "device-tests", + "mts", + ], +} diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk deleted file mode 100644 index 3453d6ec827f..000000000000 --- a/wifi/tests/Android.mk +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (C) 2016 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. - -LOCAL_PATH:= $(call my-dir) - -# Make test APK -# ============================================================ -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -# This list is generated from the java source files in this module -# The list is a comma separated list of class names with * matching zero or more characters. -# Example: -# Input files: src/com/android/server/wifi/Test.java src/com/android/server/wifi/AnotherTest.java -# Generated exclude list: com.android.server.wifi.Test*,com.android.server.wifi.AnotherTest* - -# Filter all src files to just java files -local_java_files := $(filter %.java,$(LOCAL_SRC_FILES)) -# Transform java file names into full class names. -# This only works if the class name matches the file name and the directory structure -# matches the package. -local_classes := $(subst /,.,$(patsubst src/%.java,%,$(local_java_files))) -# Convert class name list to jacoco exclude list -# This appends a * to all classes and replace the space separators with commas. -# These patterns will match all classes in this module and their inner classes. -jacoco_exclude := $(subst $(space),$(comma),$(patsubst %,%*,$(local_classes))) - -jacoco_include := android.net.wifi.* - -LOCAL_JACK_COVERAGE_INCLUDE_FILTER := $(jacoco_include) -LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude) - -LOCAL_STATIC_JAVA_LIBRARIES := \ - androidx.test.rules \ - core-test-rules \ - guava \ - mockito-target-minus-junit4 \ - net-tests-utils \ - frameworks-base-testutils \ - truth-prebuilt \ - -LOCAL_JAVA_LIBRARIES := \ - android.test.runner \ - android.test.base \ - -LOCAL_PACKAGE_NAME := FrameworksWifiApiTests -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_COMPATIBILITY_SUITE := device-tests - -include $(BUILD_PACKAGE) diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml index cae19e46c6af..34e2e3af9cda 100644 --- a/wifi/tests/AndroidTest.xml +++ b/wifi/tests/AndroidTest.xml @@ -14,7 +14,7 @@ limitations under the License. --> <configuration description="Runs Frameworks Wifi API Tests."> - <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="test-file-name" value="FrameworksWifiApiTests.apk" /> </target_preparer> @@ -25,4 +25,10 @@ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> <option name="hidden-api-checks" value="false"/> </test> + + <!-- Only run FrameworksWifiApiTests in MTS if the Wifi Mainline module is installed. --> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> + <option name="mainline-module-package-name" value="com.google.android.wifi" /> + </object> </configuration> diff --git a/wifi/tests/README.md b/wifi/tests/README.md index b0594f2d29b1..f90940470432 100644 --- a/wifi/tests/README.md +++ b/wifi/tests/README.md @@ -8,12 +8,9 @@ libraries. The easiest way to run tests is simply run ``` -frameworks/base/wifi/tests/runtests.sh +atest android.net.wifi ``` -`runtests.sh` will build the test project and all of its dependencies and push the APK to the -connected device. It will then run the tests on the device. - To pick up changes in framework/base, you will need to: 1. rebuild the framework library 'make -j32' 2. sync over the updated library to the device 'adb sync' @@ -24,22 +21,6 @@ To enable syncing data to the device for first time after clean reflash: 2. adb reboot 3. adb remount -See below for a few example of options to limit which tests are run. -See the -[AndroidJUnitRunner Documentation](https://developer.android.com/reference/android/support/test/runner/AndroidJUnitRunner.html) -for more details on the supported options. - -``` -runtests.sh -e package android.net.wifi -runtests.sh -e class android.net.wifi.WifiScannerTest -``` - -If you manually build and push the test APK to the device you can run tests using - -``` -adb shell am instrument -w 'android.net.wifi.test/androidx.test.runner.AndroidJUnitRunner' -``` - ## Adding Tests Tests can be added by adding classes to the src directory. JUnit4 style test cases can be written by simply annotating test methods with `org.junit.Test`. diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 index 56919c25de46..760c8395e659 100644 --- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 @@ -12,75 +12,75 @@ VnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSthVEF3TVR3dlRt OWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRtRnRaVDVJYjIx bFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1 dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNB -OFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklDQWdQQzlPYjJS -bFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrWlJSRTQ4 -TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1MWF6d3ZWbUZz -ZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0Fn -SUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2ClpHVk9ZVzFs -UGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpoYkhWbFBnb2dJ -Q0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4VG05a1pUNEtJ -Q0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVoYldVK0NpQWdJ -Q0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhiRzA4CkwwNXZa -R1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHljbVZrTG1OdmJU -d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lD -QWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThMMDV2WkdWT1lX -MWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0ClpU -NVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lXeDFaVDVxWVcx -bGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05 -a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BDOU9iMlJsVG1G -dFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3dlZtRnNkV1Ur -CkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0Fn -SUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0Fn -SUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1GdFpUNUZRVkJV -ZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1UrTWpFOEwxWmhi -SFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxQ -Z29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2WkR3dlRtOWta -VTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0VmpJOEwxWmhi -SFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0Np -QWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BF -NXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD -QWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQa05sY25ScFpt -bGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lXeDFaVDU0TlRB -NWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1 -dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1qVTJSbWx1WjJW -eWNISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdVKwpNV1l4WmpG +OFZtRnNkV1UrUlhoaGJYQnNaU0JPWlhSM2IzSnJQQzlXWVd4MVpUNEtJQ0FnCklDQWdJQ0E4TDA1 +dlpHVStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStSbEZF +VGp3dlRtOWsKWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVSthRzkwYzNCdmRDNWxlR0Z0 +Y0d4bExtNWxkRHd2Vm1Gc2RXVStDaUFnSUNBZwpJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhP +YjJSbFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbEp2WVcxcGJtZERiMjV6CmIzSjBhWFZ0 +VDBrOEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqRXhNakl6TXl3ME5EVTFO +alk4TDFaaGJIVmwKUGdvZ0lDQWdJQ0FnSUR3dlRtOWtaVDRLSUNBZ0lDQWdQQzlPYjJSbFBnb2dJ +Q0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0E4VG05awpaVTVoYldVK1EzSmxaR1Z1ZEdsaGJEd3ZU +bTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrClpVNWhi +V1UrVW1WaGJHMDhMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbVY0WVcxd2JH +VXVZMjl0UEM5V1lXeDEKWlQ0S0lDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEU1dlpH +VStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrVlhObApjbTVoYldWUVlYTnpkMjl5WkR3dlRt +OWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2ClpH +Vk9ZVzFsUGxWelpYSnVZVzFsUEM5T2IyUmxUbUZ0WlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRlpoYkhW +bFBuVnpaWEk4TDFaaGJIVmwKUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNB +Z1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJsVG1GdApaVDVRWVhOemQyOXlaRHd2VG05 +a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gxWlQ1alIwWjZZek5rZG1OdFVUMDhMMVpo +CmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFn +SUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNUZRVkJOWlhSb2IyUThMMDV2WkdWT1lXMWxQZ29n +SUNBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZwpJQ0E4VG05a1pVNWhiV1Ur +UlVGUVZIbHdaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqSXhQ +QzlXCllXeDFaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThU +bTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWcKSUNBOFRtOWtaVTVoYldVK1NXNXVaWEpOWlhSb2IyUThM +MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gxWlQ1TgpVeTFEU0VGUUxWWXlQ +QzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2Iy +UmxQZ29nCklDQWdJQ0FnSUR3dlRtOWtaVDRLSUNBZ0lDQWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lD +QWdJRHhPYjJSbFRtRnRaVDVFYVdkcGRHRnMKUTJWeWRHbG1hV05oZEdVOEwwNXZaR1ZPWVcxbFBn +b2dJQ0FnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbApUbUZ0WlQ1RFpY +SjBhV1pwWTJGMFpWUjVjR1U4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV +K2VEVXdPWFl6ClBDOVdZV3gxWlQ0S0lDQWdJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNB +Z0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQTgKVG05a1pVNWhiV1UrUTJWeWRGTklRVEkxTmta +cGJtZGxjbkJ5YVc1MFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaApiSFZsUGpG bU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZt -TVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNBOEwwNXZaR1Ur -Q2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0Fn -UEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4VG05awpaVDRL -SUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJ -Q0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNBOEwwNXZa -R1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2WkdWT1lXMWxQ -a1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1UrTWpROApM -MVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lD -QWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQQzlOWjIxMFZI -SmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXg1MDktY2Et -Y2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFDUlVkSlRpQkRS -VkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtRVWxNYkVaa2Qz -cE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5UbFlLUWtGTlZF -SXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpUazFxV1hkTlZF -RTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5VVEJGZUUxSlNV -Skpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMwTkJVVVZCQ25w -dVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZYTldkVzFFWWxs -SWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01TdHZSMWhhZGto -M2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJVRkRaV1pXYW1v -d2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJoU1FqZzFNVEpR -UWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdKck1IVjVhM1Jr -WW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtzM2FFUTRjRkIy -WmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxhCmFYQllOREY0 -UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVXTTJreGRIRXdO -R3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZHU1hkWU5IWnpP -RUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFFYlVGR1NYZFlO -SFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJkMFJuCldVUldV -VkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldVakJVUWtGVmQw -RjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZMDVCVVVWTVFs -RkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZDamxIUlZBdmRX -OW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNYZEpWV00zCmQy -azNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1EwOTBhWE5rUW5F -eWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JXMVdUQW94Y1VK -S2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVFMXVWR3c0ZUVW -WFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhwaFNFb3hkVlk0 -Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpGS1VDdHNlRllL -YlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNSa1RrNTJRMWw2 -YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmtsRFFWUkZMUzB0 -TFMwSwotLXtib3VuZGFyeX0tLQo= +TVdZeFpqRm1NV1l4ClpqRm1NV1l4WmpGbU1XWThMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlP +YjJSbFBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWcKSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0Fn +SUNBZ0lEeE9iMlJsVG1GdFpUNVRTVTA4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZwpQRTV2 +WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFRtRnRaVDVKVFZOSlBDOU9iMlJsVG1GdFpUNEtJ +Q0FnSUNBZ0lDQWdJQ0FnClBGWmhiSFZsUGpFeU16UTFOaW84TDFaaGJIVmxQZ29nSUNBZ0lDQWdJ +Q0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVSsKQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9i +MlJsVG1GdFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaApi +SFZsUGpJelBDOVdZV3gxWlQ0S0lDQWdJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNBOEww +NXZaR1UrQ2lBZ0lDQWdJRHd2ClRtOWtaVDRLSUNBZ0lEd3ZUbTlrWlQ0S0lDQThMMDV2WkdVK0Nq +d3ZUV2R0ZEZSeVpXVSsKCi0te2JvdW5kYXJ5fQpDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL3gt +eDUwOS1jYS1jZXJ0CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NAoKTFMwdExTMUNS +VWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVUkxSRU5EUVdoRFowRjNTVUpCWjBsS1FV +bE1iRVprZDNwTQpWblZ5VFVFd1IwTlRjVWRUU1dJelJGRkZRa04zVlVGTlFrbDRSVVJCVDBKblRs +WUtRa0ZOVkVJd1ZrSlZRMEpFVVZSRmQwaG9ZMDVOClZGbDNUVlJGZVUxVVJURk5SRVV4VjJoalRr +MXFXWGROVkVFMVRWUkZNVTFFUlRGWGFrRlRUVkpCZHdwRVoxbEVWbEZSUkVWM1pFWlIKVmtGblVU +QkZlRTFKU1VKSmFrRk9RbWRyY1docmFVYzVkekJDUVZGRlJrRkJUME5CVVRoQlRVbEpRa05uUzBO +QlVVVkJDbnB1UVZCVgplakkyVFhOaFpUUjNjelF6WTNwU05ERXZTakpSZEhKVFNWcFZTMjFXVlhO +V2RXMUVZbGxJY2xCT2RsUllTMU5OV0VGalpYZFBVa1JSCldWZ0tVbkYyU0had2JqaERjMk5DTVN0 +dlIxaGFka2gzZUdvMGVsWXdWMHR2U3pKNlpWaHJZWFV6ZG1ONWJETklTVXQxY0VwbWNUSlUKUlVG +RFpXWldhbW93ZEFwS1Z5dFlNelZRUjFkd09TOUlOWHBKVlU1V1RsWnFVemRWYlhNNE5FbDJTMmhT +UWpnMU1USlFRamxWZVVoaApaMWhaVmxnMVIxZHdRV05XY0hsbWNteFNDa1pKT1ZGa2FHZ3JVR0py +TUhWNWEzUmtZbVl2UTJSbVowaFBiMlZpY2xSMGQxSnNhazB3CmIwUjBXQ3N5UTNZMmFqQjNRa3Mz +YUVRNGNGQjJaakVyZFhrS1IzcGplbWxuUVZVdk5FdDNOMlZhY1hsa1pqbENLelZTZFhCU0swbGEK +YVhCWU5ERjRSV2xKY2t0U2QzRnBOVEUzVjFkNldHTnFZVWN5WTA1aVpqUTFNUXA0Y0VnMVVHNVdN +Mmt4ZEhFd05HcE5SMUZWZWtaMwpTVVJCVVVGQ2J6UkhRVTFJTkhkSVVWbEVWbEl3VDBKQ1dVVkdT +WGRZTkhaek9FSnBRbU5UWTI5a0NqVnViMXBJVWswNFJUUXJhVTFGClNVZEJNVlZrU1hkUk4wMUVi +VUZHU1hkWU5IWnpPRUpwUW1OVFkyOWtOVzV2V2toU1RUaEZOQ3RwYjFKaGEwWkVRVk1LVFZKQmQw +Um4KV1VSV1VWRkVSWGRrUmxGV1FXZFJNRVY0WjJkclFXZDFWVll6UkUxMFZ6WnpkMFJCV1VSV1Vq +QlVRa0ZWZDBGM1JVSXZla0ZNUW1kTwpWZ3BJVVRoRlFrRk5RMEZSV1hkRVVWbEtTMjlhU1doMlkw +NUJVVVZNUWxGQlJHZG5SVUpCUm1aUmNVOVVRVGRTZGpkTEsyeDFVVGR3CmJtRnpORUpaZDBoRkNq +bEhSVkF2ZFc5b2RqWkxUM2t3VkVkUlJtSnlVbFJxUm05TVZrNUNPVUphTVhsdFRVUmFNQzlVU1hk +SlZXTTMKZDJrM1lUaDBOVzFGY1ZsSU1UVXpkMWNLWVZkdmIybFRhbmxNVEdoMVNUUnpUbkpPUTA5 +MGFYTmtRbkV5Y2pKTlJsaDBObWd3YlVGUgpXVTlRZGpoU09FczNMMlpuVTNoSFJuRjZhSGxPYlcx +V1RBb3hjVUpLYkdSNE16UlRjSGR6VkVGTVVWWlFZalJvUjNkS2VscG1jakZRClkzQkZVWGcyZUUx +dVZHdzRlRVZYV2tVelRYTTVPWFZoVlhoaVVYRkpkMUoxQ2t4blFVOXJUa050V1RKdE9EbFdhSHBo +U0VveGRWWTQKTlVGa1RTOTBSQ3RaYzIxc2JtNXFkRGxNVWtObGFtSkNhWEJxU1VkcVQxaHlaekZL +VUN0c2VGWUtiWFZOTkhaSUsxQXZiV3h0ZUhOUQpVSG93WkRZMVlpdEZSMjFLV25CdlRHdFBMM1Jr +VGs1MlExbDZha3B3VkVWWGNFVnpUelpPVFdoTFdXODlDaTB0TFMwdFJVNUVJRU5GClVsUkpSa2xE +UVZSRkxTMHRMUzBLCi0te2JvdW5kYXJ5fS0tCg==
\ No newline at end of file diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf index a44b54222589..5b4e4cb947cd 100644 --- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf @@ -13,38 +13,38 @@ YTptbzpob3RzcG90MmRvdDAtcGVycHJvdmlkZXJzdWJzY3JpcHRpb246MS4wPC9EREZOYW1lPgog ICAgICA8L1R5cGU+CiAgICA8L1JUUHJvcGVydGllcz4KICAgIDxOb2RlPgogICAgICA8Tm9kZU5h bWU+aTAwMTwvTm9kZU5hbWU+CiAgICAgIDxOb2RlPgogICAgICAgIDxOb2RlTmFtZT5Ib21lU1A8 L05vZGVOYW1lPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkZyaWVuZGx5TmFt -ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8VmFsdWU+Q2VudHVyeSBIb3VzZTwvVmFsdWU+CiAgICAg -ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkZRRE48L05vZGVO -YW1lPgogICAgICAgICAgPFZhbHVlPm1pNi5jby51azwvVmFsdWU+CiAgICAgICAgPC9Ob2RlPgog -ICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlJvYW1pbmdDb25zb3J0aXVtT0k8L05v -ZGVOYW1lPgogICAgICAgICAgPFZhbHVlPjExMjIzMyw0NDU1NjY8L1ZhbHVlPgogICAgICAgIDwv -Tm9kZT4KICAgICAgPC9Ob2RlPgogICAgICA8Tm9kZT4KICAgICAgICA8Tm9kZU5hbWU+Q3JlZGVu -dGlhbDwvTm9kZU5hbWU+CiAgICAgICAgPE5vZGU+CiAgICAgICAgICA8Tm9kZU5hbWU+UmVhbG08 -L05vZGVOYW1lPgogICAgICAgICAgPFZhbHVlPnNoYWtlbi5zdGlycmVkLmNvbTwvVmFsdWU+CiAg -ICAgICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlVzZXJuYW1l -UGFzc3dvcmQ8L05vZGVOYW1lPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2RlTmFt -ZT5Vc2VybmFtZTwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT5qYW1lczwvVmFsdWU+CiAg -ICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1lPlBh -c3N3b3JkPC9Ob2RlTmFtZT4KICAgICAgICAgICAgPFZhbHVlPlltOXVaREF3Tnc9PTwvVmFsdWU+ -CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1l -PkVBUE1ldGhvZDwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxOb2RlPgogICAgICAgICAgICAgIDxO -b2RlTmFtZT5FQVBUeXBlPC9Ob2RlTmFtZT4KICAgICAgICAgICAgICA8VmFsdWU+MjE8L1ZhbHVl -PgogICAgICAgICAgICA8L05vZGU+CiAgICAgICAgICAgIDxOb2RlPgogICAgICAgICAgICAgIDxO -b2RlTmFtZT5Jbm5lck1ldGhvZDwvTm9kZU5hbWU+CiAgICAgICAgICAgICAgPFZhbHVlPk1TLUNI -QVAtVjI8L1ZhbHVlPgogICAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8L05vZGU+CiAgICAg -ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkRpZ2l0YWxDZXJ0 -aWZpY2F0ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1l -PkNlcnRpZmljYXRlVHlwZTwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT54NTA5djM8L1Zh +ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8VmFsdWU+RXhhbXBsZSBOZXR3b3JrPC9WYWx1ZT4KICAg +ICAgICA8L05vZGU+CiAgICAgICAgPE5vZGU+CiAgICAgICAgICA8Tm9kZU5hbWU+RlFETjwvTm9k +ZU5hbWU+CiAgICAgICAgICA8VmFsdWU+aG90c3BvdC5leGFtcGxlLm5ldDwvVmFsdWU+CiAgICAg +ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlJvYW1pbmdDb25z +b3J0aXVtT0k8L05vZGVOYW1lPgogICAgICAgICAgPFZhbHVlPjExMjIzMyw0NDU1NjY8L1ZhbHVl +PgogICAgICAgIDwvTm9kZT4KICAgICAgPC9Ob2RlPgogICAgICA8Tm9kZT4KICAgICAgICA8Tm9k +ZU5hbWU+Q3JlZGVudGlhbDwvTm9kZU5hbWU+CiAgICAgICAgPE5vZGU+CiAgICAgICAgICA8Tm9k +ZU5hbWU+UmVhbG08L05vZGVOYW1lPgogICAgICAgICAgPFZhbHVlPmV4YW1wbGUuY29tPC9WYWx1 +ZT4KICAgICAgICA8L05vZGU+CiAgICAgICAgPE5vZGU+CiAgICAgICAgICA8Tm9kZU5hbWU+VXNl +cm5hbWVQYXNzd29yZDwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5v +ZGVOYW1lPlVzZXJuYW1lPC9Ob2RlTmFtZT4KICAgICAgICAgICAgPFZhbHVlPnVzZXI8L1ZhbHVl +PgogICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2RlTmFt +ZT5QYXNzd29yZDwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT5jR0Z6YzNkdmNtUT08L1Zh bHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2Rl -TmFtZT5DZXJ0U0hBMjU2RmluZ2VycHJpbnQ8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+ -MWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYx -ZjFmMWYxZjwvVmFsdWU+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgPC9Ob2RlPgogICAgICAg -IDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlNJTTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9k -ZT4KICAgICAgICAgICAgPE5vZGVOYW1lPklNU0k8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFs -dWU+aW1zaTwvVmFsdWU+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAg -ICAgICAgPE5vZGVOYW1lPkVBUFR5cGU8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+MjQ8 -L1ZhbHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgIDwvTm9kZT4KICAgICAgPC9Ob2RlPgog -ICAgPC9Ob2RlPgogIDwvTm9kZT4KPC9NZ210VHJlZT4K +TmFtZT5FQVBNZXRob2Q8L05vZGVOYW1lPgogICAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAg +ICA8Tm9kZU5hbWU+RUFQVHlwZTwvTm9kZU5hbWU+CiAgICAgICAgICAgICAgPFZhbHVlPjIxPC9W +YWx1ZT4KICAgICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAg +ICA8Tm9kZU5hbWU+SW5uZXJNZXRob2Q8L05vZGVOYW1lPgogICAgICAgICAgICAgIDxWYWx1ZT5N +Uy1DSEFQLVYyPC9WYWx1ZT4KICAgICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPC9Ob2RlPgog +ICAgICAgIDwvTm9kZT4KICAgICAgICA8Tm9kZT4KICAgICAgICAgIDxOb2RlTmFtZT5EaWdpdGFs +Q2VydGlmaWNhdGU8L05vZGVOYW1lPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2Rl +TmFtZT5DZXJ0aWZpY2F0ZVR5cGU8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+eDUwOXYz +PC9WYWx1ZT4KICAgICAgICAgIDwvTm9kZT4KICAgICAgICAgIDxOb2RlPgogICAgICAgICAgICA8 +Tm9kZU5hbWU+Q2VydFNIQTI1NkZpbmdlcnByaW50PC9Ob2RlTmFtZT4KICAgICAgICAgICAgPFZh +bHVlPjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYx +ZjFmMWYxZjFmMWY8L1ZhbHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgIDwvTm9kZT4KICAg +ICAgICA8Tm9kZT4KICAgICAgICAgIDxOb2RlTmFtZT5TSU08L05vZGVOYW1lPgogICAgICAgICAg +PE5vZGU+CiAgICAgICAgICAgIDxOb2RlTmFtZT5JTVNJPC9Ob2RlTmFtZT4KICAgICAgICAgICAg +PFZhbHVlPjEyMzQ1Nio8L1ZhbHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPE5vZGU+ +CiAgICAgICAgICAgIDxOb2RlTmFtZT5FQVBUeXBlPC9Ob2RlTmFtZT4KICAgICAgICAgICAgPFZh +bHVlPjIzPC9WYWx1ZT4KICAgICAgICAgIDwvTm9kZT4KICAgICAgICA8L05vZGU+CiAgICAgIDwv +Tm9kZT4KICAgIDwvTm9kZT4KICA8L05vZGU+CjwvTWdtdFRyZWU+ --{boundary} Content-Type: application/x-x509-ca-cert diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64 index 906bfb397464..2775a9f419f9 100644 --- a/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64 +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64 @@ -1,85 +1,86 @@ -Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu -dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh -cHBsaWNhdGlvbi9wYXNzcG9pbnQtcHJvZmlsZQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBi -YXNlNjQKClBFMW5iWFJVY21WbElIaHRiRzV6UFNKemVXNWpiV3c2Wkcxa1pHWXhMaklpUGdvZ0lE -eFdaWEpFVkVRK01TNHlQQzlXWlhKRVZFUSsKQ2lBZ1BFNXZaR1UrQ2lBZ0lDQThUbTlrWlU1aGJX -VStVR1Z5VUhKdmRtbGtaWEpUZFdKelkzSnBjSFJwYjI0OEwwNXZaR1ZPWVcxbApQZ29nSUNBZ1BG -SlVVSEp2Y0dWeWRHbGxjejRLSUNBZ0lDQWdQRlI1Y0dVK0NpQWdJQ0FnSUNBZ1BFUkVSazVoYldV -K2RYSnVPbmRtCllUcHRienBvYjNSemNHOTBNbVJ2ZERBdGNHVnljSEp2ZG1sa1pYSnpkV0p6WTNK -cGNIUnBiMjQ2TVM0d1BDOUVSRVpPWVcxbFBnb2cKSUNBZ0lDQThMMVI1Y0dVK0NpQWdJQ0E4TDFK -VVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQThUbTlrWlU1aApiV1UrYVRB -d01Ud3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0 -WlQ1SWIyMWxVMUE4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0Fn -SUNBZ1BFNXZaR1ZPWVcxbFBrWnlhV1Z1Wkd4NVRtRnQKWlR3dlRtOWtaVTVoYldVK0NpQWdJQ0Fn -SUNBZ0lDQThWbUZzZFdVK1EyVnVkSFZ5ZVNCSWIzVnpaVHd2Vm1Gc2RXVStDaUFnSUNBZwpJQ0Fn -UEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQ -a1pSUkU0OEwwNXZaR1ZPCllXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbTFwTmk1amJ5NTFh -end2Vm1Gc2RXVStDaUFnSUNBZ0lDQWdQQzlPYjJSbFBnb2cKSUNBZ0lDQWdJRHhPYjJSbFBnb2dJ -Q0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbEp2WVcxcGJtZERiMjV6YjNKMGFYVnRUMGs4TDA1dgpa -R1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqRXhNakl6TXl3ME5EVTFOalk4TDFaaGJI -VmxQZ29nSUNBZ0lDQWdJRHd2ClRtOWtaVDRLSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBOFRt -OWtaVDRLSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrUTNKbFpHVnUKZEdsaGJEd3ZUbTlrWlU1aGJX -VStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStVbVZoYkcw -OApMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbk5vWVd0bGJpNXpkR2x5Y21W -a0xtTnZiVHd2Vm1Gc2RXVStDaUFnCklDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJS -bFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbFZ6WlhKdVlXMWwKVUdGemMzZHZjbVE4TDA1 -dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJs -VG1GdApaVDVWYzJWeWJtRnRaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gx -WlQ1cVlXMWxjend2Vm1Gc2RXVStDaUFnCklDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0Fn -SUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsQmgKYzNOM2IzSmtQQzlP -YjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGxsdE9YVmFSRUYzVG5jOVBUd3ZW -bUZzZFdVKwpDaUFnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pUNEtJ -Q0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsClBrVkJVRTFsZEdodlpEd3ZUbTlrWlU1aGJXVStD -aUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRa -VDVGUVZCVWVYQmxQQzlPYjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdVK01q -RThMMVpoYkhWbApQZ29nSUNBZ0lDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lE -eE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJRHhPCmIyUmxUbUZ0WlQ1SmJtNWxjazFsZEdodlpE -d3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGsxVExVTkkKUVZBdFZq -SThMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4TDA1 -dlpHVStDaUFnSUNBZwpJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNB -Z0lDQWdQRTV2WkdWT1lXMWxQa1JwWjJsMFlXeERaWEowCmFXWnBZMkYwWlR3dlRtOWtaVTVoYldV -K0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtO -bGNuUnBabWxqWVhSbFZIbHdaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gx -WlQ1NE5UQTVkak04TDFaaApiSFZsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0Fn -SUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJsClRtRnRaVDVEWlhKMFUwaEJNalUy -Um1sdVoyVnlVSEpwYm5ROEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVSsK -TVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1N -V1l4WmpGbU1XWXhaakZtTVdZeApaakZtTVdZeFpqd3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ0lDQThM -MDV2WkdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnCklEeE9iMlJsUGdvZ0lDQWdJ -Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBsTkpUVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBOFRt -OWsKWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQa2xOVTBrOEwwNXZaR1ZPWVcxbFBn -b2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1GcwpkV1UrYVcxemFUd3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ0lD -QThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnCklDQWdJQ0FnUEU1dlpH -Vk9ZVzFsUGtWQlVGUjVjR1U4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV -K01qUTgKTDFaaGJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHd2VG05 -a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnUEM5T2IyUmxQZ29nSUR3dlRtOWtaVDRLUEM5 -TloyMTBWSEpsWlQ0SwoKLS17Ym91bmRhcnl9CkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24veC14 -NTA5LWNhLWNlcnQKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgpMUzB0TFMxQ1JV -ZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VSTFJFTkRRV2hEWjBGM1NVSkJaMGxLUVVs -TWJFWmtkM3BNClZuVnlUVUV3UjBOVGNVZFRTV0l6UkZGRlFrTjNWVUZOUWtsNFJVUkJUMEpuVGxZ -S1FrRk5WRUl3VmtKVlEwSkVVVlJGZDBob1kwNU4KVkZsM1RWUkZlVTFVUlRGTlJFVXhWMmhqVGsx -cVdYZE5WRUUxVFZSRk1VMUVSVEZYYWtGVFRWSkJkd3BFWjFsRVZsRlJSRVYzWkVaUgpWa0ZuVVRC -RmVFMUpTVUpKYWtGT1FtZHJjV2hyYVVjNWR6QkNRVkZGUmtGQlQwTkJVVGhCVFVsSlFrTm5TME5C -VVVWQkNucHVRVkJWCmVqSTJUWE5oWlRSM2N6UXpZM3BTTkRFdlNqSlJkSEpUU1ZwVlMyMVdWWE5X -ZFcxRVlsbEljbEJPZGxSWVMxTk5XRUZqWlhkUFVrUlIKV1ZnS1VuRjJTSFp3YmpoRGMyTkNNU3R2 -UjFoYWRraDNlR28wZWxZd1YwdHZTeko2WlZocllYVXpkbU41YkROSVNVdDFjRXBtY1RKVQpSVUZE -WldaV2Ftb3dkQXBLVnl0WU16VlFSMWR3T1M5SU5YcEpWVTVXVGxacVV6ZFZiWE00TkVsMlMyaFNR -amcxTVRKUVFqbFZlVWhoCloxaFpWbGcxUjFkd1FXTldjSGxtY214U0NrWkpPVkZrYUdnclVHSnJN -SFY1YTNSa1ltWXZRMlJtWjBoUGIyVmljbFIwZDFKc2FrMHcKYjBSMFdDc3lRM1kyYWpCM1FrczNh -RVE0Y0ZCMlpqRXJkWGtLUjNwamVtbG5RVlV2TkV0M04yVmFjWGxrWmpsQ0t6VlNkWEJTSzBsYQph -WEJZTkRGNFJXbEpja3RTZDNGcE5URTNWMWQ2V0dOcVlVY3lZMDVpWmpRMU1RcDRjRWcxVUc1V00y -a3hkSEV3TkdwTlIxRlZla1ozClNVUkJVVUZDYnpSSFFVMUlOSGRJVVZsRVZsSXdUMEpDV1VWR1NY -ZFlOSFp6T0VKcFFtTlRZMjlrQ2pWdWIxcElVazA0UlRRcmFVMUYKU1VkQk1WVmtTWGRSTjAxRWJV -RkdTWGRZTkhaek9FSnBRbU5UWTI5a05XNXZXa2hTVFRoRk5DdHBiMUpoYTBaRVFWTUtUVkpCZDBS -bgpXVVJXVVZGRVJYZGtSbEZXUVdkUk1FVjRaMmRyUVdkMVZWWXpSRTEwVnpaemQwUkJXVVJXVWpC -VVFrRlZkMEYzUlVJdmVrRk1RbWRPClZncElVVGhGUWtGTlEwRlJXWGRFVVZsS1MyOWFTV2gyWTA1 -QlVVVk1RbEZCUkdkblJVSkJSbVpSY1U5VVFUZFNkamRMSzJ4MVVUZHcKYm1Gek5FSlpkMGhGQ2ps -SFJWQXZkVzlvZGpaTFQza3dWRWRSUm1KeVVsUnFSbTlNVms1Q09VSmFNWGx0VFVSYU1DOVVTWGRK -VldNMwpkMmszWVRoME5XMUZjVmxJTVRVemQxY0tZVmR2YjJsVGFubE1UR2gxU1RSelRuSk9RMDkw -YVhOa1FuRXljakpOUmxoME5tZ3diVUZSCldVOVFkamhTT0VzM0wyWm5VM2hIUm5GNmFIbE9iVzFX -VEFveGNVSktiR1I0TXpSVGNIZHpWRUZNVVZaUVlqUm9SM2RLZWxwbWNqRlEKWTNCRlVYZzJlRTF1 -Vkd3NGVFVlhXa1V6VFhNNU9YVmhWWGhpVVhGSmQxSjFDa3huUVU5clRrTnRXVEp0T0RsV2FIcGhT -RW94ZFZZNApOVUZrVFM5MFJDdFpjMjFzYm01cWREbE1Va05sYW1KQ2FYQnFTVWRxVDFoeVp6RktV -Q3RzZUZZS2JYVk5OSFpJSzFBdmJXeHRlSE5RClVIb3daRFkxWWl0RlIyMUtXbkJ2VEd0UEwzUmtU -azUyUTFsNmFrcHdWRVZYY0VWelR6Wk9UV2hMV1c4OUNpMHRMUzB0UlU1RUlFTkYKVWxSSlJrbERR -VlJGTFMwdExTMEsKLS17Ym91bmRhcnl9LS0K +TUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5 +PXtib3VuZGFyeX07IGNoYXJzZXQ9VVRGLTgKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFz +ZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9wYXNzcG9pbnQtcHJv +ZmlsZTsgY2hhcnNldD1VVEYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKClBF +MW5iWFJVY21WbElIaHRiRzV6UFNKemVXNWpiV3c2Wkcxa1pHWXhMaklpUGdvZ0lEeFdaWEpFVkVR +K01TNHlQQzlXWlhKRVZFUSsKQ2lBZ1BFNXZaR1UrQ2lBZ0lDQThUbTlrWlU1aGJXVStVR1Z5VUhK +dmRtbGtaWEpUZFdKelkzSnBjSFJwYjI0OEwwNXZaR1ZPWVcxbApQZ29nSUNBZ1BGSlVVSEp2Y0dW +eWRHbGxjejRLSUNBZ0lDQWdQRlI1Y0dVK0NpQWdJQ0FnSUNBZ1BFUkVSazVoYldVK2RYSnVPbmRt +CllUcHRienBvYjNSemNHOTBNbVJ2ZERBdGNHVnljSEp2ZG1sa1pYSnpkV0p6WTNKcGNIUnBiMjQ2 +TVM0d1BDOUVSRVpPWVcxbFBnb2cKSUNBZ0lDQThMMVI1Y0dVK0NpQWdJQ0E4TDFKVVVISnZjR1Z5 +ZEdsbGN6NEtJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQThUbTlrWlU1aApiV1UrYVRBd01Ud3ZUbTlr +WlU1aGJXVStDaUFnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0WlQ1SWIyMWxV +MUE4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZa +R1ZPWVcxbFBrWnlhV1Z1Wkd4NVRtRnQKWlR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQThW +bUZzZFdVK1JYaGhiWEJzWlNCT1pYUjNiM0pyUEM5V1lXeDFaVDRLSUNBZwpJQ0FnSUNBOEwwNXZa +R1UrQ2lBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrUmxGRVRq +d3ZUbTlrClpVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBOFZtRnNkV1UrYUc5MGMzQnZkQzVsZUdGdGNH +eGxMbTVsZER3dlZtRnNkV1UrQ2lBZ0lDQWcKSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2Iy +UmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxKdllXMXBibWREYjI1egpiM0owYVhWdFQw +azhMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQakV4TWpJek15dzBORFUxTmpZ +OEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNB +Z0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBOFRtOWsKWlU1aGJXVStRM0psWkdWdWRHbGhiRHd2VG05 +a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05awpaVTVoYldV +K1VtVmhiRzA4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUG1WNFlXMXdiR1V1 +WTI5dFBDOVdZV3gxClpUNEtJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ1BFNXZaR1Ur +Q2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1ZYTmwKY201aGJXVlFZWE56ZDI5eVpEd3ZUbTlr +WlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dgpaR1ZP +WVcxbFBsVnpaWEp1WVcxbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQ +blZ6WlhJOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdQ +RTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFRtRnQKWlQ1UVlYTnpkMjl5WkR3dlRtOWta +VTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhXWVd4MVpUNWpSMFo2WXpOa2RtTnRVVDA4TDFaaApi +SFZsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lD +QWdJQ0FnSUNBZ0lEeE9iMlJsClRtRnRaVDVGUVZCTlpYUm9iMlE4TDA1dlpHVk9ZVzFsUGdvZ0lD +QWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWcKSUNBOFRtOWtaVTVoYldVK1JV +RlFWSGx3WlR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQakl4UEM5 +VwpZV3gxWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4VG05 +a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnCklDQThUbTlrWlU1aGJXVStTVzV1WlhKTlpYUm9iMlE4TDA1 +dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJRHhXWVd4MVpUNU4KVXkxRFNFRlFMVll5UEM5 +V1lXeDFaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJs +UGdvZwpJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0Fn +SUR4T2IyUmxUbUZ0WlQ1RWFXZHBkR0ZzClEyVnlkR2xtYVdOaGRHVThMMDV2WkdWT1lXMWxQZ29n +SUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEow +YVdacFkyRjBaVlI1Y0dVOEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVStl +RFV3T1hZegpQQzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUNBZ0lDQWdJ +RHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4ClRtOWtaVTVoYldVK1EyVnlkRk5JUVRJMU5rWnBi +bWRsY25CeWFXNTBQQzlPYjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BGWmgKYkhWbFBqRm1N +V1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1X +WXhaakZtTVdZeApaakZtTVdZeFpqRm1NV1k4TDFaaGJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2Iy +UmxQZ29nSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnCklDQWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lD +QWdJRHhPYjJSbFRtRnRaVDVUU1UwOEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWcKUEU1dlpH +VStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0WlQ1SlRWTkpQQzlPYjJSbFRtRnRaVDRLSUNB +Z0lDQWdJQ0FnSUNBZwpQRlpoYkhWbFBqRXlNelExTmlvOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNB +Z1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1UrCkNpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJS +bFRtRnRaVDVGUVZCVWVYQmxQQzlPYjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BGWmgKYkhW +bFBqSXpQQzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUNBZ0lDQThMMDV2 +WkdVK0NpQWdJQ0FnSUR3dgpUbTlrWlQ0S0lDQWdJRHd2VG05a1pUNEtJQ0E4TDA1dlpHVStDand2 +VFdkdGRGUnlaV1UrCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXg1 +MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFDUlVk +SlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtRVWxN +YkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5UbFlL +UWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpUazFx +V1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5VVEJG +ZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMwTkJV +VVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZYTldk +VzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01TdHZS +MWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJVRkRa +V1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJoU1Fq +ZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdKck1I +VjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtzM2FF +UTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxhCmFY +QllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVXTTJr +eGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZHU1hk +WU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFFYlVG +R1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJkMFJu +CldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldVakJV +UWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZMDVC +VVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZDamxI +UlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNYZEpW +V00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1EwOTBh +WE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JXMVdU +QW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVFMXVW +R3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhwaFNF +b3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpGS1VD +dHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNSa1Rr +NTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmtsRFFW +UkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo=
\ No newline at end of file diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64 index 3fa97d1cdd76..7023453b3992 100644 --- a/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64 +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64 @@ -1,85 +1,86 @@ -Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu -dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh -cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6 -IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n -SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo -YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn -UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi -V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ -M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM -MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth -VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt -RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD -QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD -QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD -QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx -bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1 -MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv -Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2 -ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo -YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4 -VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo -YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi -RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj -bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i -MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM -MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy -UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX -eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD -QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD -OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3 -dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0 -S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV -K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G -dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur -TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn -SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2 -WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0 -VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM -MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ -Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi -V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ -a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX -eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD -QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q -VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV -KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG -bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB -OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB -Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4 -VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs -UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn -SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2 -WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk -V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU -bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ -QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94 -LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD -UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR -VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U -bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU -azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V -VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw -TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY -TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T -dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV -RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo -U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK -ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz -M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh -CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX -TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH -U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF -YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk -MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV -akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ -MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD -amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY -ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew -OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX -MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF -MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw -aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG -S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS -a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts -RFFWUkZMUzB0TFMwSwo= +TUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5 +PXtib3VuZGFyeX07IGNoYXJzZXQ9VVRGLTgKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFz +ZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXBhc3Nwb2ludC1w +cm9maWxlOyBjaGFyc2V0PVVURi04CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NAoK +UEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29nSUR4V1pYSkVW +RVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVoYldVK1VHVnlV +SEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0FnUEZKVVVISnZj +R1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhiV1UrZFhKdU9u +ZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZM0pwY0hScGIy +NDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThMMUpVVUhKdmNH +VnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSthVEF3TVR3dlRt +OWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRtRnRaVDVJYjIx +bFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1 +dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNB +OFZtRnNkV1UrUlhoaGJYQnNaU0JPWlhSM2IzSnJQQzlXWVd4MVpUNEtJQ0FnCklDQWdJQ0E4TDA1 +dlpHVStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStSbEZF +VGp3dlRtOWsKWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVSthRzkwYzNCdmRDNWxlR0Z0 +Y0d4bExtNWxkRHd2Vm1Gc2RXVStDaUFnSUNBZwpJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhP +YjJSbFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbEp2WVcxcGJtZERiMjV6CmIzSjBhWFZ0 +VDBrOEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqRXhNakl6TXl3ME5EVTFO +alk4TDFaaGJIVmwKUGdvZ0lDQWdJQ0FnSUR3dlRtOWtaVDRLSUNBZ0lDQWdQQzlPYjJSbFBnb2dJ +Q0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0E4VG05awpaVTVoYldVK1EzSmxaR1Z1ZEdsaGJEd3ZU +bTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrClpVNWhi +V1UrVW1WaGJHMDhMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbVY0WVcxd2JH +VXVZMjl0UEM5V1lXeDEKWlQ0S0lDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEU1dlpH +VStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrVlhObApjbTVoYldWUVlYTnpkMjl5WkR3dlRt +OWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2ClpH +Vk9ZVzFsUGxWelpYSnVZVzFsUEM5T2IyUmxUbUZ0WlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRlpoYkhW +bFBuVnpaWEk4TDFaaGJIVmwKUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNB +Z1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJsVG1GdApaVDVRWVhOemQyOXlaRHd2VG05 +a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gxWlQ1alIwWjZZek5rZG1OdFVUMDhMMVpo +CmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFn +SUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNUZRVkJOWlhSb2IyUThMMDV2WkdWT1lXMWxQZ29n +SUNBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZwpJQ0E4VG05a1pVNWhiV1Ur +UlVGUVZIbHdaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqSXhQ +QzlXCllXeDFaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThU +bTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWcKSUNBOFRtOWtaVTVoYldVK1NXNXVaWEpOWlhSb2IyUThM +MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gxWlQ1TgpVeTFEU0VGUUxWWXlQ +QzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2Iy +UmxQZ29nCklDQWdJQ0FnSUR3dlRtOWtaVDRLSUNBZ0lDQWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lD +QWdJRHhPYjJSbFRtRnRaVDVFYVdkcGRHRnMKUTJWeWRHbG1hV05oZEdVOEwwNXZaR1ZPWVcxbFBn +b2dJQ0FnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbApUbUZ0WlQ1RFpY +SjBhV1pwWTJGMFpWUjVjR1U4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV +K2VEVXdPWFl6ClBDOVdZV3gxWlQ0S0lDQWdJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNB +Z0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQTgKVG05a1pVNWhiV1UrUTJWeWRGTklRVEkxTmta +cGJtZGxjbkJ5YVc1MFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaApiSFZsUGpG +bU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZt +TVdZeFpqRm1NV1l4ClpqRm1NV1l4WmpGbU1XWThMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlP +YjJSbFBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWcKSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0Fn +SUNBZ0lEeE9iMlJsVG1GdFpUNVRTVTA4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZwpQRTV2 +WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFRtRnRaVDVKVFZOSlBDOU9iMlJsVG1GdFpUNEtJ +Q0FnSUNBZ0lDQWdJQ0FnClBGWmhiSFZsUGpFeU16UTFOaW84TDFaaGJIVmxQZ29nSUNBZ0lDQWdJ +Q0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVSsKQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9i +MlJsVG1GdFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaApi +SFZsUGpJelBDOVdZV3gxWlQ0S0lDQWdJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNBOEww +NXZaR1UrQ2lBZ0lDQWdJRHd2ClRtOWtaVDRLSUNBZ0lEd3ZUbTlrWlQ0S0lDQThMMDV2WkdVK0Nq +d3ZUV2R0ZEZSeVpXVSsKCi0te2JvdW5kYXJ5fQpDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL3gt +eDUwOS1jYS1jZXJ0CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NAoKTFMwdExTMUNS +VWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVUkxSRU5EUVdoRFowRjNTVUpCWjBsS1FV +bE1iRVprZDNwTQpWblZ5VFVFd1IwTlRjVWRUU1dJelJGRkZRa04zVlVGTlFrbDRSVVJCVDBKblRs +WUtRa0ZOVkVJd1ZrSlZRMEpFVVZSRmQwaG9ZMDVOClZGbDNUVlJGZVUxVVJURk5SRVV4VjJoalRr +MXFXWGROVkVFMVRWUkZNVTFFUlRGWGFrRlRUVkpCZHdwRVoxbEVWbEZSUkVWM1pFWlIKVmtGblVU +QkZlRTFKU1VKSmFrRk9RbWRyY1docmFVYzVkekJDUVZGRlJrRkJUME5CVVRoQlRVbEpRa05uUzBO +QlVVVkJDbnB1UVZCVgplakkyVFhOaFpUUjNjelF6WTNwU05ERXZTakpSZEhKVFNWcFZTMjFXVlhO +V2RXMUVZbGxJY2xCT2RsUllTMU5OV0VGalpYZFBVa1JSCldWZ0tVbkYyU0had2JqaERjMk5DTVN0 +dlIxaGFka2gzZUdvMGVsWXdWMHR2U3pKNlpWaHJZWFV6ZG1ONWJETklTVXQxY0VwbWNUSlUKUlVG +RFpXWldhbW93ZEFwS1Z5dFlNelZRUjFkd09TOUlOWHBKVlU1V1RsWnFVemRWYlhNNE5FbDJTMmhT +UWpnMU1USlFRamxWZVVoaApaMWhaVmxnMVIxZHdRV05XY0hsbWNteFNDa1pKT1ZGa2FHZ3JVR0py +TUhWNWEzUmtZbVl2UTJSbVowaFBiMlZpY2xSMGQxSnNhazB3CmIwUjBXQ3N5UTNZMmFqQjNRa3Mz +YUVRNGNGQjJaakVyZFhrS1IzcGplbWxuUVZVdk5FdDNOMlZhY1hsa1pqbENLelZTZFhCU0swbGEK +YVhCWU5ERjRSV2xKY2t0U2QzRnBOVEUzVjFkNldHTnFZVWN5WTA1aVpqUTFNUXA0Y0VnMVVHNVdN +Mmt4ZEhFd05HcE5SMUZWZWtaMwpTVVJCVVVGQ2J6UkhRVTFJTkhkSVVWbEVWbEl3VDBKQ1dVVkdT +WGRZTkhaek9FSnBRbU5UWTI5a0NqVnViMXBJVWswNFJUUXJhVTFGClNVZEJNVlZrU1hkUk4wMUVi +VUZHU1hkWU5IWnpPRUpwUW1OVFkyOWtOVzV2V2toU1RUaEZOQ3RwYjFKaGEwWkVRVk1LVFZKQmQw +Um4KV1VSV1VWRkVSWGRrUmxGV1FXZFJNRVY0WjJkclFXZDFWVll6UkUxMFZ6WnpkMFJCV1VSV1Vq +QlVRa0ZWZDBGM1JVSXZla0ZNUW1kTwpWZ3BJVVRoRlFrRk5RMEZSV1hkRVVWbEtTMjlhU1doMlkw +NUJVVVZNUWxGQlJHZG5SVUpCUm1aUmNVOVVRVGRTZGpkTEsyeDFVVGR3CmJtRnpORUpaZDBoRkNq +bEhSVkF2ZFc5b2RqWkxUM2t3VkVkUlJtSnlVbFJxUm05TVZrNUNPVUphTVhsdFRVUmFNQzlVU1hk +SlZXTTMKZDJrM1lUaDBOVzFGY1ZsSU1UVXpkMWNLWVZkdmIybFRhbmxNVEdoMVNUUnpUbkpPUTA5 +MGFYTmtRbkV5Y2pKTlJsaDBObWd3YlVGUgpXVTlRZGpoU09FczNMMlpuVTNoSFJuRjZhSGxPYlcx +V1RBb3hjVUpLYkdSNE16UlRjSGR6VkVGTVVWWlFZalJvUjNkS2VscG1jakZRClkzQkZVWGcyZUUx +dVZHdzRlRVZYV2tVelRYTTVPWFZoVlhoaVVYRkpkMUoxQ2t4blFVOXJUa050V1RKdE9EbFdhSHBo +U0VveGRWWTQKTlVGa1RTOTBSQ3RaYzIxc2JtNXFkRGxNVWtObGFtSkNhWEJxU1VkcVQxaHlaekZL +VUN0c2VGWUtiWFZOTkhaSUsxQXZiV3h0ZUhOUQpVSG93WkRZMVlpdEZSMjFLV25CdlRHdFBMM1Jr +VGs1MlExbDZha3B3VkVWWGNFVnpUelpPVFdoTFdXODlDaTB0TFMwdFJVNUVJRU5GClVsUkpSa2xE +UVZSRkxTMHRMUzBLCg==
\ No newline at end of file diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64 index 975f8e539cc3..5c23f61b1711 100644 --- a/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64 +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64 @@ -1,85 +1,86 @@ -Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu -dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTMyCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh -cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6 -IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n -SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo -YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn -UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi -V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ -M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM -MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth -VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt -RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD -QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD -QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD -QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx -bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1 -MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv -Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2 -ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo -YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4 -VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo -YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi -RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj -bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i -MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM -MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy -UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX -eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD -QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD -OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3 -dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0 -S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV -K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G -dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur -TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn -SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2 -WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0 -VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM -MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ -Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi -V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ -a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX -eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD -QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q -VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV -KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG -bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB -OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB -Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4 -VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs -UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn -SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2 -WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk -V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU -bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ -QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94 -LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD -UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR -VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U -bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU -azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V -VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw -TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY -TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T -dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV -RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo -U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK -ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz -M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh -CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX -TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH -U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF -YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk -MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV -akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ -MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD -amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY -ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew -OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX -MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF -MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw -aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG -S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS -a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts -RFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo= +TUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5 +PXtib3VuZGFyeX07IGNoYXJzZXQ9VVRGLTgKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogOGJp +dAoKLS17Ym91bmRhcnl9CkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24veC1wYXNzcG9pbnQtcHJv +ZmlsZTsgY2hhcnNldD1VVEYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKClBF +MW5iWFJVY21WbElIaHRiRzV6UFNKemVXNWpiV3c2Wkcxa1pHWXhMaklpUGdvZ0lEeFdaWEpFVkVR +K01TNHlQQzlXWlhKRVZFUSsKQ2lBZ1BFNXZaR1UrQ2lBZ0lDQThUbTlrWlU1aGJXVStVR1Z5VUhK +dmRtbGtaWEpUZFdKelkzSnBjSFJwYjI0OEwwNXZaR1ZPWVcxbApQZ29nSUNBZ1BGSlVVSEp2Y0dW +eWRHbGxjejRLSUNBZ0lDQWdQRlI1Y0dVK0NpQWdJQ0FnSUNBZ1BFUkVSazVoYldVK2RYSnVPbmRt +CllUcHRienBvYjNSemNHOTBNbVJ2ZERBdGNHVnljSEp2ZG1sa1pYSnpkV0p6WTNKcGNIUnBiMjQ2 +TVM0d1BDOUVSRVpPWVcxbFBnb2cKSUNBZ0lDQThMMVI1Y0dVK0NpQWdJQ0E4TDFKVVVISnZjR1Z5 +ZEdsbGN6NEtJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQThUbTlrWlU1aApiV1UrYVRBd01Ud3ZUbTlr +WlU1aGJXVStDaUFnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0WlQ1SWIyMWxV +MUE4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZa +R1ZPWVcxbFBrWnlhV1Z1Wkd4NVRtRnQKWlR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQThW +bUZzZFdVK1JYaGhiWEJzWlNCT1pYUjNiM0pyUEM5V1lXeDFaVDRLSUNBZwpJQ0FnSUNBOEwwNXZa +R1UrQ2lBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrUmxGRVRq +d3ZUbTlrClpVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBOFZtRnNkV1UrYUc5MGMzQnZkQzVsZUdGdGNH +eGxMbTVsZER3dlZtRnNkV1UrQ2lBZ0lDQWcKSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2Iy +UmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxKdllXMXBibWREYjI1egpiM0owYVhWdFQw +azhMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQakV4TWpJek15dzBORFUxTmpZ +OEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNB +Z0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBOFRtOWsKWlU1aGJXVStRM0psWkdWdWRHbGhiRHd2VG05 +a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05awpaVTVoYldV +K1VtVmhiRzA4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUG1WNFlXMXdiR1V1 +WTI5dFBDOVdZV3gxClpUNEtJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ1BFNXZaR1Ur +Q2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1ZYTmwKY201aGJXVlFZWE56ZDI5eVpEd3ZUbTlr +WlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dgpaR1ZP +WVcxbFBsVnpaWEp1WVcxbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQ +blZ6WlhJOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdQ +RTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFRtRnQKWlQ1UVlYTnpkMjl5WkR3dlRtOWta +VTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhXWVd4MVpUNWpSMFo2WXpOa2RtTnRVVDA4TDFaaApi +SFZsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lD +QWdJQ0FnSUNBZ0lEeE9iMlJsClRtRnRaVDVGUVZCTlpYUm9iMlE4TDA1dlpHVk9ZVzFsUGdvZ0lD +QWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWcKSUNBOFRtOWtaVTVoYldVK1JV +RlFWSGx3WlR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQakl4UEM5 +VwpZV3gxWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4VG05 +a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnCklDQThUbTlrWlU1aGJXVStTVzV1WlhKTlpYUm9iMlE4TDA1 +dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJRHhXWVd4MVpUNU4KVXkxRFNFRlFMVll5UEM5 +V1lXeDFaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJs +UGdvZwpJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0Fn +SUR4T2IyUmxUbUZ0WlQ1RWFXZHBkR0ZzClEyVnlkR2xtYVdOaGRHVThMMDV2WkdWT1lXMWxQZ29n +SUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEow +YVdacFkyRjBaVlI1Y0dVOEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVStl +RFV3T1hZegpQQzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUNBZ0lDQWdJ +RHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4ClRtOWtaVTVoYldVK1EyVnlkRk5JUVRJMU5rWnBi +bWRsY25CeWFXNTBQQzlPYjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BGWmgKYkhWbFBqRm1N +V1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1X +WXhaakZtTVdZeApaakZtTVdZeFpqRm1NV1k4TDFaaGJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2Iy +UmxQZ29nSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnCklDQWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lD +QWdJRHhPYjJSbFRtRnRaVDVUU1UwOEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWcKUEU1dlpH +VStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0WlQ1SlRWTkpQQzlPYjJSbFRtRnRaVDRLSUNB +Z0lDQWdJQ0FnSUNBZwpQRlpoYkhWbFBqRXlNelExTmlvOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNB +Z1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1UrCkNpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJS +bFRtRnRaVDVGUVZCVWVYQmxQQzlPYjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BGWmgKYkhW +bFBqSXpQQzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUNBZ0lDQThMMDV2 +WkdVK0NpQWdJQ0FnSUR3dgpUbTlrWlQ0S0lDQWdJRHd2VG05a1pUNEtJQ0E4TDA1dlpHVStDand2 +VFdkdGRGUnlaV1UrCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXg1 +MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFDUlVk +SlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtRVWxN +YkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5UbFlL +UWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpUazFx +V1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5VVEJG +ZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMwTkJV +VVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZYTldk +VzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01TdHZS +MWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJVRkRa +V1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJoU1Fq +ZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdKck1I +VjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtzM2FF +UTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxhCmFY +QllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVXTTJr +eGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZHU1hk +WU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFFYlVG +R1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJkMFJu +CldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldVakJV +UWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZMDVC +VVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZDamxI +UlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNYZEpW +V00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1EwOTBh +WE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JXMVdU +QW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVFMXVW +R3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhwaFNF +b3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpGS1VD +dHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNSa1Rr +NTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmtsRFFW +UkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo=
\ No newline at end of file diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithUpdateIdentifier.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithUpdateIdentifier.base64 new file mode 100644 index 000000000000..bab7607512c8 --- /dev/null +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithUpdateIdentifier.base64 @@ -0,0 +1,88 @@ +TUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5 +PXtib3VuZGFyeX07IGNoYXJzZXQ9VVRGLTgKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFz +ZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXBhc3Nwb2ludC1w +cm9maWxlOyBjaGFyc2V0PVVURi04CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NAoK +UEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29nSUR4V1pYSkVW +RVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVoYldVK1VHVnlV +SEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0FnUEZKVVVISnZj +R1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhiV1UrZFhKdU9u +ZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZM0pwY0hScGIy +NDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThMMUpVVUhKdmNH +VnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVStWWEJrWVhSbFNX +UmxiblJwWm1sbGNqd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lEeFdZV3gxWlQ0eE1qTTBQQzlXWVd4 +MVpUNEsKSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoYldV +K2FUQXdNVHd2VG05a1pVNWhiV1UrQ2lBZwpJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJS +bFRtRnRaVDVJYjIxbFUxQThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJRHhPCmIyUmxQZ29nSUNB +Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdFpUd3ZUbTlrWlU1aGJXVStDaUFn +SUNBZ0lDQWcKSUNBOFZtRnNkV1UrUlhoaGJYQnNaU0JPWlhSM2IzSnJQQzlXWVd4MVpUNEtJQ0Fn +SUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZwpQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlr +WlU1aGJXVStSbEZFVGp3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthRzkw +YzNCdmRDNWxlR0Z0Y0d4bExtNWxkRHd2Vm1Gc2RXVStDaUFnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJ +Q0FnSUNBZ0lEeE8KYjJSbFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbEp2WVcxcGJtZERi +MjV6YjNKMGFYVnRUMGs4TDA1dlpHVk9ZVzFsUGdvZwpJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqRXhN +akl6TXl3ME5EVTFOalk4TDFaaGJIVmxQZ29nSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnCklDQWdQ +QzlPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrUTNKbFpH +VnVkR2xoYkR3dlRtOWsKWlU1aGJXVStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lD +QThUbTlrWlU1aGJXVStVbVZoYkcwOEwwNXZaR1ZPWVcxbApQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJI +VmxQbVY0WVcxd2JHVXVZMjl0UEM5V1lXeDFaVDRLSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnCklD +QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrVlhObGNtNWhiV1ZRWVhO +emQyOXlaRHd2VG05a1pVNWgKYldVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNB +Z0lDQWdQRTV2WkdWT1lXMWxQbFZ6WlhKdVlXMWxQQzlPYjJSbApUbUZ0WlQ0S0lDQWdJQ0FnSUNB +Z0lDQWdQRlpoYkhWbFBuVnpaWEk4TDFaaGJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29n +CklDQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJsVG1GdFpUNVFZWE56 +ZDI5eVpEd3ZUbTlrWlU1aGJXVSsKQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gxWlQ1alIwWjZZek5r +ZG1OdFVUMDhMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbApQZ29nSUNBZ0lDQWdJQ0Fn +UEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0WlQ1RlFWQk5aWFJvYjJROEwwNXZa +R1ZPCllXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ0lDQThU +bTlrWlU1aGJXVStSVUZRVkhsd1pUd3YKVG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdQ +RlpoYkhWbFBqSXhQQzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEM5TwpiMlJsUGdvZ0lDQWdJ +Q0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrU1c1dVpY +Sk5aWFJvCmIyUThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gxWlQ1TlV5 +MURTRUZRTFZZeVBDOVdZV3gxWlQ0S0lDQWcKSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD +QWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUNBZwpJQ0E4VG05a1pU +NEtJQ0FnSUNBZ0lDQWdJRHhPYjJSbFRtRnRaVDVFYVdkcGRHRnNRMlZ5ZEdsbWFXTmhkR1U4TDA1 +dlpHVk9ZVzFsClBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJS +bFRtRnRaVDVEWlhKMGFXWnBZMkYwWlZSNWNHVTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNB +Z0lDQThWbUZzZFdVK2VEVXdPWFl6UEM5V1lXeDFaVDRLSUNBZ0lDQWdJQ0FnSUR3dgpUbTlrWlQ0 +S0lDQWdJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRMlZ5 +ZEZOSVFUSTFOa1pwCmJtZGxjbkJ5YVc1MFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0Fn +UEZaaGJIVmxQakZtTVdZeFpqRm1NV1l4WmpGbU1XWXgKWmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4 +WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZOEwxWmhiSFZsUGdvZwpJQ0Fn +SUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNBOFRtOWta +VDRLSUNBZ0lDQWdJQ0FnCklEeE9iMlJsVG1GdFpUNVRTVTA4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJ +Q0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKVFZOSlBDOU9i +MlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQakV5TXpRMU5pbzhMMVpoYkhWbApQ +Z29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lD +QWdJQ0FnSUR4T2IyUmxUbUZ0ClpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lD +QWdJQ0FnUEZaaGJIVmxQakl6UEM5V1lXeDFaVDRLSUNBZ0lDQWcKSUNBZ0lEd3ZUbTlrWlQ0S0lD +QWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUR3dlRtOWtaVDRLSUNB +OApMMDV2WkdVK0Nqd3ZUV2R0ZEZSeVpXVSsKCi0te2JvdW5kYXJ5fQpDb250ZW50LVR5cGU6IGFw +cGxpY2F0aW9uL3gteDUwOS1jYS1jZXJ0CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2 +NAoKTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVUkxSRU5EUVdoRFow +RjNTVUpCWjBsS1FVbE1iRVprZDNwTQpWblZ5VFVFd1IwTlRjVWRUU1dJelJGRkZRa04zVlVGTlFr +bDRSVVJCVDBKblRsWUtRa0ZOVkVJd1ZrSlZRMEpFVVZSRmQwaG9ZMDVOClZGbDNUVlJGZVUxVVJU +Rk5SRVV4VjJoalRrMXFXWGROVkVFMVRWUkZNVTFFUlRGWGFrRlRUVkpCZHdwRVoxbEVWbEZSUkVW +M1pFWlIKVmtGblVUQkZlRTFKU1VKSmFrRk9RbWRyY1docmFVYzVkekJDUVZGRlJrRkJUME5CVVRo +QlRVbEpRa05uUzBOQlVVVkJDbnB1UVZCVgplakkyVFhOaFpUUjNjelF6WTNwU05ERXZTakpSZEhK +VFNWcFZTMjFXVlhOV2RXMUVZbGxJY2xCT2RsUllTMU5OV0VGalpYZFBVa1JSCldWZ0tVbkYyU0ha +d2JqaERjMk5DTVN0dlIxaGFka2gzZUdvMGVsWXdWMHR2U3pKNlpWaHJZWFV6ZG1ONWJETklTVXQx +Y0VwbWNUSlUKUlVGRFpXWldhbW93ZEFwS1Z5dFlNelZRUjFkd09TOUlOWHBKVlU1V1RsWnFVemRW +YlhNNE5FbDJTMmhTUWpnMU1USlFRamxWZVVoaApaMWhaVmxnMVIxZHdRV05XY0hsbWNteFNDa1pK +T1ZGa2FHZ3JVR0pyTUhWNWEzUmtZbVl2UTJSbVowaFBiMlZpY2xSMGQxSnNhazB3CmIwUjBXQ3N5 +UTNZMmFqQjNRa3MzYUVRNGNGQjJaakVyZFhrS1IzcGplbWxuUVZVdk5FdDNOMlZhY1hsa1pqbENL +elZTZFhCU0swbGEKYVhCWU5ERjRSV2xKY2t0U2QzRnBOVEUzVjFkNldHTnFZVWN5WTA1aVpqUTFN +UXA0Y0VnMVVHNVdNMmt4ZEhFd05HcE5SMUZWZWtaMwpTVVJCVVVGQ2J6UkhRVTFJTkhkSVVWbEVW +bEl3VDBKQ1dVVkdTWGRZTkhaek9FSnBRbU5UWTI5a0NqVnViMXBJVWswNFJUUXJhVTFGClNVZEJN +VlZrU1hkUk4wMUViVUZHU1hkWU5IWnpPRUpwUW1OVFkyOWtOVzV2V2toU1RUaEZOQ3RwYjFKaGEw +WkVRVk1LVFZKQmQwUm4KV1VSV1VWRkVSWGRrUmxGV1FXZFJNRVY0WjJkclFXZDFWVll6UkUxMFZ6 +WnpkMFJCV1VSV1VqQlVRa0ZWZDBGM1JVSXZla0ZNUW1kTwpWZ3BJVVRoRlFrRk5RMEZSV1hkRVVW +bEtTMjlhU1doMlkwNUJVVVZNUWxGQlJHZG5SVUpCUm1aUmNVOVVRVGRTZGpkTEsyeDFVVGR3CmJt +RnpORUpaZDBoRkNqbEhSVkF2ZFc5b2RqWkxUM2t3VkVkUlJtSnlVbFJxUm05TVZrNUNPVUphTVhs +dFRVUmFNQzlVU1hkSlZXTTMKZDJrM1lUaDBOVzFGY1ZsSU1UVXpkMWNLWVZkdmIybFRhbmxNVEdo +MVNUUnpUbkpPUTA5MGFYTmtRbkV5Y2pKTlJsaDBObWd3YlVGUgpXVTlRZGpoU09FczNMMlpuVTNo +SFJuRjZhSGxPYlcxV1RBb3hjVUpLYkdSNE16UlRjSGR6VkVGTVVWWlFZalJvUjNkS2VscG1jakZR +ClkzQkZVWGcyZUUxdVZHdzRlRVZYV2tVelRYTTVPWFZoVlhoaVVYRkpkMUoxQ2t4blFVOXJUa050 +V1RKdE9EbFdhSHBoU0VveGRWWTQKTlVGa1RTOTBSQ3RaYzIxc2JtNXFkRGxNVWtObGFtSkNhWEJx +U1VkcVQxaHlaekZLVUN0c2VGWUtiWFZOTkhaSUsxQXZiV3h0ZUhOUQpVSG93WkRZMVlpdEZSMjFL +V25CdlRHdFBMM1JrVGs1MlExbDZha3B3VkVWWGNFVnpUelpPVFdoTFdXODlDaTB0TFMwdFJVNUVJ +RU5GClVsUkpSa2xEUVZSRkxTMHRMUzBLCi0te2JvdW5kYXJ5fS0tCg==
\ No newline at end of file diff --git a/wifi/tests/assets/hsr1/README.txt b/wifi/tests/assets/hsr1/README.txt index d1f8384fc979..9f3cdc274ee1 100644 --- a/wifi/tests/assets/hsr1/README.txt +++ b/wifi/tests/assets/hsr1/README.txt @@ -2,4 +2,5 @@ HSR1ProfileWithCACert.conf - unencoded installation file that contains a Passpoi HSR1ProfileWithCACert.base64 - base64 encoded of the data contained in HSR1ProfileWithCAWith.conf HSR1ProfileWithNonBase64Part.base64 - base64 encoded installation file that contains a part of non-base64 encoding type HSR1ProfileWithMissingBoundary.base64 - base64 encoded installation file with missing end-boundary in the MIME data -HSR1ProfileWithInvalidContentType.base64 - base64 encoded installation file with that contains a MIME part with an invalid content type. +HSR1ProfileWithInvalidContentType.base64 - base64 encoded installation file with that contains a MIME part with an invalid content type +HSR1ProfileWithUpdateIdentifier.base64 - base64 encoded installation file with that contains an R2 update identifier diff --git a/wifi/tests/assets/pps/PerProviderSubscription.xml b/wifi/tests/assets/pps/PerProviderSubscription.xml index e4472ce19d51..e9afb35fec94 100644 --- a/wifi/tests/assets/pps/PerProviderSubscription.xml +++ b/wifi/tests/assets/pps/PerProviderSubscription.xml @@ -19,6 +19,30 @@ <NodeName>VendorSpecific</NodeName> <Value>Test</Value> </Node> + <Node> + <NodeName>VendorExtension</NodeName> + <Node> + <NodeName>VendorAttribute</NodeName> + <Value>VendorValue</Value> + </Node> + </Node> + <Node> + <NodeName>Android</NodeName> + <Node> + <NodeName>AAAServerTrustedNames</NodeName> + <Node> + <NodeName>FQDN</NodeName> + <Value>trusted.fqdn.com;another-trusted.fqdn.com</Value> + </Node> + </Node> + <Node> + <NodeName>NewSubTree</NodeName> + <Node> + <NodeName>NewAttribute</NodeName> + <Value>NewValue</Value> + </Node> + </Node> + </Node> </Node> <Node> <NodeName>HomeSP</NodeName> diff --git a/wifi/tests/runtests.sh b/wifi/tests/runtests.sh deleted file mode 100755 index 219a45ee188f..000000000000 --- a/wifi/tests/runtests.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -if [ -z $ANDROID_BUILD_TOP ]; then - echo "You need to source and lunch before you can use this script" - exit 1 -fi - -echo "Running tests" - -set -e # fail early - -echo "+ mmma -j32 $ANDROID_BUILD_TOP/frameworks/base/wifi/tests" -# NOTE Don't actually run the command above since this shell doesn't inherit functions from the -# caller. -make -j32 -C $ANDROID_BUILD_TOP -f build/core/main.mk MODULES-IN-frameworks-base-wifi-tests - -set -x # print commands - -adb root -adb wait-for-device - -TARGET_ARCH=$($ANDROID_BUILD_TOP/build/soong/soong_ui.bash --dumpvar-mode TARGET_ARCH) -adb install -r -g "$OUT/testcases/FrameworksWifiApiTests/$TARGET_ARCH/FrameworksWifiApiTests.apk" - -adb shell am instrument --no-hidden-api-checks -w "$@" \ - 'android.net.wifi.test/androidx.test.runner.AndroidJUnitRunner' diff --git a/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java b/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java new file mode 100644 index 000000000000..b10141434b0b --- /dev/null +++ b/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 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.assertTrue; + +import android.util.SparseArray; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests for {@link android.net.wifi.EasyConnectStatusCallbackTest}. + */ +@SmallTest +public class EasyConnectStatusCallbackTest { + private EasyConnectStatusCallback mEasyConnectStatusCallback = new EasyConnectStatusCallback() { + @Override + public void onEnrolleeSuccess(int newNetworkId) { + + } + + @Override + public void onConfiguratorSuccess(int code) { + + } + + @Override + public void onProgress(int code) { + + } + + @Override + public void onFailure(int code) { + mOnFailureR1EventReceived = true; + mLastCode = code; + } + }; + private boolean mOnFailureR1EventReceived; + private int mLastCode; + + @Before + public void setUp() { + mOnFailureR1EventReceived = false; + mLastCode = 0; + } + + /** + * Test that the legacy R1 onFailure is called by default if the R2 onFailure is not overridden + * by the app. + */ + @Test + public void testR1OnFailureCalled() { + + SparseArray<int[]> channelList = new SparseArray<>(); + int[] channelArray = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + + channelList.append(81, channelArray); + mEasyConnectStatusCallback.onFailure( + EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK, + "SomeSSID", channelList, new int[] {81}); + + assertTrue(mOnFailureR1EventReceived); + assertEquals(mLastCode, + EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK); + } +} diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java index 54ec32502878..5516f433070f 100644 --- a/wifi/tests/src/android/net/wifi/ScanResultTest.java +++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.validateMockitoUsage; +import android.net.wifi.ScanResult.InformationElement; import android.os.Parcel; import androidx.test.filters.SmallTest; @@ -41,6 +42,70 @@ public class ScanResultTest { public static final int TEST_LEVEL = -56; public static final int TEST_FREQUENCY = 2412; public static final long TEST_TSF = 04660l; + public static final @WifiAnnotations.WifiStandard int TEST_WIFI_STANDARD = + ScanResult.WIFI_STANDARD_11AC; + + /** + * Frequency to channel map. This include some frequencies used outside the US. + * Representing it using a vector (instead of map) for simplification. + */ + private static final int[] FREQUENCY_TO_CHANNEL_MAP = { + 2412, WifiScanner.WIFI_BAND_24_GHZ, 1, + 2417, WifiScanner.WIFI_BAND_24_GHZ, 2, + 2422, WifiScanner.WIFI_BAND_24_GHZ, 3, + 2427, WifiScanner.WIFI_BAND_24_GHZ, 4, + 2432, WifiScanner.WIFI_BAND_24_GHZ, 5, + 2437, WifiScanner.WIFI_BAND_24_GHZ, 6, + 2442, WifiScanner.WIFI_BAND_24_GHZ, 7, + 2447, WifiScanner.WIFI_BAND_24_GHZ, 8, + 2452, WifiScanner.WIFI_BAND_24_GHZ, 9, + 2457, WifiScanner.WIFI_BAND_24_GHZ, 10, + 2462, WifiScanner.WIFI_BAND_24_GHZ, 11, + /* 12, 13 are only legitimate outside the US. */ + 2467, WifiScanner.WIFI_BAND_24_GHZ, 12, + 2472, WifiScanner.WIFI_BAND_24_GHZ, 13, + /* 14 is for Japan, DSSS and CCK only. */ + 2484, WifiScanner.WIFI_BAND_24_GHZ, 14, + /* 34 valid in Japan. */ + 5170, WifiScanner.WIFI_BAND_5_GHZ, 34, + 5180, WifiScanner.WIFI_BAND_5_GHZ, 36, + 5190, WifiScanner.WIFI_BAND_5_GHZ, 38, + 5200, WifiScanner.WIFI_BAND_5_GHZ, 40, + 5210, WifiScanner.WIFI_BAND_5_GHZ, 42, + 5220, WifiScanner.WIFI_BAND_5_GHZ, 44, + 5230, WifiScanner.WIFI_BAND_5_GHZ, 46, + 5240, WifiScanner.WIFI_BAND_5_GHZ, 48, + 5260, WifiScanner.WIFI_BAND_5_GHZ, 52, + 5280, WifiScanner.WIFI_BAND_5_GHZ, 56, + 5300, WifiScanner.WIFI_BAND_5_GHZ, 60, + 5320, WifiScanner.WIFI_BAND_5_GHZ, 64, + 5500, WifiScanner.WIFI_BAND_5_GHZ, 100, + 5520, WifiScanner.WIFI_BAND_5_GHZ, 104, + 5540, WifiScanner.WIFI_BAND_5_GHZ, 108, + 5560, WifiScanner.WIFI_BAND_5_GHZ, 112, + 5580, WifiScanner.WIFI_BAND_5_GHZ, 116, + /* 120, 124, 128 valid in Europe/Japan. */ + 5600, WifiScanner.WIFI_BAND_5_GHZ, 120, + 5620, WifiScanner.WIFI_BAND_5_GHZ, 124, + 5640, WifiScanner.WIFI_BAND_5_GHZ, 128, + /* 132+ valid in US. */ + 5660, WifiScanner.WIFI_BAND_5_GHZ, 132, + 5680, WifiScanner.WIFI_BAND_5_GHZ, 136, + 5700, WifiScanner.WIFI_BAND_5_GHZ, 140, + /* 144 is supported by a subset of WiFi chips. */ + 5720, WifiScanner.WIFI_BAND_5_GHZ, 144, + 5745, WifiScanner.WIFI_BAND_5_GHZ, 149, + 5765, WifiScanner.WIFI_BAND_5_GHZ, 153, + 5785, WifiScanner.WIFI_BAND_5_GHZ, 157, + 5805, WifiScanner.WIFI_BAND_5_GHZ, 161, + 5825, WifiScanner.WIFI_BAND_5_GHZ, 165, + 5845, WifiScanner.WIFI_BAND_5_GHZ, 169, + 5865, WifiScanner.WIFI_BAND_5_GHZ, 173, + /* Now some 6GHz channels */ + 5945, WifiScanner.WIFI_BAND_6_GHZ, 1, + 5960, WifiScanner.WIFI_BAND_6_GHZ, 4, + 6100, WifiScanner.WIFI_BAND_6_GHZ, 32 + }; /** * Setup before tests. @@ -124,17 +189,37 @@ public class ScanResultTest { } /** + * Verify parcel read/write for ScanResult with Information Element + */ + @Test + public void verifyScanResultParcelWithInformationElement() throws Exception { + ScanResult writeScanResult = createScanResult(); + writeScanResult.informationElements = new ScanResult.InformationElement[2]; + writeScanResult.informationElements[0] = new ScanResult.InformationElement(); + writeScanResult.informationElements[0].id = InformationElement.EID_HT_OPERATION; + writeScanResult.informationElements[0].idExt = 0; + writeScanResult.informationElements[0].bytes = new byte[]{0x11, 0x22, 0x33}; + writeScanResult.informationElements[1] = new ScanResult.InformationElement(); + writeScanResult.informationElements[1].id = InformationElement.EID_EXTENSION_PRESENT; + writeScanResult.informationElements[1].idExt = InformationElement.EID_EXT_HE_OPERATION; + writeScanResult.informationElements[1].bytes = new byte[]{0x44, 0x55, 0x66}; + ScanResult readScanResult = new ScanResult(writeScanResult); + assertScanResultEquals(writeScanResult, readScanResult); + } + + /** * Verify toString for ScanResult. */ @Test public void verifyScanResultToStringWithoutRadioChainInfo() throws Exception { ScanResult scanResult = createScanResult(); - assertEquals("SSID: \"test_ssid\", BSSID: 04:ac:fe:45:34:10, capabilities: CCMP, " + - "level: -56, frequency: 2412, timestamp: 2480, distance: 0(cm), distanceSd: 0(cm), " + - "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, " + - "80211mcResponder: is not supported, Carrier AP: no, " + - "Carrier AP EAP Type: 0, Carrier name: null, " + - "Radio Chain Infos: null", scanResult.toString()); + assertEquals("SSID: \"test_ssid\", BSSID: 04:ac:fe:45:34:10, capabilities: CCMP, " + + "level: -56, frequency: 2412, timestamp: 2480, " + + "distance: 0(cm), distanceSd: 0(cm), " + + "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, " + + "standard: 11ac, " + + "80211mcResponder: is not supported, " + + "Radio Chain Infos: null", scanResult.toString()); } /** @@ -150,13 +235,33 @@ public class ScanResultTest { scanResult.radioChainInfos[1] = new ScanResult.RadioChainInfo(); scanResult.radioChainInfos[1].id = 1; scanResult.radioChainInfos[1].level = -54; - assertEquals("SSID: \"test_ssid\", BSSID: 04:ac:fe:45:34:10, capabilities: CCMP, " + - "level: -56, frequency: 2412, timestamp: 2480, distance: 0(cm), distanceSd: 0(cm), " + - "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, " + - "80211mcResponder: is not supported, Carrier AP: no, " + - "Carrier AP EAP Type: 0, Carrier name: null, " + - "Radio Chain Infos: [RadioChainInfo: id=0, level=-45, " + - "RadioChainInfo: id=1, level=-54]", scanResult.toString()); + assertEquals("SSID: \"test_ssid\", BSSID: 04:ac:fe:45:34:10, capabilities: CCMP, " + + "level: -56, frequency: 2412, timestamp: 2480, distance: 0(cm), " + + "distanceSd: 0(cm), " + + "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, " + + "standard: 11ac, " + + "80211mcResponder: is not supported, " + + "Radio Chain Infos: [RadioChainInfo: id=0, level=-45, " + + "RadioChainInfo: id=1, level=-54]", scanResult.toString()); + } + + /** + * verify frequency to channel conversion for all possible frequencies. + */ + @Test + public void convertFrequencyToChannel() throws Exception { + for (int i = 0; i < FREQUENCY_TO_CHANNEL_MAP.length; i += 3) { + assertEquals(FREQUENCY_TO_CHANNEL_MAP[i + 2], + ScanResult.convertFrequencyMhzToChannel(FREQUENCY_TO_CHANNEL_MAP[i])); + } + } + + /** + * Verify frequency to channel conversion failed for an invalid frequency. + */ + @Test + public void convertFrequencyToChannelWithInvalidFreq() throws Exception { + assertEquals(-1, ScanResult.convertFrequencyMhzToChannel(8000)); } /** @@ -177,6 +282,7 @@ public class ScanResultTest { result.level = TEST_LEVEL; result.frequency = TEST_FREQUENCY; result.timestamp = TEST_TSF; + result.setWifiStandard(TEST_WIFI_STANDARD); return result; } @@ -187,6 +293,8 @@ public class ScanResultTest { assertEquals(expected.level, actual.level); assertEquals(expected.frequency, actual.frequency); assertEquals(expected.timestamp, actual.timestamp); + assertEquals(expected.getWifiStandard(), actual.getWifiStandard()); assertArrayEquals(expected.radioChainInfos, actual.radioChainInfos); + assertArrayEquals(expected.informationElements, actual.informationElements); } } diff --git a/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java new file mode 100644 index 000000000000..73b501a4d8f2 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 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.os.Parcel; + +import static org.junit.Assert.assertEquals; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Unit tests for {@link android.net.wifi.SoftApCapability}. + */ +@SmallTest +public class SoftApCapabilityTest { + + /** + * Verifies copy constructor. + */ + @Test + public void testCopyOperator() throws Exception { + long testSoftApFeature = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT + | SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD; + SoftApCapability capability = new SoftApCapability(testSoftApFeature); + capability.setMaxSupportedClients(10); + + SoftApCapability copiedCapability = new SoftApCapability(capability); + + assertEquals(capability, copiedCapability); + assertEquals(capability.hashCode(), copiedCapability.hashCode()); + } + + /** + * Verifies parcel serialization/deserialization. + */ + @Test + public void testParcelOperation() throws Exception { + long testSoftApFeature = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT + | SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD; + SoftApCapability capability = new SoftApCapability(testSoftApFeature); + capability.setMaxSupportedClients(10); + + Parcel parcelW = Parcel.obtain(); + capability.writeToParcel(parcelW, 0); + byte[] bytes = parcelW.marshall(); + parcelW.recycle(); + + Parcel parcelR = Parcel.obtain(); + parcelR.unmarshall(bytes, 0, bytes.length); + parcelR.setDataPosition(0); + SoftApCapability fromParcel = SoftApCapability.CREATOR.createFromParcel(parcelR); + + assertEquals(capability, fromParcel); + assertEquals(capability.hashCode(), fromParcel.hashCode()); + } + +} diff --git a/wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java b/wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java new file mode 100644 index 000000000000..f49f387cbc6b --- /dev/null +++ b/wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2019 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.assertNotNull; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Unit tests for {@link android.net.wifi.SoftApConfToXmlMigrationUtilTest}. + */ +@SmallTest +public class SoftApConfToXmlMigrationUtilTest { + private static final String TEST_SSID = "SSID"; + private static final String TEST_PASSPHRASE = "TestPassphrase"; + private static final int TEST_CHANNEL = 0; + private static final boolean TEST_HIDDEN = false; + private static final int TEST_BAND = SoftApConfiguration.BAND_5GHZ; + private static final int TEST_SECURITY = SoftApConfiguration.SECURITY_TYPE_WPA2_PSK; + + private static final String TEST_EXPECTED_XML_STRING = + "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<WifiConfigStoreData>\n" + + "<int name=\"Version\" value=\"3\" />\n" + + "<SoftAp>\n" + + "<string name=\"SSID\">" + TEST_SSID + "</string>\n" + + "<int name=\"ApBand\" value=\"" + TEST_BAND + "\" />\n" + + "<int name=\"Channel\" value=\"" + TEST_CHANNEL + "\" />\n" + + "<boolean name=\"HiddenSSID\" value=\"" + TEST_HIDDEN + "\" />\n" + + "<int name=\"SecurityType\" value=\"" + TEST_SECURITY + "\" />\n" + + "<string name=\"Passphrase\">" + TEST_PASSPHRASE + "</string>\n" + + "<int name=\"MaxNumberOfClients\" value=\"0\" />\n" + + "<boolean name=\"ClientControlByUser\" value=\"false\" />\n" + + "<boolean name=\"AutoShutdownEnabled\" value=\"true\" />\n" + + "<long name=\"ShutdownTimeoutMillis\" value=\"0\" />\n" + + "<BlockedClientList />\n" + + "<AllowedClientList />\n" + + "</SoftAp>\n" + + "</WifiConfigStoreData>\n"; + + private byte[] createLegacyApConfFile(WifiConfiguration config) throws Exception { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(outputStream); + out.writeInt(3); + out.writeUTF(config.SSID); + out.writeInt(config.apBand); + out.writeInt(config.apChannel); + out.writeBoolean(config.hiddenSSID); + int authType = config.getAuthType(); + out.writeInt(authType); + if (authType != WifiConfiguration.KeyMgmt.NONE) { + out.writeUTF(config.preSharedKey); + } + out.close(); + return outputStream.toByteArray(); + } + + /** + * Generate a SoftApConfiguration based on the specified parameters. + */ + private SoftApConfiguration setupApConfig( + String ssid, String preSharedKey, int keyManagement, int band, int channel, + boolean hiddenSSID) { + SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); + configBuilder.setSsid(ssid); + configBuilder.setPassphrase(preSharedKey, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); + if (channel == 0) { + configBuilder.setBand(band); + } else { + configBuilder.setChannel(channel, band); + } + configBuilder.setHiddenSsid(hiddenSSID); + return configBuilder.build(); + } + + /** + * Generate a WifiConfiguration based on the specified parameters. + */ + private WifiConfiguration setupWifiConfigurationApConfig( + String ssid, String preSharedKey, int keyManagement, int band, int channel, + boolean hiddenSSID) { + WifiConfiguration config = new WifiConfiguration(); + config.SSID = ssid; + config.preSharedKey = preSharedKey; + config.allowedKeyManagement.set(keyManagement); + config.apBand = band; + config.apChannel = channel; + config.hiddenSSID = hiddenSSID; + return config; + } + + /** + * Asserts that the WifiConfigurations equal to SoftApConfiguration. + * This only compares the elements saved + * for softAp used. + */ + public static void assertWifiConfigurationEqualSoftApConfiguration( + WifiConfiguration backup, SoftApConfiguration restore) { + assertEquals(backup.SSID, restore.getSsid()); + assertEquals(backup.BSSID, restore.getBssid()); + assertEquals(SoftApConfToXmlMigrationUtil.convertWifiConfigBandToSoftApConfigBand( + backup.apBand), + restore.getBand()); + assertEquals(backup.apChannel, restore.getChannel()); + assertEquals(backup.preSharedKey, restore.getPassphrase()); + if (backup.getAuthType() == WifiConfiguration.KeyMgmt.WPA2_PSK) { + assertEquals(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK, restore.getSecurityType()); + } else { + assertEquals(SoftApConfiguration.SECURITY_TYPE_OPEN, restore.getSecurityType()); + } + assertEquals(backup.hiddenSSID, restore.isHiddenSsid()); + } + + /** + * Note: This is a copy of {@link AtomicFile#readFully()} modified to use the passed in + * {@link InputStream} which was returned using {@link AtomicFile#openRead()}. + */ + private static byte[] readFully(InputStream stream) throws IOException { + try { + int pos = 0; + int avail = stream.available(); + byte[] data = new byte[avail]; + while (true) { + int amt = stream.read(data, pos, data.length - pos); + if (amt <= 0) { + return data; + } + pos += amt; + avail = stream.available(); + if (avail > data.length - pos) { + byte[] newData = new byte[pos + avail]; + System.arraycopy(data, 0, newData, 0, pos); + data = newData; + } + } + } finally { + stream.close(); + } + } + + /** + * Tests conversion from legacy .conf file to XML file format. + */ + @Test + public void testConversion() throws Exception { + WifiConfiguration backupConfig = setupWifiConfigurationApConfig( + TEST_SSID, /* SSID */ + TEST_PASSPHRASE, /* preshared key */ + WifiConfiguration.KeyMgmt.WPA2_PSK, /* key management */ + 1, /* AP band (5GHz) */ + TEST_CHANNEL, /* AP channel */ + TEST_HIDDEN /* Hidden SSID */); + SoftApConfiguration expectedConfig = setupApConfig( + TEST_SSID, /* SSID */ + TEST_PASSPHRASE, /* preshared key */ + SoftApConfiguration.SECURITY_TYPE_WPA2_PSK, /* security type */ + SoftApConfiguration.BAND_5GHZ, /* AP band (5GHz) */ + TEST_CHANNEL, /* AP channel */ + TEST_HIDDEN /* Hidden SSID */); + + assertWifiConfigurationEqualSoftApConfiguration(backupConfig, expectedConfig); + + byte[] confBytes = createLegacyApConfFile(backupConfig); + assertNotNull(confBytes); + + InputStream xmlStream = SoftApConfToXmlMigrationUtil.convert( + new ByteArrayInputStream(confBytes)); + + byte[] xmlBytes = readFully(xmlStream); + assertNotNull(xmlBytes); + + assertEquals(TEST_EXPECTED_XML_STRING, new String(xmlBytes)); + } + +} diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java new file mode 100644 index 000000000000..1a4427034756 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2019 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.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertNull; + +import android.net.MacAddress; +import android.os.Parcel; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +@SmallTest +public class SoftApConfigurationTest { + private static final String TEST_CHAR_SET_AS_STRING = "abcdefghijklmnopqrstuvwxyz0123456789"; + + private SoftApConfiguration parcelUnparcel(SoftApConfiguration configIn) { + Parcel parcel = Parcel.obtain(); + parcel.writeParcelable(configIn, 0); + parcel.setDataPosition(0); + SoftApConfiguration configOut = + parcel.readParcelable(SoftApConfiguration.class.getClassLoader()); + parcel.recycle(); + return configOut; + } + + /** + * Helper method to generate random string. + * + * Note: this method has limited use as a random string generator. + * The characters used in this method do no not cover all valid inputs. + * @param length number of characters to generate for the string + * @return String generated string of random characters + */ + private String generateRandomString(int length) { + Random random = new Random(); + StringBuilder stringBuilder = new StringBuilder(length); + int index = -1; + while (stringBuilder.length() < length) { + index = random.nextInt(TEST_CHAR_SET_AS_STRING.length()); + stringBuilder.append(TEST_CHAR_SET_AS_STRING.charAt(index)); + } + return stringBuilder.toString(); + } + + @Test + public void testBasicSettings() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setSsid("ssid") + .setBssid(MacAddress.fromString("11:22:33:44:55:66")) + .build(); + assertThat(original.getSsid()).isEqualTo("ssid"); + assertThat(original.getBssid()).isEqualTo(MacAddress.fromString("11:22:33:44:55:66")); + assertThat(original.getPassphrase()).isNull(); + assertThat(original.getSecurityType()).isEqualTo(SoftApConfiguration.SECURITY_TYPE_OPEN); + assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ); + assertThat(original.getChannel()).isEqualTo(0); + assertThat(original.isHiddenSsid()).isEqualTo(false); + assertThat(original.getMaxNumberOfClients()).isEqualTo(0); + + SoftApConfiguration unparceled = parcelUnparcel(original); + assertThat(unparceled).isNotSameAs(original); + assertThat(unparceled).isEqualTo(original); + assertThat(unparceled.hashCode()).isEqualTo(original.hashCode()); + + SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build(); + assertThat(copy).isNotSameAs(original); + assertThat(copy).isEqualTo(original); + assertThat(copy.hashCode()).isEqualTo(original.hashCode()); + } + + @Test + public void testWpa2() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) + .build(); + assertThat(original.getPassphrase()).isEqualTo("secretsecret"); + assertThat(original.getSecurityType()).isEqualTo( + SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); + assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ); + assertThat(original.getChannel()).isEqualTo(0); + assertThat(original.isHiddenSsid()).isEqualTo(false); + assertThat(original.getMaxNumberOfClients()).isEqualTo(0); + + SoftApConfiguration unparceled = parcelUnparcel(original); + assertThat(unparceled).isNotSameAs(original); + assertThat(unparceled).isEqualTo(original); + assertThat(unparceled.hashCode()).isEqualTo(original.hashCode()); + + SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build(); + assertThat(copy).isNotSameAs(original); + assertThat(copy).isEqualTo(original); + assertThat(copy.hashCode()).isEqualTo(original.hashCode()); + } + + @Test + public void testWpa2WithAllFieldCustomized() { + List<MacAddress> testBlockedClientList = new ArrayList<>(); + List<MacAddress> testAllowedClientList = new ArrayList<>(); + testBlockedClientList.add(MacAddress.fromString("11:22:33:44:55:66")); + testAllowedClientList.add(MacAddress.fromString("aa:bb:cc:dd:ee:ff")); + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .setMaxNumberOfClients(10) + .setAutoShutdownEnabled(true) + .setShutdownTimeoutMillis(500000) + .setClientControlByUserEnabled(true) + .setBlockedClientList(testBlockedClientList) + .setAllowedClientList(testAllowedClientList) + .build(); + assertThat(original.getPassphrase()).isEqualTo("secretsecret"); + assertThat(original.getSecurityType()).isEqualTo( + SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); + assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ); + assertThat(original.getChannel()).isEqualTo(149); + assertThat(original.isHiddenSsid()).isEqualTo(true); + assertThat(original.getMaxNumberOfClients()).isEqualTo(10); + assertThat(original.isAutoShutdownEnabled()).isEqualTo(true); + assertThat(original.getShutdownTimeoutMillis()).isEqualTo(500000); + assertThat(original.isClientControlByUserEnabled()).isEqualTo(true); + assertThat(original.getBlockedClientList()).isEqualTo(testBlockedClientList); + assertThat(original.getAllowedClientList()).isEqualTo(testAllowedClientList); + + SoftApConfiguration unparceled = parcelUnparcel(original); + assertThat(unparceled).isNotSameAs(original); + assertThat(unparceled).isEqualTo(original); + assertThat(unparceled.hashCode()).isEqualTo(original.hashCode()); + + SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build(); + assertThat(copy).isNotSameAs(original); + assertThat(copy).isEqualTo(original); + assertThat(copy.hashCode()).isEqualTo(original.hashCode()); + } + + @Test + public void testWpa3Sae() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + assertThat(original.getPassphrase()).isEqualTo("secretsecret"); + assertThat(original.getSecurityType()).isEqualTo( + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE); + assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ); + assertThat(original.getChannel()).isEqualTo(149); + assertThat(original.isHiddenSsid()).isEqualTo(true); + + + SoftApConfiguration unparceled = parcelUnparcel(original); + assertThat(unparceled).isNotSameAs(original); + assertThat(unparceled).isEqualTo(original); + assertThat(unparceled.hashCode()).isEqualTo(original.hashCode()); + + SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build(); + assertThat(copy).isNotSameAs(original); + assertThat(copy).isEqualTo(original); + assertThat(copy.hashCode()).isEqualTo(original.hashCode()); + } + + @Test + public void testWpa3SaeTransition() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + assertThat(original.getSecurityType()).isEqualTo( + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION); + assertThat(original.getPassphrase()).isEqualTo("secretsecret"); + assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ); + assertThat(original.getChannel()).isEqualTo(149); + assertThat(original.isHiddenSsid()).isEqualTo(true); + + + SoftApConfiguration unparceled = parcelUnparcel(original); + assertThat(unparceled).isNotSameAs(original); + assertThat(unparceled).isEqualTo(original); + assertThat(unparceled.hashCode()).isEqualTo(original.hashCode()); + + SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build(); + assertThat(copy).isNotSameAs(original); + assertThat(copy).isEqualTo(original); + assertThat(copy.hashCode()).isEqualTo(original.hashCode()); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidShortPasswordLengthForWpa2() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MIN_LEN - 1), + SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidLongPasswordLengthForWpa2() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MAX_LEN + 1), + SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidShortPasswordLengthForWpa3SaeTransition() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MIN_LEN - 1), + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidLongPasswordLengthForWpa3SaeTransition() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MAX_LEN + 1), + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalieShutdownTimeoutMillis() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setShutdownTimeoutMillis(-1) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testSetClientListExceptionWhenExistMacAddressInBothList() { + final MacAddress testMacAddress_1 = MacAddress.fromString("22:33:44:55:66:77"); + final MacAddress testMacAddress_2 = MacAddress.fromString("aa:bb:cc:dd:ee:ff"); + ArrayList<MacAddress> testAllowedClientList = new ArrayList<>(); + testAllowedClientList.add(testMacAddress_1); + testAllowedClientList.add(testMacAddress_2); + ArrayList<MacAddress> testBlockedClientList = new ArrayList<>(); + testBlockedClientList.add(testMacAddress_1); + SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); + configBuilder.setBlockedClientList(testBlockedClientList) + .setAllowedClientList(testAllowedClientList) + .build(); + } + + @Test + public void testToWifiConfigurationWithUnsupportedParameter() { + SoftApConfiguration sae_config = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) + .build(); + + assertNull(sae_config.toWifiConfiguration()); + SoftApConfiguration band_6g_config = new SoftApConfiguration.Builder() + .setBand(SoftApConfiguration.BAND_6GHZ) + .build(); + + assertNull(band_6g_config.toWifiConfiguration()); + SoftApConfiguration sae_transition_config = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) + .build(); + + assertNull(sae_transition_config.toWifiConfiguration()); + } + + @Test + public void testToWifiConfigurationWithSupportedParameter() { + SoftApConfiguration softApConfig_2g = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", + SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) + .setChannel(11, SoftApConfiguration.BAND_2GHZ) + .setHiddenSsid(true) + .build(); + WifiConfiguration wifiConfig_2g = softApConfig_2g.toWifiConfiguration(); + assertThat(wifiConfig_2g.getAuthType()).isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK); + assertThat(wifiConfig_2g.preSharedKey).isEqualTo("secretsecret"); + assertThat(wifiConfig_2g.apBand).isEqualTo(WifiConfiguration.AP_BAND_2GHZ); + assertThat(wifiConfig_2g.apChannel).isEqualTo(11); + assertThat(wifiConfig_2g.hiddenSSID).isEqualTo(true); + + SoftApConfiguration softApConfig_5g = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", + SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + WifiConfiguration wifiConfig_5g = softApConfig_5g.toWifiConfiguration(); + assertThat(wifiConfig_5g.getAuthType()).isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK); + assertThat(wifiConfig_5g.preSharedKey).isEqualTo("secretsecret"); + assertThat(wifiConfig_5g.apBand).isEqualTo(WifiConfiguration.AP_BAND_5GHZ); + assertThat(wifiConfig_5g.apChannel).isEqualTo(149); + assertThat(wifiConfig_5g.hiddenSSID).isEqualTo(true); + + SoftApConfiguration softApConfig_2g5g = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", + SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) + .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + WifiConfiguration wifiConfig_2g5g = softApConfig_2g5g.toWifiConfiguration(); + assertThat(wifiConfig_2g5g.getAuthType()).isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK); + assertThat(wifiConfig_2g5g.preSharedKey).isEqualTo("secretsecret"); + assertThat(wifiConfig_2g5g.apBand).isEqualTo(WifiConfiguration.AP_BAND_ANY); + assertThat(wifiConfig_2g5g.apChannel).isEqualTo(0); + assertThat(wifiConfig_2g5g.hiddenSSID).isEqualTo(true); + } +} diff --git a/wifi/tests/src/android/net/wifi/SoftApInfoTest.java b/wifi/tests/src/android/net/wifi/SoftApInfoTest.java new file mode 100644 index 000000000000..929f3ab88fd8 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/SoftApInfoTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 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.os.Parcel; + +import static org.junit.Assert.assertEquals; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Unit tests for {@link android.net.wifi.SoftApInfo}. + */ +@SmallTest +public class SoftApInfoTest { + + /** + * Verifies copy constructor. + */ + @Test + public void testCopyOperator() throws Exception { + SoftApInfo info = new SoftApInfo(); + info.setFrequency(2412); + info.setBandwidth(SoftApInfo.CHANNEL_WIDTH_20MHZ); + + SoftApInfo copiedInfo = new SoftApInfo(info); + + assertEquals(info, copiedInfo); + assertEquals(info.hashCode(), copiedInfo.hashCode()); + } + + /** + * Verifies parcel serialization/deserialization. + */ + @Test + public void testParcelOperation() throws Exception { + SoftApInfo info = new SoftApInfo(); + info.setFrequency(2412); + info.setBandwidth(SoftApInfo.CHANNEL_WIDTH_20MHZ); + + Parcel parcelW = Parcel.obtain(); + info.writeToParcel(parcelW, 0); + byte[] bytes = parcelW.marshall(); + parcelW.recycle(); + + Parcel parcelR = Parcel.obtain(); + parcelR.unmarshall(bytes, 0, bytes.length); + parcelR.setDataPosition(0); + SoftApInfo fromParcel = SoftApInfo.CREATOR.createFromParcel(parcelR); + + assertEquals(info, fromParcel); + assertEquals(info.hashCode(), fromParcel.hashCode()); + } + +} diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java index fe30ddd3765a..a7b6765e886a 100644 --- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java @@ -16,10 +16,19 @@ package android.net.wifi; +import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP; +import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B; +import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_OPEN; +import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_OWE; +import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_PSK; +import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_SAE; +import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_WAPI_PSK; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import android.net.MacAddress; @@ -33,9 +42,6 @@ import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Test; -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; - /** * Unit tests for {@link android.net.wifi.WifiConfiguration}. */ @@ -77,7 +83,7 @@ public class WifiConfigurationTest { // lacking a useful config.equals, check two fields near the end. assertEquals(cookie, reconfig.getMoTree()); - assertEquals(macBeforeParcel, reconfig.getOrCreateRandomizedMacAddress()); + assertEquals(macBeforeParcel, reconfig.getRandomizedMacAddress()); assertEquals(config.updateIdentifier, reconfig.updateIdentifier); assertFalse(reconfig.trusted); assertTrue(config.fromWifiNetworkSpecifier); @@ -92,37 +98,6 @@ public class WifiConfigurationTest { } @Test - public void testNetworkSelectionStatusCopy() { - NetworkSelectionStatus networkSelectionStatus = new NetworkSelectionStatus(); - networkSelectionStatus.setNotRecommended(true); - - NetworkSelectionStatus copy = new NetworkSelectionStatus(); - copy.copy(networkSelectionStatus); - - assertEquals(networkSelectionStatus.isNotRecommended(), copy.isNotRecommended()); - } - - @Test - public void testNetworkSelectionStatusParcel() { - NetworkSelectionStatus networkSelectionStatus = new NetworkSelectionStatus(); - networkSelectionStatus.setNotRecommended(true); - - Parcel parcelW = Parcel.obtain(); - networkSelectionStatus.writeToParcel(parcelW); - byte[] bytes = parcelW.marshall(); - parcelW.recycle(); - - Parcel parcelR = Parcel.obtain(); - parcelR.unmarshall(bytes, 0, bytes.length); - parcelR.setDataPosition(0); - - NetworkSelectionStatus copy = new NetworkSelectionStatus(); - copy.readFromParcel(parcelR); - - assertEquals(networkSelectionStatus.isNotRecommended(), copy.isNotRecommended()); - } - - @Test public void testIsOpenNetwork_IsOpen_NullWepKeys() { WifiConfiguration config = new WifiConfiguration(); config.allowedKeyManagement.clear(); @@ -195,19 +170,6 @@ public class WifiConfigurationTest { } @Test - public void testGetOrCreateRandomizedMacAddress_SavesAndReturnsSameAddress() { - WifiConfiguration config = new WifiConfiguration(); - MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS); - assertEquals(defaultMac, config.getRandomizedMacAddress()); - - MacAddress firstMacAddress = config.getOrCreateRandomizedMacAddress(); - MacAddress secondMacAddress = config.getOrCreateRandomizedMacAddress(); - - assertNotEquals(defaultMac, firstMacAddress); - assertEquals(firstMacAddress, secondMacAddress); - } - - @Test public void testSetRandomizedMacAddress_ChangesSavedAddress() { WifiConfiguration config = new WifiConfiguration(); MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS); @@ -221,36 +183,6 @@ public class WifiConfigurationTest { } @Test - public void testGetOrCreateRandomizedMacAddress_ReRandomizesInvalidAddress() { - WifiConfiguration config = new WifiConfiguration(); - - MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS); - MacAddress macAddressZeroes = MacAddress.ALL_ZEROS_ADDRESS; - MacAddress macAddressMulticast = MacAddress.fromString("03:ff:ff:ff:ff:ff"); - MacAddress macAddressGlobal = MacAddress.fromString("fc:ff:ff:ff:ff:ff"); - - config.setRandomizedMacAddress(null); - MacAddress macAfterChange = config.getOrCreateRandomizedMacAddress(); - assertNotEquals(macAfterChange, null); - - config.setRandomizedMacAddress(defaultMac); - macAfterChange = config.getOrCreateRandomizedMacAddress(); - assertNotEquals(macAfterChange, defaultMac); - - config.setRandomizedMacAddress(macAddressZeroes); - macAfterChange = config.getOrCreateRandomizedMacAddress(); - assertNotEquals(macAfterChange, macAddressZeroes); - - config.setRandomizedMacAddress(macAddressMulticast); - macAfterChange = config.getOrCreateRandomizedMacAddress(); - assertNotEquals(macAfterChange, macAddressMulticast); - - config.setRandomizedMacAddress(macAddressGlobal); - macAfterChange = config.getOrCreateRandomizedMacAddress(); - assertNotEquals(macAfterChange, macAddressGlobal); - } - - @Test public void testSetRandomizedMacAddress_DoesNothingWhenNull() { WifiConfiguration config = new WifiConfiguration(); MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS); @@ -271,33 +203,6 @@ public class WifiConfigurationTest { } /** - * Verifies that the serialization/de-serialization for softap config works. - */ - @Test - public void testSoftApConfigBackupAndRestore() throws Exception { - WifiConfiguration config = new WifiConfiguration(); - config.SSID = "TestAP"; - config.apBand = WifiConfiguration.AP_BAND_5GHZ; - config.apChannel = 40; - config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK); - config.preSharedKey = "TestPsk"; - config.hiddenSSID = true; - - byte[] data = config.getBytesForBackup(); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream in = new DataInputStream(bais); - WifiConfiguration restoredConfig = WifiConfiguration.getWifiConfigFromBackup(in); - - assertEquals(config.SSID, restoredConfig.SSID); - assertEquals(config.preSharedKey, restoredConfig.preSharedKey); - assertEquals(config.getAuthType(), restoredConfig.getAuthType()); - assertEquals(config.apBand, restoredConfig.apBand); - assertEquals(config.apChannel, restoredConfig.apChannel); - assertEquals(config.hiddenSSID, restoredConfig.hiddenSSID); - } - - - /** * Verifies that getKeyIdForCredentials returns the expected string for Enterprise networks * @throws Exception */ @@ -436,7 +341,23 @@ public class WifiConfigurationTest { config.allowedKeyManagement.clear(); assertEquals(mSsid + "WEP", config.getSsidAndSecurityTypeString()); + // set WEP key and give a valid index. + config.wepKeys[0] = null; + config.wepKeys[2] = "TestWep"; + config.wepTxKeyIndex = 2; + config.allowedKeyManagement.clear(); + assertEquals(mSsid + "WEP", config.getSsidAndSecurityTypeString()); + + // set WEP key but does not give a valid index. + config.wepKeys[0] = null; + config.wepKeys[2] = "TestWep"; + config.wepTxKeyIndex = 0; + config.allowedKeyManagement.clear(); + config.allowedKeyManagement.set(KeyMgmt.OWE); + assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.OWE], config.getSsidAndSecurityTypeString()); + config.wepKeys[0] = null; + config.wepTxKeyIndex = 0; config.allowedKeyManagement.clear(); config.allowedKeyManagement.set(KeyMgmt.OWE); assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.OWE], config.getSsidAndSecurityTypeString()); @@ -453,5 +374,167 @@ public class WifiConfigurationTest { config.allowedKeyManagement.clear(); config.allowedKeyManagement.set(KeyMgmt.NONE); assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.NONE], config.getSsidAndSecurityTypeString()); + + config.allowedKeyManagement.clear(); + config.allowedKeyManagement.set(KeyMgmt.WAPI_PSK); + assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.WAPI_PSK], + config.getSsidAndSecurityTypeString()); + + config.allowedKeyManagement.clear(); + config.allowedKeyManagement.set(KeyMgmt.WAPI_CERT); + assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.WAPI_CERT], + config.getSsidAndSecurityTypeString()); + } + + /** + * Ensure that the {@link NetworkSelectionStatus.DisableReasonInfo}s are populated in + * {@link NetworkSelectionStatus#DISABLE_REASON_INFOS} for reason codes from 0 to + * {@link NetworkSelectionStatus#NETWORK_SELECTION_DISABLED_MAX} - 1. + */ + @Test + public void testNetworkSelectionDisableReasonInfosPopulated() { + assertEquals(NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX, + NetworkSelectionStatus.DISABLE_REASON_INFOS.size()); + for (int i = 0; i < NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; i++) { + assertNotNull(NetworkSelectionStatus.DISABLE_REASON_INFOS.get(i)); + } + } + + /** + * Ensure that {@link NetworkSelectionStatus#getMaxNetworkSelectionDisableReason()} returns + * the maximum disable reason. + */ + @Test + public void testNetworkSelectionGetMaxNetworkSelectionDisableReason() { + int maxReason = Integer.MIN_VALUE; + for (int i = 0; i < NetworkSelectionStatus.DISABLE_REASON_INFOS.size(); i++) { + int reason = NetworkSelectionStatus.DISABLE_REASON_INFOS.keyAt(i); + maxReason = Math.max(maxReason, reason); + } + assertEquals(maxReason, NetworkSelectionStatus.getMaxNetworkSelectionDisableReason()); + } + + /** + * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the + * {@link WifiConfiguration} object correctly for SAE security type. + * @throws Exception + */ + @Test + public void testSetSecurityParamsForSae() throws Exception { + WifiConfiguration config = new WifiConfiguration(); + + config.setSecurityParams(SECURITY_TYPE_SAE); + + assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)); + assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP)); + assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.GCMP_256)); + assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP)); + assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256)); + assertTrue(config.requirePmf); + } + + /** + * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the + * {@link WifiConfiguration} object correctly for OWE security type. + * @throws Exception + */ + @Test + public void testSetSecurityParamsForOwe() throws Exception { + WifiConfiguration config = new WifiConfiguration(); + + config.setSecurityParams(SECURITY_TYPE_OWE); + + assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)); + assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP)); + assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.GCMP_256)); + assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP)); + assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256)); + assertTrue(config.requirePmf); + } + + /** + * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the + * {@link WifiConfiguration} object correctly for Suite-B security type. + * @throws Exception + */ + @Test + public void testSetSecurityParamsForSuiteB() throws Exception { + WifiConfiguration config = new WifiConfiguration(); + + config.setSecurityParams(SECURITY_TYPE_EAP_SUITE_B); + + assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)); + assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)); + assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)); + assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.GCMP_256)); + assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256)); + assertTrue(config.allowedGroupManagementCiphers + .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256)); + assertTrue(config.requirePmf); + } + + /** + * Test that the NetworkSelectionStatus Builder returns the same values that was set, and that + * calling build multiple times returns different instances. + */ + @Test + public void testNetworkSelectionStatusBuilder() throws Exception { + NetworkSelectionStatus.Builder builder = new NetworkSelectionStatus.Builder() + .setNetworkSelectionDisableReason( + NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION) + .setNetworkSelectionStatus( + NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED); + + NetworkSelectionStatus status1 = builder.build(); + + assertEquals(NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION, + status1.getNetworkSelectionDisableReason()); + assertEquals(NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED, + status1.getNetworkSelectionStatus()); + + NetworkSelectionStatus status2 = builder + .setNetworkSelectionDisableReason(NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD) + .build(); + + // different instances + assertNotSame(status1, status2); + + // assert that status1 didn't change + assertEquals(NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION, + status1.getNetworkSelectionDisableReason()); + assertEquals(NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED, + status1.getNetworkSelectionStatus()); + + // assert that status2 changed + assertEquals(NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD, + status2.getNetworkSelectionDisableReason()); + assertEquals(NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED, + status2.getNetworkSelectionStatus()); + } + + @Test + public void testNeedsPreSharedKey() throws Exception { + WifiConfiguration configuration = new WifiConfiguration(); + + configuration.setSecurityParams(SECURITY_TYPE_PSK); + assertTrue(configuration.needsPreSharedKey()); + + configuration.setSecurityParams(SECURITY_TYPE_SAE); + assertTrue(configuration.needsPreSharedKey()); + + configuration.setSecurityParams(SECURITY_TYPE_WAPI_PSK); + assertTrue(configuration.needsPreSharedKey()); + + configuration.setSecurityParams(SECURITY_TYPE_OPEN); + assertFalse(configuration.needsPreSharedKey()); + + configuration.setSecurityParams(SECURITY_TYPE_OWE); + assertFalse(configuration.needsPreSharedKey()); + + configuration.setSecurityParams(SECURITY_TYPE_EAP); + assertFalse(configuration.needsPreSharedKey()); + + configuration.setSecurityParams(SECURITY_TYPE_EAP_SUITE_B); + assertFalse(configuration.needsPreSharedKey()); } } diff --git a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java index beed6666f28f..62485ecb6f7b 100644 --- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java +++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.net.wifi.WifiEnterpriseConfig.Eap; import android.net.wifi.WifiEnterpriseConfig.Phase2; @@ -46,6 +47,7 @@ public class WifiEnterpriseConfigTest { public static final String KEYSTORE_URI = "keystore://"; public static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE; public static final String KEYSTORES_URI = "keystores://"; + private static final String TEST_DOMAIN_SUFFIX_MATCH = "domainSuffixMatch"; private WifiEnterpriseConfig mEnterpriseConfig; @@ -343,11 +345,13 @@ public class WifiEnterpriseConfigTest { enterpriseConfig.setPassword("*"); enterpriseConfig.setEapMethod(Eap.TTLS); enterpriseConfig.setPhase2Method(Phase2.GTC); + enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS); mEnterpriseConfig = new WifiEnterpriseConfig(); mEnterpriseConfig.copyFromExternal(enterpriseConfig, "*"); assertEquals("TTLS", getSupplicantEapMethod()); assertEquals("\"autheap=GTC\"", getSupplicantPhase2Method()); assertNotEquals("*", mEnterpriseConfig.getPassword()); + assertEquals(enterpriseConfig.getOcsp(), mEnterpriseConfig.getOcsp()); } /** Verfies that parceling a WifiEnterpriseConfig preseves method information. */ @@ -487,4 +491,87 @@ public class WifiEnterpriseConfigTest { assertFalse(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert()); assertTrue(mEnterpriseConfig.isAppInstalledCaCert()); } + + /** Verifies that OCSP value is set correctly. */ + @Test + public void testOcspSetGet() throws Exception { + WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); + + enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_NONE); + assertEquals(WifiEnterpriseConfig.OCSP_NONE, enterpriseConfig.getOcsp()); + + enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS); + assertEquals(WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS, enterpriseConfig.getOcsp()); + + enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_REQUEST_CERT_STATUS); + assertEquals(WifiEnterpriseConfig.OCSP_REQUEST_CERT_STATUS, enterpriseConfig.getOcsp()); + + enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS); + assertEquals(WifiEnterpriseConfig.OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS, + enterpriseConfig.getOcsp()); + } + + /** Verifies that an exception is thrown when invalid OCSP is set. */ + @Test + public void testInvalidOcspValue() { + WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); + try { + enterpriseConfig.setOcsp(-1); + fail("Should raise an IllegalArgumentException here."); + } catch (IllegalArgumentException e) { + // expected exception. + } + } + + /** Verifies that the EAP inner method is reset when we switch to Unauth-TLS */ + @Test + public void eapPhase2MethodForUnauthTls() { + // Initially select an EAP method that supports an phase2. + mEnterpriseConfig.setEapMethod(Eap.PEAP); + mEnterpriseConfig.setPhase2Method(Phase2.MSCHAPV2); + assertEquals("PEAP", getSupplicantEapMethod()); + assertEquals("\"auth=MSCHAPV2\"", getSupplicantPhase2Method()); + + // Change the EAP method to another type which supports a phase2. + mEnterpriseConfig.setEapMethod(Eap.TTLS); + assertEquals("TTLS", getSupplicantEapMethod()); + assertEquals("\"auth=MSCHAPV2\"", getSupplicantPhase2Method()); + + // Change the EAP method to Unauth-TLS which does not support a phase2. + mEnterpriseConfig.setEapMethod(Eap.UNAUTH_TLS); + assertEquals(null, getSupplicantPhase2Method()); + } + + @Test + public void testIsEnterpriseConfigSecure() { + WifiEnterpriseConfig baseConfig = new WifiEnterpriseConfig(); + baseConfig.setEapMethod(Eap.PEAP); + baseConfig.setPhase2Method(Phase2.MSCHAPV2); + assertTrue(baseConfig.isInsecure()); + + WifiEnterpriseConfig noMatchConfig = new WifiEnterpriseConfig(baseConfig); + noMatchConfig.setCaCertificate(FakeKeys.CA_CERT0); + // Missing match is insecure. + assertTrue(noMatchConfig.isInsecure()); + + WifiEnterpriseConfig noCaConfig = new WifiEnterpriseConfig(baseConfig); + noCaConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH); + // Missing CA certificate is insecure. + assertTrue(noCaConfig.isInsecure()); + + WifiEnterpriseConfig secureConfig = new WifiEnterpriseConfig(); + secureConfig.setEapMethod(Eap.PEAP); + secureConfig.setPhase2Method(Phase2.MSCHAPV2); + secureConfig.setCaCertificate(FakeKeys.CA_CERT0); + secureConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH); + assertFalse(secureConfig.isInsecure()); + + WifiEnterpriseConfig secureConfigWithCaAlias = new WifiEnterpriseConfig(); + secureConfigWithCaAlias.setEapMethod(Eap.PEAP); + secureConfigWithCaAlias.setPhase2Method(Phase2.MSCHAPV2); + secureConfigWithCaAlias.setCaCertificateAliases(new String[]{"alias1", "alisa2"}); + secureConfigWithCaAlias.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH); + assertFalse(secureConfigWithCaAlias.isInsecure()); + } + } diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java index 0ce5d66cf4f7..311bbc41b8fe 100644 --- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java +++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java @@ -18,6 +18,7 @@ package android.net.wifi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import android.os.Parcel; @@ -26,6 +27,8 @@ import androidx.test.filters.SmallTest; import org.junit.Test; +import java.nio.charset.StandardCharsets; + /** * Unit tests for {@link android.net.wifi.WifiInfo}. */ @@ -38,6 +41,14 @@ public class WifiInfoTest { private static final String TEST_PACKAGE_NAME = "com.test.example"; private static final String TEST_FQDN = "test.com"; private static final String TEST_PROVIDER_NAME = "test"; + private static final int TEST_WIFI_STANDARD = ScanResult.WIFI_STANDARD_11AC; + private static final int TEST_MAX_SUPPORTED_TX_LINK_SPEED_MBPS = 866; + private static final int TEST_MAX_SUPPORTED_RX_LINK_SPEED_MBPS = 1200; + private static final String TEST_SSID = "Test123"; + private static final String TEST_BSSID = "12:12:12:12:12:12"; + private static final int TEST_RSSI = -60; + private static final int TEST_NETWORK_ID = 5; + private static final int TEST_NETWORK_ID2 = 6; /** * Verify parcel write/read with WifiInfo. @@ -53,7 +64,10 @@ public class WifiInfoTest { writeWifiInfo.setOsuAp(true); writeWifiInfo.setFQDN(TEST_FQDN); writeWifiInfo.setProviderFriendlyName(TEST_PROVIDER_NAME); - writeWifiInfo.setNetworkSuggestionOrSpecifierPackageName(TEST_PACKAGE_NAME); + writeWifiInfo.setRequestingPackageName(TEST_PACKAGE_NAME); + writeWifiInfo.setWifiStandard(TEST_WIFI_STANDARD); + writeWifiInfo.setMaxSupportedTxLinkSpeedMbps(TEST_MAX_SUPPORTED_TX_LINK_SPEED_MBPS); + writeWifiInfo.setMaxSupportedRxLinkSpeedMbps(TEST_MAX_SUPPORTED_RX_LINK_SPEED_MBPS); Parcel parcel = Parcel.obtain(); writeWifiInfo.writeToParcel(parcel, 0); @@ -69,8 +83,69 @@ public class WifiInfoTest { assertTrue(readWifiInfo.isTrusted()); assertTrue(readWifiInfo.isOsuAp()); assertTrue(readWifiInfo.isPasspointAp()); - assertEquals(TEST_PACKAGE_NAME, readWifiInfo.getNetworkSuggestionOrSpecifierPackageName()); + assertEquals(TEST_PACKAGE_NAME, readWifiInfo.getRequestingPackageName()); assertEquals(TEST_FQDN, readWifiInfo.getPasspointFqdn()); assertEquals(TEST_PROVIDER_NAME, readWifiInfo.getPasspointProviderFriendlyName()); + assertEquals(TEST_WIFI_STANDARD, readWifiInfo.getWifiStandard()); + assertEquals(TEST_MAX_SUPPORTED_TX_LINK_SPEED_MBPS, + readWifiInfo.getMaxSupportedTxLinkSpeedMbps()); + assertEquals(TEST_MAX_SUPPORTED_RX_LINK_SPEED_MBPS, + readWifiInfo.getMaxSupportedRxLinkSpeedMbps()); + } + + /** + * Verify values after reset() + */ + @Test + public void testWifiInfoResetValue() throws Exception { + WifiInfo wifiInfo = new WifiInfo(); + wifiInfo.reset(); + assertEquals(WifiInfo.LINK_SPEED_UNKNOWN, wifiInfo.getMaxSupportedTxLinkSpeedMbps()); + assertEquals(WifiInfo.LINK_SPEED_UNKNOWN, wifiInfo.getMaxSupportedRxLinkSpeedMbps()); + assertEquals(WifiInfo.LINK_SPEED_UNKNOWN, wifiInfo.getTxLinkSpeedMbps()); + assertEquals(WifiInfo.LINK_SPEED_UNKNOWN, wifiInfo.getRxLinkSpeedMbps()); + assertEquals(WifiInfo.INVALID_RSSI, wifiInfo.getRssi()); + assertEquals(WifiManager.UNKNOWN_SSID, wifiInfo.getSSID()); + assertEquals(null, wifiInfo.getBSSID()); + assertEquals(-1, wifiInfo.getNetworkId()); + } + + /** + * Test that the WifiInfo Builder returns the same values that was set, and that + * calling build multiple times returns different instances. + */ + @Test + public void testWifiInfoBuilder() throws Exception { + WifiInfo.Builder builder = new WifiInfo.Builder() + .setSsid(TEST_SSID.getBytes(StandardCharsets.UTF_8)) + .setBssid(TEST_BSSID) + .setRssi(TEST_RSSI) + .setNetworkId(TEST_NETWORK_ID); + + WifiInfo info1 = builder.build(); + + assertEquals("\"" + TEST_SSID + "\"", info1.getSSID()); + assertEquals(TEST_BSSID, info1.getBSSID()); + assertEquals(TEST_RSSI, info1.getRssi()); + assertEquals(TEST_NETWORK_ID, info1.getNetworkId()); + + WifiInfo info2 = builder + .setNetworkId(TEST_NETWORK_ID2) + .build(); + + // different instances + assertNotSame(info1, info2); + + // assert that info1 didn't change + assertEquals("\"" + TEST_SSID + "\"", info1.getSSID()); + assertEquals(TEST_BSSID, info1.getBSSID()); + assertEquals(TEST_RSSI, info1.getRssi()); + assertEquals(TEST_NETWORK_ID, info1.getNetworkId()); + + // assert that info2 changed + assertEquals("\"" + TEST_SSID + "\"", info2.getSSID()); + assertEquals(TEST_BSSID, info2.getBSSID()); + assertEquals(TEST_RSSI, info2.getRssi()); + assertEquals(TEST_NETWORK_ID2, info2.getNetworkId()); } } diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index b130c1737c62..1398bfeef031 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -16,18 +16,31 @@ package android.net.wifi; -import static android.net.wifi.WifiManager.HOTSPOT_FAILED; -import static android.net.wifi.WifiManager.HOTSPOT_STARTED; -import static android.net.wifi.WifiManager.HOTSPOT_STOPPED; +import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED; +import static android.net.wifi.WifiManager.ActionListener; +import static android.net.wifi.WifiManager.BUSY; +import static android.net.wifi.WifiManager.ERROR; import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC; import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE; import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL; import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED; import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.REQUEST_REGISTERED; +import static android.net.wifi.WifiManager.NOT_AUTHORIZED; +import static android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener; import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL; +import static android.net.wifi.WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS; +import static android.net.wifi.WifiManager.STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; +import static android.net.wifi.WifiManager.WIFI_FEATURE_DPP; +import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE; +import static android.net.wifi.WifiManager.WIFI_FEATURE_P2P; +import static android.net.wifi.WifiManager.WIFI_FEATURE_PASSPOINT; +import static android.net.wifi.WifiManager.WIFI_FEATURE_SCANNER; +import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE; +import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B; +import static android.net.wifi.WifiManager.WpsCallback; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -37,6 +50,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyList; @@ -52,9 +66,11 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import android.app.ActivityManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.net.DhcpInfo; +import android.net.MacAddress; import android.net.wifi.WifiManager.LocalOnlyHotspotCallback; import android.net.wifi.WifiManager.LocalOnlyHotspotObserver; import android.net.wifi.WifiManager.LocalOnlyHotspotReservation; @@ -62,15 +78,20 @@ import android.net.wifi.WifiManager.LocalOnlyHotspotSubscription; import android.net.wifi.WifiManager.NetworkRequestMatchCallback; import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback; import android.net.wifi.WifiManager.OnWifiUsabilityStatsListener; +import android.net.wifi.WifiManager.ScanResultsCallback; import android.net.wifi.WifiManager.SoftApCallback; +import android.net.wifi.WifiManager.SuggestionConnectionStatusListener; import android.net.wifi.WifiManager.TrafficStateCallback; +import android.net.wifi.WifiManager.WifiConnectedNetworkScorer; +import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; -import android.os.Message; -import android.os.Messenger; +import android.os.RemoteException; +import android.os.connectivity.WifiActivityEnergyInfo; import android.os.test.TestLooper; +import android.util.SparseArray; import androidx.test.filters.SmallTest; @@ -85,6 +106,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.Executor; /** @@ -98,25 +120,78 @@ public class WifiManagerTest { private static final int TEST_UID = 14553; private static final int TEST_NETWORK_ID = 143; private static final String TEST_PACKAGE_NAME = "TestPackage"; + private static final String TEST_FEATURE_ID = "TestFeature"; private static final String TEST_COUNTRY_CODE = "US"; private static final String[] TEST_MAC_ADDRESSES = {"da:a1:19:0:0:0"}; + private static final int TEST_AP_FREQUENCY = 2412; + private static final int TEST_AP_BANDWIDTH = SoftApInfo.CHANNEL_WIDTH_20MHZ; @Mock Context mContext; @Mock android.net.wifi.IWifiManager mWifiService; @Mock ApplicationInfo mApplicationInfo; @Mock WifiConfiguration mApConfig; - @Mock IBinder mAppBinder; @Mock SoftApCallback mSoftApCallback; @Mock TrafficStateCallback mTrafficStateCallback; @Mock NetworkRequestMatchCallback mNetworkRequestMatchCallback; @Mock OnWifiUsabilityStatsListener mOnWifiUsabilityStatsListener; + @Mock OnWifiActivityEnergyInfoListener mOnWifiActivityEnergyInfoListener; + @Mock SuggestionConnectionStatusListener mListener; + @Mock Runnable mRunnable; + @Mock Executor mExecutor; + @Mock Executor mAnotherExecutor; + @Mock ActivityManager mActivityManager; + @Mock WifiConnectedNetworkScorer mWifiConnectedNetworkScorer; - private Executor mExecutor; private Handler mHandler; private TestLooper mLooper; private WifiManager mWifiManager; - private Messenger mWifiServiceMessenger; - final ArgumentCaptor<Messenger> mMessengerCaptor = ArgumentCaptor.forClass(Messenger.class); + private WifiNetworkSuggestion mWifiNetworkSuggestion; + private ScanResultsCallback mScanResultsCallback; + private WifiActivityEnergyInfo mWifiActivityEnergyInfo; + + /** + * Util function to check public field which used for softap in WifiConfiguration + * same as the value in SoftApConfiguration. + * + */ + private boolean compareWifiAndSoftApConfiguration( + SoftApConfiguration softApConfig, WifiConfiguration wifiConfig) { + if (!Objects.equals(wifiConfig.SSID, softApConfig.getSsid())) { + return false; + } + if (!Objects.equals(wifiConfig.BSSID, softApConfig.getBssid())) { + return false; + } + if (!Objects.equals(wifiConfig.preSharedKey, softApConfig.getPassphrase())) { + return false; + } + + if (wifiConfig.hiddenSSID != softApConfig.isHiddenSsid()) { + return false; + } + switch (softApConfig.getSecurityType()) { + case SoftApConfiguration.SECURITY_TYPE_OPEN: + if (wifiConfig.getAuthType() != WifiConfiguration.KeyMgmt.NONE) { + return false; + } + break; + case SoftApConfiguration.SECURITY_TYPE_WPA2_PSK: + if (wifiConfig.getAuthType() != WifiConfiguration.KeyMgmt.WPA2_PSK) { + return false; + } + break; + default: + return false; + } + return true; + } + + private SoftApConfiguration generatorTestSoftApConfig() { + return new SoftApConfiguration.Builder() + .setSsid("TestSSID") + .setPassphrase("TestPassphrase", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) + .build(); + } @Before public void setUp() throws Exception { @@ -126,10 +201,16 @@ public class WifiManagerTest { mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo); when(mContext.getOpPackageName()).thenReturn(TEST_PACKAGE_NAME); - - mWifiServiceMessenger = new Messenger(mHandler); mWifiManager = new WifiManager(mContext, mWifiService, mLooper.getLooper()); verify(mWifiService).getVerboseLoggingLevel(); + mWifiNetworkSuggestion = new WifiNetworkSuggestion(); + mScanResultsCallback = new ScanResultsCallback() { + @Override + public void onScanResultsAvailable() { + mRunnable.run(); + } + }; + mWifiActivityEnergyInfo = new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0); } /** @@ -171,19 +252,50 @@ public class WifiManagerTest { } /** + * Check the call to startSoftAp calls WifiService to startSoftAp with the provided + * WifiConfiguration. Verify that the return value is propagated to the caller. + */ + @Test + public void testStartTetheredHotspotCallsServiceWithSoftApConfig() throws Exception { + SoftApConfiguration softApConfig = generatorTestSoftApConfig(); + when(mWifiService.startTetheredHotspot(eq(softApConfig))).thenReturn(true); + assertTrue(mWifiManager.startTetheredHotspot(softApConfig)); + + when(mWifiService.startTetheredHotspot(eq(softApConfig))).thenReturn(false); + assertFalse(mWifiManager.startTetheredHotspot(softApConfig)); + } + + /** + * Check the call to startSoftAp calls WifiService to startSoftAp with a null config. Verify + * that the return value is propagated to the caller. + */ + @Test + public void testStartTetheredHotspotCallsServiceWithNullConfig() throws Exception { + when(mWifiService.startTetheredHotspot(eq(null))).thenReturn(true); + assertTrue(mWifiManager.startTetheredHotspot(null)); + + when(mWifiService.startTetheredHotspot(eq(null))).thenReturn(false); + assertFalse(mWifiManager.startTetheredHotspot(null)); + } + + /** * Test creation of a LocalOnlyHotspotReservation and verify that close properly calls * WifiService.stopLocalOnlyHotspot. */ @Test public void testCreationAndCloseOfLocalOnlyHotspotReservation() throws Exception { + SoftApConfiguration softApConfig = generatorTestSoftApConfig(); TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); - when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), - anyString())).thenReturn(REQUEST_REGISTERED); + when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), + nullable(String.class), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); - callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(mApConfig)); + callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(softApConfig)); + + assertEquals(softApConfig, callback.mRes.getSoftApConfiguration()); + WifiConfiguration wifiConfig = callback.mRes.getWifiConfiguration(); + assertTrue(compareWifiAndSoftApConfiguration(softApConfig, wifiConfig)); - assertEquals(mApConfig, callback.mRes.getWifiConfiguration()); callback.mRes.close(); verify(mWifiService).stopLocalOnlyHotspot(); } @@ -194,15 +306,18 @@ public class WifiManagerTest { @Test public void testLocalOnlyHotspotReservationCallsStopProperlyInTryWithResources() throws Exception { + SoftApConfiguration softApConfig = generatorTestSoftApConfig(); TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); - when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), - anyString())).thenReturn(REQUEST_REGISTERED); + when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), + nullable(String.class), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); - callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(mApConfig)); + callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(softApConfig)); try (WifiManager.LocalOnlyHotspotReservation res = callback.mRes) { - assertEquals(mApConfig, res.getWifiConfiguration()); + assertEquals(softApConfig, res.getSoftApConfiguration()); + WifiConfiguration wifiConfig = callback.mRes.getWifiConfiguration(); + assertTrue(compareWifiAndSoftApConfiguration(softApConfig, wifiConfig)); } verify(mWifiService).stopLocalOnlyHotspot(); @@ -252,6 +367,7 @@ public class WifiManagerTest { */ @Test public void testLocalOnlyHotspotCallback() { + SoftApConfiguration softApConfig = generatorTestSoftApConfig(); TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); assertFalse(callback.mOnStartedCalled); assertFalse(callback.mOnStoppedCalled); @@ -260,7 +376,7 @@ public class WifiManagerTest { // test onStarted WifiManager.LocalOnlyHotspotReservation res = - mWifiManager.new LocalOnlyHotspotReservation(mApConfig); + mWifiManager.new LocalOnlyHotspotReservation(softApConfig); callback.onStarted(res); assertEquals(res, callback.mRes); assertTrue(callback.mOnStartedCalled); @@ -286,7 +402,7 @@ public class WifiManagerTest { public boolean mOnRegistered = false; public boolean mOnStartedCalled = false; public boolean mOnStoppedCalled = false; - public WifiConfiguration mConfig = null; + public SoftApConfiguration mConfig = null; public LocalOnlyHotspotSubscription mSub = null; public long mCallingThreadId = -1; @@ -298,7 +414,7 @@ public class WifiManagerTest { } @Override - public void onStarted(WifiConfiguration config) { + public void onStarted(SoftApConfiguration config) { mOnStartedCalled = true; mConfig = config; mCallingThreadId = Thread.currentThread().getId(); @@ -317,6 +433,7 @@ public class WifiManagerTest { @Test public void testLocalOnlyHotspotObserver() { TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver(); + SoftApConfiguration softApConfig = generatorTestSoftApConfig(); assertFalse(observer.mOnRegistered); assertFalse(observer.mOnStartedCalled); assertFalse(observer.mOnStoppedCalled); @@ -332,18 +449,18 @@ public class WifiManagerTest { assertEquals(null, observer.mConfig); assertEquals(sub, observer.mSub); - observer.onStarted(mApConfig); + observer.onStarted(softApConfig); assertTrue(observer.mOnRegistered); assertTrue(observer.mOnStartedCalled); assertFalse(observer.mOnStoppedCalled); - assertEquals(mApConfig, observer.mConfig); + assertEquals(softApConfig, observer.mConfig); assertEquals(sub, observer.mSub); observer.onStopped(); assertTrue(observer.mOnRegistered); assertTrue(observer.mOnStartedCalled); assertTrue(observer.mOnStoppedCalled); - assertEquals(mApConfig, observer.mConfig); + assertEquals(softApConfig, observer.mConfig); assertEquals(sub, observer.mSub); } @@ -355,8 +472,8 @@ public class WifiManagerTest { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); mWifiManager.startLocalOnlyHotspot(callback, mHandler); - verify(mWifiService) - .startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), anyString()); + verify(mWifiService).startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), + anyString(), nullable(String.class), eq(null)); } /** @@ -366,8 +483,9 @@ public class WifiManagerTest { @Test(expected = SecurityException.class) public void testStartLocalOnlyHotspotThrowsSecurityException() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); - doThrow(new SecurityException()).when(mWifiService) - .startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), anyString()); + doThrow(new SecurityException()).when(mWifiService).startLocalOnlyHotspot( + any(ILocalOnlyHotspotCallback.class), anyString(), nullable(String.class), + eq(null)); mWifiManager.startLocalOnlyHotspot(callback, mHandler); } @@ -378,8 +496,9 @@ public class WifiManagerTest { @Test(expected = IllegalStateException.class) public void testStartLocalOnlyHotspotThrowsIllegalStateException() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); - doThrow(new IllegalStateException()).when(mWifiService) - .startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), anyString()); + doThrow(new IllegalStateException()).when(mWifiService).startLocalOnlyHotspot( + any(ILocalOnlyHotspotCallback.class), anyString(), nullable(String.class), + eq(null)); mWifiManager.startLocalOnlyHotspot(callback, mHandler); } @@ -389,12 +508,13 @@ public class WifiManagerTest { @Test public void testCorrectLooperIsUsedForHandler() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); - when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), - anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE); + when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), + nullable(String.class), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason); verify(mContext, never()).getMainLooper(); + verify(mContext, never()).getMainExecutor(); } /** @@ -405,15 +525,15 @@ public class WifiManagerTest { public void testMainLooperIsUsedWhenHandlerNotProvided() throws Exception { // record thread from looper.getThread and check ids. TestLooper altLooper = new TestLooper(); - when(mContext.getMainLooper()).thenReturn(altLooper.getLooper()); + when(mContext.getMainExecutor()).thenReturn(altLooper.getNewExecutor()); TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); - when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), - anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE); + when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), + nullable(String.class), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE); mWifiManager.startLocalOnlyHotspot(callback, null); altLooper.dispatchAll(); assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason); assertEquals(altLooper.getLooper().getThread().getId(), callback.mCallingThreadId); - verify(mContext).getMainLooper(); + verify(mContext).getMainExecutor(); } /** @@ -422,25 +542,58 @@ public class WifiManagerTest { */ @Test public void testOnStartedIsCalledWithReservation() throws Exception { + SoftApConfiguration softApConfig = generatorTestSoftApConfig(); TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); TestLooper callbackLooper = new TestLooper(); Handler callbackHandler = new Handler(callbackLooper.getLooper()); - when(mWifiService.startLocalOnlyHotspot(mMessengerCaptor.capture(), - any(IBinder.class), anyString())).thenReturn(REQUEST_REGISTERED); + ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = + ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); + when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), + nullable(String.class), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); callbackLooper.dispatchAll(); mLooper.dispatchAll(); assertFalse(callback.mOnStartedCalled); assertEquals(null, callback.mRes); // now trigger the callback - Message msg = new Message(); - msg.what = HOTSPOT_STARTED; - msg.obj = mApConfig; - mMessengerCaptor.getValue().send(msg); + internalCallback.getValue().onHotspotStarted(softApConfig); mLooper.dispatchAll(); callbackLooper.dispatchAll(); assertTrue(callback.mOnStartedCalled); - assertEquals(mApConfig, callback.mRes.getWifiConfiguration()); + assertEquals(softApConfig, callback.mRes.getSoftApConfiguration()); + WifiConfiguration wifiConfig = callback.mRes.getWifiConfiguration(); + assertTrue(compareWifiAndSoftApConfiguration(softApConfig, wifiConfig)); + } + + /** + * Verify the LOHS onStarted callback is triggered when WifiManager receives a HOTSPOT_STARTED + * message from WifiServiceImpl when softap enabled with SAE security type. + */ + @Test + public void testOnStartedIsCalledWithReservationAndSaeSoftApConfig() throws Exception { + SoftApConfiguration softApConfig = new SoftApConfiguration.Builder() + .setSsid("TestSSID") + .setPassphrase("TestPassphrase", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) + .build(); + TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); + TestLooper callbackLooper = new TestLooper(); + Handler callbackHandler = new Handler(callbackLooper.getLooper()); + ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = + ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); + when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), + nullable(String.class), eq(null))).thenReturn(REQUEST_REGISTERED); + mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); + callbackLooper.dispatchAll(); + mLooper.dispatchAll(); + assertFalse(callback.mOnStartedCalled); + assertEquals(null, callback.mRes); + // now trigger the callback + internalCallback.getValue().onHotspotStarted(softApConfig); + mLooper.dispatchAll(); + callbackLooper.dispatchAll(); + assertTrue(callback.mOnStartedCalled); + assertEquals(softApConfig, callback.mRes.getSoftApConfiguration()); + assertEquals(null, callback.mRes.getWifiConfiguration()); } /** @@ -452,17 +605,17 @@ public class WifiManagerTest { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); TestLooper callbackLooper = new TestLooper(); Handler callbackHandler = new Handler(callbackLooper.getLooper()); - when(mWifiService.startLocalOnlyHotspot(mMessengerCaptor.capture(), - any(IBinder.class), anyString())).thenReturn(REQUEST_REGISTERED); + ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = + ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); + when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), + nullable(String.class), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); callbackLooper.dispatchAll(); mLooper.dispatchAll(); assertFalse(callback.mOnStartedCalled); assertEquals(null, callback.mRes); // now trigger the callback - Message msg = new Message(); - msg.what = HOTSPOT_STARTED; - mMessengerCaptor.getValue().send(msg); + internalCallback.getValue().onHotspotStarted(null); mLooper.dispatchAll(); callbackLooper.dispatchAll(); assertFalse(callback.mOnStartedCalled); @@ -477,16 +630,16 @@ public class WifiManagerTest { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); TestLooper callbackLooper = new TestLooper(); Handler callbackHandler = new Handler(callbackLooper.getLooper()); - when(mWifiService.startLocalOnlyHotspot(mMessengerCaptor.capture(), - any(IBinder.class), anyString())).thenReturn(REQUEST_REGISTERED); + ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = + ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); + when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), + nullable(String.class), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); callbackLooper.dispatchAll(); mLooper.dispatchAll(); assertFalse(callback.mOnStoppedCalled); // now trigger the callback - Message msg = new Message(); - msg.what = HOTSPOT_STOPPED; - mMessengerCaptor.getValue().send(msg); + internalCallback.getValue().onHotspotStopped(); mLooper.dispatchAll(); callbackLooper.dispatchAll(); assertTrue(callback.mOnStoppedCalled); @@ -500,17 +653,16 @@ public class WifiManagerTest { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); TestLooper callbackLooper = new TestLooper(); Handler callbackHandler = new Handler(callbackLooper.getLooper()); - when(mWifiService.startLocalOnlyHotspot(mMessengerCaptor.capture(), - any(IBinder.class), anyString())).thenReturn(REQUEST_REGISTERED); + ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = + ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); + when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), + nullable(String.class), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); callbackLooper.dispatchAll(); mLooper.dispatchAll(); assertEquals(ERROR_NOT_SET, callback.mFailureReason); // now trigger the callback - Message msg = new Message(); - msg.what = HOTSPOT_FAILED; - msg.arg1 = ERROR_NO_CHANNEL; - mMessengerCaptor.getValue().send(msg); + internalCallback.getValue().onHotspotFailed(ERROR_NO_CHANNEL); mLooper.dispatchAll(); callbackLooper.dispatchAll(); assertEquals(ERROR_NO_CHANNEL, callback.mFailureReason); @@ -522,8 +674,8 @@ public class WifiManagerTest { @Test public void testLocalOnlyHotspotCallbackFullOnIncompatibleMode() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); - when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), - anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE); + when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), + nullable(String.class), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason); @@ -538,8 +690,8 @@ public class WifiManagerTest { @Test public void testLocalOnlyHotspotCallbackFullOnTetheringDisallowed() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); - when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), - anyString())).thenReturn(ERROR_TETHERING_DISALLOWED); + when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), + nullable(String.class), eq(null))).thenReturn(ERROR_TETHERING_DISALLOWED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); assertEquals(ERROR_TETHERING_DISALLOWED, callback.mFailureReason); @@ -555,8 +707,9 @@ public class WifiManagerTest { @Test(expected = SecurityException.class) public void testLocalOnlyHotspotCallbackFullOnSecurityException() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); - doThrow(new SecurityException()).when(mWifiService) - .startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), anyString()); + doThrow(new SecurityException()).when(mWifiService).startLocalOnlyHotspot( + any(ILocalOnlyHotspotCallback.class), anyString(), nullable(String.class), + eq(null)); try { mWifiManager.startLocalOnlyHotspot(callback, mHandler); } catch (SecurityException e) { @@ -576,8 +729,8 @@ public class WifiManagerTest { @Test public void testLocalOnlyHotspotCallbackFullOnNoChannelError() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); - when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), - anyString())).thenReturn(REQUEST_REGISTERED); + when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), + nullable(String.class), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); //assertEquals(ERROR_NO_CHANNEL, callback.mFailureReason); @@ -592,8 +745,8 @@ public class WifiManagerTest { @Test public void testCancelLocalOnlyHotspotRequestCallsStopOnWifiService() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); - when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), - anyString())).thenReturn(REQUEST_REGISTERED); + when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), + nullable(String.class), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mWifiManager.cancelLocalOnlyHotspotRequest(); verify(mWifiService).stopLocalOnlyHotspot(); @@ -614,8 +767,8 @@ public class WifiManagerTest { @Test public void testCallbackAfterLocalOnlyHotspotWasCancelled() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); - when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), - anyString())).thenReturn(REQUEST_REGISTERED); + when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), + nullable(String.class), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mWifiManager.cancelLocalOnlyHotspotRequest(); verify(mWifiService).stopLocalOnlyHotspot(); @@ -633,8 +786,8 @@ public class WifiManagerTest { @Test public void testCancelAfterLocalOnlyHotspotCallbackTriggered() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); - when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), - anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE); + when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), + nullable(String.class), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason); @@ -645,6 +798,17 @@ public class WifiManagerTest { verify(mWifiService, never()).stopLocalOnlyHotspot(); } + @Test + public void testStartLocalOnlyHotspotForwardsCustomConfig() throws Exception { + SoftApConfiguration customConfig = new SoftApConfiguration.Builder() + .setSsid("customSsid") + .build(); + TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); + mWifiManager.startLocalOnlyHotspot(customConfig, mExecutor, callback); + verify(mWifiService).startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), + anyString(), nullable(String.class), eq(customConfig)); + } + /** * Verify the watchLocalOnlyHotspot call goes to WifiServiceImpl. */ @@ -653,7 +817,7 @@ public class WifiManagerTest { TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver(); mWifiManager.watchLocalOnlyHotspot(observer, mHandler); - verify(mWifiService).startWatchLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)); + verify(mWifiService).startWatchLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class)); } /** @@ -664,7 +828,7 @@ public class WifiManagerTest { public void testStartWatchLocalOnlyHotspotThrowsSecurityException() throws Exception { TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver(); doThrow(new SecurityException()).when(mWifiService) - .startWatchLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)); + .startWatchLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class)); mWifiManager.watchLocalOnlyHotspot(observer, mHandler); } @@ -676,7 +840,7 @@ public class WifiManagerTest { public void testStartWatchLocalOnlyHotspotThrowsIllegalStateException() throws Exception { TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver(); doThrow(new IllegalStateException()).when(mWifiService) - .startWatchLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)); + .startWatchLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class)); mWifiManager.watchLocalOnlyHotspot(observer, mHandler); } @@ -773,11 +937,74 @@ public class WifiManagerTest { verify(mSoftApCallback).onConnectedClientsChanged(testClients); } + + /* + * Verify client-provided callback is being called through callback proxy + */ + @Test + public void softApCallbackProxyCallsOnSoftApInfoChanged() throws Exception { + SoftApInfo testSoftApInfo = new SoftApInfo(); + testSoftApInfo.setFrequency(TEST_AP_FREQUENCY); + testSoftApInfo.setBandwidth(TEST_AP_BANDWIDTH); + ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(ISoftApCallback.Stub.class); + mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback); + verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(), + anyInt()); + + callbackCaptor.getValue().onInfoChanged(testSoftApInfo); + mLooper.dispatchAll(); + verify(mSoftApCallback).onInfoChanged(testSoftApInfo); + } + + + /* + * Verify client-provided callback is being called through callback proxy + */ + @Test + public void softApCallbackProxyCallsOnCapabilityChanged() throws Exception { + SoftApCapability testSoftApCapability = new SoftApCapability(0); + testSoftApCapability.setMaxSupportedClients(10); + ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(ISoftApCallback.Stub.class); + mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback); + verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(), + anyInt()); + + callbackCaptor.getValue().onCapabilityChanged(testSoftApCapability); + mLooper.dispatchAll(); + verify(mSoftApCallback).onCapabilityChanged(testSoftApCapability); + } + + /* + * Verify client-provided callback is being called through callback proxy + */ + @Test + public void softApCallbackProxyCallsOnBlockedClientConnecting() throws Exception { + WifiClient testWifiClient = new WifiClient(MacAddress.fromString("22:33:44:55:66:77")); + ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(ISoftApCallback.Stub.class); + mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback); + verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(), + anyInt()); + + callbackCaptor.getValue().onBlockedClientConnecting(testWifiClient, + WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS); + mLooper.dispatchAll(); + verify(mSoftApCallback).onBlockedClientConnecting(testWifiClient, + WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS); + } + /* * Verify client-provided callback is being called through callback proxy on multiple events */ @Test public void softApCallbackProxyCallsOnMultipleUpdates() throws Exception { + SoftApInfo testSoftApInfo = new SoftApInfo(); + testSoftApInfo.setFrequency(TEST_AP_FREQUENCY); + testSoftApInfo.setBandwidth(TEST_AP_BANDWIDTH); + SoftApCapability testSoftApCapability = new SoftApCapability(0); + testSoftApCapability.setMaxSupportedClients(10); ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor = ArgumentCaptor.forClass(ISoftApCallback.Stub.class); mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback); @@ -787,12 +1014,17 @@ public class WifiManagerTest { final List<WifiClient> testClients = new ArrayList(); callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_ENABLING, 0); callbackCaptor.getValue().onConnectedClientsChanged(testClients); + callbackCaptor.getValue().onInfoChanged(testSoftApInfo); callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL); + callbackCaptor.getValue().onCapabilityChanged(testSoftApCapability); + mLooper.dispatchAll(); verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_ENABLING, 0); verify(mSoftApCallback).onConnectedClientsChanged(testClients); + verify(mSoftApCallback).onInfoChanged(testSoftApInfo); verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL); + verify(mSoftApCallback).onCapabilityChanged(testSoftApCapability); } /* @@ -823,6 +1055,7 @@ public class WifiManagerTest { verify(mWifiService).registerSoftApCallback(any(IBinder.class), any(ISoftApCallback.Stub.class), anyInt()); verify(mContext, never()).getMainLooper(); + verify(mContext, never()).getMainExecutor(); } /** @@ -835,6 +1068,7 @@ public class WifiManagerTest { mLooper.dispatchAll(); assertTrue(observer.mOnRegistered); verify(mContext, never()).getMainLooper(); + verify(mContext, never()).getMainExecutor(); } /** @@ -845,13 +1079,13 @@ public class WifiManagerTest { public void testMainLooperIsUsedWhenHandlerNotProvidedForObserver() throws Exception { // record thread from looper.getThread and check ids. TestLooper altLooper = new TestLooper(); - when(mContext.getMainLooper()).thenReturn(altLooper.getLooper()); + when(mContext.getMainExecutor()).thenReturn(altLooper.getNewExecutor()); TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver(); mWifiManager.watchLocalOnlyHotspot(observer, null); altLooper.dispatchAll(); assertTrue(observer.mOnRegistered); assertEquals(altLooper.getLooper().getThread().getId(), observer.mCallingThreadId); - verify(mContext).getMainLooper(); + verify(mContext).getMainExecutor(); } /** @@ -866,8 +1100,7 @@ public class WifiManagerTest { assertFalse(observer.mOnRegistered); assertEquals(null, observer.mSub); mWifiManager.watchLocalOnlyHotspot(observer, observerHandler); - verify(mWifiService).startWatchLocalOnlyHotspot(mMessengerCaptor.capture(), - any(IBinder.class)); + verify(mWifiService).startWatchLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class)); // now trigger the callback observerLooper.dispatchAll(); mLooper.dispatchAll(); @@ -881,24 +1114,23 @@ public class WifiManagerTest { */ @Test public void testObserverOnStartedIsCalledWithWifiConfig() throws Exception { + SoftApConfiguration softApConfig = generatorTestSoftApConfig(); TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver(); TestLooper observerLooper = new TestLooper(); Handler observerHandler = new Handler(observerLooper.getLooper()); mWifiManager.watchLocalOnlyHotspot(observer, observerHandler); - verify(mWifiService).startWatchLocalOnlyHotspot(mMessengerCaptor.capture(), - any(IBinder.class)); + ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = + ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); + verify(mWifiService).startWatchLocalOnlyHotspot(internalCallback.capture()); observerLooper.dispatchAll(); mLooper.dispatchAll(); assertFalse(observer.mOnStartedCalled); // now trigger the callback - Message msg = new Message(); - msg.what = HOTSPOT_STARTED; - msg.obj = mApConfig; - mMessengerCaptor.getValue().send(msg); + internalCallback.getValue().onHotspotStarted(softApConfig); mLooper.dispatchAll(); observerLooper.dispatchAll(); assertTrue(observer.mOnStartedCalled); - assertEquals(mApConfig, observer.mConfig); + assertEquals(softApConfig, observer.mConfig); } /** @@ -911,15 +1143,14 @@ public class WifiManagerTest { TestLooper observerLooper = new TestLooper(); Handler observerHandler = new Handler(observerLooper.getLooper()); mWifiManager.watchLocalOnlyHotspot(observer, observerHandler); - verify(mWifiService).startWatchLocalOnlyHotspot(mMessengerCaptor.capture(), - any(IBinder.class)); + ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = + ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); + verify(mWifiService).startWatchLocalOnlyHotspot(internalCallback.capture()); observerLooper.dispatchAll(); mLooper.dispatchAll(); assertFalse(observer.mOnStartedCalled); // now trigger the callback - Message msg = new Message(); - msg.what = HOTSPOT_STARTED; - mMessengerCaptor.getValue().send(msg); + internalCallback.getValue().onHotspotStarted(null); mLooper.dispatchAll(); observerLooper.dispatchAll(); assertFalse(observer.mOnStartedCalled); @@ -937,15 +1168,14 @@ public class WifiManagerTest { TestLooper observerLooper = new TestLooper(); Handler observerHandler = new Handler(observerLooper.getLooper()); mWifiManager.watchLocalOnlyHotspot(observer, observerHandler); - verify(mWifiService).startWatchLocalOnlyHotspot(mMessengerCaptor.capture(), - any(IBinder.class)); + ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = + ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); + verify(mWifiService).startWatchLocalOnlyHotspot(internalCallback.capture()); observerLooper.dispatchAll(); mLooper.dispatchAll(); assertFalse(observer.mOnStoppedCalled); // now trigger the callback - Message msg = new Message(); - msg.what = HOTSPOT_STOPPED; - mMessengerCaptor.getValue().send(msg); + internalCallback.getValue().onHotspotStopped(); mLooper.dispatchAll(); observerLooper.dispatchAll(); assertTrue(observer.mOnStoppedCalled); @@ -974,25 +1204,6 @@ public class WifiManagerTest { } /** - * Verify that calls WifiServiceImpl to set country code when no exception happens. - */ - @Test - public void testSetWifiCountryCode() throws Exception { - mWifiManager.setCountryCode(TEST_COUNTRY_CODE); - verify(mWifiService).setCountryCode(TEST_COUNTRY_CODE); - } - - /** - * Verify that WifiManager.setCountryCode() rethrows exceptions if caller does not - * have necessary permissions. - */ - @Test(expected = SecurityException.class) - public void testSetWifiCountryCodeFailedOnSecurityException() throws Exception { - doThrow(new SecurityException()).when(mWifiService).setCountryCode(anyString()); - mWifiManager.setCountryCode(TEST_COUNTRY_CODE); - } - - /** * Test that calls to get the current WPS config token return null and do not have any * interactions with WifiServiceImpl. */ @@ -1003,7 +1214,7 @@ public class WifiManagerTest { } - class WpsCallbackTester extends WifiManager.WpsCallback { + class WpsCallbackTester extends WpsCallback { public boolean mStarted = false; public boolean mSucceeded = false; public boolean mFailed = false; @@ -1035,7 +1246,7 @@ public class WifiManagerTest { WpsCallbackTester wpsCallback = new WpsCallbackTester(); mWifiManager.startWps(null, wpsCallback); assertTrue(wpsCallback.mFailed); - assertEquals(WifiManager.ERROR, wpsCallback.mFailureCode); + assertEquals(ERROR, wpsCallback.mFailureCode); assertFalse(wpsCallback.mStarted); assertFalse(wpsCallback.mSucceeded); verifyNoMoreInteractions(mWifiService); @@ -1058,7 +1269,7 @@ public class WifiManagerTest { WpsCallbackTester wpsCallback = new WpsCallbackTester(); mWifiManager.cancelWps(wpsCallback); assertTrue(wpsCallback.mFailed); - assertEquals(WifiManager.ERROR, wpsCallback.mFailureCode); + assertEquals(ERROR, wpsCallback.mFailureCode); assertFalse(wpsCallback.mStarted); assertFalse(wpsCallback.mSucceeded); verifyNoMoreInteractions(mWifiService); @@ -1111,14 +1322,53 @@ public class WifiManagerTest { } /** + * Verify that a successful call properly returns true. + */ + @Test + public void testSetSoftApConfigurationSuccessReturnsTrue() throws Exception { + SoftApConfiguration apConfig = generatorTestSoftApConfig(); + + when(mWifiService.setSoftApConfiguration(eq(apConfig), eq(TEST_PACKAGE_NAME))) + .thenReturn(true); + assertTrue(mWifiManager.setSoftApConfiguration(apConfig)); + } + + /** + * Verify that a failed call properly returns false. + */ + @Test + public void testSetSoftApConfigurationFailureReturnsFalse() throws Exception { + SoftApConfiguration apConfig = generatorTestSoftApConfig(); + + when(mWifiService.setSoftApConfiguration(eq(apConfig), eq(TEST_PACKAGE_NAME))) + .thenReturn(false); + assertFalse(mWifiManager.setSoftApConfiguration(apConfig)); + } + + /** + * Verify Exceptions are rethrown when underlying calls to WifiService throw exceptions. + */ + @Test + public void testSetSoftApConfigurationRethrowsException() throws Exception { + doThrow(new SecurityException()).when(mWifiService).setSoftApConfiguration(any(), any()); + + try { + mWifiManager.setSoftApConfiguration(generatorTestSoftApConfig()); + fail("setWifiApConfiguration should rethrow Exceptions from WifiService"); + } catch (SecurityException e) { } + } + + /** * Check the call to startScan calls WifiService. */ @Test public void testStartScan() throws Exception { - when(mWifiService.startScan(TEST_PACKAGE_NAME)).thenReturn(true); + when(mWifiService.startScan(eq(TEST_PACKAGE_NAME), nullable(String.class))).thenReturn( + true); assertTrue(mWifiManager.startScan()); - when(mWifiService.startScan(TEST_PACKAGE_NAME)).thenReturn(false); + when(mWifiService.startScan(eq(TEST_PACKAGE_NAME), nullable(String.class))).thenReturn( + false); assertFalse(mWifiManager.startScan()); } @@ -1128,10 +1378,10 @@ public class WifiManagerTest { @Test public void registerTrafficStateCallbackUsesMainLooperOnNullArgumentForHandler() throws Exception { - when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); ArgumentCaptor<ITrafficStateCallback.Stub> callbackCaptor = ArgumentCaptor.forClass(ITrafficStateCallback.Stub.class); - mWifiManager.registerTrafficStateCallback(mTrafficStateCallback, null); + mWifiManager.registerTrafficStateCallback( + new HandlerExecutor(new Handler(mLooper.getLooper())), mTrafficStateCallback); verify(mWifiService).registerTrafficStateCallback( any(IBinder.class), callbackCaptor.capture(), anyInt()); @@ -1147,7 +1397,8 @@ public class WifiManagerTest { @Test public void unregisterTrafficStateCallbackCallGoesToWifiServiceImpl() throws Exception { ArgumentCaptor<Integer> callbackIdentifier = ArgumentCaptor.forClass(Integer.class); - mWifiManager.registerTrafficStateCallback(mTrafficStateCallback, mHandler); + mWifiManager.registerTrafficStateCallback(new HandlerExecutor(mHandler), + mTrafficStateCallback); verify(mWifiService).registerTrafficStateCallback(any(IBinder.class), any(ITrafficStateCallback.Stub.class), callbackIdentifier.capture()); @@ -1163,7 +1414,8 @@ public class WifiManagerTest { public void trafficStateCallbackProxyCallsOnMultipleUpdates() throws Exception { ArgumentCaptor<ITrafficStateCallback.Stub> callbackCaptor = ArgumentCaptor.forClass(ITrafficStateCallback.Stub.class); - mWifiManager.registerTrafficStateCallback(mTrafficStateCallback, mHandler); + mWifiManager.registerTrafficStateCallback(new HandlerExecutor(mHandler), + mTrafficStateCallback); verify(mWifiService).registerTrafficStateCallback( any(IBinder.class), callbackCaptor.capture(), anyInt()); @@ -1182,7 +1434,7 @@ public class WifiManagerTest { TrafficStateCallback.DATA_ACTIVITY_OUT); } - /* + /** * Verify client-provided callback is being called on the correct thread */ @Test @@ -1191,8 +1443,10 @@ public class WifiManagerTest { ArgumentCaptor.forClass(ITrafficStateCallback.Stub.class); TestLooper altLooper = new TestLooper(); Handler altHandler = new Handler(altLooper.getLooper()); - mWifiManager.registerTrafficStateCallback(mTrafficStateCallback, altHandler); + mWifiManager.registerTrafficStateCallback(new HandlerExecutor(altHandler), + mTrafficStateCallback); verify(mContext, never()).getMainLooper(); + verify(mContext, never()).getMainExecutor(); verify(mWifiService).registerTrafficStateCallback( any(IBinder.class), callbackCaptor.capture(), anyInt()); @@ -1208,10 +1462,11 @@ public class WifiManagerTest { @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); + mWifiManager.registerNetworkRequestMatchCallback( + new HandlerExecutor(new Handler(mLooper.getLooper())), + mNetworkRequestMatchCallback); verify(mWifiService).registerNetworkRequestMatchCallback( any(IBinder.class), callbackCaptor.capture(), anyInt()); @@ -1245,7 +1500,8 @@ public class WifiManagerTest { @Test public void unregisterNetworkRequestMatchCallbackCallGoesToWifiServiceImpl() throws Exception { ArgumentCaptor<Integer> callbackIdentifier = ArgumentCaptor.forClass(Integer.class); - mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback, mHandler); + mWifiManager.registerNetworkRequestMatchCallback(new HandlerExecutor(mHandler), + mNetworkRequestMatchCallback); verify(mWifiService).registerNetworkRequestMatchCallback( any(IBinder.class), any(INetworkRequestMatchCallback.class), callbackIdentifier.capture()); @@ -1262,10 +1518,11 @@ public class WifiManagerTest { @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); + mWifiManager.registerNetworkRequestMatchCallback( + new HandlerExecutor(new Handler(mLooper.getLooper())), + mNetworkRequestMatchCallback); verify(mWifiService).registerNetworkRequestMatchCallback( any(IBinder.class), callbackCaptor.capture(), anyInt()); @@ -1293,14 +1550,15 @@ public class WifiManagerTest { */ @Test public void testGetAllMatchingWifiConfigs() throws Exception { - Map<String, List<ScanResult>> fqdns = new HashMap<>(); - fqdns.put("www.test.com", new ArrayList<>()); - when(mWifiService.getAllMatchingFqdnsForScanResults(any(List.class))).thenReturn(fqdns); + Map<String, List<ScanResult>> passpointProfiles = new HashMap<>(); + passpointProfiles.put("www.test.com_987a69bca26", new ArrayList<>()); + when(mWifiService.getAllMatchingPasspointProfilesForScanResults( + any(List.class))).thenReturn(passpointProfiles); InOrder inOrder = inOrder(mWifiService); mWifiManager.getAllMatchingWifiConfigs(new ArrayList<>()); - inOrder.verify(mWifiService).getAllMatchingFqdnsForScanResults(any(List.class)); + inOrder.verify(mWifiService).getAllMatchingPasspointProfilesForScanResults(any(List.class)); inOrder.verify(mWifiService).getWifiConfigsForPasspointProfiles(any(List.class)); } @@ -1316,21 +1574,29 @@ public class WifiManagerTest { } /** - * Verify calls to {@link WifiManager#addNetworkSuggestions(List)} and + * Verify calls to {@link WifiManager#addNetworkSuggestions(List)}, + * {@link WifiManager#getNetworkSuggestions()} and * {@link WifiManager#removeNetworkSuggestions(List)}. */ @Test - public void addRemoveNetworkSuggestions() throws Exception { - when(mWifiService.addNetworkSuggestions(any(List.class), anyString())) - .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS); - when(mWifiService.removeNetworkSuggestions(any(List.class), anyString())) - .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS); + public void addGetRemoveNetworkSuggestions() throws Exception { + List<WifiNetworkSuggestion> testList = new ArrayList<>(); + when(mWifiService.addNetworkSuggestions(any(List.class), anyString(), + nullable(String.class))).thenReturn(STATUS_NETWORK_SUGGESTIONS_SUCCESS); + when(mWifiService.removeNetworkSuggestions(any(List.class), anyString())).thenReturn( + STATUS_NETWORK_SUGGESTIONS_SUCCESS); + when(mWifiService.getNetworkSuggestions(anyString())) + .thenReturn(testList); + + assertEquals(STATUS_NETWORK_SUGGESTIONS_SUCCESS, + mWifiManager.addNetworkSuggestions(testList)); + verify(mWifiService).addNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME), + nullable(String.class)); - assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, - mWifiManager.addNetworkSuggestions(new ArrayList<>())); - verify(mWifiService).addNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME)); + assertEquals(testList, mWifiManager.getNetworkSuggestions()); + verify(mWifiService).getNetworkSuggestions(eq(TEST_PACKAGE_NAME)); - assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, + assertEquals(STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiManager.removeNetworkSuggestions(new ArrayList<>())); verify(mWifiService).removeNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME)); } @@ -1340,8 +1606,15 @@ public class WifiManagerTest { */ @Test public void getMaxNumberOfNetworkSuggestionsPerApp() { - assertEquals(WifiManager.NETWORK_SUGGESTIONS_MAX_PER_APP, - mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp()); + when(mContext.getSystemServiceName(ActivityManager.class)) + .thenReturn(Context.ACTIVITY_SERVICE); + when(mContext.getSystemService(Context.ACTIVITY_SERVICE)) + .thenReturn(mActivityManager); + when(mActivityManager.isLowRamDevice()).thenReturn(true); + assertEquals(256, mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp()); + + when(mActivityManager.isLowRamDevice()).thenReturn(false); + assertEquals(1024, mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp()); } /** @@ -1382,24 +1655,15 @@ public class WifiManagerTest { } /** - * Defined for testing purpose. - */ - class SynchronousExecutor implements Executor { - public void execute(Runnable r) { - r.run(); - } - } - - /** * Test behavior of isEnhancedOpenSupported */ @Test public void testIsEnhancedOpenSupported() throws Exception { when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WifiManager.WIFI_FEATURE_OWE)); + .thenReturn(new Long(WIFI_FEATURE_OWE)); assertTrue(mWifiManager.isEnhancedOpenSupported()); when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WifiManager.WIFI_FEATURE_OWE)); + .thenReturn(new Long(~WIFI_FEATURE_OWE)); assertFalse(mWifiManager.isEnhancedOpenSupported()); } @@ -1409,10 +1673,10 @@ public class WifiManagerTest { @Test public void testIsWpa3SaeSupported() throws Exception { when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WifiManager.WIFI_FEATURE_WPA3_SAE)); + .thenReturn(new Long(WIFI_FEATURE_WPA3_SAE)); assertTrue(mWifiManager.isWpa3SaeSupported()); when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WifiManager.WIFI_FEATURE_WPA3_SAE)); + .thenReturn(new Long(~WIFI_FEATURE_WPA3_SAE)); assertFalse(mWifiManager.isWpa3SaeSupported()); } @@ -1422,10 +1686,10 @@ public class WifiManagerTest { @Test public void testIsWpa3SuiteBSupported() throws Exception { when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WifiManager.WIFI_FEATURE_WPA3_SUITE_B)); + .thenReturn(new Long(WIFI_FEATURE_WPA3_SUITE_B)); assertTrue(mWifiManager.isWpa3SuiteBSupported()); when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WifiManager.WIFI_FEATURE_WPA3_SUITE_B)); + .thenReturn(new Long(~WIFI_FEATURE_WPA3_SUITE_B)); assertFalse(mWifiManager.isWpa3SuiteBSupported()); } @@ -1435,10 +1699,10 @@ public class WifiManagerTest { @Test public void testIsEasyConnectSupported() throws Exception { when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WifiManager.WIFI_FEATURE_DPP)); + .thenReturn(new Long(WIFI_FEATURE_DPP)); assertTrue(mWifiManager.isEasyConnectSupported()); when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WifiManager.WIFI_FEATURE_DPP)); + .thenReturn(new Long(~WIFI_FEATURE_DPP)); assertFalse(mWifiManager.isEasyConnectSupported()); } @@ -1502,6 +1766,49 @@ public class WifiManagerTest { } /** + * Test behavior of {@link WifiManager#allowAutojoin(int, boolean)} + * @throws Exception + */ + @Test + public void testAllowAutojoin() throws Exception { + mWifiManager.allowAutojoin(1, true); + verify(mWifiService).allowAutojoin(1, true); + } + + /** + * Test behavior of {@link WifiManager#allowAutojoinPasspoint(String, boolean)} + * @throws Exception + */ + @Test + public void testAllowAutojoinPasspoint() throws Exception { + final String fqdn = "FullyQualifiedDomainName"; + mWifiManager.allowAutojoinPasspoint(fqdn, true); + verify(mWifiService).allowAutojoinPasspoint(fqdn, true); + } + + /** + * Test behavior of + * {@link WifiManager#setMacRandomizationSettingPasspointEnabled(String, boolean)} + */ + @Test + public void testSetMacRandomizationSettingPasspointEnabled() throws Exception { + final String fqdn = "FullyQualifiedDomainName"; + mWifiManager.setMacRandomizationSettingPasspointEnabled(fqdn, true); + verify(mWifiService).setMacRandomizationSettingPasspointEnabled(fqdn, true); + } + + /** + * Test behavior of + * {@link WifiManager#setMacRandomizationSettingPasspointEnabled(String, boolean)} + */ + @Test + public void testSetPasspointMeteredOverride() throws Exception { + final String fqdn = "FullyQualifiedDomainName"; + mWifiManager.setPasspointMeteredOverride(fqdn, METERED_OVERRIDE_METERED); + verify(mWifiService).setPasspointMeteredOverride(fqdn, METERED_OVERRIDE_METERED); + } + + /** * Test behavior of {@link WifiManager#disconnect()} */ @Test @@ -1537,9 +1844,9 @@ public class WifiManagerTest { @Test public void testGetSupportedFeatures() throws Exception { long supportedFeatures = - WifiManager.WIFI_FEATURE_SCANNER - | WifiManager.WIFI_FEATURE_PASSPOINT - | WifiManager.WIFI_FEATURE_P2P; + WIFI_FEATURE_SCANNER + | WIFI_FEATURE_PASSPOINT + | WIFI_FEATURE_P2P; when(mWifiService.getSupportedFeatures()) .thenReturn(Long.valueOf(supportedFeatures)); @@ -1547,7 +1854,6 @@ public class WifiManagerTest { assertTrue(mWifiManager.isPasspointSupported()); assertTrue(mWifiManager.isP2pSupported()); assertFalse(mWifiManager.isPortableHotspotSupported()); - assertFalse(mWifiManager.is5GHzBandSupported()); assertFalse(mWifiManager.isDeviceToDeviceRttSupported()); assertFalse(mWifiManager.isDeviceToApRttSupported()); assertFalse(mWifiManager.isPreferredNetworkOffloadSupported()); @@ -1558,15 +1864,55 @@ public class WifiManagerTest { } /** - * Test behavior of {@link WifiManager#getControllerActivityEnergyInfo()} + * Tests that passing a null Executor to {@link WifiManager#getWifiActivityEnergyInfoAsync} + * throws an exception. + */ + @Test(expected = NullPointerException.class) + public void testGetWifiActivityInfoNullExecutor() throws Exception { + mWifiManager.getWifiActivityEnergyInfoAsync(null, mOnWifiActivityEnergyInfoListener); + } + + /** + * Tests that passing a null listener to {@link WifiManager#getWifiActivityEnergyInfoAsync} + * throws an exception. */ + @Test(expected = NullPointerException.class) + public void testGetWifiActivityInfoNullListener() throws Exception { + mWifiManager.getWifiActivityEnergyInfoAsync(mExecutor, null); + } + + /** Tests that the listener runs on the correct Executor. */ + @Test + public void testGetWifiActivityInfoRunsOnCorrectExecutor() throws Exception { + mWifiManager.getWifiActivityEnergyInfoAsync(mExecutor, mOnWifiActivityEnergyInfoListener); + ArgumentCaptor<IOnWifiActivityEnergyInfoListener> listenerCaptor = + ArgumentCaptor.forClass(IOnWifiActivityEnergyInfoListener.class); + verify(mWifiService).getWifiActivityEnergyInfoAsync(listenerCaptor.capture()); + IOnWifiActivityEnergyInfoListener listener = listenerCaptor.getValue(); + listener.onWifiActivityEnergyInfo(mWifiActivityEnergyInfo); + verify(mExecutor).execute(any()); + + // ensure that the executor is only triggered once + listener.onWifiActivityEnergyInfo(mWifiActivityEnergyInfo); + verify(mExecutor).execute(any()); + } + + /** Tests that the correct listener runs. */ @Test - public void testGetControllerActivityEnergyInfo() throws Exception { - WifiActivityEnergyInfo activityEnergyInfo = - new WifiActivityEnergyInfo(5, 3, 3, new long[]{5L, 5L, 5L}, 5, 5, 5, 5); - when(mWifiService.reportActivityInfo()).thenReturn(activityEnergyInfo); + public void testGetWifiActivityInfoRunsCorrectListener() throws Exception { + int[] flag = {0}; + mWifiManager.getWifiActivityEnergyInfoAsync( + new SynchronousExecutor(), info -> flag[0]++); + ArgumentCaptor<IOnWifiActivityEnergyInfoListener> listenerCaptor = + ArgumentCaptor.forClass(IOnWifiActivityEnergyInfoListener.class); + verify(mWifiService).getWifiActivityEnergyInfoAsync(listenerCaptor.capture()); + IOnWifiActivityEnergyInfoListener listener = listenerCaptor.getValue(); + listener.onWifiActivityEnergyInfo(mWifiActivityEnergyInfo); + assertEquals(1, flag[0]); - assertEquals(activityEnergyInfo, mWifiManager.getControllerActivityEnergyInfo()); + // ensure that the listener is only triggered once + listener.onWifiActivityEnergyInfo(mWifiActivityEnergyInfo); + assertEquals(1, flag[0]); } /** @@ -1575,29 +1921,41 @@ public class WifiManagerTest { @Test public void testGetConnectionInfo() throws Exception { WifiInfo wifiInfo = new WifiInfo(); - when(mWifiService.getConnectionInfo(anyString())).thenReturn(wifiInfo); + when(mWifiService.getConnectionInfo(anyString(), nullable(String.class))).thenReturn( + wifiInfo); assertEquals(wifiInfo, mWifiManager.getConnectionInfo()); } /** - * Test behavior of {@link WifiManager#isDualModeSupported()} ()} + * Test behavior of {@link WifiManager#is5GHzBandSupported()} */ @Test - public void testIsDualModeSupported() throws Exception { - when(mWifiService.needs5GHzToAnyApBandConversion()).thenReturn(true); - assertTrue(mWifiManager.isDualModeSupported()); - verify(mWifiService).needs5GHzToAnyApBandConversion(); + public void testIs5GHzBandSupported() throws Exception { + when(mWifiService.is5GHzBandSupported()).thenReturn(true); + assertTrue(mWifiManager.is5GHzBandSupported()); + verify(mWifiService).is5GHzBandSupported(); } /** - * Test behavior of {@link WifiManager#isDualBandSupported()} + * Test behavior of {@link WifiManager#is6GHzBandSupported()} */ @Test - public void testIsDualBandSupported() throws Exception { - when(mWifiService.isDualBandSupported()).thenReturn(true); - assertTrue(mWifiManager.isDualBandSupported()); - verify(mWifiService).isDualBandSupported(); + public void testIs6GHzBandSupported() throws Exception { + when(mWifiService.is6GHzBandSupported()).thenReturn(true); + assertTrue(mWifiManager.is6GHzBandSupported()); + verify(mWifiService).is6GHzBandSupported(); + } + + /** + * Test behavior of {@link WifiManager#isWifiStandardSupported()} + */ + @Test + public void testIsWifiStandardSupported() throws Exception { + int standard = ScanResult.WIFI_STANDARD_11AX; + when(mWifiService.isWifiStandardSupported(standard)).thenReturn(true); + assertTrue(mWifiManager.isWifiStandardSupported(standard)); + verify(mWifiService).isWifiStandardSupported(standard); } /** @@ -1623,4 +1981,409 @@ public class WifiManagerTest { assertTrue(mWifiManager.setWifiEnabled(false)); verify(mWifiService).setWifiEnabled(mContext.getOpPackageName(), false); } + + /** + * Test behavior of {@link WifiManager#connect(int, ActionListener)} + */ + @Test + public void testConnectWithListener() throws Exception { + ActionListener externalListener = mock(ActionListener.class); + mWifiManager.connect(TEST_NETWORK_ID, externalListener); + + ArgumentCaptor<IActionListener> binderListenerCaptor = + ArgumentCaptor.forClass(IActionListener.class); + verify(mWifiService).connect(eq(null), eq(TEST_NETWORK_ID), any(Binder.class), + binderListenerCaptor.capture(), anyInt()); + assertNotNull(binderListenerCaptor.getValue()); + + // Trigger on success. + binderListenerCaptor.getValue().onSuccess(); + mLooper.dispatchAll(); + verify(externalListener).onSuccess(); + + // Trigger on failure. + binderListenerCaptor.getValue().onFailure(BUSY); + mLooper.dispatchAll(); + verify(externalListener).onFailure(BUSY); + } + + /** + * Test behavior of {@link WifiManager#connect(int, ActionListener)} + */ + @Test + public void testConnectWithListenerHandleSecurityException() throws Exception { + doThrow(new SecurityException()).when(mWifiService) + .connect(eq(null), anyInt(), any(IBinder.class), + any(IActionListener.class), anyInt()); + ActionListener externalListener = mock(ActionListener.class); + mWifiManager.connect(TEST_NETWORK_ID, externalListener); + + mLooper.dispatchAll(); + verify(externalListener).onFailure(NOT_AUTHORIZED); + } + + /** + * Test behavior of {@link WifiManager#connect(int, ActionListener)} + */ + @Test + public void testConnectWithListenerHandleRemoteException() throws Exception { + doThrow(new RemoteException()).when(mWifiService) + .connect(eq(null), anyInt(), any(IBinder.class), + any(IActionListener.class), anyInt()); + ActionListener externalListener = mock(ActionListener.class); + mWifiManager.connect(TEST_NETWORK_ID, externalListener); + + mLooper.dispatchAll(); + verify(externalListener).onFailure(ERROR); + } + + /** + * Test behavior of {@link WifiManager#connect(int, ActionListener)} + */ + @Test + public void testConnectWithoutListener() throws Exception { + WifiConfiguration configuration = new WifiConfiguration(); + mWifiManager.connect(configuration, null); + + verify(mWifiService).connect(configuration, WifiConfiguration.INVALID_NETWORK_ID, null, + null, 0); + } + + /** + * Verify an IllegalArgumentException is thrown if callback is not provided. + */ + @Test(expected = IllegalArgumentException.class) + public void testRegisterScanResultsCallbackWithNullCallback() throws Exception { + mWifiManager.registerScanResultsCallback(mExecutor, null); + } + + /** + * Verify an IllegalArgumentException is thrown if executor is not provided. + */ + @Test(expected = IllegalArgumentException.class) + public void testRegisterCallbackWithNullExecutor() throws Exception { + mWifiManager.registerScanResultsCallback(null, mScanResultsCallback); + } + + /** + * Verify client provided callback is being called to the right callback. + */ + @Test + public void testAddScanResultsCallbackAndReceiveEvent() throws Exception { + ArgumentCaptor<IScanResultsCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(IScanResultsCallback.Stub.class); + mWifiManager.registerScanResultsCallback(new SynchronousExecutor(), mScanResultsCallback); + verify(mWifiService).registerScanResultsCallback(callbackCaptor.capture()); + callbackCaptor.getValue().onScanResultsAvailable(); + verify(mRunnable).run(); + } + + /** + * Verify client provided callback is being called to the right executor. + */ + @Test + public void testRegisterScanResultsCallbackWithTheTargetExecutor() throws Exception { + ArgumentCaptor<IScanResultsCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(IScanResultsCallback.Stub.class); + mWifiManager.registerScanResultsCallback(mExecutor, mScanResultsCallback); + verify(mWifiService).registerScanResultsCallback(callbackCaptor.capture()); + mWifiManager.registerScanResultsCallback(mAnotherExecutor, mScanResultsCallback); + callbackCaptor.getValue().onScanResultsAvailable(); + verify(mExecutor, never()).execute(any(Runnable.class)); + verify(mAnotherExecutor).execute(any(Runnable.class)); + } + + /** + * Verify client register unregister then register again, to ensure callback still works. + */ + @Test + public void testRegisterUnregisterThenRegisterAgainWithScanResultCallback() throws Exception { + ArgumentCaptor<IScanResultsCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(IScanResultsCallback.Stub.class); + mWifiManager.registerScanResultsCallback(new SynchronousExecutor(), mScanResultsCallback); + verify(mWifiService).registerScanResultsCallback(callbackCaptor.capture()); + mWifiManager.unregisterScanResultsCallback(mScanResultsCallback); + callbackCaptor.getValue().onScanResultsAvailable(); + verify(mRunnable, never()).run(); + mWifiManager.registerScanResultsCallback(new SynchronousExecutor(), mScanResultsCallback); + callbackCaptor.getValue().onScanResultsAvailable(); + verify(mRunnable).run(); + } + + /** + * Verify client unregisterScanResultsCallback. + */ + @Test + public void testUnregisterScanResultsCallback() throws Exception { + mWifiManager.unregisterScanResultsCallback(mScanResultsCallback); + verify(mWifiService).unregisterScanResultsCallback(any()); + } + + /** + * Verify client unregisterScanResultsCallback with null callback will cause an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testUnregisterScanResultsCallbackWithNullCallback() throws Exception { + mWifiManager.unregisterScanResultsCallback(null); + } + + /** + * Verify an IllegalArgumentException is thrown if executor not provided. + */ + @Test(expected = IllegalArgumentException.class) + public void testAddSuggestionConnectionStatusListenerWithNullExecutor() { + mWifiManager.addSuggestionConnectionStatusListener(null, mListener); + } + + /** + * Verify an IllegalArgumentException is thrown if listener is not provided. + */ + @Test(expected = IllegalArgumentException.class) + public void testAddSuggestionConnectionStatusListenerWithNullListener() { + mWifiManager.addSuggestionConnectionStatusListener(mExecutor, null); + } + + /** + * Verify client provided listener is being called to the right listener. + */ + @Test + public void testAddSuggestionConnectionStatusListenerAndReceiveEvent() throws Exception { + int errorCode = STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION; + ArgumentCaptor<ISuggestionConnectionStatusListener.Stub> callbackCaptor = + ArgumentCaptor.forClass(ISuggestionConnectionStatusListener.Stub.class); + Executor executor = new SynchronousExecutor(); + mWifiManager.addSuggestionConnectionStatusListener(executor, mListener); + verify(mWifiService).registerSuggestionConnectionStatusListener(any(IBinder.class), + callbackCaptor.capture(), anyInt(), anyString(), nullable(String.class)); + callbackCaptor.getValue().onConnectionStatus(mWifiNetworkSuggestion, errorCode); + verify(mListener).onConnectionStatus(any(WifiNetworkSuggestion.class), eq(errorCode)); + } + + /** + * Verify client provided listener is being called to the right executor. + */ + @Test + public void testAddSuggestionConnectionStatusListenerWithTheTargetExecutor() throws Exception { + int errorCode = STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION; + ArgumentCaptor<ISuggestionConnectionStatusListener.Stub> callbackCaptor = + ArgumentCaptor.forClass(ISuggestionConnectionStatusListener.Stub.class); + mWifiManager.addSuggestionConnectionStatusListener(mExecutor, mListener); + verify(mWifiService).registerSuggestionConnectionStatusListener(any(IBinder.class), + callbackCaptor.capture(), anyInt(), anyString(), nullable(String.class)); + callbackCaptor.getValue().onConnectionStatus(any(WifiNetworkSuggestion.class), errorCode); + verify(mExecutor).execute(any(Runnable.class)); + } + + /** + * Verify an IllegalArgumentException is thrown if listener is not provided. + */ + @Test(expected = IllegalArgumentException.class) + public void testRemoveSuggestionConnectionListenerWithNullListener() { + mWifiManager.removeSuggestionConnectionStatusListener(null); + } + + /** + * Verify removeSuggestionConnectionListener. + */ + @Test + public void testRemoveSuggestionConnectionListener() throws Exception { + mWifiManager.removeSuggestionConnectionStatusListener(mListener); + verify(mWifiService).unregisterSuggestionConnectionStatusListener(anyInt(), anyString()); + } + + /** Test {@link WifiManager#calculateSignalLevel(int)} */ + @Test + public void testCalculateSignalLevel() throws Exception { + when(mWifiService.calculateSignalLevel(anyInt())).thenReturn(3); + int actual = mWifiManager.calculateSignalLevel(-60); + verify(mWifiService).calculateSignalLevel(-60); + assertEquals(3, actual); + } + + /** Test {@link WifiManager#getMaxSignalLevel()} */ + @Test + public void testGetMaxSignalLevel() throws Exception { + when(mWifiService.calculateSignalLevel(anyInt())).thenReturn(4); + int actual = mWifiManager.getMaxSignalLevel(); + verify(mWifiService).calculateSignalLevel(Integer.MAX_VALUE); + assertEquals(4, actual); + } + + /* + * Test behavior of isWapiSupported + * @throws Exception + */ + @Test + public void testIsWapiSupported() throws Exception { + when(mWifiService.getSupportedFeatures()) + .thenReturn(new Long(WifiManager.WIFI_FEATURE_WAPI)); + assertTrue(mWifiManager.isWapiSupported()); + when(mWifiService.getSupportedFeatures()) + .thenReturn(new Long(~WifiManager.WIFI_FEATURE_WAPI)); + assertFalse(mWifiManager.isWapiSupported()); + } + + /* + * Test that DPP channel list is parsed correctly + */ + @Test + public void testparseDppChannelList() throws Exception { + String channelList = "81/1,2,3,4,5,6,7,8,9,10,11,115/36,40,44,48"; + SparseArray<int[]> expectedResult = new SparseArray<>(); + expectedResult.append(81, new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}); + expectedResult.append(115, new int[]{36, 40, 44, 48}); + + SparseArray<int[]> result = WifiManager.parseDppChannelList(channelList); + assertEquals(expectedResult.size(), result.size()); + + int index = 0; + int key; + + // Compare the two primitive int arrays + do { + try { + key = result.keyAt(index); + } catch (java.lang.ArrayIndexOutOfBoundsException e) { + break; + } + int[] expected = expectedResult.get(key); + int[] output = result.get(key); + assertEquals(expected.length, output.length); + for (int i = 0; i < output.length; i++) { + assertEquals(expected[i], output[i]); + } + index++; + } while (true); + } + + /* + * Test that DPP channel list parser gracefully fails for invalid input + */ + @Test + public void testparseDppChannelListWithInvalidFormats() throws Exception { + String channelList = "1,2,3,4,5,6,7,8,9,10,11,36,40,44,48"; + SparseArray<int[]> result = WifiManager.parseDppChannelList(channelList); + assertEquals(result.size(), 0); + + channelList = "ajgalskgjalskjg3-09683dh"; + result = WifiManager.parseDppChannelList(channelList); + assertEquals(result.size(), 0); + + channelList = "13/abc,46////"; + result = WifiManager.parseDppChannelList(channelList); + assertEquals(result.size(), 0); + + channelList = "11/4,5,13/"; + result = WifiManager.parseDppChannelList(channelList); + assertEquals(result.size(), 0); + + channelList = "/24,6"; + result = WifiManager.parseDppChannelList(channelList); + assertEquals(result.size(), 0); + } + + /** + * Test getWifiConfigsForMatchedNetworkSuggestions for given scanResults. + */ + @Test + public void testGetWifiConfigsForMatchedNetworkSuggestions() throws Exception { + List<WifiConfiguration> testResults = new ArrayList<>(); + testResults.add(new WifiConfiguration()); + + when(mWifiService.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(any(List.class))) + .thenReturn(testResults); + assertEquals(testResults, mWifiManager + .getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(new ArrayList<>())); + } + + /** + * Verify the call to setWifiConnectedNetworkScorer goes to WifiServiceImpl. + */ + @Test + public void setWifiConnectedNetworkScorerGoesToWifiServiceImpl() throws Exception { + mExecutor = new SynchronousExecutor(); + mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer); + verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class), + any(IWifiConnectedNetworkScorer.Stub.class)); + } + + /** + * Verify the call to clearWifiConnectedNetworkScorer goes to WifiServiceImpl. + */ + @Test + public void clearWifiConnectedNetworkScorerGoesToWifiServiceImpl() throws Exception { + mExecutor = new SynchronousExecutor(); + mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer); + verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class), + any(IWifiConnectedNetworkScorer.Stub.class)); + + mWifiManager.clearWifiConnectedNetworkScorer(); + verify(mWifiService).clearWifiConnectedNetworkScorer(); + } + + /** + * Verify that Wi-Fi connected scorer receives score update observer after registeration. + */ + @Test + public void verifyScorerReceiveScoreUpdateObserverAfterRegistration() throws Exception { + mExecutor = new SynchronousExecutor(); + mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer); + ArgumentCaptor<IWifiConnectedNetworkScorer.Stub> scorerCaptor = + ArgumentCaptor.forClass(IWifiConnectedNetworkScorer.Stub.class); + verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class), + scorerCaptor.capture()); + scorerCaptor.getValue().onSetScoreUpdateObserver(any()); + mLooper.dispatchAll(); + verify(mWifiConnectedNetworkScorer).onSetScoreUpdateObserver(any()); + } + + /** + * Verify that Wi-Fi connected scorer receives session ID when onStart/onStop methods + * are called. + */ + @Test + public void verifyScorerReceiveSessionIdWhenStartStopIsCalled() throws Exception { + mExecutor = new SynchronousExecutor(); + mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer); + ArgumentCaptor<IWifiConnectedNetworkScorer.Stub> callbackCaptor = + ArgumentCaptor.forClass(IWifiConnectedNetworkScorer.Stub.class); + verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class), + callbackCaptor.capture()); + callbackCaptor.getValue().onStart(0); + callbackCaptor.getValue().onStop(10); + mLooper.dispatchAll(); + verify(mWifiConnectedNetworkScorer).onStart(0); + verify(mWifiConnectedNetworkScorer).onStop(10); + } + + @Test + public void testScanThrottle() throws Exception { + mWifiManager.setScanThrottleEnabled(true); + verify(mWifiService).setScanThrottleEnabled(true); + + when(mWifiService.isScanThrottleEnabled()).thenReturn(false); + assertFalse(mWifiManager.isScanThrottleEnabled()); + verify(mWifiService).isScanThrottleEnabled(); + } + + @Test + public void testAutoWakeup() throws Exception { + mWifiManager.setAutoWakeupEnabled(true); + verify(mWifiService).setAutoWakeupEnabled(true); + + when(mWifiService.isAutoWakeupEnabled()).thenReturn(false); + assertFalse(mWifiManager.isAutoWakeupEnabled()); + verify(mWifiService).isAutoWakeupEnabled(); + } + + + @Test + public void testScanAvailable() throws Exception { + mWifiManager.setScanAlwaysAvailable(true); + verify(mWifiService).setScanAlwaysAvailable(true); + + when(mWifiService.isScanAlwaysAvailable()).thenReturn(false); + assertFalse(mWifiManager.isScanAlwaysAvailable()); + verify(mWifiService).isScanAlwaysAvailable(); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java index b58b3215e5ea..d479aacdd296 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java @@ -35,10 +35,6 @@ import org.junit.Test; */ @SmallTest public class WifiNetworkAgentSpecifierTest { - private static final int TEST_UID = 5; - private static final int TEST_UID_1 = 8; - private static final String TEST_PACKAGE = "com.test"; - private static final String TEST_PACKAGE_1 = "com.test.1"; private static final String TEST_SSID = "Test123"; private static final String TEST_SSID_PATTERN = "Test"; private static final String TEST_SSID_1 = "456test"; @@ -94,15 +90,13 @@ public class WifiNetworkAgentSpecifierTest { WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration(); WifiNetworkAgentSpecifier specifier1 = new WifiNetworkAgentSpecifier( - wifiConfiguration1, - TEST_UID, TEST_PACKAGE); + wifiConfiguration1); WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1); wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkAgentSpecifier specifier2 = new WifiNetworkAgentSpecifier( - wifiConfiguration2, - TEST_UID, TEST_PACKAGE); + wifiConfiguration2); assertFalse(specifier2.equals(specifier1)); } @@ -118,15 +112,13 @@ public class WifiNetworkAgentSpecifierTest { WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration(); WifiNetworkAgentSpecifier specifier1 = new WifiNetworkAgentSpecifier( - wifiConfiguration1, - TEST_UID, TEST_PACKAGE); + wifiConfiguration1); WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1); wifiConfiguration2.SSID = TEST_SSID_1; WifiNetworkAgentSpecifier specifier2 = new WifiNetworkAgentSpecifier( - wifiConfiguration2, - TEST_UID, TEST_PACKAGE); + wifiConfiguration2); assertFalse(specifier2.equals(specifier1)); } @@ -142,15 +134,13 @@ public class WifiNetworkAgentSpecifierTest { WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration(); WifiNetworkAgentSpecifier specifier1 = new WifiNetworkAgentSpecifier( - wifiConfiguration1, - TEST_UID, TEST_PACKAGE); + wifiConfiguration1); WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1); wifiConfiguration2.BSSID = TEST_BSSID_1; WifiNetworkAgentSpecifier specifier2 = new WifiNetworkAgentSpecifier( - wifiConfiguration2, - TEST_UID, TEST_PACKAGE); + wifiConfiguration2); assertFalse(specifier2.equals(specifier1)); } @@ -197,15 +187,14 @@ public class WifiNetworkAgentSpecifierTest { PatternMatcher ssidPattern = new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX); Pair<MacAddress, MacAddress> bssidPattern = - Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS); + Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS); WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration(); wificonfigurationNetworkSpecifier.allowedKeyManagement .set(WifiConfiguration.KeyMgmt.WPA_PSK); WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertTrue(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertTrue(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -233,8 +222,7 @@ public class WifiNetworkAgentSpecifierTest { WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertTrue(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertTrue(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -262,8 +250,7 @@ public class WifiNetworkAgentSpecifierTest { WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertTrue(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertTrue(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -282,21 +269,19 @@ public class WifiNetworkAgentSpecifierTest { wifiConfigurationNetworkAgent.SSID = "\"" + TEST_SSID_1 + "\""; WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = new WifiNetworkAgentSpecifier( - wifiConfigurationNetworkAgent, - TEST_UID, TEST_PACKAGE); + wifiConfigurationNetworkAgent); PatternMatcher ssidPattern = new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX); Pair<MacAddress, MacAddress> bssidPattern = - Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS); + Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS); WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration(); wificonfigurationNetworkSpecifier.allowedKeyManagement .set(WifiConfiguration.KeyMgmt.WPA_PSK); WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -315,8 +300,7 @@ public class WifiNetworkAgentSpecifierTest { wifiConfigurationNetworkAgent.BSSID = TEST_BSSID_1; WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = new WifiNetworkAgentSpecifier( - wifiConfigurationNetworkAgent, - TEST_UID, TEST_PACKAGE); + wifiConfigurationNetworkAgent); PatternMatcher ssidPattern = new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB); @@ -329,8 +313,7 @@ public class WifiNetworkAgentSpecifierTest { WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -349,8 +332,7 @@ public class WifiNetworkAgentSpecifierTest { wifiConfigurationNetworkAgent.BSSID = TEST_BSSID_1; WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = new WifiNetworkAgentSpecifier( - wifiConfigurationNetworkAgent, - TEST_UID, TEST_PACKAGE); + wifiConfigurationNetworkAgent); PatternMatcher ssidPattern = new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX); @@ -363,8 +345,7 @@ public class WifiNetworkAgentSpecifierTest { WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -391,41 +372,12 @@ public class WifiNetworkAgentSpecifierTest { WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(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, TEST_PACKAGE_1); - - assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); - assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); - } private WifiConfiguration createDefaultWifiConfiguration() { WifiConfiguration wifiConfiguration = new WifiConfiguration(); @@ -437,8 +389,7 @@ public class WifiNetworkAgentSpecifierTest { } private WifiNetworkAgentSpecifier createDefaultNetworkAgentSpecifier() { - return new WifiNetworkAgentSpecifier(createDefaultWifiConfiguration(), TEST_UID, - TEST_PACKAGE); + return new WifiNetworkAgentSpecifier(createDefaultWifiConfiguration()); } } diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java index 5261e7ac83e4..fc0ef469ad80 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java @@ -29,7 +29,6 @@ import android.net.MatchAllNetworkSpecifier; import android.net.NetworkSpecifier; import android.os.Parcel; import android.os.PatternMatcher; -import android.os.Process; import android.util.Pair; import androidx.test.filters.SmallTest; @@ -41,8 +40,6 @@ import org.junit.Test; */ @SmallTest public class WifiNetworkSpecifierTest { - private static final int TEST_UID = 5; - private static final String TEST_PACKAGE_NAME = "com.test"; 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"; @@ -62,11 +59,12 @@ public class WifiNetworkSpecifierTest { 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); + assertEquals(WifiManager.ALL_ZEROS_MAC_ADDRESS, + wifiNetworkSpecifier.bssidPatternMatcher.first); + assertEquals(WifiManager.ALL_ZEROS_MAC_ADDRESS, + wifiNetworkSpecifier.bssidPatternMatcher.second); assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement .get(WifiConfiguration.KeyMgmt.NONE)); } @@ -210,7 +208,8 @@ public class WifiNetworkSpecifierTest { @Test(expected = IllegalStateException.class) public void testWifiNetworkSpecifierBuilderWithMatchAllBssidPattern() { new WifiNetworkSpecifier.Builder() - .setBssidPattern(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS) + .setBssidPattern(WifiManager.ALL_ZEROS_MAC_ADDRESS, + WifiManager.ALL_ZEROS_MAC_ADDRESS) .build(); } @@ -265,7 +264,7 @@ public class WifiNetworkSpecifierTest { @Test(expected = IllegalStateException.class) public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern3() { new WifiNetworkSpecifier.Builder() - .setBssid(MacAddress.ALL_ZEROS_ADDRESS) + .setBssid(WifiManager.ALL_ZEROS_MAC_ADDRESS) .build(); } @@ -364,8 +363,7 @@ public class WifiNetworkSpecifierTest { 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, TEST_PACKAGE_NAME); + wifiConfiguration); Parcel parcelW = Parcel.obtain(); specifier.writeToParcel(parcelW, 0); @@ -384,11 +382,11 @@ public class WifiNetworkSpecifierTest { /** * Validate NetworkSpecifier matching. * a) Create a network specifier for WPA_PSK network - * b) Ensure that the specifier matches {@code null} and {@link MatchAllNetworkSpecifier} + * b) Ensure that the specifier does not match {@code null} and {@link MatchAllNetworkSpecifier} * specifiers. */ @Test - public void testWifiNetworkSpecifierSatisfiesNullAndAllMatch() { + public void testWifiNetworkSpecifierDoesNotSatisfyNullAndAllMatch() { WifiConfiguration wifiConfiguration = new WifiConfiguration(); wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY; @@ -396,11 +394,10 @@ public class WifiNetworkSpecifierTest { 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, TEST_PACKAGE_NAME); + wifiConfiguration); - assertTrue(specifier.canBeSatisfiedBy(null)); - assertTrue(specifier.canBeSatisfiedBy(new MatchAllNetworkSpecifier())); + assertFalse(specifier.canBeSatisfiedBy(null)); + assertFalse(specifier.canBeSatisfiedBy(new MatchAllNetworkSpecifier())); } /** @@ -419,15 +416,13 @@ public class WifiNetworkSpecifierTest { 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, TEST_PACKAGE_NAME); + wifiConfiguration); 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, TEST_PACKAGE_NAME); + wifiConfiguration); assertTrue(specifier2.canBeSatisfiedBy(specifier1)); } @@ -448,8 +443,7 @@ public class WifiNetworkSpecifierTest { 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, TEST_PACKAGE_NAME); + wifiConfiguration1); WifiConfiguration wifiConfiguration2 = new WifiConfiguration(); wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); @@ -457,8 +451,7 @@ public class WifiNetworkSpecifierTest { 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, TEST_PACKAGE_NAME); + wifiConfiguration2); assertFalse(specifier2.canBeSatisfiedBy(specifier1)); } @@ -479,15 +472,13 @@ public class WifiNetworkSpecifierTest { new WifiNetworkSpecifier(new PatternMatcher("", PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), - wifiConfiguration, - TEST_UID, TEST_PACKAGE_NAME); + wifiConfiguration); 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, TEST_PACKAGE_NAME); + wifiConfiguration); assertFalse(specifier2.canBeSatisfiedBy(specifier1)); } @@ -508,43 +499,13 @@ public class WifiNetworkSpecifierTest { 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, TEST_PACKAGE_NAME); + wifiConfiguration); WifiNetworkSpecifier specifier2 = new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), - Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS), - wifiConfiguration, - TEST_UID, TEST_PACKAGE_NAME); - - assertFalse(specifier2.canBeSatisfiedBy(specifier1)); - } - - /** - * Validate NetworkSpecifier matching. - * a) Create network specifier 1 for WPA_PSK network - * b) Create network specifier 2 with different package name . - * c) Ensure that the specifier 2 is not satisfied by specifier 1. - */ - @Test - public void testWifiNetworkSpecifierDoesNotSatisfyWhenPackageNameDifferent() { - 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, TEST_PACKAGE_NAME); - - 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, TEST_PACKAGE_NAME + "blah"); + Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, + WifiManager.ALL_ZEROS_MAC_ADDRESS), + wifiConfiguration); assertFalse(specifier2.canBeSatisfiedBy(specifier1)); } diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java index 4dfa96b8c606..16b4ad08a830 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java @@ -19,8 +19,9 @@ package android.net.wifi; import static org.junit.Assert.*; import android.net.MacAddress; +import android.net.wifi.hotspot2.PasspointConfiguration; +import android.net.wifi.hotspot2.PasspointTestUtils; import android.os.Parcel; -import android.os.Process; import androidx.test.filters.SmallTest; @@ -31,14 +32,13 @@ import org.junit.Test; */ @SmallTest public class WifiNetworkSuggestionTest { - private static final int TEST_UID = 45677; - private static final int TEST_UID_OTHER = 45673; - private static final String TEST_PACKAGE_NAME = "com.test.packagename"; - private static final String TEST_PACKAGE_NAME_OTHER = "com.test.packagenameother"; private static final String TEST_SSID = "\"Test123\""; private static final String TEST_BSSID = "12:12:12:12:12:12"; private static final String TEST_SSID_1 = "\"Test1234\""; private static final String TEST_PRESHARED_KEY = "Test123"; + private static final String TEST_FQDN = "fqdn"; + private static final String TEST_WAPI_CERT_SUITE = "suite"; + private static final String TEST_DOMAIN_SUFFIX_MATCH = "domainSuffixMatch"; /** * Validate correctness of WifiNetworkSuggestion object created by @@ -52,7 +52,6 @@ public class WifiNetworkSuggestionTest { .setIsAppInteractionRequired(true) .build(); - assertEquals(Process.myUid(), suggestion.suggestorUid); assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); assertTrue(suggestion.wifiConfiguration.allowedKeyManagement .get(WifiConfiguration.KeyMgmt.NONE)); @@ -61,12 +60,14 @@ public class WifiNetworkSuggestionTest { assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE, suggestion.wifiConfiguration.meteredOverride); assertEquals(-1, suggestion.wifiConfiguration.priority); + assertFalse(suggestion.isUserAllowedToManuallyConnect); + assertTrue(suggestion.isInitialAutoJoinEnabled); } /** * Validate correctness of WifiNetworkSuggestion object created by * {@link WifiNetworkSuggestion.Builder#build()} for WPA_EAP network which requires - * app interaction and has a priority of zero set. + * app interaction, not share credential and has a priority of zero set. */ @Test public void @@ -75,6 +76,7 @@ public class WifiNetworkSuggestionTest { .setSsid(TEST_SSID) .setWpa2Passphrase(TEST_PRESHARED_KEY) .setIsAppInteractionRequired(true) + .setCredentialSharedWithUser(false) .setPriority(0) .build(); @@ -88,6 +90,8 @@ public class WifiNetworkSuggestionTest { assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE, suggestion.wifiConfiguration.meteredOverride); assertEquals(0, suggestion.wifiConfiguration.priority); + assertFalse(suggestion.isUserAllowedToManuallyConnect); + assertTrue(suggestion.isInitialAutoJoinEnabled); } /** @@ -102,6 +106,7 @@ public class WifiNetworkSuggestionTest { .setSsid(TEST_SSID) .setWpa2Passphrase(TEST_PRESHARED_KEY) .setIsUserInteractionRequired(true) + .setIsInitialAutojoinEnabled(false) .setIsMetered(true) .build(); @@ -115,6 +120,38 @@ public class WifiNetworkSuggestionTest { assertEquals(WifiConfiguration.METERED_OVERRIDE_METERED, suggestion.wifiConfiguration.meteredOverride); assertEquals(-1, suggestion.wifiConfiguration.priority); + assertTrue(suggestion.isUserAllowedToManuallyConnect); + assertFalse(suggestion.isInitialAutoJoinEnabled); + } + + /** + * Validate correctness of WifiNetworkSuggestion object created by + * {@link WifiNetworkSuggestion.Builder#build()} for WPA_PSK network which requires + * user interaction and is not metered. + */ + @Test + public void + testWifiNetworkSuggestionBuilderForWpa2PskNetworkWithNotMeteredAndReqUserInteraction() { + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setWpa2Passphrase(TEST_PRESHARED_KEY) + .setIsUserInteractionRequired(true) + .setIsInitialAutojoinEnabled(false) + .setIsMetered(false) + .build(); + + 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_NOT_METERED, + suggestion.wifiConfiguration.meteredOverride); + assertEquals(-1, suggestion.wifiConfiguration.priority); + assertTrue(suggestion.isUserAllowedToManuallyConnect); + assertFalse(suggestion.isInitialAutoJoinEnabled); } /** @@ -134,7 +171,9 @@ public class WifiNetworkSuggestionTest { assertTrue(suggestion.wifiConfiguration.allowedKeyManagement .get(WifiConfiguration.KeyMgmt.OWE)); assertNull(suggestion.wifiConfiguration.preSharedKey); - assertTrue(suggestion.wifiConfiguration.requirePMF); + assertTrue(suggestion.wifiConfiguration.requirePmf); + assertFalse(suggestion.isUserAllowedToManuallyConnect); + assertTrue(suggestion.isInitialAutoJoinEnabled); } /** @@ -146,6 +185,8 @@ public class WifiNetworkSuggestionTest { WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() .setSsid(TEST_SSID) .setWpa3Passphrase(TEST_PRESHARED_KEY) + .setCredentialSharedWithUser(true) + .setIsInitialAutojoinEnabled(false) .build(); assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); @@ -153,7 +194,9 @@ public class WifiNetworkSuggestionTest { .get(WifiConfiguration.KeyMgmt.SAE)); assertEquals("\"" + TEST_PRESHARED_KEY + "\"", suggestion.wifiConfiguration.preSharedKey); - assertTrue(suggestion.wifiConfiguration.requirePMF); + assertTrue(suggestion.wifiConfiguration.requirePmf); + assertTrue(suggestion.isUserAllowedToManuallyConnect); + assertFalse(suggestion.isInitialAutoJoinEnabled); } @@ -166,6 +209,8 @@ public class WifiNetworkSuggestionTest { WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS); enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC); + enterpriseConfig.setCaCertificate(FakeKeys.CA_CERT0); + enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH); WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() .setSsid(TEST_SSID) @@ -179,10 +224,149 @@ public class WifiNetworkSuggestionTest { .get(WifiConfiguration.GroupCipher.GCMP_256)); assertTrue(suggestion.wifiConfiguration.allowedGroupManagementCiphers .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256)); - assertTrue(suggestion.wifiConfiguration.requirePMF); + assertTrue(suggestion.wifiConfiguration.requirePmf); assertNull(suggestion.wifiConfiguration.preSharedKey); // allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested // here. + assertTrue(suggestion.isUserAllowedToManuallyConnect); + assertTrue(suggestion.isInitialAutoJoinEnabled); + } + + /** + * Ensure create enterprise suggestion requires CA, when CA certificate is missing, will throw + * an exception. + */ + @Test (expected = IllegalArgumentException.class) + public void testWifiNetworkSuggestionBuilderForEapNetworkWithoutCa() { + WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); + enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS); + enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC); + enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH); + + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setWpa2EnterpriseConfig(enterpriseConfig) + .build(); + } + + /** + * Ensure create enterprise suggestion requires CA, when both domain suffix and alt subject + * match are missing, will throw an exception. + */ + @Test (expected = IllegalArgumentException.class) + public void testWifiNetworkSuggestionBuilderForEapNetworkWithoutMatch() { + WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); + enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS); + enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC); + enterpriseConfig.setCaCertificate(FakeKeys.CA_CERT0); + + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setWpa3EnterpriseConfig(enterpriseConfig) + .build(); + } + + /** + * Validate correctness of WifiNetworkSuggestion object created by + * {@link WifiNetworkSuggestion.Builder#build()} for WAPI-PSK network. + */ + @Test + public void testWifiNetworkSuggestionBuilderForWapiPskNetwork() { + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setWapiPassphrase(TEST_PRESHARED_KEY) + .build(); + + assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); + assertTrue(suggestion.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.WAPI_PSK)); + assertTrue(suggestion.wifiConfiguration.allowedPairwiseCiphers + .get(WifiConfiguration.PairwiseCipher.SMS4)); + assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers + .get(WifiConfiguration.GroupCipher.SMS4)); + assertEquals("\"" + TEST_PRESHARED_KEY + "\"", + suggestion.wifiConfiguration.preSharedKey); + } + + + /** + * Validate correctness of WifiNetworkSuggestion object created by + * {@link WifiNetworkSuggestion.Builder#build()} for WAPI-CERT network. + */ + @Test + public void testWifiNetworkSuggestionBuilderForWapiCertNetwork() { + WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); + enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.WAPI_CERT); + enterpriseConfig.setWapiCertSuite(TEST_WAPI_CERT_SUITE); + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setWapiEnterpriseConfig(enterpriseConfig) + .build(); + + assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); + assertTrue(suggestion.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.WAPI_CERT)); + assertTrue(suggestion.wifiConfiguration.allowedPairwiseCiphers + .get(WifiConfiguration.PairwiseCipher.SMS4)); + assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers + .get(WifiConfiguration.GroupCipher.SMS4)); + assertNull(suggestion.wifiConfiguration.preSharedKey); + assertNotNull(suggestion.wifiConfiguration.enterpriseConfig); + assertEquals(WifiEnterpriseConfig.Eap.WAPI_CERT, + suggestion.wifiConfiguration.enterpriseConfig.getEapMethod()); + assertEquals(TEST_WAPI_CERT_SUITE, + suggestion.wifiConfiguration.enterpriseConfig.getWapiCertSuite()); + } + + /** + * Validate correctness of WifiNetworkSuggestion object created by + * {@link WifiNetworkSuggestion.Builder#build()} for WAPI-CERT network + * which selects the certificate suite automatically. + */ + @Test + public void testWifiNetworkSuggestionBuilderForWapiCertAutoNetwork() { + WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); + enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.WAPI_CERT); + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setWapiEnterpriseConfig(enterpriseConfig) + .build(); + + assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); + assertTrue(suggestion.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.WAPI_CERT)); + assertTrue(suggestion.wifiConfiguration.allowedPairwiseCiphers + .get(WifiConfiguration.PairwiseCipher.SMS4)); + assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers + .get(WifiConfiguration.GroupCipher.SMS4)); + assertNull(suggestion.wifiConfiguration.preSharedKey); + assertNotNull(suggestion.wifiConfiguration.enterpriseConfig); + assertEquals(WifiEnterpriseConfig.Eap.WAPI_CERT, + suggestion.wifiConfiguration.enterpriseConfig.getEapMethod()); + assertEquals("", + suggestion.wifiConfiguration.enterpriseConfig.getWapiCertSuite()); + } + + /** + * Validate correctness of WifiNetworkSuggestion object created by + * {@link WifiNetworkSuggestion.Builder#build()} for Passpoint network which requires + * app interaction and metered. + */ + @Test + public void testWifiNetworkSuggestionBuilderForPasspointNetworkWithReqAppInteractionMetered() { + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setPasspointConfig(passpointConfiguration) + .setIsAppInteractionRequired(true) + .setIsMetered(true) + .build(); + assertEquals(TEST_FQDN, suggestion.wifiConfiguration.FQDN); + assertTrue(suggestion.isAppInteractionRequired); + assertEquals(suggestion.wifiConfiguration.meteredOverride, + WifiConfiguration.METERED_OVERRIDE_METERED); + assertEquals(suggestion.getPasspointConfig().getMeteredOverride(), + WifiConfiguration.METERED_OVERRIDE_METERED); + assertTrue(suggestion.isUserAllowedToManuallyConnect); } /** @@ -209,6 +393,18 @@ public class WifiNetworkSuggestionTest { } /** + * Ensure {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)}} + * throws an exception when the PasspointConfiguration is not valid. + */ + @Test(expected = IllegalArgumentException.class) + public void testWifiNetworkSuggestionBuilderSetPasspointConfigWithNonValid() { + PasspointConfiguration passpointConfiguration = new PasspointConfiguration(); + new WifiNetworkSuggestion.Builder() + .setPasspointConfig(passpointConfiguration) + .build(); + } + + /** * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception * when {@link WifiNetworkSuggestion.Builder#setSsid(String)} is not set. */ @@ -251,7 +447,7 @@ public class WifiNetworkSuggestionTest { public void testWifiNetworkSuggestionBuilderWithInvalidAllZeroBssid() { new WifiNetworkSuggestion.Builder() .setSsid(TEST_SSID) - .setBssid(MacAddress.ALL_ZEROS_ADDRESS) + .setBssid(WifiManager.ALL_ZEROS_MAC_ADDRESS) .build(); } @@ -311,6 +507,91 @@ public class WifiNetworkSuggestionTest { } /** + * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception + * when both {@link WifiNetworkSuggestion.Builder#setSsid(String)} and + * {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} are invoked. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSuggestionBuilderWithBothSsidAndPasspointConfig() { + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setPasspointConfig(passpointConfiguration) + .build(); + } + + /** + * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception + * when both {@link WifiNetworkSuggestion.Builder#setWpa2Passphrase(String)} and + * {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} are invoked. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSuggestionBuilderWithBothWpa2PassphraseAndPasspointConfig() { + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + new WifiNetworkSuggestion.Builder() + .setWpa2Passphrase(TEST_PRESHARED_KEY) + .setPasspointConfig(passpointConfiguration) + .build(); + } + + /** + * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception + * when both {@link WifiNetworkSuggestion.Builder#setWpa3Passphrase(String)} and + * {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} are invoked. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSuggestionBuilderWithBothWpa3PassphraseAndPasspointConfig() { + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + new WifiNetworkSuggestion.Builder() + .setWpa3Passphrase(TEST_PRESHARED_KEY) + .setPasspointConfig(passpointConfiguration) + .build(); + } + + /** + * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception + * when both {@link WifiNetworkSuggestion.Builder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)} + * and {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} are + * invoked. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSuggestionBuilderWithBothEnterpriseAndPasspointConfig() { + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + new WifiNetworkSuggestion.Builder() + .setWpa3EnterpriseConfig(new WifiEnterpriseConfig()) + .setPasspointConfig(passpointConfiguration) + .build(); + } + + /** + * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception + * when both {@link WifiNetworkSuggestion.Builder#setIsEnhancedOpen(boolean)} and + * {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} are invoked. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSuggestionBuilderWithBothEnhancedOpenAndPasspointConfig() { + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + new WifiNetworkSuggestion.Builder() + .setIsEnhancedOpen(true) + .setPasspointConfig(passpointConfiguration) + .build(); + } + + /** + * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception + * when both {@link WifiNetworkSuggestion.Builder#setIsHiddenSsid(boolean)} and + * {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} are invoked. + */ + @Test(expected = IllegalStateException.class) + public void testWifiNetworkSuggestionBuilderWithBothHiddenSsidAndPasspointConfig() { + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + new WifiNetworkSuggestion.Builder() + .setIsHiddenSsid(true) + .setPasspointConfig(passpointConfiguration) + .build(); + } + + /** * Check that parcel marshalling/unmarshalling works */ @Test @@ -319,8 +600,8 @@ public class WifiNetworkSuggestionTest { configuration.SSID = TEST_SSID; configuration.BSSID = TEST_BSSID; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); - WifiNetworkSuggestion suggestion = - new WifiNetworkSuggestion(configuration, false, true, TEST_UID, TEST_PACKAGE_NAME); + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion( + configuration, null, false, true, true, true); Parcel parcelW = Parcel.obtain(); suggestion.writeToParcel(parcelW, 0); @@ -337,10 +618,47 @@ public class WifiNetworkSuggestionTest { // 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.hashCode(), parcelSuggestion.hashCode()); assertEquals(suggestion.isAppInteractionRequired, parcelSuggestion.isAppInteractionRequired); assertEquals(suggestion.isUserInteractionRequired, parcelSuggestion.isUserInteractionRequired); + assertEquals(suggestion.isInitialAutoJoinEnabled, + parcelSuggestion.isInitialAutoJoinEnabled); + } + + /** + * Check that parcel marshalling/unmarshalling works + */ + @Test + public void testPasspointNetworkSuggestionParcel() { + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setPasspointConfig(passpointConfiguration) + .build(); + + 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.hashCode(), parcelSuggestion.hashCode()); + assertEquals(suggestion.isAppInteractionRequired, + parcelSuggestion.isAppInteractionRequired); + assertEquals(suggestion.isUserInteractionRequired, + parcelSuggestion.isUserInteractionRequired); + assertEquals(suggestion.isInitialAutoJoinEnabled, + parcelSuggestion.isInitialAutoJoinEnabled); } /** @@ -354,18 +672,17 @@ public class WifiNetworkSuggestionTest { configuration.BSSID = TEST_BSSID; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); WifiNetworkSuggestion suggestion = - new WifiNetworkSuggestion(configuration, true, false, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration, null, true, false, true, true); WifiConfiguration configuration1 = new WifiConfiguration(); configuration1.SSID = TEST_SSID; configuration1.BSSID = TEST_BSSID; configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); WifiNetworkSuggestion suggestion1 = - new WifiNetworkSuggestion(configuration1, false, true, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration1, null, false, true, true, true); assertEquals(suggestion, suggestion1); + assertEquals(suggestion.hashCode(), suggestion1.hashCode()); } /** @@ -378,15 +695,13 @@ public class WifiNetworkSuggestionTest { configuration.SSID = TEST_SSID; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkSuggestion suggestion = - new WifiNetworkSuggestion(configuration, false, false, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration, null, false, false, true, true); WifiConfiguration configuration1 = new WifiConfiguration(); configuration1.SSID = TEST_SSID_1; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkSuggestion suggestion1 = - new WifiNetworkSuggestion(configuration1, false, false, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration1, null, false, false, true, true); assertNotEquals(suggestion, suggestion1); } @@ -402,15 +717,13 @@ public class WifiNetworkSuggestionTest { configuration.BSSID = TEST_BSSID; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkSuggestion suggestion = - new WifiNetworkSuggestion(configuration, false, false, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration, null, false, false, true, true); WifiConfiguration configuration1 = new WifiConfiguration(); configuration1.SSID = TEST_SSID; configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkSuggestion suggestion1 = - new WifiNetworkSuggestion(configuration1, false, false, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration1, null, false, false, true, true); assertNotEquals(suggestion, suggestion1); } @@ -425,55 +738,156 @@ public class WifiNetworkSuggestionTest { configuration.SSID = TEST_SSID; configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkSuggestion suggestion = - new WifiNetworkSuggestion(configuration, false, false, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration, null, false, false, true, true); WifiConfiguration configuration1 = new WifiConfiguration(); configuration1.SSID = TEST_SSID; configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); WifiNetworkSuggestion suggestion1 = - new WifiNetworkSuggestion(configuration1, false, false, TEST_UID, - TEST_PACKAGE_NAME); + new WifiNetworkSuggestion(configuration1, null, false, false, true, true); assertNotEquals(suggestion, suggestion1); } /** - * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same - * SSID, BSSID and key mgmt, but different UID. + * Check NetworkSuggestion equals returns {@code true} for 2 Passpoint network suggestions with + * same FQDN. */ @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, TEST_UID, - TEST_PACKAGE_NAME); + public void testPasspointNetworkSuggestionEqualsSameWithSameFQDN() { + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + PasspointConfiguration passpointConfiguration1 = PasspointTestUtils.createConfig(); + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setPasspointConfig(passpointConfiguration) + .build(); + WifiNetworkSuggestion suggestion1 = new WifiNetworkSuggestion.Builder() + .setPasspointConfig(passpointConfiguration1) + .build(); + assertEquals(suggestion, suggestion1); + assertEquals(suggestion.hashCode(), suggestion1.hashCode()); + } - WifiNetworkSuggestion suggestion1 = - new WifiNetworkSuggestion(configuration, false, false, TEST_UID_OTHER, - TEST_PACKAGE_NAME); + /** + * Check NetworkSuggestion equals returns {@code false} for 2 Passpoint network suggestions with + * different FQDN. + */ + @Test + public void testPasspointNetworkSuggestionNotEqualsSameWithDifferentFQDN() { + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + PasspointConfiguration passpointConfiguration1 = PasspointTestUtils.createConfig(); + passpointConfiguration1.getHomeSp().setFqdn(TEST_FQDN + 1); + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setPasspointConfig(passpointConfiguration) + .build(); + WifiNetworkSuggestion suggestion1 = new WifiNetworkSuggestion.Builder() + .setPasspointConfig(passpointConfiguration1) + .build(); assertNotEquals(suggestion, suggestion1); } /** - * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same - * SSID, BSSID and key mgmt, but different package name. + * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception + * when {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} to + * true on a open network suggestion. + */ + @Test(expected = IllegalStateException.class) + public void testSetCredentialSharedWithUserWithOpenNetwork() { + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setCredentialSharedWithUser(true) + .build(); + } + + /** + * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception + * when {@link WifiNetworkSuggestion.Builder#setIsInitialAutojoinEnabled(boolean)} to + * false on a open network suggestion. + */ + @Test(expected = IllegalStateException.class) + public void testSetIsAutoJoinDisabledWithOpenNetwork() { + new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setIsInitialAutojoinEnabled(false) + .build(); + } + + /** + * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception + * when set both {@link WifiNetworkSuggestion.Builder#setIsInitialAutojoinEnabled(boolean)} + * and {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} (boolean)} + * to false on a network suggestion. + */ + @Test(expected = IllegalStateException.class) + public void testSetIsAutoJoinDisabledWithSecureNetworkNotSharedWithUser() { + new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setWpa2Passphrase(TEST_PRESHARED_KEY) + .setCredentialSharedWithUser(false) + .setIsInitialAutojoinEnabled(false) + .build(); + } + + /** + * Validate {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} set the + * correct value to the WifiConfiguration. */ @Test - public void testWifiNetworkSuggestionEqualsFailsWhenPackageNameIsDifferent() { - WifiConfiguration configuration = new WifiConfiguration(); - configuration.SSID = TEST_SSID; - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); - WifiNetworkSuggestion suggestion = - new WifiNetworkSuggestion(configuration, false, false, TEST_UID, TEST_PACKAGE_NAME); + public void testSetIsNetworkAsUntrusted() { + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setWpa2Passphrase(TEST_PRESHARED_KEY) + .setUntrusted(true) + .build(); + assertTrue(suggestion.isUntrusted()); + assertFalse(suggestion.isUserAllowedToManuallyConnect); + } - WifiNetworkSuggestion suggestion1 = - new WifiNetworkSuggestion(configuration, false, false, TEST_UID, - TEST_PACKAGE_NAME_OTHER); + /** + * Validate {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} set the + * correct value to the WifiConfiguration. + * Also the {@link WifiNetworkSuggestion#isUserAllowedToManuallyConnect} should be false; + */ + @Test + public void testSetIsNetworkAsUntrustedOnPasspointNetwork() { + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setPasspointConfig(passpointConfiguration) + .setUntrusted(true) + .build(); + assertTrue(suggestion.isUntrusted()); + assertFalse(suggestion.isUserAllowedToManuallyConnect); + } - assertNotEquals(suggestion, suggestion1); + /** + * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception + * when set {@link WifiNetworkSuggestion.Builder#setUntrusted(boolean)} to true and + * set {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} to true + * together. + */ + @Test(expected = IllegalStateException.class) + public void testSetCredentialSharedWithUserWithSetIsNetworkAsUntrusted() { + new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setWpa2Passphrase(TEST_PRESHARED_KEY) + .setCredentialSharedWithUser(true) + .setUntrusted(true) + .build(); + } + + /** + * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception + * when set both {@link WifiNetworkSuggestion.Builder#setIsInitialAutojoinEnabled(boolean)} + * and {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} (boolean)} + * to false on a passpoint suggestion. + */ + @Test(expected = IllegalStateException.class) + public void testSetIsAutoJoinDisabledWithSecureNetworkNotSharedWithUserForPasspoint() { + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + new WifiNetworkSuggestion.Builder() + .setPasspointConfig(passpointConfiguration) + .setCredentialSharedWithUser(false) + .setIsInitialAutojoinEnabled(false) + .build(); } } diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java index dd05b47fbd4f..b68616f560f3 100644 --- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java @@ -22,8 +22,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.validateMockitoUsage; @@ -34,6 +35,7 @@ import android.content.Context; import android.net.wifi.WifiScanner.PnoSettings; import android.net.wifi.WifiScanner.PnoSettings.PnoNetwork; import android.net.wifi.WifiScanner.ScanData; +import android.net.wifi.WifiScanner.ScanListener; import android.net.wifi.WifiScanner.ScanSettings; import android.os.Bundle; import android.os.Handler; @@ -52,8 +54,10 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import java.util.Arrays; +import java.util.concurrent.Executor; /** * Unit tests for {@link android.net.wifi.WifiScanner}. @@ -64,19 +68,23 @@ public class WifiScannerTest { private Context mContext; @Mock private IWifiScanner mService; + @Spy + private Executor mExecutor = new SynchronousExecutor(); + @Mock + private ScanListener mScanListener; + @Mock + private WifiScanner.ParcelableScanData mParcelableScanData; + private ScanData[] mScanData = {}; private static final boolean TEST_PNOSETTINGS_IS_CONNECTED = false; private static final int TEST_PNOSETTINGS_MIN_5GHZ_RSSI = -60; private static final int TEST_PNOSETTINGS_MIN_2GHZ_RSSI = -70; - private static final int TEST_PNOSETTINGS_INITIAL_SCORE_MAX = 50; - private static final int TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS = 10; - private static final int TEST_PNOSETTINGS_SAME_NETWORK_BONUS = 11; - private static final int TEST_PNOSETTINGS_SECURE_BONUS = 12; - private static final int TEST_PNOSETTINGS_BAND_5GHZ_BONUS = 13; + private static final int TEST_PNOSETTINGS_MIN_6GHZ_RSSI = -55; private static final String TEST_SSID_1 = "TEST1"; private static final String TEST_SSID_2 = "TEST2"; private static final int[] TEST_FREQUENCIES_1 = {}; - private static final int[] TEST_FREQUENCIES_2 = {2500, 5124}; + private static final int[] TEST_FREQUENCIES_2 = {2500, 5124, 6245}; + private static final String DESCRIPTION_NOT_AUTHORIZED = "Not authorized"; private WifiScanner mWifiScanner; private TestLooper mLooper; @@ -96,6 +104,7 @@ public class WifiScannerTest { when(mService.getMessenger()).thenReturn(mBidirectionalAsyncChannelServer.getMessenger()); mWifiScanner = new WifiScanner(mContext, mService, mLooper.getLooper()); mLooper.dispatchAll(); + when(mParcelableScanData.getResults()).thenReturn(mScanData); } /** @@ -112,7 +121,7 @@ public class WifiScannerTest { @Test public void verifyScanSettingsParcelWithBand() throws Exception { ScanSettings writeSettings = new ScanSettings(); - writeSettings.type = WifiScanner.TYPE_LOW_POWER; + writeSettings.type = WifiScanner.SCAN_TYPE_LOW_POWER; writeSettings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS; ScanSettings readSettings = parcelWriteRead(writeSettings); @@ -127,7 +136,7 @@ public class WifiScannerTest { @Test public void verifyScanSettingsParcelWithChannels() throws Exception { ScanSettings writeSettings = new ScanSettings(); - writeSettings.type = WifiScanner.TYPE_HIGH_ACCURACY; + writeSettings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY; writeSettings.band = WifiScanner.WIFI_BAND_UNSPECIFIED; writeSettings.channels = new WifiScanner.ChannelSpec[] { new WifiScanner.ChannelSpec(5), @@ -170,11 +179,7 @@ public class WifiScannerTest { pnoSettings.isConnected = TEST_PNOSETTINGS_IS_CONNECTED; pnoSettings.min5GHzRssi = TEST_PNOSETTINGS_MIN_5GHZ_RSSI; pnoSettings.min24GHzRssi = TEST_PNOSETTINGS_MIN_2GHZ_RSSI; - pnoSettings.initialScoreMax = TEST_PNOSETTINGS_INITIAL_SCORE_MAX; - pnoSettings.currentConnectionBonus = TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS; - pnoSettings.sameNetworkBonus = TEST_PNOSETTINGS_SAME_NETWORK_BONUS; - pnoSettings.secureBonus = TEST_PNOSETTINGS_SECURE_BONUS; - pnoSettings.band5GHzBonus = TEST_PNOSETTINGS_BAND_5GHZ_BONUS; + pnoSettings.min6GHzRssi = TEST_PNOSETTINGS_MIN_6GHZ_RSSI; Parcel parcel = Parcel.obtain(); pnoSettings.writeToParcel(parcel, 0); @@ -187,13 +192,7 @@ public class WifiScannerTest { assertEquals(TEST_PNOSETTINGS_IS_CONNECTED, pnoSettingsDeserialized.isConnected); assertEquals(TEST_PNOSETTINGS_MIN_5GHZ_RSSI, pnoSettingsDeserialized.min5GHzRssi); assertEquals(TEST_PNOSETTINGS_MIN_2GHZ_RSSI, pnoSettingsDeserialized.min24GHzRssi); - assertEquals(TEST_PNOSETTINGS_INITIAL_SCORE_MAX, pnoSettingsDeserialized.initialScoreMax); - assertEquals(TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS, - pnoSettingsDeserialized.currentConnectionBonus); - assertEquals(TEST_PNOSETTINGS_SAME_NETWORK_BONUS, - pnoSettingsDeserialized.sameNetworkBonus); - assertEquals(TEST_PNOSETTINGS_SECURE_BONUS, pnoSettingsDeserialized.secureBonus); - assertEquals(TEST_PNOSETTINGS_BAND_5GHZ_BONUS, pnoSettingsDeserialized.band5GHzBonus); + assertEquals(TEST_PNOSETTINGS_MIN_6GHZ_RSSI, pnoSettingsDeserialized.min6GHzRssi); // Test parsing of PnoNetwork assertEquals(pnoSettings.networkList.length, pnoSettingsDeserialized.networkList.length); @@ -244,13 +243,13 @@ public class WifiScannerTest { /** - * Test behavior of {@link WifiScanner#startScan(ScanSettings, WifiScanner.ScanListener)} + * Test behavior of {@link WifiScanner#startScan(ScanSettings, ScanListener)} * @throws Exception */ @Test public void testStartScan() throws Exception { ScanSettings scanSettings = new ScanSettings(); - WifiScanner.ScanListener scanListener = mock(WifiScanner.ScanListener.class); + ScanListener scanListener = mock(ScanListener.class); mWifiScanner.startScan(scanSettings, scanListener); mLooper.dispatchAll(); @@ -268,17 +267,19 @@ public class WifiScannerTest { assertNull(messageBundle.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY)); assertEquals(mContext.getOpPackageName(), messageBundle.getParcelable(WifiScanner.REQUEST_PACKAGE_NAME_KEY)); + assertEquals(mContext.getAttributionTag(), + messageBundle.getParcelable(WifiScanner.REQUEST_FEATURE_ID_KEY)); } /** - * Test behavior of {@link WifiScanner#stopScan(WifiScanner.ScanListener)} + * Test behavior of {@link WifiScanner#stopScan(ScanListener)} * @throws Exception */ @Test public void testStopScan() throws Exception { ScanSettings scanSettings = new ScanSettings(); - WifiScanner.ScanListener scanListener = mock(WifiScanner.ScanListener.class); + ScanListener scanListener = mock(ScanListener.class); mWifiScanner.startScan(scanSettings, scanListener); mLooper.dispatchAll(); @@ -296,17 +297,18 @@ public class WifiScannerTest { Bundle messageBundle = (Bundle) message.obj; assertEquals(mContext.getOpPackageName(), messageBundle.getParcelable(WifiScanner.REQUEST_PACKAGE_NAME_KEY)); - + assertEquals(mContext.getAttributionTag(), + messageBundle.getParcelable(WifiScanner.REQUEST_FEATURE_ID_KEY)); } /** - * Test behavior of {@link WifiScanner#startScan(ScanSettings, WifiScanner.ScanListener)} + * Test behavior of {@link WifiScanner#startScan(ScanSettings, ScanListener)} * @throws Exception */ @Test public void testStartScanListenerOnSuccess() throws Exception { ScanSettings scanSettings = new ScanSettings(); - WifiScanner.ScanListener scanListener = mock(WifiScanner.ScanListener.class); + ScanListener scanListener = mock(ScanListener.class); mWifiScanner.startScan(scanSettings, scanListener); mLooper.dispatchAll(); @@ -330,13 +332,13 @@ public class WifiScannerTest { } /** - * Test behavior of {@link WifiScanner#startScan(ScanSettings, WifiScanner.ScanListener)} + * Test behavior of {@link WifiScanner#startScan(ScanSettings, ScanListener)} * @throws Exception */ @Test public void testStartScanListenerOnResults() throws Exception { ScanSettings scanSettings = new ScanSettings(); - WifiScanner.ScanListener scanListener = mock(WifiScanner.ScanListener.class); + ScanListener scanListener = mock(ScanListener.class); mWifiScanner.startScan(scanSettings, scanListener); mLooper.dispatchAll(); @@ -364,7 +366,7 @@ public class WifiScannerTest { /** * Test behavior of {@link WifiScanner#startDisconnectedPnoScan(ScanSettings, PnoSettings, - * WifiScanner.PnoScanListener)} + * Executor, WifiScanner.PnoScanListener)} * @throws Exception */ @Test @@ -373,7 +375,8 @@ public class WifiScannerTest { PnoSettings pnoSettings = new PnoSettings(); WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class); - mWifiScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, pnoScanListener); + mWifiScanner.startDisconnectedPnoScan( + scanSettings, pnoSettings, mock(Executor.class), pnoScanListener); mLooper.dispatchAll(); ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); @@ -394,7 +397,7 @@ public class WifiScannerTest { /** * Test behavior of {@link WifiScanner#startConnectedPnoScan(ScanSettings, PnoSettings, - * WifiScanner.PnoScanListener)} + * Executor, WifiScanner.PnoScanListener)} * @throws Exception */ @Test @@ -403,7 +406,8 @@ public class WifiScannerTest { PnoSettings pnoSettings = new PnoSettings(); WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class); - mWifiScanner.startConnectedPnoScan(scanSettings, pnoSettings, pnoScanListener); + mWifiScanner.startConnectedPnoScan( + scanSettings, pnoSettings, mock(Executor.class), pnoScanListener); mLooper.dispatchAll(); ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); @@ -423,8 +427,8 @@ public class WifiScannerTest { } /** - * Test behavior of {@link WifiScanner#stopPnoScan(WifiScanner.ScanListener)} - * WifiScanner.PnoScanListener)} + * Test behavior of {@link WifiScanner#stopPnoScan(ScanListener)} + * Executor, WifiScanner.PnoScanListener)} * @throws Exception */ @Test @@ -433,7 +437,8 @@ public class WifiScannerTest { PnoSettings pnoSettings = new PnoSettings(); WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class); - mWifiScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, pnoScanListener); + mWifiScanner.startDisconnectedPnoScan( + scanSettings, pnoSettings, mock(Executor.class), pnoScanListener); mLooper.dispatchAll(); mWifiScanner.stopPnoScan(pnoScanListener); mLooper.dispatchAll(); @@ -445,4 +450,185 @@ public class WifiScannerTest { assertEquals(WifiScanner.CMD_STOP_PNO_SCAN, message.what); } + + @Test + public void testScanDataAddResults() throws Exception { + ScanResult scanResult1 = new ScanResult(); + scanResult1.SSID = TEST_SSID_1; + ScanData scanData = new ScanData(0, 0, new ScanResult[]{scanResult1}); + + ScanResult scanResult2 = new ScanResult(); + scanResult2.SSID = TEST_SSID_2; + scanData.addResults(new ScanResult[]{scanResult2}); + + ScanResult[] consolidatedScanResults = scanData.getResults(); + assertEquals(2, consolidatedScanResults.length); + assertEquals(TEST_SSID_1, consolidatedScanResults[0].SSID); + assertEquals(TEST_SSID_2, consolidatedScanResults[1].SSID); + } + + @Test + public void testScanDataParcel() throws Exception { + ScanResult scanResult1 = new ScanResult(); + scanResult1.SSID = TEST_SSID_1; + ScanData scanData = new ScanData(5, 4, new ScanResult[]{scanResult1}); + + Parcel parcel = Parcel.obtain(); + scanData.writeToParcel(parcel, 0); + parcel.setDataPosition(0); // Rewind data position back to the beginning for read. + ScanData readScanData = ScanData.CREATOR.createFromParcel(parcel); + + assertEquals(scanData.getId(), readScanData.getId()); + assertEquals(scanData.getFlags(), readScanData.getFlags()); + assertEquals(scanData.getResults().length, readScanData.getResults().length); + assertEquals(scanData.getResults()[0].SSID, readScanData.getResults()[0].SSID); + } + + /** Tests that upon registration success, {@link ScanListener#onSuccess()} is called. */ + @Test + public void testRegisterScanListenerSuccess() throws Exception { + mWifiScanner.registerScanListener(mExecutor, mScanListener); + mLooper.dispatchAll(); + + ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); + verify(mHandler).handleMessage(messageArgumentCaptor.capture()); + Message sentMessage = messageArgumentCaptor.getValue(); + assertNotNull(sentMessage); + + assertEquals(1, mBidirectionalAsyncChannelServer.getClientMessengers().size()); + Messenger scannerMessenger = + mBidirectionalAsyncChannelServer.getClientMessengers().iterator().next(); + + Message responseMessage = Message.obtain(); + responseMessage.what = WifiScanner.CMD_OP_SUCCEEDED; + responseMessage.arg2 = sentMessage.arg2; + scannerMessenger.send(responseMessage); + mLooper.dispatchAll(); + + verify(mExecutor).execute(any()); + verify(mScanListener).onSuccess(); + } + + /** + * Tests that upon registration failed, {@link ScanListener#onFailure(int, String)} is called. + */ + @Test + public void testRegisterScanListenerFailed() throws Exception { + mWifiScanner.registerScanListener(mExecutor, mScanListener); + mLooper.dispatchAll(); + + ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); + verify(mHandler).handleMessage(messageArgumentCaptor.capture()); + Message sentMessage = messageArgumentCaptor.getValue(); + assertNotNull(sentMessage); + + assertEquals(1, mBidirectionalAsyncChannelServer.getClientMessengers().size()); + Messenger scannerMessenger = + mBidirectionalAsyncChannelServer.getClientMessengers().iterator().next(); + + { + Message responseMessage = Message.obtain(); + responseMessage.what = WifiScanner.CMD_OP_FAILED; + responseMessage.arg2 = sentMessage.arg2; + responseMessage.obj = new WifiScanner.OperationResult( + WifiScanner.REASON_NOT_AUTHORIZED, DESCRIPTION_NOT_AUTHORIZED); + scannerMessenger.send(responseMessage); + mLooper.dispatchAll(); + } + + verify(mExecutor).execute(any()); + verify(mScanListener).onFailure( + WifiScanner.REASON_NOT_AUTHORIZED, DESCRIPTION_NOT_AUTHORIZED); + + // CMD_OP_FAILED should have caused the removal of the listener, verify this + { + Message responseMessage = Message.obtain(); + responseMessage.what = WifiScanner.CMD_SCAN_RESULT; + responseMessage.arg2 = sentMessage.arg2; + responseMessage.obj = mParcelableScanData; + scannerMessenger.send(responseMessage); + mLooper.dispatchAll(); + } + // execute() called once before, not called again + verify(mExecutor, times(1)).execute(any()); + // onResults() never triggered + verify(mScanListener, never()).onResults(any()); + } + + /** + * Tests that when the ScanListener is triggered, {@link ScanListener#onResults(ScanData[])} + * is called. + */ + @Test + public void testRegisterScanListenerReceiveScanResults() throws Exception { + mWifiScanner.registerScanListener(mExecutor, mScanListener); + mLooper.dispatchAll(); + + ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); + verify(mHandler).handleMessage(messageArgumentCaptor.capture()); + Message sentMessage = messageArgumentCaptor.getValue(); + assertNotNull(sentMessage); + + assertEquals(1, mBidirectionalAsyncChannelServer.getClientMessengers().size()); + Messenger scannerMessenger = + mBidirectionalAsyncChannelServer.getClientMessengers().iterator().next(); + + Message responseMessage = Message.obtain(); + responseMessage.what = WifiScanner.CMD_SCAN_RESULT; + responseMessage.arg2 = sentMessage.arg2; + responseMessage.obj = mParcelableScanData; + scannerMessenger.send(responseMessage); + mLooper.dispatchAll(); + + verify(mExecutor).execute(any()); + verify(mScanListener).onResults(mScanData); + } + + /** + * Tests that after unregistering a scan listener, {@link ScanListener#onResults(ScanData[])} + * is not called. + */ + @Test + public void testUnregisterScanListener() throws Exception { + mWifiScanner.registerScanListener(mExecutor, mScanListener); + mWifiScanner.unregisterScanListener(mScanListener); + mLooper.dispatchAll(); + + assertEquals(1, mBidirectionalAsyncChannelServer.getClientMessengers().size()); + Messenger scannerMessenger = + mBidirectionalAsyncChannelServer.getClientMessengers().iterator().next(); + + ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); + verify(mHandler, times(2)).handleMessage(messageArgumentCaptor.capture()); + Message sentMessage = messageArgumentCaptor.getValue(); + assertNotNull(sentMessage); + + Message responseMessage = Message.obtain(); + responseMessage.what = WifiScanner.CMD_SCAN_RESULT; + responseMessage.obj = mParcelableScanData; + responseMessage.arg2 = sentMessage.arg2; + scannerMessenger.send(responseMessage); + mLooper.dispatchAll(); + + verify(mExecutor, never()).execute(any()); + verify(mScanListener, never()).onResults(mScanData); + } + + /** + * Tests isFullBandScan() method with and without DFS check + */ + @Test + public void testIsFullBandScan() throws Exception { + assertFalse(WifiScanner.isFullBandScan(WifiScanner.WIFI_BAND_24_GHZ, true)); + assertFalse(WifiScanner.isFullBandScan(WifiScanner.WIFI_BAND_5_GHZ, true)); + assertFalse(WifiScanner.isFullBandScan(WifiScanner.WIFI_BAND_6_GHZ, true)); + assertFalse(WifiScanner.isFullBandScan( + WifiScanner.WIFI_BAND_6_GHZ | WifiScanner.WIFI_BAND_5_GHZ, true)); + assertTrue(WifiScanner.isFullBandScan( + WifiScanner.WIFI_BAND_24_GHZ | WifiScanner.WIFI_BAND_5_GHZ, true)); + assertFalse(WifiScanner.isFullBandScan( + WifiScanner.WIFI_BAND_24_GHZ | WifiScanner.WIFI_BAND_5_GHZ, false)); + assertTrue(WifiScanner.isFullBandScan(WifiScanner.WIFI_BAND_BOTH_WITH_DFS, true)); + assertTrue(WifiScanner.isFullBandScan(WifiScanner.WIFI_BAND_BOTH_WITH_DFS, false)); + } } diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java index 1b48a67e9f55..b65de6b9789d 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java @@ -171,6 +171,6 @@ public class WifiAwareAgentNetworkSpecifierTest { WifiAwareNetworkSpecifier getMockNetworkSpecifier(int clientId) { return new WifiAwareNetworkSpecifier(WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB, WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, clientId, 0, 0, new byte[6], - null, null, 10, 5, 0); + null, null, 10, 5); } } diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index 7bc5f6260816..43d728bf593e 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -36,6 +36,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.net.MacAddress; import android.net.wifi.RttManager; +import android.net.wifi.util.HexEncoding; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -44,8 +45,6 @@ import android.os.test.TestLooper; import androidx.test.filters.SmallTest; -import libcore.util.HexEncoding; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -168,7 +167,7 @@ public class WifiAwareManagerTest { // (1) connect + success mDut.attach(mockCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).connect(binder.capture(), any(), + inOrder.verify(mockAwareService).connect(binder.capture(), any(), any(), clientProxyCallback.capture(), isNull(), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); @@ -178,7 +177,8 @@ public class WifiAwareManagerTest { // (2) publish - should succeed PublishConfig publishConfig = new PublishConfig.Builder().build(); session.publish(publishConfig, mockSessionCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).publish(any(), eq(clientId), eq(publishConfig), any()); + inOrder.verify(mockAwareService).publish(any(), any(), eq(clientId), eq(publishConfig), + any()); // (3) disconnect session.close(); @@ -190,7 +190,7 @@ public class WifiAwareManagerTest { // (5) connect mDut.attach(mockCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).connect(binder.capture(), any(), any(), isNull(), + inOrder.verify(mockAwareService).connect(binder.capture(), any(), any(), any(), isNull(), eq(false)); verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService); @@ -212,7 +212,7 @@ public class WifiAwareManagerTest { // (1) connect + failure mDut.attach(mockCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + inOrder.verify(mockAwareService).connect(any(), any(), any(), clientProxyCallback.capture(), isNull(), eq(false)); clientProxyCallback.getValue().onConnectFail(reason); mMockLooper.dispatchAll(); @@ -220,7 +220,7 @@ public class WifiAwareManagerTest { // (2) connect + success mDut.attach(mockCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + inOrder.verify(mockAwareService).connect(any(), any(), any(), clientProxyCallback.capture(), isNull(), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); @@ -230,7 +230,8 @@ public class WifiAwareManagerTest { // (4) subscribe: should succeed SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).subscribe(any(), eq(clientId), eq(subscribeConfig), any()); + inOrder.verify(mockAwareService).subscribe(any(), any(), eq(clientId), eq(subscribeConfig), + any()); verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService); } @@ -249,7 +250,7 @@ public class WifiAwareManagerTest { // (1) connect + success mDut.attach(mockCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + inOrder.verify(mockAwareService).connect(any(), any(), any(), clientProxyCallback.capture(), isNull(), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); @@ -257,7 +258,7 @@ public class WifiAwareManagerTest { // (2) connect + success mDut.attach(mockCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + inOrder.verify(mockAwareService).connect(any(), any(), any(), clientProxyCallback.capture(), isNull(), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId + 1); mMockLooper.dispatchAll(); @@ -304,7 +305,7 @@ public class WifiAwareManagerTest { // (0) connect + success mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + inOrder.verify(mockAwareService).connect(any(), any(), any(), clientProxyCallback.capture(), eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); @@ -313,7 +314,7 @@ public class WifiAwareManagerTest { // (1) publish session.publish(publishConfig, mockSessionCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).publish(any(), eq(clientId), eq(publishConfig), + inOrder.verify(mockAwareService).publish(any(), any(), eq(clientId), eq(publishConfig), sessionProxyCallback.capture()); // (2) publish session created @@ -396,7 +397,7 @@ public class WifiAwareManagerTest { // (1) connect successfully mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + inOrder.verify(mockAwareService).connect(any(), any(), any(), clientProxyCallback.capture(), eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); @@ -405,7 +406,7 @@ public class WifiAwareManagerTest { // (2) publish: successfully - then terminated session.publish(publishConfig, mockSessionCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).publish(any(), eq(clientId), eq(publishConfig), + inOrder.verify(mockAwareService).publish(any(), any(), eq(clientId), eq(publishConfig), sessionProxyCallback.capture()); sessionProxyCallback.getValue().onSessionStarted(sessionId); sessionProxyCallback.getValue().onSessionTerminated(0); @@ -453,7 +454,7 @@ public class WifiAwareManagerTest { // (0) connect + success mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + inOrder.verify(mockAwareService).connect(any(), any(), any(), clientProxyCallback.capture(), eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); @@ -462,7 +463,7 @@ public class WifiAwareManagerTest { // (1) subscribe session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).subscribe(any(), eq(clientId), eq(subscribeConfig), + inOrder.verify(mockAwareService).subscribe(any(), any(), eq(clientId), eq(subscribeConfig), sessionProxyCallback.capture()); // (2) subscribe session created @@ -538,7 +539,7 @@ public class WifiAwareManagerTest { // (1) connect successfully mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + inOrder.verify(mockAwareService).connect(any(), any(), any(), clientProxyCallback.capture(), eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); @@ -547,7 +548,7 @@ public class WifiAwareManagerTest { // (2) subscribe: successfully - then terminated session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).subscribe(any(), eq(clientId), eq(subscribeConfig), + inOrder.verify(mockAwareService).subscribe(any(), any(), eq(clientId), eq(subscribeConfig), sessionProxyCallback.capture()); sessionProxyCallback.getValue().onSessionStarted(sessionId); sessionProxyCallback.getValue().onSessionTerminated(0); @@ -577,12 +578,15 @@ public class WifiAwareManagerTest { collector.checkThat("mMasterPreference", 0, equalTo(configRequest.mMasterPreference)); collector.checkThat("mSupport5gBand", true, equalTo(configRequest.mSupport5gBand)); - collector.checkThat("mDiscoveryWindowInterval.length", 2, + collector.checkThat("mSupport6gBand", false, equalTo(configRequest.mSupport6gBand)); + collector.checkThat("mDiscoveryWindowInterval.length", 3, equalTo(configRequest.mDiscoveryWindowInterval.length)); collector.checkThat("mDiscoveryWindowInterval[2.4GHz]", ConfigRequest.DW_INTERVAL_NOT_INIT, equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ])); collector.checkThat("mDiscoveryWindowInterval[5Hz]", ConfigRequest.DW_INTERVAL_NOT_INIT, equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ])); + collector.checkThat("mDiscoveryWindowInterval[6Hz]", ConfigRequest.DW_INTERVAL_NOT_INIT, + equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_6GHZ])); } @Test @@ -591,12 +595,16 @@ public class WifiAwareManagerTest { final int clusterLow = 5; final int masterPreference = 55; final boolean supportBand5g = true; + final boolean supportBand6g = true; final int dwWindow5GHz = 3; + final int dwWindow6GHz = 4; ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh) .setClusterLow(clusterLow).setMasterPreference(masterPreference) .setSupport5gBand(supportBand5g) + .setSupport6gBand(supportBand6g) .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, dwWindow5GHz) + .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_6GHZ, dwWindow6GHz) .build(); collector.checkThat("mClusterHigh", clusterHigh, equalTo(configRequest.mClusterHigh)); @@ -604,12 +612,15 @@ public class WifiAwareManagerTest { collector.checkThat("mMasterPreference", masterPreference, equalTo(configRequest.mMasterPreference)); collector.checkThat("mSupport5gBand", supportBand5g, equalTo(configRequest.mSupport5gBand)); - collector.checkThat("mDiscoveryWindowInterval.length", 2, + collector.checkThat("mSupport6gBand", supportBand6g, equalTo(configRequest.mSupport6gBand)); + collector.checkThat("mDiscoveryWindowInterval.length", 3, equalTo(configRequest.mDiscoveryWindowInterval.length)); collector.checkThat("mDiscoveryWindowInterval[2.4GHz]", ConfigRequest.DW_INTERVAL_NOT_INIT, equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ])); collector.checkThat("mDiscoveryWindowInterval[5GHz]", dwWindow5GHz, equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ])); + collector.checkThat("mDiscoveryWindowInterval[6GHz]", dwWindow6GHz, + equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_6GHZ])); } @Test(expected = IllegalArgumentException.class) @@ -688,14 +699,18 @@ public class WifiAwareManagerTest { final int clusterLow = 25; final int masterPreference = 177; final boolean supportBand5g = true; + final boolean supportBand6g = false; final int dwWindow24GHz = 1; final int dwWindow5GHz = 5; + final int dwWindow6GHz = 4; ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh) .setClusterLow(clusterLow).setMasterPreference(masterPreference) .setSupport5gBand(supportBand5g) + .setSupport6gBand(supportBand6g) .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, dwWindow24GHz) .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, dwWindow5GHz) + .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_6GHZ, dwWindow6GHz) .build(); Parcel parcelW = Parcel.obtain(); @@ -942,7 +957,7 @@ public class WifiAwareManagerTest { // (1) connect successfully mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + inOrder.verify(mockAwareService).connect(any(), any(), any(), clientProxyCallback.capture(), eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); @@ -951,7 +966,7 @@ public class WifiAwareManagerTest { // (2) publish successfully session.publish(publishConfig, mockSessionCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).publish(any(), eq(clientId), eq(publishConfig), + inOrder.verify(mockAwareService).publish(any(), any(), eq(clientId), eq(publishConfig), sessionProxyCallback.capture()); sessionProxyCallback.getValue().onSessionStarted(sessionId); mMockLooper.dispatchAll(); @@ -1055,7 +1070,7 @@ public class WifiAwareManagerTest { // (1) connect successfully mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + inOrder.verify(mockAwareService).connect(any(), any(), any(), clientProxyCallback.capture(), eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); @@ -1239,7 +1254,7 @@ public class WifiAwareManagerTest { // (1) connect successfully mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + inOrder.verify(mockAwareService).connect(any(), any(), any(), clientProxyCallback.capture(), eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); @@ -1248,7 +1263,7 @@ public class WifiAwareManagerTest { // (2) publish successfully session.publish(publishConfig, mockSessionCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).publish(any(), eq(clientId), eq(publishConfig), + inOrder.verify(mockAwareService).publish(any(), any(), eq(clientId), eq(publishConfig), sessionProxyCallback.capture()); sessionProxyCallback.getValue().onSessionStarted(sessionId); mMockLooper.dispatchAll(); @@ -1474,7 +1489,7 @@ public class WifiAwareManagerTest { // (1) connect successfully mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + inOrder.verify(mockAwareService).connect(any(), any(), any(), clientProxyCallback.capture(), eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); @@ -1514,7 +1529,7 @@ public class WifiAwareManagerTest { // (1) connect successfully mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + inOrder.verify(mockAwareService).connect(any(), any(), any(), clientProxyCallback.capture(), eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); @@ -1524,7 +1539,7 @@ public class WifiAwareManagerTest { if (isPublish) { // (2) publish successfully session.publish(publishConfig, mockSessionCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).publish(any(), eq(clientId), eq(publishConfig), + inOrder.verify(mockAwareService).publish(any(), any(), eq(clientId), eq(publishConfig), sessionProxyCallback.capture()); sessionProxyCallback.getValue().onSessionStarted(sessionId); mMockLooper.dispatchAll(); @@ -1533,8 +1548,8 @@ public class WifiAwareManagerTest { } else { // (2) subscribe successfully session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).subscribe(any(), eq(clientId), eq(subscribeConfig), - sessionProxyCallback.capture()); + inOrder.verify(mockAwareService).subscribe(any(), any(), eq(clientId), + eq(subscribeConfig), sessionProxyCallback.capture()); sessionProxyCallback.getValue().onSessionStarted(sessionId); mMockLooper.dispatchAll(); inOrder.verify(mockSessionCallback).onSubscribeStarted(subscribeSession.capture()); @@ -1549,7 +1564,7 @@ public class WifiAwareManagerTest { WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier(NETWORK_SPECIFIER_TYPE_IB, WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, 5, 568, 334, HexEncoding.decode("000102030405".toCharArray(), false), - "01234567890123456789012345678901".getBytes(), "blah blah", 666, 4, 10001); + "01234567890123456789012345678901".getBytes(), "blah blah", 666, 4); Parcel parcelW = Parcel.obtain(); ns.writeToParcel(parcelW, 0); diff --git a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java index d9a1d9afff61..439e67259cb9 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java @@ -54,6 +54,8 @@ public class ConfigParserTest { "assets/hsr1/HSR1ProfileWithInvalidContentType.base64"; private static final String PASSPOINT_INSTALLATION_FILE_WITHOUT_PROFILE = "assets/hsr1/HSR1ProfileWithoutProfile.base64"; + private static final String PASSPOINT_INSTALLATION_FILE_WITH_UPDATE_ID = + "assets/hsr1/HSR1ProfileWithUpdateIdentifier.base64"; /** * Read the content of the given resource file into a String. @@ -85,17 +87,17 @@ public class ConfigParserTest { // HomeSP configuration. HomeSp homeSp = new HomeSp(); - homeSp.setFriendlyName("Century House"); - homeSp.setFqdn("mi6.co.uk"); + homeSp.setFriendlyName("Example Network"); + homeSp.setFqdn("hotspot.example.net"); homeSp.setRoamingConsortiumOis(new long[] {0x112233L, 0x445566L}); config.setHomeSp(homeSp); // Credential configuration. Credential credential = new Credential(); - credential.setRealm("shaken.stirred.com"); + credential.setRealm("example.com"); Credential.UserCredential userCredential = new Credential.UserCredential(); - userCredential.setUsername("james"); - userCredential.setPassword("Ym9uZDAwNw=="); + userCredential.setUsername("user"); + userCredential.setPassword("cGFzc3dvcmQ="); userCredential.setEapType(21); userCredential.setNonEapInnerMethod("MS-CHAP-V2"); credential.setUserCredential(userCredential); @@ -106,8 +108,8 @@ public class ConfigParserTest { certCredential.setCertSha256Fingerprint(certSha256Fingerprint); credential.setCertCredential(certCredential); Credential.SimCredential simCredential = new Credential.SimCredential(); - simCredential.setImsi("imsi"); - simCredential.setEapType(24); + simCredential.setImsi("123456*"); + simCredential.setEapType(23); credential.setSimCredential(simCredential); credential.setCaCertificate(FakeKeys.CA_CERT0); config.setCredential(credential); @@ -201,4 +203,21 @@ public class ConfigParserTest { assertNull(ConfigParser.parsePasspointConfig( "application/x-wifi-config", configStr.getBytes())); } + + /** + * Verify a valid installation file is parsed successfully with the matching contents, and that + * Update identifier is cleared. + * + * @throws Exception + */ + @Test + public void parseConfigFileWithUpdateIdentifier() throws Exception { + String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_UPDATE_ID); + PasspointConfiguration expectedConfig = generateConfigurationFromProfile(); + PasspointConfiguration actualConfig = + ConfigParser.parsePasspointConfig( + "application/x-wifi-config", configStr.getBytes()); + // Expected configuration does not contain an update identifier + assertTrue(actualConfig.equals(expectedConfig)); + } }
\ No newline at end of file diff --git a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java index c7e009ee9864..2ded849331d7 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java @@ -19,7 +19,6 @@ package android.net.wifi.hotspot2; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import android.graphics.drawable.Icon; import android.net.Uri; import android.net.wifi.WifiSsid; import android.os.Parcel; @@ -56,7 +55,6 @@ public class OsuProviderTest { private static final String TEST_NAI = "test.access.com"; private static final List<Integer> TEST_METHOD_LIST = Arrays.asList(OsuProvider.METHOD_SOAP_XML_SPP); - private static final Icon TEST_ICON = Icon.createWithData(new byte[10], 0, 10); /** * Verify parcel write and read consistency for the given {@link OsuProvider}. @@ -82,7 +80,7 @@ public class OsuProviderTest { */ @Test public void verifyParcelWithEmptyProviderInfo() throws Exception { - verifyParcel(new OsuProvider(null, null, null, null, null, null, null)); + verifyParcel(new OsuProvider((WifiSsid) null, null, null, null, null, null)); } /** @@ -93,7 +91,7 @@ public class OsuProviderTest { @Test public void verifyParcelWithFullProviderInfo() throws Exception { verifyParcel(new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES, - TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON)); + TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST)); } /** @@ -102,7 +100,7 @@ public class OsuProviderTest { */ @Test public void verifyCopyConstructorWithNullSource() throws Exception { - OsuProvider expected = new OsuProvider(null, null, null, null, null, null, null); + OsuProvider expected = new OsuProvider((WifiSsid) null, null, null, null, null, null); assertEquals(expected, new OsuProvider(null)); } @@ -114,7 +112,7 @@ public class OsuProviderTest { @Test public void verifyCopyConstructorWithValidSource() throws Exception { OsuProvider source = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES, - TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON); + TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST); assertEquals(source, new OsuProvider(source)); } @@ -126,7 +124,7 @@ public class OsuProviderTest { @Test public void verifyGetters() throws Exception { OsuProvider provider = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES, - TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON); + TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST); assertTrue(TEST_SSID.equals(provider.getOsuSsid())); assertTrue(TEST_FRIENDLY_NAME.equals(provider.getFriendlyName())); @@ -135,6 +133,5 @@ public class OsuProviderTest { assertTrue(TEST_SERVER_URI.equals(provider.getServerUri())); assertTrue(TEST_NAI.equals(provider.getNetworkAccessIdentifier())); assertTrue(TEST_METHOD_LIST.equals(provider.getMethodList())); - assertTrue(TEST_ICON.sameAs(provider.getIcon())); } } diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java index fc03e7eb6176..638efb9f14ee 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java @@ -16,26 +16,24 @@ package android.net.wifi.hotspot2; +import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE; + +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; -import android.net.wifi.EAPConstants; import android.net.wifi.hotspot2.pps.Credential; import android.net.wifi.hotspot2.pps.HomeSp; -import android.net.wifi.hotspot2.pps.Policy; -import android.net.wifi.hotspot2.pps.UpdateParameter; import android.os.Parcel; -import android.util.Base64; import androidx.test.filters.SmallTest; import org.junit.Test; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -47,134 +45,6 @@ public class PasspointConfigurationTest { private static final int CERTIFICATE_FINGERPRINT_BYTES = 32; /** - * Utility function for creating a {@link android.net.wifi.hotspot2.pps.HomeSP}. - * - * @return {@link android.net.wifi.hotspot2.pps.HomeSP} - */ - private static HomeSp createHomeSp() { - HomeSp homeSp = new HomeSp(); - homeSp.setFqdn("fqdn"); - homeSp.setFriendlyName("friendly name"); - homeSp.setRoamingConsortiumOis(new long[] {0x55, 0x66}); - return homeSp; - } - - /** - * Utility function for creating a {@link android.net.wifi.hotspot2.pps.Credential}. - * - * @return {@link android.net.wifi.hotspot2.pps.Credential} - */ - private static Credential createCredential() { - Credential cred = new Credential(); - cred.setRealm("realm"); - cred.setUserCredential(null); - cred.setCertCredential(null); - cred.setSimCredential(new Credential.SimCredential()); - cred.getSimCredential().setImsi("1234*"); - cred.getSimCredential().setEapType(EAPConstants.EAP_SIM); - cred.setCaCertificate(null); - cred.setClientCertificateChain(null); - cred.setClientPrivateKey(null); - return cred; - } - - /** - * Helper function for creating a {@link Policy} for testing. - * - * @return {@link Policy} - */ - private static Policy createPolicy() { - Policy policy = new Policy(); - policy.setMinHomeDownlinkBandwidth(123); - policy.setMinHomeUplinkBandwidth(345); - policy.setMinRoamingDownlinkBandwidth(567); - policy.setMinRoamingUplinkBandwidth(789); - policy.setMaximumBssLoadValue(12); - policy.setExcludedSsidList(new String[] {"ssid1", "ssid2"}); - HashMap<Integer, String> requiredProtoPortMap = new HashMap<>(); - requiredProtoPortMap.put(12, "23,342,123"); - requiredProtoPortMap.put(23, "789,372,1235"); - policy.setRequiredProtoPortMap(requiredProtoPortMap); - - List<Policy.RoamingPartner> preferredRoamingPartnerList = new ArrayList<>(); - Policy.RoamingPartner partner1 = new Policy.RoamingPartner(); - partner1.setFqdn("partner1.com"); - partner1.setFqdnExactMatch(true); - partner1.setPriority(12); - partner1.setCountries("us,jp"); - Policy.RoamingPartner partner2 = new Policy.RoamingPartner(); - partner2.setFqdn("partner2.com"); - partner2.setFqdnExactMatch(false); - partner2.setPriority(42); - partner2.setCountries("ca,fr"); - preferredRoamingPartnerList.add(partner1); - preferredRoamingPartnerList.add(partner2); - policy.setPreferredRoamingPartnerList(preferredRoamingPartnerList); - - UpdateParameter policyUpdate = new UpdateParameter(); - policyUpdate.setUpdateIntervalInMinutes(1712); - policyUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_OMADM); - policyUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_HOMESP); - policyUpdate.setServerUri("policy.update.com"); - policyUpdate.setUsername("username"); - policyUpdate.setBase64EncodedPassword( - Base64.encodeToString("password".getBytes(), Base64.DEFAULT)); - policyUpdate.setTrustRootCertUrl("trust.cert.com"); - policyUpdate.setTrustRootCertSha256Fingerprint( - new byte[CERTIFICATE_FINGERPRINT_BYTES]); - policy.setPolicyUpdate(policyUpdate); - - return policy; - } - - private static UpdateParameter createSubscriptionUpdate() { - UpdateParameter subUpdate = new UpdateParameter(); - subUpdate.setUpdateIntervalInMinutes(9021); - subUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_SSP); - subUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_ROAMING_PARTNER); - subUpdate.setServerUri("subscription.update.com"); - subUpdate.setUsername("subUsername"); - subUpdate.setBase64EncodedPassword( - Base64.encodeToString("subPassword".getBytes(), Base64.DEFAULT)); - subUpdate.setTrustRootCertUrl("subscription.trust.cert.com"); - subUpdate.setTrustRootCertSha256Fingerprint(new byte[CERTIFICATE_FINGERPRINT_BYTES]); - return subUpdate; - } - /** - * Helper function for creating a {@link PasspointConfiguration} for testing. - * - * @return {@link PasspointConfiguration} - */ - private static PasspointConfiguration createConfig() { - PasspointConfiguration config = new PasspointConfiguration(); - config.setUpdateIdentifier(1234); - config.setHomeSp(createHomeSp()); - config.setCredential(createCredential()); - config.setPolicy(createPolicy()); - config.setSubscriptionUpdate(createSubscriptionUpdate()); - Map<String, byte[]> trustRootCertList = new HashMap<>(); - trustRootCertList.put("trustRoot.cert1.com", - new byte[CERTIFICATE_FINGERPRINT_BYTES]); - trustRootCertList.put("trustRoot.cert2.com", - new byte[CERTIFICATE_FINGERPRINT_BYTES]); - config.setTrustRootCertList(trustRootCertList); - config.setUpdateIdentifier(1); - config.setCredentialPriority(120); - config.setSubscriptionCreationTimeInMillis(231200); - config.setSubscriptionExpirationTimeInMillis(2134232); - config.setSubscriptionType("Gold"); - config.setUsageLimitUsageTimePeriodInMinutes(3600); - config.setUsageLimitStartTimeInMillis(124214213); - config.setUsageLimitDataLimit(14121); - config.setUsageLimitTimeLimitInMinutes(78912); - Map<String, String> friendlyNames = new HashMap<>(); - friendlyNames.put("en", "ServiceName1"); - friendlyNames.put("kr", "ServiceName2"); - config.setServiceFriendlyNames(friendlyNames); - return config; - } - - /** * Verify parcel write and read consistency for the given configuration. * * @param writeConfig The configuration to verify @@ -207,7 +77,7 @@ public class PasspointConfigurationTest { */ @Test public void verifyParcelWithFullConfiguration() throws Exception { - verifyParcel(createConfig()); + verifyParcel(PasspointTestUtils.createConfig()); } /** @@ -217,7 +87,7 @@ public class PasspointConfigurationTest { */ @Test public void verifyParcelWithoutServiceNames() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); config.setServiceFriendlyNames(null); verifyParcel(config); } @@ -229,7 +99,7 @@ public class PasspointConfigurationTest { */ @Test public void verifyParcelWithoutHomeSP() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); config.setHomeSp(null); verifyParcel(config); } @@ -241,7 +111,7 @@ public class PasspointConfigurationTest { */ @Test public void verifyParcelWithoutCredential() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); config.setCredential(null); verifyParcel(config); } @@ -253,7 +123,7 @@ public class PasspointConfigurationTest { */ @Test public void verifyParcelWithoutPolicy() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); config.setPolicy(null); verifyParcel(config); } @@ -265,7 +135,7 @@ public class PasspointConfigurationTest { */ @Test public void verifyParcelWithoutSubscriptionUpdate() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); config.setSubscriptionUpdate(null); verifyParcel(config); } @@ -278,12 +148,25 @@ public class PasspointConfigurationTest { */ @Test public void verifyParcelWithoutTrustRootCertList() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); config.setTrustRootCertList(null); verifyParcel(config); } /** + * Verify parcel read/write for a configuration that doesn't contain AAA server trusted names + * list. + * + * @throws Exception + */ + @Test + public void verifyParcelWithoutAaaServerTrustedNames() throws Exception { + PasspointConfiguration config = PasspointTestUtils.createConfig(); + config.setAaaServerTrustedNames(null); + verifyParcel(config); + } + + /** * Verify that a default/empty configuration is invalid. * * @throws Exception @@ -294,6 +177,9 @@ public class PasspointConfigurationTest { assertFalse(config.validate()); assertFalse(config.validateForR2()); + assertTrue(config.isAutojoinEnabled()); + assertTrue(config.isMacRandomizationEnabled()); + assertTrue(config.getMeteredOverride() == METERED_OVERRIDE_NONE); } /** @@ -303,10 +189,10 @@ public class PasspointConfigurationTest { */ @Test public void validateFullConfig() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); assertTrue(config.validate()); - assertTrue(config.validateForR2()); + assertFalse(config.isOsuProvisioned()); } /** @@ -317,7 +203,7 @@ public class PasspointConfigurationTest { */ @Test public void validateFullConfigWithoutUpdateIdentifier() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); config.setUpdateIdentifier(Integer.MIN_VALUE); assertTrue(config.validate()); @@ -331,7 +217,7 @@ public class PasspointConfigurationTest { */ @Test public void validateConfigWithoutCredential() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); config.setCredential(null); assertFalse(config.validate()); @@ -345,7 +231,7 @@ public class PasspointConfigurationTest { */ @Test public void validateConfigWithoutHomeSp() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); config.setHomeSp(null); assertFalse(config.validate()); @@ -360,11 +246,10 @@ public class PasspointConfigurationTest { */ @Test public void validateConfigWithoutPolicy() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); config.setPolicy(null); assertTrue(config.validate()); - assertTrue(config.validateForR2()); } /** @@ -375,7 +260,7 @@ public class PasspointConfigurationTest { */ @Test public void validateConfigWithoutSubscriptionUpdate() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); config.setSubscriptionUpdate(null); assertTrue(config.validate()); @@ -383,6 +268,20 @@ public class PasspointConfigurationTest { } /** + * Verify that a configuration without AAA server trusted names is valid for R1 and R2, + * since AAA server trusted names are optional for R1 and R2. + * + * @throws Exception + */ + @Test + public void validateConfigWithoutAaaServerTrustedNames() throws Exception { + PasspointConfiguration config = PasspointTestUtils.createConfig(); + config.setAaaServerTrustedNames(null); + + assertTrue(config.validate()); + } + + /** * Verify that a configuration with a trust root certificate URL exceeding the max size * is invalid. * @@ -390,7 +289,7 @@ public class PasspointConfigurationTest { */ @Test public void validateConfigWithInvalidTrustRootCertUrl() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); byte[] rawUrlBytes = new byte[MAX_URL_BYTES + 1]; Map<String, byte[]> trustRootCertList = new HashMap<>(); Arrays.fill(rawUrlBytes, (byte) 'a'); @@ -415,7 +314,7 @@ public class PasspointConfigurationTest { */ @Test public void validateConfigWithInvalidTrustRootCertFingerprint() throws Exception { - PasspointConfiguration config = createConfig(); + PasspointConfiguration config = PasspointTestUtils.createConfig(); Map<String, byte[]> trustRootCertList = new HashMap<>(); trustRootCertList.put("test.cert.com", new byte[CERTIFICATE_FINGERPRINT_BYTES + 1]); config.setTrustRootCertList(trustRootCertList); @@ -452,8 +351,98 @@ public class PasspointConfigurationTest { */ @Test public void validateCopyConstructorWithValidSource() throws Exception { - PasspointConfiguration sourceConfig = createConfig(); + PasspointConfiguration sourceConfig = PasspointTestUtils.createConfig(); PasspointConfiguration copyConfig = new PasspointConfiguration(sourceConfig); assertTrue(copyConfig.equals(sourceConfig)); } + + /** + * Verify that a configuration containing all fields is valid for R2. + * + * @throws Exception + */ + @Test + public void validateFullR2Config() throws Exception { + PasspointConfiguration config = PasspointTestUtils.createR2Config(); + assertTrue(config.validate()); + assertTrue(config.validateForR2()); + assertTrue(config.isOsuProvisioned()); + } + + /** + * Verify that the unique identifier generated is identical for two instances + * + * @throws Exception + */ + @Test + public void validateUniqueId() throws Exception { + PasspointConfiguration config1 = PasspointTestUtils.createConfig(); + PasspointConfiguration config2 = PasspointTestUtils.createConfig(); + + assertEquals(config1.getUniqueId(), config2.getUniqueId()); + } + + /** + * Verify that the unique identifier generated is different for two instances with different + * HomeSp node + * + * @throws Exception + */ + @Test + public void validateUniqueIdDifferentHomeSp() throws Exception { + PasspointConfiguration config1 = PasspointTestUtils.createConfig(); + + // Modify config2's RCOIs to a different set of values + PasspointConfiguration config2 = PasspointTestUtils.createConfig(); + HomeSp homeSp = config2.getHomeSp(); + homeSp.setRoamingConsortiumOis(new long[] {0xaa, 0xbb}); + config2.setHomeSp(homeSp); + + assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); + } + + /** + * Verify that the unique identifier generated is different for two instances with different + * Credential node + * + * @throws Exception + */ + @Test + public void validateUniqueIdDifferentCredential() throws Exception { + PasspointConfiguration config1 = PasspointTestUtils.createConfig(); + + // Modify config2's RCOIs to a different set of values + PasspointConfiguration config2 = PasspointTestUtils.createConfig(); + Credential credential = config2.getCredential(); + credential.setRealm("realm2.example.com"); + credential.getSimCredential().setImsi("350460*"); + config2.setCredential(credential); + + assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); + } + + /** + * Verify that the unique identifier API generates an exception if HomeSP is not initialized. + * + * @throws Exception + */ + @Test (expected = IllegalStateException.class) + public void validateUniqueIdExceptionWithEmptyHomeSp() throws Exception { + PasspointConfiguration config = PasspointTestUtils.createConfig(); + config.setHomeSp(null); + String uniqueId = config.getUniqueId(); + } + + /** + * Verify that the unique identifier API generates an exception if Credential is not + * initialized. + * + * @throws Exception + */ + @Test (expected = IllegalStateException.class) + public void validateUniqueIdExceptionWithEmptyCredential() throws Exception { + PasspointConfiguration config = PasspointTestUtils.createConfig(); + config.setCredential(null); + String uniqueId = config.getUniqueId(); + } } diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java new file mode 100644 index 000000000000..8d55acb87f15 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2019 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.hotspot2; + +import android.net.wifi.EAPConstants; +import android.net.wifi.hotspot2.pps.Credential; +import android.net.wifi.hotspot2.pps.HomeSp; +import android.net.wifi.hotspot2.pps.Policy; +import android.net.wifi.hotspot2.pps.UpdateParameter; +import android.util.Base64; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PasspointTestUtils { + private static final int CERTIFICATE_FINGERPRINT_BYTES = 32; + + /** + * Utility function for creating a {@link android.net.wifi.hotspot2.pps.HomeSP}. + * + * @return {@link android.net.wifi.hotspot2.pps.HomeSP} + */ + private static HomeSp createHomeSp() { + HomeSp homeSp = new HomeSp(); + homeSp.setFqdn("fqdn"); + homeSp.setFriendlyName("friendly name"); + homeSp.setRoamingConsortiumOis(new long[] {0x55, 0x66}); + return homeSp; + } + + /** + * Utility function for creating a {@link android.net.wifi.hotspot2.pps.Credential}. + * + * @return {@link android.net.wifi.hotspot2.pps.Credential} + */ + private static Credential createCredential() { + Credential cred = new Credential(); + cred.setRealm("realm"); + cred.setUserCredential(null); + cred.setCertCredential(null); + cred.setSimCredential(new Credential.SimCredential()); + cred.getSimCredential().setImsi("1234*"); + cred.getSimCredential().setEapType(EAPConstants.EAP_SIM); + cred.setCaCertificate(null); + cred.setClientCertificateChain(null); + cred.setClientPrivateKey(null); + return cred; + } + + /** + * Helper function for creating a {@link Policy} for testing. + * + * @return {@link Policy} + */ + private static Policy createPolicy() { + Policy policy = new Policy(); + policy.setMinHomeDownlinkBandwidth(123); + policy.setMinHomeUplinkBandwidth(345); + policy.setMinRoamingDownlinkBandwidth(567); + policy.setMinRoamingUplinkBandwidth(789); + policy.setMaximumBssLoadValue(12); + policy.setExcludedSsidList(new String[] {"ssid1", "ssid2"}); + HashMap<Integer, String> requiredProtoPortMap = new HashMap<>(); + requiredProtoPortMap.put(12, "23,342,123"); + requiredProtoPortMap.put(23, "789,372,1235"); + policy.setRequiredProtoPortMap(requiredProtoPortMap); + + List<Policy.RoamingPartner> preferredRoamingPartnerList = new ArrayList<>(); + Policy.RoamingPartner partner1 = new Policy.RoamingPartner(); + partner1.setFqdn("partner1.com"); + partner1.setFqdnExactMatch(true); + partner1.setPriority(12); + partner1.setCountries("us,jp"); + Policy.RoamingPartner partner2 = new Policy.RoamingPartner(); + partner2.setFqdn("partner2.com"); + partner2.setFqdnExactMatch(false); + partner2.setPriority(42); + partner2.setCountries("ca,fr"); + preferredRoamingPartnerList.add(partner1); + preferredRoamingPartnerList.add(partner2); + policy.setPreferredRoamingPartnerList(preferredRoamingPartnerList); + + UpdateParameter policyUpdate = new UpdateParameter(); + policyUpdate.setUpdateIntervalInMinutes(1712); + policyUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_OMADM); + policyUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_HOMESP); + policyUpdate.setServerUri("policy.update.com"); + policyUpdate.setUsername("username"); + policyUpdate.setBase64EncodedPassword( + Base64.encodeToString("password".getBytes(), Base64.DEFAULT)); + policyUpdate.setTrustRootCertUrl("trust.cert.com"); + policyUpdate.setTrustRootCertSha256Fingerprint( + new byte[CERTIFICATE_FINGERPRINT_BYTES]); + policy.setPolicyUpdate(policyUpdate); + + return policy; + } + + private static UpdateParameter createSubscriptionUpdate() { + UpdateParameter subUpdate = new UpdateParameter(); + subUpdate.setUpdateIntervalInMinutes(9021); + subUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_SSP); + subUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_ROAMING_PARTNER); + subUpdate.setServerUri("subscription.update.com"); + subUpdate.setUsername("subUsername"); + subUpdate.setBase64EncodedPassword( + Base64.encodeToString("subPassword".getBytes(), Base64.DEFAULT)); + subUpdate.setTrustRootCertUrl("subscription.trust.cert.com"); + subUpdate.setTrustRootCertSha256Fingerprint(new byte[CERTIFICATE_FINGERPRINT_BYTES]); + return subUpdate; + } + /** + * Helper function for creating a {@link PasspointConfiguration} for testing. + * + * @return {@link PasspointConfiguration} + */ + public static PasspointConfiguration createConfig() { + PasspointConfiguration config = new PasspointConfiguration(); + config.setHomeSp(createHomeSp()); + config.setAaaServerTrustedNames( + new String[] {"trusted.fqdn.com", "another-trusted.fqdn.com"}); + config.setCredential(createCredential()); + config.setPolicy(createPolicy()); + config.setSubscriptionUpdate(createSubscriptionUpdate()); + Map<String, byte[]> trustRootCertList = new HashMap<>(); + trustRootCertList.put("trustRoot.cert1.com", + new byte[CERTIFICATE_FINGERPRINT_BYTES]); + trustRootCertList.put("trustRoot.cert2.com", + new byte[CERTIFICATE_FINGERPRINT_BYTES]); + config.setTrustRootCertList(trustRootCertList); + config.setCredentialPriority(120); + config.setSubscriptionCreationTimeInMillis(231200); + config.setSubscriptionExpirationTimeInMillis(2134232); + config.setSubscriptionType("Gold"); + config.setUsageLimitUsageTimePeriodInMinutes(3600); + config.setUsageLimitStartTimeInMillis(124214213); + config.setUsageLimitDataLimit(14121); + config.setUsageLimitTimeLimitInMinutes(78912); + Map<String, String> friendlyNames = new HashMap<>(); + friendlyNames.put("en", "ServiceName1"); + friendlyNames.put("kr", "ServiceName2"); + config.setServiceFriendlyNames(friendlyNames); + return config; + } + + /** + * Helper function for creating an R2 {@link PasspointConfiguration} for testing. + * + * @return {@link PasspointConfiguration} + */ + public static PasspointConfiguration createR2Config() { + PasspointConfiguration config = createConfig(); + config.setUpdateIdentifier(1234); + return config; + } +} diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java index 66c595f92861..1ac9cb87c0e1 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java @@ -135,6 +135,9 @@ public class PpsMoParserTest { homeSp.setOtherHomePartners(new String[] {"other.fqdn.com"}); config.setHomeSp(homeSp); + config.setAaaServerTrustedNames( + new String[] {"trusted.fqdn.com", "another-trusted.fqdn.com"}); + // Credential configuration. Credential credential = new Credential(); credential.setCreationTimeInMillis(format.parse("2016-01-01T10:00:00Z").getTime()); diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java index 0a3e989d18f0..829d8f0a9a3a 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java @@ -18,6 +18,7 @@ package android.net.wifi.hotspot2.pps; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import android.net.wifi.EAPConstants; @@ -158,7 +159,7 @@ public class CredentialTest { } /** - * Verify parcel read/write for an user credential. + * Verify parcel read/write for a user credential. * * @throws Exception */ @@ -176,14 +177,14 @@ public class CredentialTest { Credential cred = createCredentialWithUserCredential(); // For R1 validation - assertTrue(cred.validate(true)); + assertTrue(cred.validate()); // For R2 validation - assertTrue(cred.validate(false)); + assertTrue(cred.validate()); } /** - * Verify that an user credential without CA Certificate is invalid. + * Verify that a user credential without CA Certificate is valid. * * @throws Exception */ @@ -192,15 +193,12 @@ public class CredentialTest { Credential cred = createCredentialWithUserCredential(); cred.setCaCertificate(null); - // For R1 validation - assertFalse(cred.validate(true)); - - // For R2 validation - assertTrue(cred.validate(false)); + // Accept a configuration with no CA certificate, the system will use the default cert store + assertTrue(cred.validate()); } /** - * Verify that an user credential with EAP type other than EAP-TTLS is invalid. + * Verify that a user credential with EAP type other than EAP-TTLS is invalid. * * @throws Exception */ @@ -210,15 +208,15 @@ public class CredentialTest { cred.getUserCredential().setEapType(EAPConstants.EAP_TLS); // For R1 validation - assertFalse(cred.validate(true)); + assertFalse(cred.validate()); // For R2 validation - assertFalse(cred.validate(false)); + assertFalse(cred.validate()); } /** - * Verify that an user credential without realm is invalid. + * Verify that a user credential without realm is invalid. * * @throws Exception */ @@ -228,14 +226,14 @@ public class CredentialTest { cred.setRealm(null); // For R1 validation - assertFalse(cred.validate(true)); + assertFalse(cred.validate()); // For R2 validation - assertFalse(cred.validate(false)); + assertFalse(cred.validate()); } /** - * Verify that an user credential without username is invalid. + * Verify that a user credential without username is invalid. * * @throws Exception */ @@ -245,14 +243,14 @@ public class CredentialTest { cred.getUserCredential().setUsername(null); // For R1 validation - assertFalse(cred.validate(true)); + assertFalse(cred.validate()); // For R2 validation - assertFalse(cred.validate(false)); + assertFalse(cred.validate()); } /** - * Verify that an user credential without password is invalid. + * Verify that a user credential without password is invalid. * * @throws Exception */ @@ -262,14 +260,14 @@ public class CredentialTest { cred.getUserCredential().setPassword(null); // For R1 validation - assertFalse(cred.validate(true)); + assertFalse(cred.validate()); // For R2 validation - assertFalse(cred.validate(false)); + assertFalse(cred.validate()); } /** - * Verify that an user credential without auth methoh (non-EAP inner method) is invalid. + * Verify that a user credential without auth methoh (non-EAP inner method) is invalid. * * @throws Exception */ @@ -279,10 +277,10 @@ public class CredentialTest { cred.getUserCredential().setNonEapInnerMethod(null); // For R1 validation - assertFalse(cred.validate(true)); + assertFalse(cred.validate()); // For R2 validation - assertFalse(cred.validate(false)); + assertFalse(cred.validate()); } /** @@ -297,10 +295,10 @@ public class CredentialTest { Credential cred = createCredentialWithCertificateCredential(); // For R1 validation - assertTrue(cred.validate(true)); + assertTrue(cred.validate()); // For R2 validation - assertTrue(cred.validate(true)); + assertTrue(cred.validate()); } /** @@ -313,11 +311,8 @@ public class CredentialTest { Credential cred = createCredentialWithCertificateCredential(); cred.setCaCertificate(null); - // For R1 validation - assertFalse(cred.validate(true)); - - // For R2 validation - assertTrue(cred.validate(false)); + // Accept a configuration with no CA certificate, the system will use the default cert store + assertTrue(cred.validate()); } /** @@ -331,10 +326,10 @@ public class CredentialTest { cred.setClientCertificateChain(null); // For R1 validation - assertFalse(cred.validate(true)); + assertFalse(cred.validate()); // For R2 validation - assertFalse(cred.validate(false)); + assertFalse(cred.validate()); } /** @@ -348,10 +343,10 @@ public class CredentialTest { cred.setClientPrivateKey(null); // For R1 validation - assertFalse(cred.validate(true)); + assertFalse(cred.validate()); // For R2 validation - assertFalse(cred.validate(false)); + assertFalse(cred.validate()); } /** @@ -366,10 +361,10 @@ public class CredentialTest { cred.getCertCredential().setCertSha256Fingerprint(new byte[32]); // For R1 validation - assertFalse(cred.validate(true)); + assertFalse(cred.validate()); // For R2 validation - assertFalse(cred.validate(false)); + assertFalse(cred.validate()); } /** @@ -382,10 +377,10 @@ public class CredentialTest { Credential cred = createCredentialWithSimCredential(); // For R1 validation - assertTrue(cred.validate(true)); + assertTrue(cred.validate()); // For R2 validation - assertTrue(cred.validate(false)); + assertTrue(cred.validate()); } /** @@ -399,10 +394,10 @@ public class CredentialTest { cred.getSimCredential().setEapType(EAPConstants.EAP_AKA); // For R1 validation - assertTrue(cred.validate(true)); + assertTrue(cred.validate()); // For R2 validation - assertTrue(cred.validate(false)); + assertTrue(cred.validate()); } /** @@ -416,10 +411,10 @@ public class CredentialTest { cred.getSimCredential().setEapType(EAPConstants.EAP_AKA_PRIME); // For R1 validation - assertTrue(cred.validate(true)); + assertTrue(cred.validate()); // For R2 validation - assertTrue(cred.validate(false)); + assertTrue(cred.validate()); } /** @@ -433,10 +428,10 @@ public class CredentialTest { cred.getSimCredential().setImsi(null); // For R1 validation - assertFalse(cred.validate(true)); + assertFalse(cred.validate()); // For R2 validation - assertFalse(cred.validate(false)); + assertFalse(cred.validate()); } /** @@ -450,10 +445,10 @@ public class CredentialTest { cred.getSimCredential().setImsi("dummy"); // For R1 validation - assertFalse(cred.validate(true)); + assertFalse(cred.validate()); // For R2 validation - assertFalse(cred.validate(false)); + assertFalse(cred.validate()); } /** @@ -467,14 +462,14 @@ public class CredentialTest { cred.getSimCredential().setEapType(EAPConstants.EAP_TLS); // For R1 validation - assertFalse(cred.validate(true)); + assertFalse(cred.validate()); // For R2 validation - assertFalse(cred.validate(false)); + assertFalse(cred.validate()); } /** - * Verify that a credential contained both an user and a SIM credential is invalid. + * Verify that a credential contained both a user and a SIM credential is invalid. * * @throws Exception */ @@ -488,10 +483,10 @@ public class CredentialTest { cred.setSimCredential(simCredential); // For R1 validation - assertFalse(cred.validate(true)); + assertFalse(cred.validate()); // For R2 validation - assertFalse(cred.validate(false)); + assertFalse(cred.validate()); } /** @@ -557,4 +552,68 @@ public class CredentialTest { public void validateTwoCertificateDifferent() { assertFalse(Credential.isX509CertificateEquals(FakeKeys.CA_CERT0, FakeKeys.CA_CERT1)); } + + /** + * Verify that unique identifiers are the same for objects with the same credentials + */ + @Test + public void testUniqueIdSameCredentialTypes() throws Exception { + assertEquals(createCredentialWithSimCredential().getUniqueId(), + createCredentialWithSimCredential().getUniqueId()); + assertEquals(createCredentialWithCertificateCredential().getUniqueId(), + createCredentialWithCertificateCredential().getUniqueId()); + assertEquals(createCredentialWithUserCredential().getUniqueId(), + createCredentialWithUserCredential().getUniqueId()); + } + + /** + * Verify that unique identifiers are different for each credential + */ + @Test + public void testUniqueIdDifferentForDifferentCredentialTypes() throws Exception { + Credential simCred = createCredentialWithSimCredential(); + Credential certCred = createCredentialWithCertificateCredential(); + Credential userCred = createCredentialWithUserCredential(); + + assertNotEquals(simCred.getUniqueId(), userCred.getUniqueId()); + assertNotEquals(simCred.getUniqueId(), certCred.getUniqueId()); + assertNotEquals(certCred.getUniqueId(), userCred.getUniqueId()); + } + + /** + * Verify that unique identifiers are different for a credential with different values + */ + @Test + public void testUniqueIdDifferentForSimCredentialsWithDifferentValues() throws Exception { + Credential simCred1 = createCredentialWithSimCredential(); + Credential simCred2 = createCredentialWithSimCredential(); + simCred2.getSimCredential().setImsi("567890*"); + + assertNotEquals(simCred1.getUniqueId(), simCred2.getUniqueId()); + } + + /** + * Verify that unique identifiers are different for a credential with different values + */ + @Test + public void testUniqueIdDifferentForUserCredentialsWithDifferentValues() throws Exception { + Credential userCred1 = createCredentialWithUserCredential(); + Credential userCred2 = createCredentialWithUserCredential(); + userCred2.getUserCredential().setUsername("anotheruser"); + + assertNotEquals(userCred1.getUniqueId(), userCred2.getUniqueId()); + } + + /** + * Verify that unique identifiers are different for a credential with different values + */ + @Test + public void testUniqueIdDifferentForCertCredentialsWithDifferentValues() throws Exception { + Credential certCred1 = createCredentialWithCertificateCredential(); + Credential certCred2 = createCredentialWithCertificateCredential(); + certCred2.getCertCredential().setCertSha256Fingerprint( + MessageDigest.getInstance("SHA-256").digest(FakeKeys.CA_CERT0.getEncoded())); + + assertNotEquals(certCred1.getUniqueId(), certCred2.getUniqueId()); + } } diff --git a/wifi/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java b/wifi/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java new file mode 100644 index 000000000000..7b900fec70a8 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.nl80211; + +import static org.junit.Assert.assertEquals; + +import android.net.wifi.ScanResult; +import android.os.Parcel; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests for {@link android.net.wifi.nl80211.DeviceWiphyCapabilities}. + */ +@SmallTest +public class DeviceWiphyCapabilitiesTest { + @Before + public void setUp() {} + + /** + * DeviceWiphyCapabilities object can be serialized and deserialized, while keeping the + * values unchanged. + */ + @Test + public void canSerializeAndDeserialize() { + DeviceWiphyCapabilities capa = new DeviceWiphyCapabilities(); + + capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11N, true); + capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AC, true); + capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AX, false); + capa.setChannelWidthSupported(ScanResult.CHANNEL_WIDTH_160MHZ, true); + capa.setChannelWidthSupported(ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ, false); + capa.setMaxNumberTxSpatialStreams(2); + capa.setMaxNumberRxSpatialStreams(1); + + Parcel parcel = Parcel.obtain(); + capa.writeToParcel(parcel, 0); + // Rewind the pointer to the head of the parcel. + parcel.setDataPosition(0); + DeviceWiphyCapabilities capaDeserialized = + DeviceWiphyCapabilities.CREATOR.createFromParcel(parcel); + + assertEquals(capa, capaDeserialized); + assertEquals(capa.hashCode(), capaDeserialized.hashCode()); + } + + /** + * Test mapping wifi standard support into channel width support + */ + @Test + public void testMappingWifiStandardIntoChannelWidthSupport() { + DeviceWiphyCapabilities capa = new DeviceWiphyCapabilities(); + + capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11N, false); + capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AC, false); + capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AX, false); + assertEquals(true, capa.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_20MHZ)); + assertEquals(false, capa.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_40MHZ)); + assertEquals(false, capa.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_80MHZ)); + + capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11N, true); + assertEquals(true, capa.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_20MHZ)); + assertEquals(true, capa.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_40MHZ)); + assertEquals(false, capa.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_80MHZ)); + + capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AC, true); + assertEquals(true, capa.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_20MHZ)); + assertEquals(true, capa.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_40MHZ)); + assertEquals(true, capa.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_80MHZ)); + } +} diff --git a/wifi/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java b/wifi/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java new file mode 100644 index 000000000000..8ddd1899179a --- /dev/null +++ b/wifi/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2017 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.nl80211; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.os.Parcel; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Unit tests for {@link android.net.wifi.nl80211.NativeScanResult}. + */ +@SmallTest +public class NativeScanResultTest { + + private static final byte[] TEST_SSID = + new byte[] {'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'}; + private static final byte[] TEST_BSSID = + new byte[] {(byte) 0x12, (byte) 0xef, (byte) 0xa1, + (byte) 0x2c, (byte) 0x97, (byte) 0x8b}; + private static final byte[] TEST_INFO_ELEMENT = + new byte[] {(byte) 0x01, (byte) 0x03, (byte) 0x12, (byte) 0xbe, (byte) 0xff}; + private static final int TEST_FREQUENCY = 2456; + private static final int TEST_SIGNAL_MBM = -45; + private static final long TEST_TSF = 34455441; + private static final int TEST_CAPABILITY = (0x1 << 2) | (0x1 << 5); + private static final boolean TEST_ASSOCIATED = true; + private static final int[] RADIO_CHAIN_IDS = { 0, 1 }; + private static final int[] RADIO_CHAIN_LEVELS = { -56, -65 }; + + /** + * NativeScanResult object can be serialized and deserialized, while keeping the + * values unchanged. + */ + @Test + public void canSerializeAndDeserialize() { + NativeScanResult scanResult = new NativeScanResult(); + scanResult.ssid = TEST_SSID; + scanResult.bssid = TEST_BSSID; + scanResult.infoElement = TEST_INFO_ELEMENT; + scanResult.frequency = TEST_FREQUENCY; + scanResult.signalMbm = TEST_SIGNAL_MBM; + scanResult.tsf = TEST_TSF; + scanResult.capability = TEST_CAPABILITY; + scanResult.associated = TEST_ASSOCIATED; + scanResult.radioChainInfos = new ArrayList<>(Arrays.asList( + new RadioChainInfo(RADIO_CHAIN_IDS[0], RADIO_CHAIN_LEVELS[0]), + new RadioChainInfo(RADIO_CHAIN_IDS[1], RADIO_CHAIN_LEVELS[1]))); + Parcel parcel = Parcel.obtain(); + scanResult.writeToParcel(parcel, 0); + // Rewind the pointer to the head of the parcel. + parcel.setDataPosition(0); + NativeScanResult scanResultDeserialized = NativeScanResult.CREATOR.createFromParcel(parcel); + + assertArrayEquals(scanResult.ssid, scanResultDeserialized.ssid); + assertArrayEquals(scanResult.bssid, scanResultDeserialized.bssid); + assertArrayEquals(scanResult.infoElement, scanResultDeserialized.infoElement); + assertEquals(scanResult.frequency, scanResultDeserialized.frequency); + assertEquals(scanResult.signalMbm, scanResultDeserialized.signalMbm); + assertEquals(scanResult.tsf, scanResultDeserialized.tsf); + assertEquals(scanResult.capability, scanResultDeserialized.capability); + assertEquals(scanResult.associated, scanResultDeserialized.associated); + assertTrue(scanResult.radioChainInfos.containsAll(scanResultDeserialized.radioChainInfos)); + } +} diff --git a/wifi/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java b/wifi/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java new file mode 100644 index 000000000000..dec1db8d274e --- /dev/null +++ b/wifi/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 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.nl80211; + +import static org.junit.Assert.assertEquals; + +import android.os.Parcel; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +/** + * Unit tests for {@link android.net.wifi.nl80211.PnoSettings}. + */ +@SmallTest +public class PnoSettingsTest { + + private static final byte[] TEST_SSID_1 = + new byte[] {'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'}; + private static final byte[] TEST_SSID_2 = + new byte[] {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'T', 'e', 's', 't'}; + private static final int[] TEST_FREQUENCIES_1 = {}; + private static final int[] TEST_FREQUENCIES_2 = {2500, 5124}; + private static final int TEST_INTERVAL_MS = 30000; + private static final int TEST_MIN_2G_RSSI = -60; + private static final int TEST_MIN_5G_RSSI = -65; + private static final int TEST_VALUE = 42; + + private PnoNetwork mPnoNetwork1; + private PnoNetwork mPnoNetwork2; + + @Before + public void setUp() { + mPnoNetwork1 = new PnoNetwork(); + mPnoNetwork1.setSsid(TEST_SSID_1); + mPnoNetwork1.setHidden(true); + mPnoNetwork1.setFrequenciesMhz(TEST_FREQUENCIES_1); + + mPnoNetwork2 = new PnoNetwork(); + mPnoNetwork2.setSsid(TEST_SSID_2); + mPnoNetwork2.setHidden(false); + mPnoNetwork2.setFrequenciesMhz(TEST_FREQUENCIES_2); + } + + /** + * PnoSettings object can be serialized and deserialized, while keeping the + * values unchanged. + */ + @Test + public void canSerializeAndDeserialize() { + PnoSettings pnoSettings = new PnoSettings(); + pnoSettings.setIntervalMillis(TEST_INTERVAL_MS); + pnoSettings.setMin2gRssiDbm(TEST_MIN_2G_RSSI); + pnoSettings.setMin5gRssiDbm(TEST_MIN_5G_RSSI); + pnoSettings.setPnoNetworks(new ArrayList<>(Arrays.asList(mPnoNetwork1, mPnoNetwork2))); + + Parcel parcel = Parcel.obtain(); + pnoSettings.writeToParcel(parcel, 0); + // Rewind the pointer to the head of the parcel. + parcel.setDataPosition(0); + PnoSettings pnoSettingsDeserialized = PnoSettings.CREATOR.createFromParcel(parcel); + + assertEquals(pnoSettings, pnoSettingsDeserialized); + assertEquals(pnoSettings.hashCode(), pnoSettingsDeserialized.hashCode()); + } + + /** + * Tests usage of {@link PnoSettings} as a HashMap key type. + */ + @Test + public void testAsHashMapKey() { + PnoSettings pnoSettings1 = new PnoSettings(); + pnoSettings1.setIntervalMillis(TEST_INTERVAL_MS); + pnoSettings1.setMin2gRssiDbm(TEST_MIN_2G_RSSI); + pnoSettings1.setMin5gRssiDbm(TEST_MIN_5G_RSSI); + pnoSettings1.setPnoNetworks(new ArrayList<>(Arrays.asList(mPnoNetwork1, mPnoNetwork2))); + + PnoSettings pnoSettings2 = new PnoSettings(); + pnoSettings2.setIntervalMillis(TEST_INTERVAL_MS); + pnoSettings2.setMin2gRssiDbm(TEST_MIN_2G_RSSI); + pnoSettings2.setMin5gRssiDbm(TEST_MIN_5G_RSSI); + pnoSettings2.setPnoNetworks(new ArrayList<>(Arrays.asList(mPnoNetwork1, mPnoNetwork2))); + + assertEquals(pnoSettings1, pnoSettings2); + assertEquals(pnoSettings1.hashCode(), pnoSettings2.hashCode()); + + HashMap<PnoSettings, Integer> map = new HashMap<>(); + map.put(pnoSettings1, TEST_VALUE); + + assertEquals(TEST_VALUE, map.get(pnoSettings2).intValue()); + } +} diff --git a/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java b/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java new file mode 100644 index 000000000000..905920888012 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2017 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.nl80211; + +import static org.junit.Assert.assertEquals; + +import android.os.Parcel; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +/** + * Unit tests for {@link android.net.wifi.nl80211.SingleScanSettingsResult}. + */ +@SmallTest +public class SingleScanSettingsTest { + + private static final byte[] TEST_SSID_1 = + new byte[] {'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'}; + private static final byte[] TEST_SSID_2 = + new byte[] {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'T', 'e', 's', 't'}; + private static final int TEST_FREQUENCY_1 = 2456; + private static final int TEST_FREQUENCY_2 = 5215; + private static final int TEST_VALUE = 42; + + private ChannelSettings mChannelSettings1; + private ChannelSettings mChannelSettings2; + private HiddenNetwork mHiddenNetwork1; + private HiddenNetwork mHiddenNetwork2; + + @Before + public void setUp() { + mChannelSettings1 = new ChannelSettings(); + mChannelSettings1.frequency = TEST_FREQUENCY_1; + mChannelSettings2 = new ChannelSettings(); + mChannelSettings2.frequency = TEST_FREQUENCY_2; + + mHiddenNetwork1 = new HiddenNetwork(); + mHiddenNetwork1.ssid = TEST_SSID_1; + mHiddenNetwork2 = new HiddenNetwork(); + mHiddenNetwork2.ssid = TEST_SSID_2; + } + + /** + * SingleScanSettings object can be serialized and deserialized, while keeping the + * values unchanged. + */ + @Test + public void canSerializeAndDeserialize() { + SingleScanSettings scanSettings = new SingleScanSettings(); + scanSettings.scanType = IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY; + + scanSettings.channelSettings = + new ArrayList<>(Arrays.asList(mChannelSettings1, mChannelSettings2)); + scanSettings.hiddenNetworks = + new ArrayList<>(Arrays.asList(mHiddenNetwork1, mHiddenNetwork2)); + + Parcel parcel = Parcel.obtain(); + scanSettings.writeToParcel(parcel, 0); + // Rewind the pointer to the head of the parcel. + parcel.setDataPosition(0); + SingleScanSettings scanSettingsDeserialized = + SingleScanSettings.CREATOR.createFromParcel(parcel); + + assertEquals(scanSettings, scanSettingsDeserialized); + assertEquals(scanSettings.hashCode(), scanSettingsDeserialized.hashCode()); + } + + /** + * Tests usage of {@link SingleScanSettings} as a HashMap key type. + */ + @Test + public void testAsHashMapKey() { + SingleScanSettings scanSettings1 = new SingleScanSettings(); + scanSettings1.scanType = IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY; + scanSettings1.channelSettings = + new ArrayList<>(Arrays.asList(mChannelSettings1, mChannelSettings2)); + scanSettings1.hiddenNetworks = + new ArrayList<>(Arrays.asList(mHiddenNetwork1, mHiddenNetwork2)); + + SingleScanSettings scanSettings2 = new SingleScanSettings(); + scanSettings2.scanType = IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY; + scanSettings2.channelSettings = + new ArrayList<>(Arrays.asList(mChannelSettings1, mChannelSettings2)); + scanSettings2.hiddenNetworks = + new ArrayList<>(Arrays.asList(mHiddenNetwork1, mHiddenNetwork2)); + + assertEquals(scanSettings1, scanSettings2); + assertEquals(scanSettings1.hashCode(), scanSettings2.hashCode()); + + HashMap<SingleScanSettings, Integer> map = new HashMap<>(); + map.put(scanSettings1, TEST_VALUE); + + assertEquals(TEST_VALUE, map.get(scanSettings2).intValue()); + } +} diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java new file mode 100644 index 000000000000..9ee0acbfbaa2 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java @@ -0,0 +1,1265 @@ +/* + * Copyright (C) 2017 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.nl80211; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.app.AlarmManager; +import android.app.test.TestAlarmManager; +import android.content.Context; +import android.net.MacAddress; +import android.net.wifi.ScanResult; +import android.net.wifi.SoftApInfo; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiScanner; +import android.net.wifi.util.HexEncoding; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.test.TestLooper; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.AdditionalMatchers; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Unit tests for {@link android.net.wifi.nl80211.WifiNl80211Manager}. + */ +@SmallTest +public class WifiNl80211ManagerTest { + @Mock + private IWificond mWificond; + @Mock + private IBinder mWifiCondBinder; + @Mock + private IClientInterface mClientInterface; + @Mock + private IWifiScannerImpl mWifiScannerImpl; + @Mock + private IApInterface mApInterface; + @Mock + private WifiNl80211Manager.SoftApCallback mSoftApListener; + @Mock + private WifiNl80211Manager.SendMgmtFrameCallback mSendMgmtFrameCallback; + @Mock + private WifiNl80211Manager.ScanEventCallback mNormalScanCallback; + @Mock + private WifiNl80211Manager.ScanEventCallback mPnoScanCallback; + @Mock + private WifiNl80211Manager.PnoScanRequestCallback mPnoScanRequestCallback; + @Mock + private Context mContext; + private TestLooper mLooper; + private TestAlarmManager mTestAlarmManager; + private AlarmManager mAlarmManager; + private WifiNl80211Manager mWificondControl; + private static final String TEST_INTERFACE_NAME = "test_wlan_if"; + private static final String TEST_INTERFACE_NAME1 = "test_wlan_if1"; + private static final String TEST_INVALID_INTERFACE_NAME = "asdf"; + private static final byte[] TEST_SSID = + new byte[]{'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'}; + private static final byte[] TEST_PSK = + new byte[]{'T', 'e', 's', 't'}; + + private static final Set<Integer> SCAN_FREQ_SET = + new HashSet<Integer>() {{ + add(2410); + add(2450); + add(5050); + add(5200); + }}; + private static final String TEST_QUOTED_SSID_1 = "\"testSsid1\""; + private static final String TEST_QUOTED_SSID_2 = "\"testSsid2\""; + private static final int[] TEST_FREQUENCIES_1 = {}; + private static final int[] TEST_FREQUENCIES_2 = {2500, 5124}; + private static final MacAddress TEST_RAW_MAC_BYTES = MacAddress.fromBytes( + new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}); + + private static final List<byte[]> SCAN_HIDDEN_NETWORK_SSID_LIST = + new ArrayList<byte[]>() {{ + add(LocalNativeUtil.byteArrayFromArrayList( + LocalNativeUtil.decodeSsid(TEST_QUOTED_SSID_1))); + add(LocalNativeUtil.byteArrayFromArrayList( + LocalNativeUtil.decodeSsid(TEST_QUOTED_SSID_2))); + }}; + + private static final PnoSettings TEST_PNO_SETTINGS = new PnoSettings(); + static { + TEST_PNO_SETTINGS.setIntervalMillis(6000); + List<PnoNetwork> initPnoNetworks = new ArrayList<>(); + PnoNetwork network = new PnoNetwork(); + network.setSsid(LocalNativeUtil.byteArrayFromArrayList( + LocalNativeUtil.decodeSsid(TEST_QUOTED_SSID_1))); + network.setHidden(true); + network.setFrequenciesMhz(TEST_FREQUENCIES_1); + initPnoNetworks.add(network); + network.setSsid(LocalNativeUtil.byteArrayFromArrayList( + LocalNativeUtil.decodeSsid(TEST_QUOTED_SSID_2))); + network.setHidden(false); + network.setFrequenciesMhz(TEST_FREQUENCIES_2); + initPnoNetworks.add(network); + TEST_PNO_SETTINGS.setPnoNetworks(initPnoNetworks); + } + + private static final int TEST_MCS_RATE = 5; + private static final int TEST_SEND_MGMT_FRAME_ELAPSED_TIME_MS = 100; + private static final byte[] TEST_PROBE_FRAME = { + 0x40, 0x00, 0x3c, 0x00, (byte) 0xa8, (byte) 0xbd, 0x27, 0x5b, + 0x33, 0x72, (byte) 0xf4, (byte) 0xf5, (byte) 0xe8, 0x51, (byte) 0x9e, 0x09, + (byte) 0xa8, (byte) 0xbd, 0x27, 0x5b, 0x33, 0x72, (byte) 0xb0, 0x66, + 0x00, 0x00 + }; + + @Before + public void setUp() throws Exception { + // Setup mocks for successful WificondControl operation. Failure case mocks should be + // created in specific tests + MockitoAnnotations.initMocks(this); + + mTestAlarmManager = new TestAlarmManager(); + mAlarmManager = mTestAlarmManager.getAlarmManager(); + when(mContext.getSystemServiceName(AlarmManager.class)).thenReturn(Context.ALARM_SERVICE); + when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager); + + mLooper = new TestLooper(); + when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); + + when(mWificond.asBinder()).thenReturn(mWifiCondBinder); + when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl); + when(mWificond.createClientInterface(any())).thenReturn(mClientInterface); + when(mWificond.createApInterface(any())).thenReturn(mApInterface); + when(mWificond.tearDownClientInterface(any())).thenReturn(true); + when(mWificond.tearDownApInterface(any())).thenReturn(true); + when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl); + when(mClientInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME); + mWificondControl = new WifiNl80211Manager(mContext, mWificond); + assertEquals(true, + mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run, + mNormalScanCallback, mPnoScanCallback)); + } + + /** + * Verifies that setupInterfaceForClientMode(TEST_INTERFACE_NAME) calls Wificond. + */ + @Test + public void testSetupInterfaceForClientMode() throws Exception { + when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface); + verify(mWificond).createClientInterface(TEST_INTERFACE_NAME); + } + + /** + * Verifies that setupInterfaceForClientMode(TEST_INTERFACE_NAME) calls subscribeScanEvents(). + */ + @Test + public void testSetupInterfaceForClientModeCallsScanEventSubscripiton() throws Exception { + verify(mWifiScannerImpl).subscribeScanEvents(any(IScanEvent.class)); + } + + /** + * Verifies that tearDownClientInterface(TEST_INTERFACE_NAME) calls Wificond. + */ + @Test + public void testTeardownClientInterface() throws Exception { + when(mWificond.tearDownClientInterface(TEST_INTERFACE_NAME)).thenReturn(true); + + assertTrue(mWificondControl.tearDownClientInterface(TEST_INTERFACE_NAME)); + verify(mWifiScannerImpl).unsubscribeScanEvents(); + verify(mWifiScannerImpl).unsubscribePnoScanEvents(); + verify(mWificond).tearDownClientInterface(TEST_INTERFACE_NAME); + } + + /** + * Verifies that tearDownClientInterface(TEST_INTERFACE_NAME) calls Wificond. + */ + @Test + public void testTeardownClientInterfaceOnInvalidIface() throws Exception { + when(mWificond.tearDownClientInterface(TEST_INTERFACE_NAME1)).thenReturn(true); + + assertFalse(mWificondControl.tearDownClientInterface(TEST_INTERFACE_NAME1)); + verify(mWifiScannerImpl, never()).unsubscribeScanEvents(); + verify(mWifiScannerImpl, never()).unsubscribePnoScanEvents(); + verify(mWificond, never()).tearDownClientInterface(any()); + } + + /** + * Verifies that tearDownClientInterface(TEST_INTERFACE_NAME) calls Wificond. + */ + @Test + public void testTeardownClientInterfaceFailDueToExceptionScannerUnsubscribe() throws Exception { + when(mWificond.tearDownClientInterface(TEST_INTERFACE_NAME)).thenReturn(true); + doThrow(new RemoteException()).when(mWifiScannerImpl).unsubscribeScanEvents(); + + assertFalse(mWificondControl.tearDownClientInterface(TEST_INTERFACE_NAME)); + verify(mWifiScannerImpl).unsubscribeScanEvents(); + verify(mWifiScannerImpl, never()).unsubscribePnoScanEvents(); + verify(mWificond, never()).tearDownClientInterface(TEST_INTERFACE_NAME); + } + + /** + * Verifies that tearDownClientInterface(TEST_INTERFACE_NAME) calls Wificond. + */ + @Test + public void testTeardownClientInterfaceErrorWhenWificondFailed() throws Exception { + when(mWificond.tearDownClientInterface(TEST_INTERFACE_NAME)).thenReturn(false); + + assertFalse(mWificondControl.tearDownClientInterface(TEST_INTERFACE_NAME)); + verify(mWifiScannerImpl).unsubscribeScanEvents(); + verify(mWifiScannerImpl).unsubscribePnoScanEvents(); + verify(mWificond).tearDownClientInterface(TEST_INTERFACE_NAME); + } + + /** + * Verifies that the client handles are cleared after teardown. + */ + @Test + public void testTeardownClientInterfaceClearsHandles() throws Exception { + testTeardownClientInterface(); + + assertNull(mWificondControl.signalPoll(TEST_INTERFACE_NAME)); + verify(mClientInterface, never()).signalPoll(); + + assertFalse(mWificondControl.startScan( + TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_LATENCY, + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST)); + verify(mWifiScannerImpl, never()).scan(any()); + } + + /** + * Verifies that setupInterfaceForSoftApMode(TEST_INTERFACE_NAME) calls wificond. + */ + @Test + public void testSetupInterfaceForSoftApMode() throws Exception { + when(mWificond.createApInterface(TEST_INTERFACE_NAME)).thenReturn(mApInterface); + + assertEquals(true, mWificondControl.setupInterfaceForSoftApMode(TEST_INTERFACE_NAME)); + verify(mWificond).createApInterface(TEST_INTERFACE_NAME); + } + + /** + * Verifies that setupInterfaceForSoftAp() returns null when wificond is not started. + */ + @Test + public void testSetupInterfaceForSoftApModeErrorWhenWificondIsNotStarted() throws Exception { + // Invoke wificond death handler to clear the handle. + mWificondControl.binderDied(); + mLooper.dispatchAll(); + + assertEquals(false, mWificondControl.setupInterfaceForSoftApMode(TEST_INTERFACE_NAME)); + } + + /** + * Verifies that setupInterfaceForSoftApMode(TEST_INTERFACE_NAME) returns null when wificond + * failed to setup AP interface. + */ + @Test + public void testSetupInterfaceForSoftApModeErrorWhenWificondFailedToSetupInterface() + throws Exception { + when(mWificond.createApInterface(TEST_INTERFACE_NAME)).thenReturn(null); + + assertEquals(false, mWificondControl.setupInterfaceForSoftApMode(TEST_INTERFACE_NAME)); + } + + /** + * Verifies that tearDownClientInterface(TEST_INTERFACE_NAME) calls Wificond. + */ + @Test + public void testTeardownSoftApInterface() throws Exception { + testSetupInterfaceForSoftApMode(); + when(mWificond.tearDownApInterface(TEST_INTERFACE_NAME)).thenReturn(true); + + assertTrue(mWificondControl.tearDownSoftApInterface(TEST_INTERFACE_NAME)); + verify(mWificond).tearDownApInterface(TEST_INTERFACE_NAME); + } + + /** + * Verifies that tearDownSoftapInterface(TEST_INTERFACE_NAME) calls Wificond. + */ + @Test + public void testTeardownSoftApInterfaceOnInvalidIface() throws Exception { + testSetupInterfaceForSoftApMode(); + when(mWificond.tearDownApInterface(TEST_INTERFACE_NAME1)).thenReturn(true); + + assertFalse(mWificondControl.tearDownSoftApInterface(TEST_INTERFACE_NAME1)); + verify(mWificond, never()).tearDownApInterface(any()); + } + + /** + * Verifies that tearDownClientInterface(TEST_INTERFACE_NAME) calls Wificond. + */ + @Test + public void testTeardownSoftApInterfaceErrorWhenWificondFailed() throws Exception { + testSetupInterfaceForSoftApMode(); + when(mWificond.tearDownApInterface(TEST_INTERFACE_NAME)).thenReturn(false); + + assertFalse(mWificondControl.tearDownSoftApInterface(TEST_INTERFACE_NAME)); + verify(mWificond).tearDownApInterface(TEST_INTERFACE_NAME); + } + + /** + * Verifies that the SoftAp handles are cleared after teardown. + */ + @Test + public void testTeardownSoftApInterfaceClearsHandles() throws Exception { + testTeardownSoftApInterface(); + + assertFalse(mWificondControl.registerApCallback( + TEST_INTERFACE_NAME, Runnable::run, mSoftApListener)); + verify(mApInterface, never()).registerCallback(any()); + } + + /** + * Verifies that we can setup concurrent interfaces. + */ + @Test + public void testSetupMultipleInterfaces() throws Exception { + when(mWificond.createApInterface(TEST_INTERFACE_NAME1)).thenReturn(mApInterface); + + assertEquals(true, mWificondControl.setupInterfaceForSoftApMode(TEST_INTERFACE_NAME1)); + + verify(mWificond).createClientInterface(TEST_INTERFACE_NAME); + verify(mWificond).createApInterface(TEST_INTERFACE_NAME1); + } + + /** + * Verifies that we can setup concurrent interfaces. + */ + @Test + public void testTeardownMultipleInterfaces() throws Exception { + testSetupMultipleInterfaces(); + assertTrue(mWificondControl.tearDownClientInterface(TEST_INTERFACE_NAME)); + assertTrue(mWificondControl.tearDownSoftApInterface(TEST_INTERFACE_NAME1)); + + verify(mWificond).tearDownClientInterface(TEST_INTERFACE_NAME); + verify(mWificond).tearDownApInterface(TEST_INTERFACE_NAME1); + } + + /** + * Verifies that tearDownInterfaces() calls wificond. + */ + @Test + public void testTearDownInterfaces() throws Exception { + assertTrue(mWificondControl.tearDownInterfaces()); + verify(mWificond).tearDownInterfaces(); + } + + /** + * Verifies that tearDownInterfaces() calls unsubscribeScanEvents() when there was + * a configured client interface. + */ + @Test + public void testTearDownInterfacesRemovesScanEventSubscription() throws Exception { + assertTrue(mWificondControl.tearDownInterfaces()); + verify(mWifiScannerImpl).unsubscribeScanEvents(); + } + + /** + * Verifies that tearDownInterfaces() returns false when wificond is not started. + */ + @Test + public void testTearDownInterfacesErrorWhenWificondIsNotStarterd() throws Exception { + // Invoke wificond death handler to clear the handle. + mWificondControl.binderDied(); + mLooper.dispatchAll(); + assertFalse(mWificondControl.tearDownInterfaces()); + } + + /** + * Verifies that signalPoll() calls wificond. + */ + @Test + public void testSignalPoll() throws Exception { + when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface); + + mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run, + mNormalScanCallback, mPnoScanCallback); + mWificondControl.signalPoll(TEST_INTERFACE_NAME); + verify(mClientInterface).signalPoll(); + } + + /** + * Verifies that signalPoll() returns null when there is no configured client interface. + */ + @Test + public void testSignalPollErrorWhenNoClientInterfaceConfigured() throws Exception { + when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface); + + // Configure client interface. + assertEquals(true, mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, + Runnable::run, mNormalScanCallback, mPnoScanCallback)); + + // Tear down interfaces. + assertTrue(mWificondControl.tearDownInterfaces()); + + // Signal poll should fail. + assertEquals(null, mWificondControl.signalPoll(TEST_INTERFACE_NAME)); + } + + /** + * Verifies that getTxPacketCounters() calls wificond. + */ + @Test + public void testGetTxPacketCounters() throws Exception { + when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface); + + mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run, + mNormalScanCallback, mPnoScanCallback); + mWificondControl.getTxPacketCounters(TEST_INTERFACE_NAME); + verify(mClientInterface).getPacketCounters(); + } + + /** + * Verifies that getTxPacketCounters() returns null when there is no configured client + * interface. + */ + @Test + public void testGetTxPacketCountersErrorWhenNoClientInterfaceConfigured() throws Exception { + when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface); + + // Configure client interface. + assertEquals(true, mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, + Runnable::run, mNormalScanCallback, mPnoScanCallback)); + + // Tear down interfaces. + assertTrue(mWificondControl.tearDownInterfaces()); + + // Signal poll should fail. + assertEquals(null, mWificondControl.getTxPacketCounters(TEST_INTERFACE_NAME)); + } + + /** + * Verifies that getScanResults() returns null when there is no configured client + * interface. + */ + @Test + public void testGetScanResultsErrorWhenNoClientInterfaceConfigured() throws Exception { + when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface); + + // Configure client interface. + assertEquals(true, mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, + Runnable::run, mNormalScanCallback, mPnoScanCallback)); + + // Tear down interfaces. + assertTrue(mWificondControl.tearDownInterfaces()); + + // getScanResults should fail. + assertEquals(0, + mWificondControl.getScanResults(TEST_INTERFACE_NAME, + WifiNl80211Manager.SCAN_TYPE_SINGLE_SCAN).size()); + } + + /** + * Verifies that Scan() can convert input parameters to SingleScanSettings correctly. + */ + @Test + public void testScan() throws Exception { + when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true); + assertTrue(mWificondControl.startScan( + TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER, + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST)); + verify(mWifiScannerImpl).scan(argThat(new ScanMatcher( + IWifiScannerImpl.SCAN_TYPE_LOW_POWER, + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST))); + } + + /** + * Verifies that Scan() removes duplicates hiddenSsids passed in from input. + */ + @Test + public void testScanWithDuplicateHiddenSsids() throws Exception { + when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true); + // Create a list of hiddenSsid that has a duplicate element + List<byte[]> hiddenSsidWithDup = new ArrayList<>(SCAN_HIDDEN_NETWORK_SSID_LIST); + hiddenSsidWithDup.add(SCAN_HIDDEN_NETWORK_SSID_LIST.get(0)); + assertEquals(hiddenSsidWithDup.get(0), + hiddenSsidWithDup.get(hiddenSsidWithDup.size() - 1)); + // Pass the List with duplicate elements into scan() + assertTrue(mWificondControl.startScan( + TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER, + SCAN_FREQ_SET, hiddenSsidWithDup)); + // But the argument passed down should have the duplicate removed. + verify(mWifiScannerImpl).scan(argThat(new ScanMatcher( + IWifiScannerImpl.SCAN_TYPE_LOW_POWER, + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST))); + } + + /** + * Verifies that Scan() can handle null input parameters correctly. + */ + @Test + public void testScanNullParameters() throws Exception { + when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true); + assertTrue(mWificondControl.startScan( + TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_HIGH_ACCURACY, null, null)); + verify(mWifiScannerImpl).scan(argThat(new ScanMatcher( + IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY, null, null))); + } + + /** + * Verifies that Scan() can handle wificond scan failure. + */ + @Test + public void testScanFailure() throws Exception { + when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(false); + assertFalse(mWificondControl.startScan( + TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_LATENCY, + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST)); + verify(mWifiScannerImpl).scan(any(SingleScanSettings.class)); + } + + /** + * Verifies that Scan() can handle invalid type. + */ + @Test + public void testScanFailureDueToInvalidType() throws Exception { + assertFalse(mWificondControl.startScan( + TEST_INTERFACE_NAME, 100, + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST)); + verify(mWifiScannerImpl, never()).scan(any(SingleScanSettings.class)); + } + + /** + * Verifies that startPnoScan() can convert input parameters to PnoSettings correctly. + */ + @Test + public void testStartPnoScan() throws Exception { + when(mWifiScannerImpl.startPnoScan(any(PnoSettings.class))).thenReturn(true); + assertTrue( + mWificondControl.startPnoScan(TEST_INTERFACE_NAME, TEST_PNO_SETTINGS, Runnable::run, + mPnoScanRequestCallback)); + verify(mWifiScannerImpl).startPnoScan(eq(TEST_PNO_SETTINGS)); + verify(mPnoScanRequestCallback).onPnoRequestSucceeded(); + } + + /** + * Verifies that stopPnoScan() calls underlying wificond. + */ + @Test + public void testStopPnoScan() throws Exception { + when(mWifiScannerImpl.stopPnoScan()).thenReturn(true); + assertTrue(mWificondControl.stopPnoScan(TEST_INTERFACE_NAME)); + verify(mWifiScannerImpl).stopPnoScan(); + } + + /** + * Verifies that stopPnoScan() can handle wificond failure. + */ + @Test + public void testStopPnoScanFailure() throws Exception { + + when(mWifiScannerImpl.stopPnoScan()).thenReturn(false); + assertFalse(mWificondControl.stopPnoScan(TEST_INTERFACE_NAME)); + verify(mWifiScannerImpl).stopPnoScan(); + } + + /** + * Verifies that WificondControl can invoke WifiMonitor broadcast methods upon scan + * result event. + */ + @Test + public void testScanResultEvent() throws Exception { + ArgumentCaptor<IScanEvent> messageCaptor = ArgumentCaptor.forClass(IScanEvent.class); + verify(mWifiScannerImpl).subscribeScanEvents(messageCaptor.capture()); + IScanEvent scanEvent = messageCaptor.getValue(); + assertNotNull(scanEvent); + scanEvent.OnScanResultReady(); + + verify(mNormalScanCallback).onScanResultReady(); + } + + /** + * Verifies that WificondControl can invoke WifiMonitor broadcast methods upon scan + * failed event. + */ + @Test + public void testScanFailedEvent() throws Exception { + ArgumentCaptor<IScanEvent> messageCaptor = ArgumentCaptor.forClass(IScanEvent.class); + verify(mWifiScannerImpl).subscribeScanEvents(messageCaptor.capture()); + IScanEvent scanEvent = messageCaptor.getValue(); + assertNotNull(scanEvent); + scanEvent.OnScanFailed(); + + verify(mNormalScanCallback).onScanFailed(); + } + + /** + * Verifies that WificondControl can invoke WifiMonitor broadcast methods upon pno scan + * result event. + */ + @Test + public void testPnoScanResultEvent() throws Exception { + ArgumentCaptor<IPnoScanEvent> messageCaptor = ArgumentCaptor.forClass(IPnoScanEvent.class); + verify(mWifiScannerImpl).subscribePnoScanEvents(messageCaptor.capture()); + IPnoScanEvent pnoScanEvent = messageCaptor.getValue(); + assertNotNull(pnoScanEvent); + pnoScanEvent.OnPnoNetworkFound(); + verify(mPnoScanCallback).onScanResultReady(); + } + + /** + * Verifies that WificondControl can invoke WifiMetrics pno scan count methods upon pno event. + */ + @Test + public void testPnoScanEventsForMetrics() throws Exception { + ArgumentCaptor<IPnoScanEvent> messageCaptor = ArgumentCaptor.forClass(IPnoScanEvent.class); + verify(mWifiScannerImpl).subscribePnoScanEvents(messageCaptor.capture()); + IPnoScanEvent pnoScanEvent = messageCaptor.getValue(); + assertNotNull(pnoScanEvent); + + pnoScanEvent.OnPnoNetworkFound(); + verify(mPnoScanCallback).onScanResultReady(); + + pnoScanEvent.OnPnoScanFailed(); + verify(mPnoScanCallback).onScanFailed(); + } + + /** + * Verifies that startPnoScan() can invoke WifiMetrics pno scan count methods correctly. + */ + @Test + public void testStartPnoScanForMetrics() throws Exception { + when(mWifiScannerImpl.startPnoScan(any(PnoSettings.class))).thenReturn(false); + + assertFalse( + mWificondControl.startPnoScan(TEST_INTERFACE_NAME, TEST_PNO_SETTINGS, Runnable::run, + mPnoScanRequestCallback)); + verify(mPnoScanRequestCallback).onPnoRequestFailed(); + } + + /** + * Verifies that abortScan() calls underlying wificond. + */ + @Test + public void testAbortScan() throws Exception { + mWificondControl.abortScan(TEST_INTERFACE_NAME); + verify(mWifiScannerImpl).abortScan(); + } + + /** + * Ensures that the Ap interface callbacks are forwarded to the + * SoftApListener used for starting soft AP. + */ + @Test + public void testSoftApListenerInvocation() throws Exception { + testSetupInterfaceForSoftApMode(); + + WifiConfiguration config = new WifiConfiguration(); + config.SSID = new String(TEST_SSID, StandardCharsets.UTF_8); + + when(mApInterface.registerCallback(any())).thenReturn(true); + + final ArgumentCaptor<IApInterfaceEventCallback> apInterfaceCallbackCaptor = + ArgumentCaptor.forClass(IApInterfaceEventCallback.class); + + assertTrue(mWificondControl.registerApCallback( + TEST_INTERFACE_NAME, Runnable::run, mSoftApListener)); + verify(mApInterface).registerCallback(apInterfaceCallbackCaptor.capture()); + + final NativeWifiClient testClient = new NativeWifiClient(TEST_RAW_MAC_BYTES); + apInterfaceCallbackCaptor.getValue().onConnectedClientsChanged(testClient, true); + verify(mSoftApListener).onConnectedClientsChanged(eq(testClient), eq(true)); + + int channelFrequency = 2437; + int channelBandwidth = IApInterfaceEventCallback.BANDWIDTH_20; + apInterfaceCallbackCaptor.getValue().onSoftApChannelSwitched(channelFrequency, + channelBandwidth); + verify(mSoftApListener).onSoftApChannelSwitched(eq(channelFrequency), + eq(SoftApInfo.CHANNEL_WIDTH_20MHZ)); + } + + /** + * Verifies registration and invocation of wificond death handler. + */ + @Test + public void testRegisterDeathHandler() throws Exception { + Runnable deathHandler = mock(Runnable.class); + mWificondControl.setOnServiceDeadCallback(deathHandler); + mWificondControl.binderDied(); + mLooper.dispatchAll(); + verify(deathHandler).run(); + } + + /** + * Verifies handling of wificond death and ensures that all internal state is cleared and + * handlers are invoked. + */ + @Test + public void testDeathHandling() throws Exception { + Runnable deathHandler = mock(Runnable.class); + mWificondControl.setOnServiceDeadCallback(deathHandler); + + testSetupInterfaceForClientMode(); + + mWificondControl.binderDied(); + mLooper.dispatchAll(); + verify(deathHandler).run(); + + // The handles should be cleared after death. + assertEquals(0, mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ).length); + verify(mWificond, never()).getAvailable5gNonDFSChannels(); + } + + /** + * sendMgmtFrame() should fail if a null callback is passed in. + */ + @Test + public void testSendMgmtFrameNullCallback() throws Exception { + mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, + Runnable::run, null); + + verify(mClientInterface, never()).SendMgmtFrame(any(), any(), anyInt()); + } + + /** + * sendMgmtFrame() should fail if a null frame is passed in. + */ + @Test + public void testSendMgmtFrameNullFrame() throws Exception { + mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, null, TEST_MCS_RATE, Runnable::run, + mSendMgmtFrameCallback); + + verify(mClientInterface, never()).SendMgmtFrame(any(), any(), anyInt()); + verify(mSendMgmtFrameCallback).onFailure(anyInt()); + } + + /** + * sendMgmtFrame() should fail if an interface name that does not exist is passed in. + */ + @Test + public void testSendMgmtFrameInvalidInterfaceName() throws Exception { + mWificondControl.sendMgmtFrame(TEST_INVALID_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, + Runnable::run, mSendMgmtFrameCallback); + + verify(mClientInterface, never()).SendMgmtFrame(any(), any(), anyInt()); + verify(mSendMgmtFrameCallback).onFailure(anyInt()); + } + + /** + * sendMgmtFrame() should fail if it is called a second time before the first call completed. + */ + @Test + public void testSendMgmtFrameCalledTwiceBeforeFinished() throws Exception { + WifiNl80211Manager.SendMgmtFrameCallback cb1 = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); + WifiNl80211Manager.SendMgmtFrameCallback cb2 = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); + + mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, + Runnable::run, cb1); + verify(cb1, never()).onFailure(anyInt()); + verify(mClientInterface, times(1)) + .SendMgmtFrame(AdditionalMatchers.aryEq(TEST_PROBE_FRAME), + any(), eq(TEST_MCS_RATE)); + + mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, + Runnable::run, cb2); + verify(cb2).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_ALREADY_STARTED); + // verify SendMgmtFrame() still was only called once i.e. not called again + verify(mClientInterface, times(1)) + .SendMgmtFrame(any(), any(), anyInt()); + } + + /** + * Tests that when a RemoteException is triggered on AIDL call, onFailure() is called only once. + */ + @Test + public void testSendMgmtFrameThrowsException() throws Exception { + WifiNl80211Manager.SendMgmtFrameCallback cb = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); + + final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = + ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); + + doThrow(new RemoteException()).when(mClientInterface) + .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); + + final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + final ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class); + doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(), + alarmListenerCaptor.capture(), handlerCaptor.capture()); + + mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, + Runnable::run, cb); + mLooper.dispatchAll(); + + verify(cb).onFailure(anyInt()); + verify(mAlarmManager).cancel(eq(alarmListenerCaptor.getValue())); + + sendMgmtFrameEventCaptor.getValue().OnFailure( + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); + mLooper.dispatchAll(); + + handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm()); + mLooper.dispatchAll(); + + verifyNoMoreInteractions(cb); + } + + /** + * Tests that the onAck() callback is triggered correctly. + */ + @Test + public void testSendMgmtFrameSuccess() throws Exception { + WifiNl80211Manager.SendMgmtFrameCallback cb = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); + + final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = + ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); + doNothing().when(mClientInterface) + .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); + final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + final ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class); + doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(), + alarmListenerCaptor.capture(), handlerCaptor.capture()); + mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, + Runnable::run, cb); + + sendMgmtFrameEventCaptor.getValue().OnAck(TEST_SEND_MGMT_FRAME_ELAPSED_TIME_MS); + mLooper.dispatchAll(); + verify(cb).onAck(eq(TEST_SEND_MGMT_FRAME_ELAPSED_TIME_MS)); + verify(cb, never()).onFailure(anyInt()); + verify(mAlarmManager).cancel(eq(alarmListenerCaptor.getValue())); + + // verify that even if timeout is triggered afterwards, SendMgmtFrameCallback is not + // triggered again + handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm()); + mLooper.dispatchAll(); + verify(cb, times(1)).onAck(anyInt()); + verify(cb, never()).onFailure(anyInt()); + } + + /** + * Tests that the onFailure() callback is triggered correctly. + */ + @Test + public void testSendMgmtFrameFailure() throws Exception { + WifiNl80211Manager.SendMgmtFrameCallback cb = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); + + final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = + ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); + doNothing().when(mClientInterface) + .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); + final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + final ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class); + doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(), + alarmListenerCaptor.capture(), handlerCaptor.capture()); + mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, + Runnable::run, cb); + + sendMgmtFrameEventCaptor.getValue().OnFailure( + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); + mLooper.dispatchAll(); + verify(cb, never()).onAck(anyInt()); + verify(cb).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); + verify(mAlarmManager).cancel(eq(alarmListenerCaptor.getValue())); + + // verify that even if timeout is triggered afterwards, SendMgmtFrameCallback is not + // triggered again + handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm()); + mLooper.dispatchAll(); + verify(cb, never()).onAck(anyInt()); + verify(cb, times(1)).onFailure(anyInt()); + } + + /** + * Tests that the onTimeout() callback is triggered correctly. + */ + @Test + public void testSendMgmtFrameTimeout() throws Exception { + WifiNl80211Manager.SendMgmtFrameCallback cb = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); + + final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = + ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); + doNothing().when(mClientInterface) + .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); + final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + final ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class); + doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(), + alarmListenerCaptor.capture(), handlerCaptor.capture()); + mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, + Runnable::run, cb); + + handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm()); + mLooper.dispatchAll(); + verify(cb, never()).onAck(anyInt()); + verify(cb).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT); + + // verify that even if onAck() callback is triggered after timeout, + // SendMgmtFrameCallback is not triggered again + sendMgmtFrameEventCaptor.getValue().OnAck(TEST_SEND_MGMT_FRAME_ELAPSED_TIME_MS); + mLooper.dispatchAll(); + verify(cb, never()).onAck(anyInt()); + verify(cb, times(1)).onFailure(anyInt()); + } + + /** + * Tests every possible test outcome followed by every other test outcome to ensure that the + * internal state is reset correctly between calls. + * i.e. (success, success), (success, failure), (success, timeout), + * (failure, failure), (failure, success), (failure, timeout), + * (timeout, timeout), (timeout, success), (timeout, failure) + * + * Also tests that internal state is reset correctly after a transient AIDL RemoteException. + */ + @Test + public void testSendMgmtFrameMixed() throws Exception { + testSendMgmtFrameThrowsException(); + testSendMgmtFrameSuccess(); + testSendMgmtFrameSuccess(); + testSendMgmtFrameFailure(); + testSendMgmtFrameFailure(); + testSendMgmtFrameTimeout(); + testSendMgmtFrameTimeout(); + testSendMgmtFrameSuccess(); + testSendMgmtFrameTimeout(); + testSendMgmtFrameFailure(); + testSendMgmtFrameSuccess(); + } + + /** + * Tests that OnAck() does not perform any non-thread-safe operations on the binder thread. + * + * The sequence of instructions are: + * 1. post onAlarm() onto main thread + * 2. OnAck() + * 3. mLooper.dispatchAll() + * + * The actual order of execution is: + * 1. binder thread portion of OnAck() + * 2. onAlarm() (which purely executes on the main thread) + * 3. main thread portion of OnAck() + * + * If the binder thread portion of OnAck() is not thread-safe, it can possibly mess up + * onAlarm(). Tests that this does not occur. + */ + @Test + public void testSendMgmtFrameTimeoutAckThreadSafe() throws Exception { + final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = + ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); + doNothing().when(mClientInterface) + .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); + final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + final ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class); + doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(), + alarmListenerCaptor.capture(), handlerCaptor.capture()); + mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, + Runnable::run, mSendMgmtFrameCallback); + + // AlarmManager should post the onAlarm() callback onto the handler, but since we are + // triggering onAlarm() ourselves during the test, manually post onto handler + handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm()); + // OnAck posts to the handler + sendMgmtFrameEventCaptor.getValue().OnAck(TEST_SEND_MGMT_FRAME_ELAPSED_TIME_MS); + mLooper.dispatchAll(); + verify(mSendMgmtFrameCallback, never()).onAck(anyInt()); + verify(mSendMgmtFrameCallback).onFailure( + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT); + } + + /** + * See {@link #testSendMgmtFrameTimeoutAckThreadSafe()}. This test replaces OnAck() with + * OnFailure(). + */ + @Test + public void testSendMgmtFrameTimeoutFailureThreadSafe() throws Exception { + final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = + ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); + doNothing().when(mClientInterface) + .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); + final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + final ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class); + doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(), + alarmListenerCaptor.capture(), handlerCaptor.capture()); + mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, + Runnable::run, mSendMgmtFrameCallback); + + // AlarmManager should post the onAlarm() callback onto the handler, but since we are + // triggering onAlarm() ourselves during the test, manually post onto handler + handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm()); + // OnFailure posts to the handler + sendMgmtFrameEventCaptor.getValue().OnFailure( + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); + mLooper.dispatchAll(); + verify(mSendMgmtFrameCallback).onFailure( + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT); + } + + /** + * Tests getDeviceWiphyCapabililties + */ + @Test + public void testGetDeviceWiphyCapabilities() throws Exception { + DeviceWiphyCapabilities capaExpected = new DeviceWiphyCapabilities(); + + capaExpected.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11N, true); + capaExpected.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AC, true); + capaExpected.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AX, false); + capaExpected.setChannelWidthSupported(ScanResult.CHANNEL_WIDTH_160MHZ, true); + capaExpected.setChannelWidthSupported(ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ, false); + capaExpected.setMaxNumberTxSpatialStreams(2); + capaExpected.setMaxNumberRxSpatialStreams(1); + + when(mWificond.getDeviceWiphyCapabilities(TEST_INTERFACE_NAME)) + .thenReturn(capaExpected); + + DeviceWiphyCapabilities capaActual = + mWificondControl.getDeviceWiphyCapabilities(TEST_INTERFACE_NAME); + assertEquals(capaExpected, capaActual); + } + + // Create a ArgumentMatcher which captures a SingleScanSettings parameter and checks if it + // matches the provided frequency set and ssid set. + private class ScanMatcher implements ArgumentMatcher<SingleScanSettings> { + int mExpectedScanType; + private final Set<Integer> mExpectedFreqs; + private final List<byte[]> mExpectedSsids; + + ScanMatcher(int expectedScanType, Set<Integer> expectedFreqs, List<byte[]> expectedSsids) { + this.mExpectedScanType = expectedScanType; + this.mExpectedFreqs = expectedFreqs; + this.mExpectedSsids = expectedSsids; + } + + @Override + public boolean matches(SingleScanSettings settings) { + if (settings.scanType != mExpectedScanType) { + return false; + } + ArrayList<ChannelSettings> channelSettings = settings.channelSettings; + ArrayList<HiddenNetwork> hiddenNetworks = settings.hiddenNetworks; + if (mExpectedFreqs != null) { + Set<Integer> freqSet = new HashSet<Integer>(); + for (ChannelSettings channel : channelSettings) { + freqSet.add(channel.frequency); + } + if (!mExpectedFreqs.equals(freqSet)) { + return false; + } + } else { + if (channelSettings != null && channelSettings.size() > 0) { + return false; + } + } + + if (mExpectedSsids != null) { + List<byte[]> ssidSet = new ArrayList<>(); + for (HiddenNetwork network : hiddenNetworks) { + ssidSet.add(network.ssid); + } + if (!mExpectedSsids.equals(ssidSet)) { + return false; + } + + } else { + if (hiddenNetworks != null && hiddenNetworks.size() > 0) { + return false; + } + } + return true; + } + + @Override + public String toString() { + return "ScanMatcher{mExpectedFreqs=" + mExpectedFreqs + + ", mExpectedSsids=" + mExpectedSsids + '}'; + } + } + + private static class LocalNativeUtil { + private static final int SSID_BYTES_MAX_LEN = 32; + + /** + * Converts an ArrayList<Byte> of UTF_8 byte values to string. + * The string will either be: + * a) UTF-8 String encapsulated in quotes (if all the bytes are UTF-8 encodeable and non + * null), + * or + * b) Hex string with no delimiters. + * + * @param bytes List of bytes for ssid. + * @throws IllegalArgumentException for null bytes. + */ + public static String bytesToHexOrQuotedString(ArrayList<Byte> bytes) { + if (bytes == null) { + throw new IllegalArgumentException("null ssid bytes"); + } + byte[] byteArray = byteArrayFromArrayList(bytes); + // Check for 0's in the byte stream in which case we cannot convert this into a string. + if (!bytes.contains(Byte.valueOf((byte) 0))) { + CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); + try { + CharBuffer decoded = decoder.decode(ByteBuffer.wrap(byteArray)); + return "\"" + decoded.toString() + "\""; + } catch (CharacterCodingException cce) { + } + } + return hexStringFromByteArray(byteArray); + } + + /** + * Converts an ssid string to an arraylist of UTF_8 byte values. + * These forms are acceptable: + * a) UTF-8 String encapsulated in quotes, or + * b) Hex string with no delimiters. + * + * @param ssidStr String to be converted. + * @throws IllegalArgumentException for null string. + */ + public static ArrayList<Byte> decodeSsid(String ssidStr) { + ArrayList<Byte> ssidBytes = hexOrQuotedStringToBytes(ssidStr); + if (ssidBytes.size() > SSID_BYTES_MAX_LEN) { + throw new IllegalArgumentException( + "ssid bytes size out of range: " + ssidBytes.size()); + } + return ssidBytes; + } + + /** + * Convert from an array list of Byte to an array of primitive bytes. + */ + public static byte[] byteArrayFromArrayList(ArrayList<Byte> bytes) { + byte[] byteArray = new byte[bytes.size()]; + int i = 0; + for (Byte b : bytes) { + byteArray[i++] = b; + } + return byteArray; + } + + /** + * Converts a byte array to hex string. + * + * @param bytes List of bytes for ssid. + * @throws IllegalArgumentException for null bytes. + */ + public static String hexStringFromByteArray(byte[] bytes) { + if (bytes == null) { + throw new IllegalArgumentException("null hex bytes"); + } + return new String(HexEncoding.encode(bytes)).toLowerCase(); + } + + /** + * Converts an string to an arraylist of UTF_8 byte values. + * These forms are acceptable: + * a) UTF-8 String encapsulated in quotes, or + * b) Hex string with no delimiters. + * + * @param str String to be converted. + * @throws IllegalArgumentException for null string. + */ + public static ArrayList<Byte> hexOrQuotedStringToBytes(String str) { + if (str == null) { + throw new IllegalArgumentException("null string"); + } + int length = str.length(); + if ((length > 1) && (str.charAt(0) == '"') && (str.charAt(length - 1) == '"')) { + str = str.substring(1, str.length() - 1); + return stringToByteArrayList(str); + } else { + return byteArrayToArrayList(hexStringToByteArray(str)); + } + } + + /** + * Convert the string to byte array list. + * + * @return the UTF_8 char byte values of str, as an ArrayList. + * @throws IllegalArgumentException if a null or unencodable string is sent. + */ + public static ArrayList<Byte> stringToByteArrayList(String str) { + if (str == null) { + throw new IllegalArgumentException("null string"); + } + // Ensure that the provided string is UTF_8 encoded. + CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder(); + try { + ByteBuffer encoded = encoder.encode(CharBuffer.wrap(str)); + byte[] byteArray = new byte[encoded.remaining()]; + encoded.get(byteArray); + return byteArrayToArrayList(byteArray); + } catch (CharacterCodingException cce) { + throw new IllegalArgumentException("cannot be utf-8 encoded", cce); + } + } + + /** + * Convert from an array of primitive bytes to an array list of Byte. + */ + public static ArrayList<Byte> byteArrayToArrayList(byte[] bytes) { + ArrayList<Byte> byteList = new ArrayList<>(); + for (Byte b : bytes) { + byteList.add(b); + } + return byteList; + } + + /** + * Converts a hex string to byte array. + * + * @param hexStr String to be converted. + * @throws IllegalArgumentException for null string. + */ + public static byte[] hexStringToByteArray(String hexStr) { + if (hexStr == null) { + throw new IllegalArgumentException("null hex string"); + } + return HexEncoding.decode(hexStr.toCharArray(), false); + } + } +} diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java index 17ee75594c2f..6edc287068e8 100644 --- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java +++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java @@ -45,7 +45,7 @@ public class WifiP2pDeviceTest { assertEquals(devA.groupCapability, devB.groupCapability); assertEquals(devA.status, devB.status); if (devA.wfdInfo != null) { - assertEquals(devA.wfdInfo.isWfdEnabled(), devB.wfdInfo.isWfdEnabled()); + assertEquals(devA.wfdInfo.isEnabled(), devB.wfdInfo.isEnabled()); assertEquals(devA.wfdInfo.getDeviceInfoHex(), devB.wfdInfo.getDeviceInfoHex()); assertEquals(devA.wfdInfo.getControlPort(), devB.wfdInfo.getControlPort()); assertEquals(devA.wfdInfo.getMaxThroughput(), devB.wfdInfo.getMaxThroughput()); diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java index d2f11688e4e5..2a9b36b47172 100644 --- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java +++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java @@ -43,8 +43,9 @@ public class WifiP2pWfdInfoTest { @Before public void setUp() { // initialize device info flags. - mSourceInfo.setDeviceType(WifiP2pWfdInfo.WFD_SOURCE); + mSourceInfo.setDeviceType(WifiP2pWfdInfo.DEVICE_TYPE_WFD_SOURCE); mSourceInfo.setSessionAvailable(true); + mSourceInfo.setContentProtectionSupported(true); } /** @@ -54,28 +55,25 @@ public class WifiP2pWfdInfoTest { public void testSettersGetters() throws Exception { WifiP2pWfdInfo info = new WifiP2pWfdInfo(); - info.setWfdEnabled(true); - assertTrue(info.isWfdEnabled()); + info.setEnabled(true); + assertTrue(info.isEnabled()); - info.setDeviceType(WifiP2pWfdInfo.WFD_SOURCE); - assertEquals(WifiP2pWfdInfo.WFD_SOURCE, info.getDeviceType()); - - info.setCoupledSinkSupportAtSource(true); - assertTrue(info.isCoupledSinkSupportedAtSource()); - - info.setCoupledSinkSupportAtSink(true); - assertTrue(info.isCoupledSinkSupportedAtSink()); + info.setDeviceType(WifiP2pWfdInfo.DEVICE_TYPE_WFD_SOURCE); + assertEquals(WifiP2pWfdInfo.DEVICE_TYPE_WFD_SOURCE, info.getDeviceType()); info.setSessionAvailable(true); assertTrue(info.isSessionAvailable()); + info.setContentProtectionSupported(true); + assertTrue(info.isContentProtectionSupported()); + info.setControlPort(TEST_CTRL_PORT); assertEquals(TEST_CTRL_PORT, info.getControlPort()); info.setMaxThroughput(TEST_MAX_TPUT); assertEquals(TEST_MAX_TPUT, info.getMaxThroughput()); - assertEquals("0018270f0400", info.getDeviceInfoHex()); + assertEquals("0110270f0400", info.getDeviceInfoHex()); } /** diff --git a/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java b/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java index b02eebbe9a01..271339cecf1e 100644 --- a/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java +++ b/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java @@ -20,6 +20,7 @@ import android.location.Address; import android.location.Location; import android.net.MacAddress; import android.os.Parcel; +import android.util.SparseArray; import android.webkit.MimeTypeMap; import static junit.framework.Assert.assertEquals; @@ -505,6 +506,30 @@ public class ResponderLocationTest { } /** + * Test that a Civic Location sparseArray can be extracted from a valid lcr buffer. + */ + @Test + public void testLcrTestCivicLocationSparseArray() { + byte[] testLciBuffer = concatenateArrays(sTestLciIeHeader, sTestLciSE); + byte[] testLcrBuffer = + concatenateArrays(sTestLcrBufferHeader, sTestCivicLocationSEWithAddress); + ResponderLocation responderLocation = new ResponderLocation(testLciBuffer, testLcrBuffer); + + boolean valid = responderLocation.isValid(); + SparseArray<String> civicLocationSparseArray = responderLocation + .toCivicLocationSparseArray(); + + assertTrue(valid); + assertEquals("15", civicLocationSparseArray.get(CivicLocationKeys.HNO)); + assertEquals("Alto", + civicLocationSparseArray.get(CivicLocationKeys.PRIMARY_ROAD_NAME)); + assertEquals("Road", + civicLocationSparseArray.get(CivicLocationKeys.STREET_NAME_POST_MODIFIER)); + assertEquals("Mtn View", civicLocationSparseArray.get(CivicLocationKeys.CITY)); + assertEquals("94043", civicLocationSparseArray.get(CivicLocationKeys.POSTAL_CODE)); + } + + /** * Test that a URL can be extracted from a valid lcr buffer with a map image subelement. */ @Test diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java index 53bd837d29e0..e6eae416ba78 100644 --- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java +++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java @@ -54,6 +54,7 @@ public class WifiRttManagerTest { private Executor mMockLooperExecutor; private final String packageName = "some.package.name.for.rtt.app"; + private final String featureId = "some.feature.id.in.rtt.app"; @Mock public Context mockContext; @@ -70,6 +71,7 @@ public class WifiRttManagerTest { mMockLooperExecutor = mMockLooper.getNewExecutor(); when(mockContext.getOpPackageName()).thenReturn(packageName); + when(mockContext.getAttributionTag()).thenReturn(featureId); } /** @@ -87,8 +89,8 @@ public class WifiRttManagerTest { // verify ranging request passed to service mDut.startRanging(request, mMockLooperExecutor, callbackMock); - verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(null), - eq(request), callbackCaptor.capture()); + verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(featureId), + eq(null), eq(request), callbackCaptor.capture()); // service calls back with success callbackCaptor.getValue().onRangingResults(results); @@ -111,8 +113,8 @@ public class WifiRttManagerTest { // verify ranging request passed to service mDut.startRanging(request, mMockLooperExecutor, callbackMock); - verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(null), - eq(request), callbackCaptor.capture()); + verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(featureId), + eq(null), eq(request), callbackCaptor.capture()); // service calls back with failure code callbackCaptor.getValue().onRangingFailure(failureCode); diff --git a/wifi/tests/src/android/net/wifi/util/HexEncodingTest.java b/wifi/tests/src/android/net/wifi/util/HexEncodingTest.java new file mode 100644 index 000000000000..0d751389e244 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/util/HexEncodingTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 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.util; + +import static android.net.wifi.util.HexEncoding.decode; +import static android.net.wifi.util.HexEncoding.encode; +import static android.net.wifi.util.HexEncoding.encodeToString; + +import junit.framework.TestCase; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Locale; + +/** Copied from {@link libcore.libcore.util.HexEncodingTest}. */ +public class HexEncodingTest extends TestCase { + + public void testEncodeByte() { + Object[][] testCases = new Object[][]{ + {0x01, "01"}, + {0x09, "09"}, + {0x0A, "0A"}, + {0x0F, "0F"}, + {0x10, "10"}, + {0x1F, "1F"}, + {0x20, "20"}, + {0x7F, "7F"}, + {0x80, "80"}, + {0xFF, "FF"}, + }; + for (Object[] testCase : testCases) { + Number toEncode = (Number) testCase[0]; + String expected = (String) testCase[1]; + + String actualUpper = encodeToString(toEncode.byteValue(), true /* upperCase */); + assertEquals(upper(expected), actualUpper); + + String actualLower = encodeToString(toEncode.byteValue(), false /* upperCase */); + assertEquals(lower(expected), actualLower); + } + } + + public void testEncodeBytes() { + Object[][] testCases = new Object[][]{ + {"avocados".getBytes(StandardCharsets.UTF_8), "61766F6361646F73"}, + }; + + for (Object[] testCase : testCases) { + byte[] bytes = (byte[]) testCase[0]; + String encodedLower = lower((String) testCase[1]); + String encodedUpper = upper((String) testCase[1]); + + assertArraysEqual(encodedUpper.toCharArray(), encode(bytes)); + assertArraysEqual(encodedUpper.toCharArray(), encode(bytes, true /* upperCase */)); + assertArraysEqual(encodedLower.toCharArray(), encode(bytes, false /* upperCase */)); + + assertArraysEqual(bytes, decode(encode(bytes), false /* allowSingleChar */)); + + // Make sure we can handle lower case hex encodings as well. + assertArraysEqual(bytes, + decode(encodedLower.toCharArray(), false /* allowSingleChar */)); + } + } + + public void testDecode_allow4Bit() { + assertArraysEqual(new byte[]{6}, decode("6".toCharArray(), true)); + assertArraysEqual(new byte[]{6, 0x76}, decode("676".toCharArray(), true)); + } + + public void testDecode_disallow4Bit() { + try { + decode("676".toCharArray(), false /* allowSingleChar */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testDecode_invalid() { + try { + decode("DEADBARD".toCharArray(), false /* allowSingleChar */); + fail(); + } catch (IllegalArgumentException expected) { + } + + // This demonstrates a difference in behaviour from apache commons : apache + // commons uses Character.isDigit and would successfully decode a string with + // arabic and devanagari characters. + try { + decode("६१٧٥٥F6361646F73".toCharArray(), false /* allowSingleChar */); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + decode("#%6361646F73".toCharArray(), false /* allowSingleChar */); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + private static void assertArraysEqual(char[] lhs, char[] rhs) { + assertEquals(new String(lhs), new String(rhs)); + } + + private static void assertArraysEqual(byte[] lhs, byte[] rhs) { + assertEquals(Arrays.toString(lhs), Arrays.toString(rhs)); + } + + private static String lower(String string) { + return string.toLowerCase(Locale.ROOT); + } + + private static String upper(String string) { + return string.toUpperCase(Locale.ROOT); + } +} |