From 3976a1d61b3d8f75eba9921428748e304c903365 Mon Sep 17 00:00:00 2001 From: David Su Date: Tue, 29 Dec 2020 04:49:50 +0000 Subject: frameworks/base/wifi: unpack non-updatable/ folder Everything remaining in frameworks/base/wifi is non-updatable. Thus, unpack non-updatable/ folder and move everything under wifi/. Bug: 137323948 Test: presubmit Change-Id: I6e1bd3fe6335b5dbe5f8ffa84a84df6434c95674 --- wifi/java/Android.bp | 35 + .../net/wifi/SoftApConfToXmlMigrationUtil.java | 284 +++++ wifi/java/src/android/net/wifi/WifiMigration.java | 558 +++++++++ .../android/net/wifi/WifiNetworkScoreCache.java | 316 +++++ .../android/net/wifi/nl80211/ChannelSettings.java | 95 ++ .../net/wifi/nl80211/DeviceWiphyCapabilities.java | 283 +++++ .../android/net/wifi/nl80211/HiddenNetwork.java | 87 ++ .../android/net/wifi/nl80211/NativeScanResult.java | 310 +++++ .../android/net/wifi/nl80211/NativeWifiClient.java | 103 ++ .../src/android/net/wifi/nl80211/PnoNetwork.java | 171 +++ .../src/android/net/wifi/nl80211/PnoSettings.java | 209 ++++ .../android/net/wifi/nl80211/RadioChainInfo.java | 125 ++ .../net/wifi/nl80211/SingleScanSettings.java | 118 ++ .../net/wifi/nl80211/WifiNl80211Manager.java | 1287 ++++++++++++++++++++ wifi/migration_samples/README.txt | 35 + wifi/migration_samples/Shared_WifiConfigStore.xml | 200 +++ .../Shared_WifiConfigStoreSoftAp.xml | 22 + wifi/migration_samples/User_WifiConfigStore.xml | 81 ++ .../User_WifiConfigStoreNetworkSuggestions.xml | 155 +++ wifi/non-updatable/java/Android.bp | 35 - .../net/wifi/SoftApConfToXmlMigrationUtil.java | 284 ----- .../java/src/android/net/wifi/WifiMigration.java | 558 --------- .../android/net/wifi/WifiNetworkScoreCache.java | 316 ----- .../android/net/wifi/nl80211/ChannelSettings.java | 95 -- .../net/wifi/nl80211/DeviceWiphyCapabilities.java | 283 ----- .../android/net/wifi/nl80211/HiddenNetwork.java | 87 -- .../android/net/wifi/nl80211/NativeScanResult.java | 310 ----- .../android/net/wifi/nl80211/NativeWifiClient.java | 103 -- .../src/android/net/wifi/nl80211/PnoNetwork.java | 171 --- .../src/android/net/wifi/nl80211/PnoSettings.java | 209 ---- .../android/net/wifi/nl80211/RadioChainInfo.java | 125 -- .../net/wifi/nl80211/SingleScanSettings.java | 118 -- .../net/wifi/nl80211/WifiNl80211Manager.java | 1287 -------------------- wifi/non-updatable/migration_samples/README.txt | 35 - .../migration_samples/Shared_WifiConfigStore.xml | 200 --- .../Shared_WifiConfigStoreSoftAp.xml | 22 - .../migration_samples/User_WifiConfigStore.xml | 81 -- .../User_WifiConfigStoreNetworkSuggestions.xml | 155 --- wifi/non-updatable/tests/Android.bp | 44 - wifi/non-updatable/tests/AndroidManifest.xml | 38 - wifi/non-updatable/tests/AndroidTest.xml | 28 - wifi/non-updatable/tests/README.md | 32 - .../net/wifi/SoftApConfToXmlMigrationUtilTest.java | 199 --- .../net/wifi/WifiNetworkScoreCacheTest.java | 225 ---- .../wifi/nl80211/DeviceWiphyCapabilitiesTest.java | 88 -- .../net/wifi/nl80211/NativeScanResultTest.java | 87 -- .../android/net/wifi/nl80211/PnoSettingsTest.java | 112 -- .../net/wifi/nl80211/SingleScanSettingsTest.java | 116 -- .../net/wifi/nl80211/WifiNl80211ManagerTest.java | 1265 ------------------- wifi/tests/Android.bp | 44 + wifi/tests/AndroidManifest.xml | 38 + wifi/tests/AndroidTest.xml | 28 + wifi/tests/README.md | 32 + .../net/wifi/SoftApConfToXmlMigrationUtilTest.java | 199 +++ .../net/wifi/WifiNetworkScoreCacheTest.java | 225 ++++ .../wifi/nl80211/DeviceWiphyCapabilitiesTest.java | 88 ++ .../net/wifi/nl80211/NativeScanResultTest.java | 87 ++ .../android/net/wifi/nl80211/PnoSettingsTest.java | 112 ++ .../net/wifi/nl80211/SingleScanSettingsTest.java | 116 ++ .../net/wifi/nl80211/WifiNl80211ManagerTest.java | 1265 +++++++++++++++++++ 60 files changed, 6708 insertions(+), 6708 deletions(-) create mode 100644 wifi/java/Android.bp create mode 100755 wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java create mode 100755 wifi/java/src/android/net/wifi/WifiMigration.java create mode 100755 wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java create mode 100644 wifi/java/src/android/net/wifi/nl80211/ChannelSettings.java create mode 100644 wifi/java/src/android/net/wifi/nl80211/DeviceWiphyCapabilities.java create mode 100644 wifi/java/src/android/net/wifi/nl80211/HiddenNetwork.java create mode 100644 wifi/java/src/android/net/wifi/nl80211/NativeScanResult.java create mode 100644 wifi/java/src/android/net/wifi/nl80211/NativeWifiClient.java create mode 100644 wifi/java/src/android/net/wifi/nl80211/PnoNetwork.java create mode 100644 wifi/java/src/android/net/wifi/nl80211/PnoSettings.java create mode 100644 wifi/java/src/android/net/wifi/nl80211/RadioChainInfo.java create mode 100644 wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java create mode 100644 wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java create mode 100644 wifi/migration_samples/README.txt create mode 100644 wifi/migration_samples/Shared_WifiConfigStore.xml create mode 100644 wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml create mode 100644 wifi/migration_samples/User_WifiConfigStore.xml create mode 100644 wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml delete mode 100644 wifi/non-updatable/java/Android.bp delete mode 100755 wifi/non-updatable/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java delete mode 100755 wifi/non-updatable/java/src/android/net/wifi/WifiMigration.java delete mode 100755 wifi/non-updatable/java/src/android/net/wifi/WifiNetworkScoreCache.java delete mode 100644 wifi/non-updatable/java/src/android/net/wifi/nl80211/ChannelSettings.java delete mode 100644 wifi/non-updatable/java/src/android/net/wifi/nl80211/DeviceWiphyCapabilities.java delete mode 100644 wifi/non-updatable/java/src/android/net/wifi/nl80211/HiddenNetwork.java delete mode 100644 wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeScanResult.java delete mode 100644 wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeWifiClient.java delete mode 100644 wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoNetwork.java delete mode 100644 wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoSettings.java delete mode 100644 wifi/non-updatable/java/src/android/net/wifi/nl80211/RadioChainInfo.java delete mode 100644 wifi/non-updatable/java/src/android/net/wifi/nl80211/SingleScanSettings.java delete mode 100644 wifi/non-updatable/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java delete mode 100644 wifi/non-updatable/migration_samples/README.txt delete mode 100644 wifi/non-updatable/migration_samples/Shared_WifiConfigStore.xml delete mode 100644 wifi/non-updatable/migration_samples/Shared_WifiConfigStoreSoftAp.xml delete mode 100644 wifi/non-updatable/migration_samples/User_WifiConfigStore.xml delete mode 100644 wifi/non-updatable/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml delete mode 100644 wifi/non-updatable/tests/Android.bp delete mode 100644 wifi/non-updatable/tests/AndroidManifest.xml delete mode 100644 wifi/non-updatable/tests/AndroidTest.xml delete mode 100644 wifi/non-updatable/tests/README.md delete mode 100644 wifi/non-updatable/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java delete mode 100644 wifi/non-updatable/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java delete mode 100644 wifi/non-updatable/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java delete mode 100644 wifi/non-updatable/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java delete mode 100644 wifi/non-updatable/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java delete mode 100644 wifi/non-updatable/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java delete mode 100644 wifi/non-updatable/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java create mode 100644 wifi/tests/Android.bp create mode 100644 wifi/tests/AndroidManifest.xml create mode 100644 wifi/tests/AndroidTest.xml create mode 100644 wifi/tests/README.md create mode 100644 wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java create mode 100644 wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java create mode 100644 wifi/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java create mode 100644 wifi/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java create mode 100644 wifi/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java create mode 100644 wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java create mode 100644 wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java (limited to 'wifi') diff --git a/wifi/java/Android.bp b/wifi/java/Android.bp new file mode 100644 index 000000000000..b35b4be55818 --- /dev/null +++ b/wifi/java/Android.bp @@ -0,0 +1,35 @@ +// 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. + +// This directory contains framework Wifi APIs that are not part of the Wifi module (i.e. not +// updatable), and are generally only called by the Wifi module. + +// necessary since we only want the `path` property to only refer to these files +filegroup { + name: "framework-wifi-non-updatable-sources-internal", + srcs: ["src/**/*.java"], + path: "src", + visibility: ["//visibility:private"], +} + +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. + ":framework-wifi-non-updatable-sources-internal", + ":libwificond_ipc_aidl", + ], +} diff --git a/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java b/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java new file mode 100755 index 000000000000..c5472ce34478 --- /dev/null +++ b/wifi/java/src/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: + *
  • 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.
  • + *
  • Most of this class was copied over from WifiApConfigStore class in Android 10 and + * SoftApStoreData class in Android 11
  • + * @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/src/android/net/wifi/WifiMigration.java b/wifi/java/src/android/net/wifi/WifiMigration.java new file mode 100755 index 000000000000..4fabc0b0babc --- /dev/null +++ b/wifi/java/src/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//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//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 STORE_ID_TO_FILE_NAME = + new SparseArray() {{ + 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. + *

    + * Expected AOSP format is available in the sample files under {@code + * frameworks/base/wifi/non-updatable/migration_samples/}. + *

    + *

    + * Note: + *

  • 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.
  • + *
  • 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.
  • + *
  • Once the migration is done, the apex will invoke + * {@link #removeSharedConfigStoreFile(int)} to delete the store file.
  • + *
  • 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. + *
  • 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). + *
  • + * + * @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. + *

    + * Expected AOSP format is available in the sample files under {@code + * frameworks/base/wifi/non-updatable/migration_samples/}. + *

    + *

    + * Note: + *

  • 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.
  • + *
  • 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.
  • + *
  • Once the migration is done, the apex will invoke + * {@link #removeUserConfigStoreFile(int, UserHandle)} to delete the store file.
  • + *
  • 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. + *
  • + *
  • 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). + *
  • + * + * @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 CREATOR = + new Parcelable.Creator() { + @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. + * + *

    + * Note: + *

  • 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. + *
  • + * + * @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/src/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java new file mode 100755 index 000000000000..39036580e2ef --- /dev/null +++ b/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java @@ -0,0 +1,316 @@ +/* + * 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.Manifest.permission; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.net.INetworkScoreCache; +import android.net.NetworkKey; +import android.net.ScoredNetwork; +import android.os.Handler; +import android.os.Process; +import android.util.Log; +import android.util.LruCache; + +import com.android.internal.annotations.GuardedBy; + +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 { + private static final String TAG = "WifiNetworkScoreCache"; + private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); + + // A Network scorer returns a score in the range [-128, +127] + // We treat the lowest possible score as though there were no score, effectively allowing the + // scorer to provide an RSSI threshold below which a network should not be used. + public static final int INVALID_NETWORK_SCORE = Byte.MIN_VALUE; + + /** Default number entries to be stored in the {@link LruCache}. */ + private static final int DEFAULT_MAX_CACHE_SIZE = 100; + + // See {@link #CacheListener}. + @Nullable + @GuardedBy("mLock") + private CacheListener mListener; + + private final Context mContext; + private final Object mLock = new Object(); + + // The key is of the form "" + // TODO: What about SSIDs that can't be encoded as UTF-8? + @GuardedBy("mLock") + private final LruCache mCache; + + public WifiNetworkScoreCache(Context context) { + this(context, null /* listener */); + } + + /** + * Instantiates a WifiNetworkScoreCache. + * + * @param context Application context + * @param listener CacheListener for cache updates + */ + public WifiNetworkScoreCache(Context context, @Nullable CacheListener listener) { + this(context, listener, DEFAULT_MAX_CACHE_SIZE); + } + + public WifiNetworkScoreCache( + Context context, @Nullable CacheListener listener, int maxCacheSize) { + mContext = context.getApplicationContext(); + mListener = listener; + mCache = new LruCache<>(maxCacheSize); + } + + @Override public final void updateScores(List networks) { + if (networks == null || networks.isEmpty()) { + return; + } + if (DBG) { + Log.d(TAG, "updateScores list size=" + networks.size()); + } + + boolean changed = false; + + synchronized (mLock) { + for (ScoredNetwork network : networks) { + String networkKey = buildNetworkKey(network); + if (networkKey == null) { + if (DBG) { + Log.d(TAG, "Failed to build network key for ScoredNetwork" + network); + } + continue; + } + mCache.put(networkKey, network); + changed = true; + } + + if (mListener != null && changed) { + mListener.post(networks); + } + } + } + + @Override public final void clearScores() { + synchronized (mLock) { + mCache.evictAll(); + } + } + + /** + * Returns whether there is any score info for the given ScanResult. + * + * This includes null-score info, so it should only be used when determining whether to request + * scores from the network scorer. + */ + public boolean isScoredNetwork(ScanResult result) { + return getScoredNetwork(result) != null; + } + + /** + * Returns whether there is a non-null score curve for the given ScanResult. + * + * A null score curve has special meaning - we should never connect to an ephemeral network if + * the score curve is null. + */ + public boolean hasScoreCurve(ScanResult result) { + ScoredNetwork network = getScoredNetwork(result); + return network != null && network.rssiCurve != null; + } + + public int getNetworkScore(ScanResult result) { + int score = INVALID_NETWORK_SCORE; + + ScoredNetwork network = getScoredNetwork(result); + if (network != null && network.rssiCurve != null) { + score = network.rssiCurve.lookupScore(result.level); + if (DBG) { + Log.d(TAG, "getNetworkScore found scored network " + network.networkKey + + " score " + Integer.toString(score) + + " RSSI " + result.level); + } + } + return score; + } + + /** + * Returns the ScoredNetwork metered hint for a given ScanResult. + * + * If there is no ScoredNetwork associated with the ScanResult then false will be returned. + */ + public boolean getMeteredHint(ScanResult result) { + ScoredNetwork network = getScoredNetwork(result); + return network != null && network.meteredHint; + } + + public int getNetworkScore(ScanResult result, boolean isActiveNetwork) { + int score = INVALID_NETWORK_SCORE; + + ScoredNetwork network = getScoredNetwork(result); + if (network != null && network.rssiCurve != null) { + score = network.rssiCurve.lookupScore(result.level, isActiveNetwork); + if (DBG) { + Log.d(TAG, "getNetworkScore found scored network " + network.networkKey + + " score " + Integer.toString(score) + + " RSSI " + result.level + + " isActiveNetwork " + isActiveNetwork); + } + } + return score; + } + + @Nullable + public ScoredNetwork getScoredNetwork(ScanResult result) { + String key = buildNetworkKey(result); + if (key == null) return null; + + synchronized (mLock) { + ScoredNetwork network = mCache.get(key); + return network; + } + } + + /** Returns the ScoredNetwork for the given key. */ + @Nullable + public ScoredNetwork getScoredNetwork(NetworkKey networkKey) { + String key = buildNetworkKey(networkKey); + if (key == null) { + if (DBG) { + Log.d(TAG, "Could not build key string for Network Key: " + networkKey); + } + return null; + } + synchronized (mLock) { + return mCache.get(key); + } + } + + private String buildNetworkKey(ScoredNetwork network) { + if (network == null) { + return null; + } + return buildNetworkKey(network.networkKey); + } + + private String buildNetworkKey(NetworkKey networkKey) { + if (networkKey == null) { + return null; + } + if (networkKey.wifiKey == null) return null; + if (networkKey.type == NetworkKey.TYPE_WIFI) { + String key = networkKey.wifiKey.ssid; + if (key == null) return null; + if (networkKey.wifiKey.bssid != null) { + key = key + networkKey.wifiKey.bssid; + } + return key; + } + return null; + } + + private String buildNetworkKey(ScanResult result) { + if (result == null || result.SSID == null) { + return null; + } + StringBuilder key = new StringBuilder("\""); + key.append(result.SSID); + key.append("\""); + if (result.BSSID != null) { + key.append(result.BSSID); + } + return key.toString(); + } + + @Override protected final void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG); + String header = String.format("WifiNetworkScoreCache (%s/%d)", + mContext.getPackageName(), Process.myUid()); + writer.println(header); + writer.println(" All score curves:"); + synchronized (mLock) { + for (ScoredNetwork score : mCache.snapshot().values()) { + writer.println(" " + score); + } + writer.println(" Network scores for latest ScanResults:"); + WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + for (ScanResult scanResult : wifiManager.getScanResults()) { + writer.println( + " " + buildNetworkKey(scanResult) + ": " + getNetworkScore(scanResult)); + } + } + } + + /** Registers a CacheListener instance, replacing the previous listener if it existed. */ + public void registerListener(CacheListener listener) { + synchronized (mLock) { + mListener = listener; + } + } + + /** Removes the registered CacheListener. */ + public void unregisterListener() { + synchronized (mLock) { + mListener = null; + } + } + + /** Listener for updates to the cache inside WifiNetworkScoreCache. */ + public abstract static class CacheListener { + private Handler mHandler; + + /** + * Constructor for CacheListener. + * + * @param handler the Handler on which to invoke the {@link #networkCacheUpdated} method. + * This cannot be null. + */ + public CacheListener(@NonNull Handler handler) { + Objects.requireNonNull(handler); + mHandler = handler; + } + + /** Invokes the {@link #networkCacheUpdated(List)} method on the handler. */ + void post(List updatedNetworks) { + mHandler.post(new Runnable() { + @Override + public void run() { + networkCacheUpdated(updatedNetworks); + } + }); + } + + /** + * Invoked whenever the cache is updated. + * + *

    Clearing the cache does not invoke this method. + * + * @param updatedNetworks the networks that were updated + */ + public abstract void networkCacheUpdated(List updatedNetworks); + } +} diff --git a/wifi/java/src/android/net/wifi/nl80211/ChannelSettings.java b/wifi/java/src/android/net/wifi/nl80211/ChannelSettings.java new file mode 100644 index 000000000000..4c14fd499c28 --- /dev/null +++ b/wifi/java/src/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 CREATOR = + new Parcelable.Creator() { + /** + * 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/src/android/net/wifi/nl80211/DeviceWiphyCapabilities.java b/wifi/java/src/android/net/wifi/nl80211/DeviceWiphyCapabilities.java new file mode 100644 index 000000000000..bb0cc975a3db --- /dev/null +++ b/wifi/java/src/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 CREATOR = + new Parcelable.Creator() { + /** + * 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/src/android/net/wifi/nl80211/HiddenNetwork.java b/wifi/java/src/android/net/wifi/nl80211/HiddenNetwork.java new file mode 100644 index 000000000000..b1475b2c7b43 --- /dev/null +++ b/wifi/java/src/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 CREATOR = + new Parcelable.Creator() { + /** + * 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/src/android/net/wifi/nl80211/NativeScanResult.java b/wifi/java/src/android/net/wifi/nl80211/NativeScanResult.java new file mode 100644 index 000000000000..a8e999973fe8 --- /dev/null +++ b/wifi/java/src/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 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 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 CREATOR = + new Parcelable.Creator() { + @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/src/android/net/wifi/nl80211/NativeWifiClient.java b/wifi/java/src/android/net/wifi/nl80211/NativeWifiClient.java new file mode 100644 index 000000000000..984d7d034302 --- /dev/null +++ b/wifi/java/src/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 CREATOR = + new Parcelable.Creator() { + @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/src/android/net/wifi/nl80211/PnoNetwork.java b/wifi/java/src/android/net/wifi/nl80211/PnoNetwork.java new file mode 100644 index 000000000000..e8eff09583b9 --- /dev/null +++ b/wifi/java/src/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 CREATOR = + new Parcelable.Creator() { + @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/src/android/net/wifi/nl80211/PnoSettings.java b/wifi/java/src/android/net/wifi/nl80211/PnoSettings.java new file mode 100644 index 000000000000..00ebe624ba0d --- /dev/null +++ b/wifi/java/src/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 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 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 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 CREATOR = + new Parcelable.Creator() { + @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/src/android/net/wifi/nl80211/RadioChainInfo.java b/wifi/java/src/android/net/wifi/nl80211/RadioChainInfo.java new file mode 100644 index 000000000000..2c12163dc9a1 --- /dev/null +++ b/wifi/java/src/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 CREATOR = + new Parcelable.Creator() { + /** + * 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/src/android/net/wifi/nl80211/SingleScanSettings.java b/wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java new file mode 100644 index 000000000000..24b1854fbf6c --- /dev/null +++ b/wifi/java/src/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; + public ArrayList 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 CREATOR = + new Parcelable.Creator() { + /** + * 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(); + in.readTypedList(result.channelSettings, ChannelSettings.CREATOR); + result.hiddenNetworks = new ArrayList(); + 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/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java new file mode 100644 index 000000000000..4116234c4c8d --- /dev/null +++ b/wifi/java/src/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 mClientInterfaces = new HashMap<>(); + private HashMap mApInterfaces = new HashMap<>(); + private HashMap mWificondScanners = new HashMap<>(); + private HashMap mScanEventHandlers = new HashMap<>(); + private HashMap mPnoScanEventHandlers = new HashMap<>(); + private HashMap 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 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 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 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 freqs, @Nullable List 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). + *

    + * 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 keyManagement; + /** + * Supported pairwise cipher types defined + * in {@link android.net.wifi.WifiAnnotations.Cipher}. + */ + @NonNull public final List 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 keyManagement, + @NonNull List pairwiseCipher, + @WifiAnnotations.Cipher int groupCipher) { + this.protocol = protocol; + this.keyManagement = (keyManagement != null) + ? keyManagement : new ArrayList(); + this.pairwiseCipher = (pairwiseCipher != null) + ? pairwiseCipher : new ArrayList(); + 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/migration_samples/README.txt b/wifi/migration_samples/README.txt new file mode 100644 index 000000000000..264debaa51f9 --- /dev/null +++ b/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//wifi/WifiConfigStore.xml +AOSP Path in Android 11: /data/misc_ce//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//wifi/WifiConfigStoreNetworkSuggestions.xml +AOSP Path in Android 11: /data/misc_ce//apexdata/com.android/wifi/WifiConfigStoreNetworkSuggestions.xml +Sample File (in this folder): User_WifiConfigStoreNetworkSuggestions.xml diff --git a/wifi/migration_samples/Shared_WifiConfigStore.xml b/wifi/migration_samples/Shared_WifiConfigStore.xml new file mode 100644 index 000000000000..3063276fae6a --- /dev/null +++ b/wifi/migration_samples/Shared_WifiConfigStore.xml @@ -0,0 +1,200 @@ + + + + + + +"OPEN_SSID"NONE +"OPEN_SSID" + + + + + +01 +03 + +2f +0e +04 + + + + + + + + + + + + + + + + + +android.uid.system:1000 + +android.uid.system:1000 + + + +ce:b1:36:bb:71:ac + + + + +NETWORK_SELECTION_ENABLED +NETWORK_SELECTION_ENABLE +"ENTERPRISE_SSID"WPA_EAP + + + +DHCP +NONE + + + + +"ENTERPRISE_SSID"WPA_EAP +"ENTERPRISE_SSID" + + + + + +0c +03 + +2f +0e +04 + + + + + + + + + + + + + + + + + +android.uid.system:1000 + +android.uid.system:1000 + + + +f6:b3:94:44:40:87 + + + + +NETWORK_SELECTION_TEMPORARY_DISABLED +NETWORK_SELECTION_DISABLED_AUTHENTICATION_FAILURE + + + + +DHCP +NONE + + +adadadasdaddsa +asdadaddadasd +adasdadadad + + + +0 + + + +adsad +/system/etc/security/cacerts + + + + + + + + + + +"WPA3_SSID"SAE +"WPA3_SSID" +"sfsdfsfdsfsdf" + + + + +0001 +02 + +28 +0c +04 + + + + + + + + + + + + + + + + + +android.uid.system:1000 + +android.uid.system:1000 + + + +a6:3d:b0:13:ed:41 + + + + +NETWORK_SELECTION_PERMANENTLY_DISABLED +NETWORK_SELECTION_DISABLED_BY_WRONG_PASSWORD + + + + +DHCP +NONE + + + + + + + + + + + + + + + + + + + diff --git a/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml b/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml new file mode 100644 index 000000000000..fd99dd3df8b2 --- /dev/null +++ b/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml @@ -0,0 +1,22 @@ + + + + +HOTSPOT_SSID + + + + +blahblahblah + + + + + +00:11:22:33:44:55 + + +aa:bb:cc:dd:ee:ff + + + diff --git a/wifi/migration_samples/User_WifiConfigStore.xml b/wifi/migration_samples/User_WifiConfigStore.xml new file mode 100644 index 000000000000..67d5aab215f2 --- /dev/null +++ b/wifi/migration_samples/User_WifiConfigStore.xml @@ -0,0 +1,81 @@ + + + + + + + + + +com.android.certinstaller + +HS2_0_0 + + + + + + + + + + + + + + + + + +Passpoint.net +Passpoint Friendly Name + + + + + + + + + + +passpoint.com + + +blahblahblah +doubleblahlah + + + + +PAP + + + + + + + + + + + + + + +com.android.cts.verifier + +OPEN_SSID +00:11:22:33:44:55 + + + + + + + + + + + + diff --git a/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml b/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml new file mode 100644 index 000000000000..4ecdd29709b4 --- /dev/null +++ b/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml @@ -0,0 +1,155 @@ + + + + + +com.android.cts.verifier + + + + + + + +"OPEN_SSID"NONE +"OPEN_SSID" + + + + + +01 + + + + + + + + + + + + + + + + + + + + + + +com.android.cts.verifier + + + + + +02:00:00:00:00:00 + + + + + + + + + + + +passpoint.net + + + + + + + + + + + + + + + + + + +passpoint.net + + + + + + + + + + +com.android.cts.verifier + + + + + +02:00:00:00:00:00 + + + + + + + + + + + + + + + + +passpoint.net +Passpoint Friendly Name + + + + + + + + + + +passpoint.com + + +blahblahblah +doubleblahblah + + + + +PAP + + + + + + + + + + + + + + + + + + + diff --git a/wifi/non-updatable/java/Android.bp b/wifi/non-updatable/java/Android.bp deleted file mode 100644 index b35b4be55818..000000000000 --- a/wifi/non-updatable/java/Android.bp +++ /dev/null @@ -1,35 +0,0 @@ -// 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. - -// This directory contains framework Wifi APIs that are not part of the Wifi module (i.e. not -// updatable), and are generally only called by the Wifi module. - -// necessary since we only want the `path` property to only refer to these files -filegroup { - name: "framework-wifi-non-updatable-sources-internal", - srcs: ["src/**/*.java"], - path: "src", - visibility: ["//visibility:private"], -} - -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. - ":framework-wifi-non-updatable-sources-internal", - ":libwificond_ipc_aidl", - ], -} diff --git a/wifi/non-updatable/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java b/wifi/non-updatable/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java deleted file mode 100755 index c5472ce34478..000000000000 --- a/wifi/non-updatable/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * 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: - *

  • 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.
  • - *
  • Most of this class was copied over from WifiApConfigStore class in Android 10 and - * SoftApStoreData class in Android 11
  • - * @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/non-updatable/java/src/android/net/wifi/WifiMigration.java b/wifi/non-updatable/java/src/android/net/wifi/WifiMigration.java deleted file mode 100755 index 4fabc0b0babc..000000000000 --- a/wifi/non-updatable/java/src/android/net/wifi/WifiMigration.java +++ /dev/null @@ -1,558 +0,0 @@ -/* - * 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//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//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 STORE_ID_TO_FILE_NAME = - new SparseArray() {{ - 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. - *

    - * Expected AOSP format is available in the sample files under {@code - * frameworks/base/wifi/non-updatable/migration_samples/}. - *

    - *

    - * Note: - *

  • 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.
  • - *
  • 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.
  • - *
  • Once the migration is done, the apex will invoke - * {@link #removeSharedConfigStoreFile(int)} to delete the store file.
  • - *
  • 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. - *
  • 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). - *
  • - * - * @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. - *

    - * Expected AOSP format is available in the sample files under {@code - * frameworks/base/wifi/non-updatable/migration_samples/}. - *

    - *

    - * Note: - *

  • 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.
  • - *
  • 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.
  • - *
  • Once the migration is done, the apex will invoke - * {@link #removeUserConfigStoreFile(int, UserHandle)} to delete the store file.
  • - *
  • 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. - *
  • - *
  • 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). - *
  • - * - * @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 CREATOR = - new Parcelable.Creator() { - @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. - * - *

    - * Note: - *

  • 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. - *
  • - * - * @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/non-updatable/java/src/android/net/wifi/WifiNetworkScoreCache.java b/wifi/non-updatable/java/src/android/net/wifi/WifiNetworkScoreCache.java deleted file mode 100755 index 39036580e2ef..000000000000 --- a/wifi/non-updatable/java/src/android/net/wifi/WifiNetworkScoreCache.java +++ /dev/null @@ -1,316 +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.Manifest.permission; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.net.INetworkScoreCache; -import android.net.NetworkKey; -import android.net.ScoredNetwork; -import android.os.Handler; -import android.os.Process; -import android.util.Log; -import android.util.LruCache; - -import com.android.internal.annotations.GuardedBy; - -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 { - private static final String TAG = "WifiNetworkScoreCache"; - private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); - - // A Network scorer returns a score in the range [-128, +127] - // We treat the lowest possible score as though there were no score, effectively allowing the - // scorer to provide an RSSI threshold below which a network should not be used. - public static final int INVALID_NETWORK_SCORE = Byte.MIN_VALUE; - - /** Default number entries to be stored in the {@link LruCache}. */ - private static final int DEFAULT_MAX_CACHE_SIZE = 100; - - // See {@link #CacheListener}. - @Nullable - @GuardedBy("mLock") - private CacheListener mListener; - - private final Context mContext; - private final Object mLock = new Object(); - - // The key is of the form "" - // TODO: What about SSIDs that can't be encoded as UTF-8? - @GuardedBy("mLock") - private final LruCache mCache; - - public WifiNetworkScoreCache(Context context) { - this(context, null /* listener */); - } - - /** - * Instantiates a WifiNetworkScoreCache. - * - * @param context Application context - * @param listener CacheListener for cache updates - */ - public WifiNetworkScoreCache(Context context, @Nullable CacheListener listener) { - this(context, listener, DEFAULT_MAX_CACHE_SIZE); - } - - public WifiNetworkScoreCache( - Context context, @Nullable CacheListener listener, int maxCacheSize) { - mContext = context.getApplicationContext(); - mListener = listener; - mCache = new LruCache<>(maxCacheSize); - } - - @Override public final void updateScores(List networks) { - if (networks == null || networks.isEmpty()) { - return; - } - if (DBG) { - Log.d(TAG, "updateScores list size=" + networks.size()); - } - - boolean changed = false; - - synchronized (mLock) { - for (ScoredNetwork network : networks) { - String networkKey = buildNetworkKey(network); - if (networkKey == null) { - if (DBG) { - Log.d(TAG, "Failed to build network key for ScoredNetwork" + network); - } - continue; - } - mCache.put(networkKey, network); - changed = true; - } - - if (mListener != null && changed) { - mListener.post(networks); - } - } - } - - @Override public final void clearScores() { - synchronized (mLock) { - mCache.evictAll(); - } - } - - /** - * Returns whether there is any score info for the given ScanResult. - * - * This includes null-score info, so it should only be used when determining whether to request - * scores from the network scorer. - */ - public boolean isScoredNetwork(ScanResult result) { - return getScoredNetwork(result) != null; - } - - /** - * Returns whether there is a non-null score curve for the given ScanResult. - * - * A null score curve has special meaning - we should never connect to an ephemeral network if - * the score curve is null. - */ - public boolean hasScoreCurve(ScanResult result) { - ScoredNetwork network = getScoredNetwork(result); - return network != null && network.rssiCurve != null; - } - - public int getNetworkScore(ScanResult result) { - int score = INVALID_NETWORK_SCORE; - - ScoredNetwork network = getScoredNetwork(result); - if (network != null && network.rssiCurve != null) { - score = network.rssiCurve.lookupScore(result.level); - if (DBG) { - Log.d(TAG, "getNetworkScore found scored network " + network.networkKey - + " score " + Integer.toString(score) - + " RSSI " + result.level); - } - } - return score; - } - - /** - * Returns the ScoredNetwork metered hint for a given ScanResult. - * - * If there is no ScoredNetwork associated with the ScanResult then false will be returned. - */ - public boolean getMeteredHint(ScanResult result) { - ScoredNetwork network = getScoredNetwork(result); - return network != null && network.meteredHint; - } - - public int getNetworkScore(ScanResult result, boolean isActiveNetwork) { - int score = INVALID_NETWORK_SCORE; - - ScoredNetwork network = getScoredNetwork(result); - if (network != null && network.rssiCurve != null) { - score = network.rssiCurve.lookupScore(result.level, isActiveNetwork); - if (DBG) { - Log.d(TAG, "getNetworkScore found scored network " + network.networkKey - + " score " + Integer.toString(score) - + " RSSI " + result.level - + " isActiveNetwork " + isActiveNetwork); - } - } - return score; - } - - @Nullable - public ScoredNetwork getScoredNetwork(ScanResult result) { - String key = buildNetworkKey(result); - if (key == null) return null; - - synchronized (mLock) { - ScoredNetwork network = mCache.get(key); - return network; - } - } - - /** Returns the ScoredNetwork for the given key. */ - @Nullable - public ScoredNetwork getScoredNetwork(NetworkKey networkKey) { - String key = buildNetworkKey(networkKey); - if (key == null) { - if (DBG) { - Log.d(TAG, "Could not build key string for Network Key: " + networkKey); - } - return null; - } - synchronized (mLock) { - return mCache.get(key); - } - } - - private String buildNetworkKey(ScoredNetwork network) { - if (network == null) { - return null; - } - return buildNetworkKey(network.networkKey); - } - - private String buildNetworkKey(NetworkKey networkKey) { - if (networkKey == null) { - return null; - } - if (networkKey.wifiKey == null) return null; - if (networkKey.type == NetworkKey.TYPE_WIFI) { - String key = networkKey.wifiKey.ssid; - if (key == null) return null; - if (networkKey.wifiKey.bssid != null) { - key = key + networkKey.wifiKey.bssid; - } - return key; - } - return null; - } - - private String buildNetworkKey(ScanResult result) { - if (result == null || result.SSID == null) { - return null; - } - StringBuilder key = new StringBuilder("\""); - key.append(result.SSID); - key.append("\""); - if (result.BSSID != null) { - key.append(result.BSSID); - } - return key.toString(); - } - - @Override protected final void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG); - String header = String.format("WifiNetworkScoreCache (%s/%d)", - mContext.getPackageName(), Process.myUid()); - writer.println(header); - writer.println(" All score curves:"); - synchronized (mLock) { - for (ScoredNetwork score : mCache.snapshot().values()) { - writer.println(" " + score); - } - writer.println(" Network scores for latest ScanResults:"); - WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); - for (ScanResult scanResult : wifiManager.getScanResults()) { - writer.println( - " " + buildNetworkKey(scanResult) + ": " + getNetworkScore(scanResult)); - } - } - } - - /** Registers a CacheListener instance, replacing the previous listener if it existed. */ - public void registerListener(CacheListener listener) { - synchronized (mLock) { - mListener = listener; - } - } - - /** Removes the registered CacheListener. */ - public void unregisterListener() { - synchronized (mLock) { - mListener = null; - } - } - - /** Listener for updates to the cache inside WifiNetworkScoreCache. */ - public abstract static class CacheListener { - private Handler mHandler; - - /** - * Constructor for CacheListener. - * - * @param handler the Handler on which to invoke the {@link #networkCacheUpdated} method. - * This cannot be null. - */ - public CacheListener(@NonNull Handler handler) { - Objects.requireNonNull(handler); - mHandler = handler; - } - - /** Invokes the {@link #networkCacheUpdated(List)} method on the handler. */ - void post(List updatedNetworks) { - mHandler.post(new Runnable() { - @Override - public void run() { - networkCacheUpdated(updatedNetworks); - } - }); - } - - /** - * Invoked whenever the cache is updated. - * - *

    Clearing the cache does not invoke this method. - * - * @param updatedNetworks the networks that were updated - */ - public abstract void networkCacheUpdated(List updatedNetworks); - } -} diff --git a/wifi/non-updatable/java/src/android/net/wifi/nl80211/ChannelSettings.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/ChannelSettings.java deleted file mode 100644 index 4c14fd499c28..000000000000 --- a/wifi/non-updatable/java/src/android/net/wifi/nl80211/ChannelSettings.java +++ /dev/null @@ -1,95 +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.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 CREATOR = - new Parcelable.Creator() { - /** - * 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/non-updatable/java/src/android/net/wifi/nl80211/DeviceWiphyCapabilities.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/DeviceWiphyCapabilities.java deleted file mode 100644 index bb0cc975a3db..000000000000 --- a/wifi/non-updatable/java/src/android/net/wifi/nl80211/DeviceWiphyCapabilities.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * 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 CREATOR = - new Parcelable.Creator() { - /** - * 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/non-updatable/java/src/android/net/wifi/nl80211/HiddenNetwork.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/HiddenNetwork.java deleted file mode 100644 index b1475b2c7b43..000000000000 --- a/wifi/non-updatable/java/src/android/net/wifi/nl80211/HiddenNetwork.java +++ /dev/null @@ -1,87 +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.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 CREATOR = - new Parcelable.Creator() { - /** - * 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/non-updatable/java/src/android/net/wifi/nl80211/NativeScanResult.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeScanResult.java deleted file mode 100644 index a8e999973fe8..000000000000 --- a/wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeScanResult.java +++ /dev/null @@ -1,310 +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.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 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 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 CREATOR = - new Parcelable.Creator() { - @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/non-updatable/java/src/android/net/wifi/nl80211/NativeWifiClient.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeWifiClient.java deleted file mode 100644 index 984d7d034302..000000000000 --- a/wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeWifiClient.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 CREATOR = - new Parcelable.Creator() { - @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/non-updatable/java/src/android/net/wifi/nl80211/PnoNetwork.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoNetwork.java deleted file mode 100644 index e8eff09583b9..000000000000 --- a/wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoNetwork.java +++ /dev/null @@ -1,171 +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.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 CREATOR = - new Parcelable.Creator() { - @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/non-updatable/java/src/android/net/wifi/nl80211/PnoSettings.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoSettings.java deleted file mode 100644 index 00ebe624ba0d..000000000000 --- a/wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoSettings.java +++ /dev/null @@ -1,209 +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.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 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 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 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 CREATOR = - new Parcelable.Creator() { - @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/non-updatable/java/src/android/net/wifi/nl80211/RadioChainInfo.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/RadioChainInfo.java deleted file mode 100644 index 2c12163dc9a1..000000000000 --- a/wifi/non-updatable/java/src/android/net/wifi/nl80211/RadioChainInfo.java +++ /dev/null @@ -1,125 +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.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 CREATOR = - new Parcelable.Creator() { - /** - * 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/non-updatable/java/src/android/net/wifi/nl80211/SingleScanSettings.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/SingleScanSettings.java deleted file mode 100644 index 24b1854fbf6c..000000000000 --- a/wifi/non-updatable/java/src/android/net/wifi/nl80211/SingleScanSettings.java +++ /dev/null @@ -1,118 +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.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; - public ArrayList 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 CREATOR = - new Parcelable.Creator() { - /** - * 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(); - in.readTypedList(result.channelSettings, ChannelSettings.CREATOR); - result.hiddenNetworks = new ArrayList(); - 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/non-updatable/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java deleted file mode 100644 index 4116234c4c8d..000000000000 --- a/wifi/non-updatable/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java +++ /dev/null @@ -1,1287 +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.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 mClientInterfaces = new HashMap<>(); - private HashMap mApInterfaces = new HashMap<>(); - private HashMap mWificondScanners = new HashMap<>(); - private HashMap mScanEventHandlers = new HashMap<>(); - private HashMap mPnoScanEventHandlers = new HashMap<>(); - private HashMap 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 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 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 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 freqs, @Nullable List 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). - *

    - * 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 keyManagement; - /** - * Supported pairwise cipher types defined - * in {@link android.net.wifi.WifiAnnotations.Cipher}. - */ - @NonNull public final List 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 keyManagement, - @NonNull List pairwiseCipher, - @WifiAnnotations.Cipher int groupCipher) { - this.protocol = protocol; - this.keyManagement = (keyManagement != null) - ? keyManagement : new ArrayList(); - this.pairwiseCipher = (pairwiseCipher != null) - ? pairwiseCipher : new ArrayList(); - 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/non-updatable/migration_samples/README.txt b/wifi/non-updatable/migration_samples/README.txt deleted file mode 100644 index 264debaa51f9..000000000000 --- a/wifi/non-updatable/migration_samples/README.txt +++ /dev/null @@ -1,35 +0,0 @@ -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//wifi/WifiConfigStore.xml -AOSP Path in Android 11: /data/misc_ce//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//wifi/WifiConfigStoreNetworkSuggestions.xml -AOSP Path in Android 11: /data/misc_ce//apexdata/com.android/wifi/WifiConfigStoreNetworkSuggestions.xml -Sample File (in this folder): User_WifiConfigStoreNetworkSuggestions.xml diff --git a/wifi/non-updatable/migration_samples/Shared_WifiConfigStore.xml b/wifi/non-updatable/migration_samples/Shared_WifiConfigStore.xml deleted file mode 100644 index 3063276fae6a..000000000000 --- a/wifi/non-updatable/migration_samples/Shared_WifiConfigStore.xml +++ /dev/null @@ -1,200 +0,0 @@ - - - - - - -"OPEN_SSID"NONE -"OPEN_SSID" - - - - - -01 -03 - -2f -0e -04 - - - - - - - - - - - - - - - - - -android.uid.system:1000 - -android.uid.system:1000 - - - -ce:b1:36:bb:71:ac - - - - -NETWORK_SELECTION_ENABLED -NETWORK_SELECTION_ENABLE -"ENTERPRISE_SSID"WPA_EAP - - - -DHCP -NONE - - - - -"ENTERPRISE_SSID"WPA_EAP -"ENTERPRISE_SSID" - - - - - -0c -03 - -2f -0e -04 - - - - - - - - - - - - - - - - - -android.uid.system:1000 - -android.uid.system:1000 - - - -f6:b3:94:44:40:87 - - - - -NETWORK_SELECTION_TEMPORARY_DISABLED -NETWORK_SELECTION_DISABLED_AUTHENTICATION_FAILURE - - - - -DHCP -NONE - - -adadadasdaddsa -asdadaddadasd -adasdadadad - - - -0 - - - -adsad -/system/etc/security/cacerts - - - - - - - - - - -"WPA3_SSID"SAE -"WPA3_SSID" -"sfsdfsfdsfsdf" - - - - -0001 -02 - -28 -0c -04 - - - - - - - - - - - - - - - - - -android.uid.system:1000 - -android.uid.system:1000 - - - -a6:3d:b0:13:ed:41 - - - - -NETWORK_SELECTION_PERMANENTLY_DISABLED -NETWORK_SELECTION_DISABLED_BY_WRONG_PASSWORD - - - - -DHCP -NONE - - - - - - - - - - - - - - - - - - - diff --git a/wifi/non-updatable/migration_samples/Shared_WifiConfigStoreSoftAp.xml b/wifi/non-updatable/migration_samples/Shared_WifiConfigStoreSoftAp.xml deleted file mode 100644 index fd99dd3df8b2..000000000000 --- a/wifi/non-updatable/migration_samples/Shared_WifiConfigStoreSoftAp.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - -HOTSPOT_SSID - - - - -blahblahblah - - - - - -00:11:22:33:44:55 - - -aa:bb:cc:dd:ee:ff - - - diff --git a/wifi/non-updatable/migration_samples/User_WifiConfigStore.xml b/wifi/non-updatable/migration_samples/User_WifiConfigStore.xml deleted file mode 100644 index 67d5aab215f2..000000000000 --- a/wifi/non-updatable/migration_samples/User_WifiConfigStore.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - -com.android.certinstaller - -HS2_0_0 - - - - - - - - - - - - - - - - - -Passpoint.net -Passpoint Friendly Name - - - - - - - - - - -passpoint.com - - -blahblahblah -doubleblahlah - - - - -PAP - - - - - - - - - - - - - - -com.android.cts.verifier - -OPEN_SSID -00:11:22:33:44:55 - - - - - - - - - - - - diff --git a/wifi/non-updatable/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml b/wifi/non-updatable/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml deleted file mode 100644 index 4ecdd29709b4..000000000000 --- a/wifi/non-updatable/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml +++ /dev/null @@ -1,155 +0,0 @@ - - - - - -com.android.cts.verifier - - - - - - - -"OPEN_SSID"NONE -"OPEN_SSID" - - - - - -01 - - - - - - - - - - - - - - - - - - - - - - -com.android.cts.verifier - - - - - -02:00:00:00:00:00 - - - - - - - - - - - -passpoint.net - - - - - - - - - - - - - - - - - - -passpoint.net - - - - - - - - - - -com.android.cts.verifier - - - - - -02:00:00:00:00:00 - - - - - - - - - - - - - - - - -passpoint.net -Passpoint Friendly Name - - - - - - - - - - -passpoint.com - - -blahblahblah -doubleblahblah - - - - -PAP - - - - - - - - - - - - - - - - - - - diff --git a/wifi/non-updatable/tests/Android.bp b/wifi/non-updatable/tests/Android.bp deleted file mode 100644 index 3f5cacff017f..000000000000 --- a/wifi/non-updatable/tests/Android.bp +++ /dev/null @@ -1,44 +0,0 @@ -// 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. - -android_test { - name: "FrameworksWifiNonUpdatableApiTests", - - defaults: ["framework-wifi-test-defaults"], - - srcs: ["src/**/*.java"], - - jacoco: { - include_filter: ["android.net.wifi.*"], - // TODO(b/147521214) need to exclude test classes - exclude_filter: [], - }, - - static_libs: [ - "androidx.test.rules", - "frameworks-base-testutils", - "guava", - "mockito-target-minus-junit4", - "truth-prebuilt", - ], - - libs: [ - "android.test.runner", - "android.test.base", - ], - - test_suites: [ - "general-tests", - ], -} diff --git a/wifi/non-updatable/tests/AndroidManifest.xml b/wifi/non-updatable/tests/AndroidManifest.xml deleted file mode 100644 index b4b6b2d7997a..000000000000 --- a/wifi/non-updatable/tests/AndroidManifest.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/wifi/non-updatable/tests/AndroidTest.xml b/wifi/non-updatable/tests/AndroidTest.xml deleted file mode 100644 index 5f3fdd46556e..000000000000 --- a/wifi/non-updatable/tests/AndroidTest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/wifi/non-updatable/tests/README.md b/wifi/non-updatable/tests/README.md deleted file mode 100644 index ad535f4ed86a..000000000000 --- a/wifi/non-updatable/tests/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Wifi Non-Updatable Framework Unit Tests -This package contains unit tests for the non-updatable part (i.e. outside the Wifi module) of the -Android Wifi framework APIs based on the -[Android Testing Support Library](http://developer.android.com/tools/testing-support-library/index.html). -The test cases are built using the [JUnit](http://junit.org/) and [Mockito](http://mockito.org/) -libraries. - -## Running Tests -The easiest way to run tests is simply run - -``` -atest android.net.wifi -``` - -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' -3. restart framework on the device 'adb shell stop' then 'adb shell start' - -To enable syncing data to the device for first time after clean reflash: -1. adb disable-verity -2. adb reboot -3. adb remount - -## 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`. - -## Debugging Tests -If you are trying to debug why tests are not doing what you expected, you can add android log -statements and use logcat to view them. The beginning and end of every tests is automatically logged -with the tag `TestRunner`. diff --git a/wifi/non-updatable/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java b/wifi/non-updatable/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java deleted file mode 100644 index f49f387cbc6b..000000000000 --- a/wifi/non-updatable/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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 = - "\n" - + "\n" - + "\n" - + "\n" - + "" + TEST_SSID + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "" + TEST_PASSPHRASE + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\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/non-updatable/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/non-updatable/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java deleted file mode 100644 index c4967ebf1736..000000000000 --- a/wifi/non-updatable/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java +++ /dev/null @@ -1,225 +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 static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assert.fail; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.NetworkKey; -import android.net.RssiCurve; -import android.net.ScoredNetwork; -import android.net.WifiKey; -import android.net.wifi.WifiNetworkScoreCache.CacheListener; -import android.os.Handler; -import android.os.HandlerThread; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.google.common.collect.ImmutableList; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** Unit tests for {@link WifiNetworkScoreCache}. */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class WifiNetworkScoreCacheTest { - - public static final String SSID = "ssid"; - public static final String SSID2 = "ssid2"; - public static final String SSID3 = "ssid3"; - public static final String FORMATTED_SSID = "\"" + SSID + "\""; - public static final String FORMATTED_SSID2 = "\"" + SSID2 + "\""; - public static final String FORMATTED_SSID3 = "\"" + SSID3 + "\""; - public static final String BSSID = "AA:AA:AA:AA:AA:AA"; - - public static final WifiKey VALID_KEY = new WifiKey(FORMATTED_SSID, BSSID); - - public static final ScanResult VALID_SCAN_RESULT = buildScanResult(SSID, BSSID); - - @Mock private Context mockApplicationContext; - @Mock private Context mockContext; // isn't used, can be null - @Mock private RssiCurve mockRssiCurve; - - - private CacheListener mCacheListener; - private CountDownLatch mLatch; - private Handler mHandler; - private List mUpdatedNetworksCaptor; - private ScoredNetwork mValidScoredNetwork; - private WifiNetworkScoreCache mScoreCache; - - private static ScanResult buildScanResult(String ssid, String bssid) { - return new ScanResult( - WifiSsid.createFromAsciiEncoded(ssid), - bssid, - "" /* caps */, - 0 /* level */, - 0 /* frequency */, - 0 /* tsf */, - 0 /* distCm */, - 0 /* distSdCm*/); - } - - private static ScoredNetwork buildScoredNetwork(WifiKey key, RssiCurve curve) { - return new ScoredNetwork(new NetworkKey(key), curve); - } - - // Called from setup - private void initializeCacheWithValidScoredNetwork() { - mScoreCache.updateScores(ImmutableList.of(mValidScoredNetwork)); - } - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - when(mockContext.getApplicationContext()).thenReturn(mockApplicationContext); - - mValidScoredNetwork = buildScoredNetwork(VALID_KEY, mockRssiCurve); - mScoreCache = new WifiNetworkScoreCache(mockContext); - initializeCacheWithValidScoredNetwork(); - - HandlerThread thread = new HandlerThread("WifiNetworkScoreCacheTest Handler Thread"); - thread.start(); - mHandler = new Handler(thread.getLooper()); - mLatch = new CountDownLatch(1); - mCacheListener = new CacheListener(mHandler) { - @Override - public void networkCacheUpdated(List updatedNetworks) { - mUpdatedNetworksCaptor = updatedNetworks; - mLatch.countDown(); - } - }; - } - - - @Test - public void isScoredNetworkShouldReturnTrueAfterUpdateScoresIsCalled() { - assertThat(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT)).isTrue(); - } - - @Test - public void isScoredNetworkShouldReturnFalseAfterClearScoresIsCalled() { - mScoreCache.clearScores(); - assertThat(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT)).isFalse(); - } - - @Test - public void updateScoresShouldAddNewNetwork() { - WifiKey key2 = new WifiKey("\"ssid2\"", BSSID); - ScoredNetwork network2 = buildScoredNetwork(key2, mockRssiCurve); - ScanResult result2 = buildScanResult("ssid2", BSSID); - - mScoreCache.updateScores(ImmutableList.of(network2)); - - assertThat(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT)).isTrue(); - assertThat(mScoreCache.isScoredNetwork(result2)).isTrue(); - } - - @Test - public void hasScoreCurveShouldReturnTrue() { - assertThat(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT)).isTrue(); - } - - @Test - public void hasScoreCurveShouldReturnFalseWhenNoCachedNetwork() { - ScanResult unscored = buildScanResult("fake", BSSID); - assertThat(mScoreCache.hasScoreCurve(unscored)).isFalse(); - } - - @Test - public void hasScoreCurveShouldReturnFalseWhenScoredNetworkHasNoCurve() { - ScoredNetwork noCurve = buildScoredNetwork(VALID_KEY, null /* rssiCurve */); - mScoreCache.updateScores(ImmutableList.of(noCurve)); - - assertThat(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT)).isFalse(); - } - - @Test - public void getNetworkScoreShouldReturnScore() { - final byte score = 50; - final int rssi = -70; - ScanResult result = new ScanResult(VALID_SCAN_RESULT); - result.level = rssi; - - when(mockRssiCurve.lookupScore(rssi)).thenReturn(score); - - assertThat(mScoreCache.getNetworkScore(result)).isEqualTo(score); - } - - @Test - public void getMeteredHintShouldReturnFalse() { - assertThat(mScoreCache.getMeteredHint(VALID_SCAN_RESULT)).isFalse(); - } - - @Test - public void getMeteredHintShouldReturnTrue() { - ScoredNetwork network = - new ScoredNetwork( - new NetworkKey(VALID_KEY), mockRssiCurve, true /* metered Hint */); - mScoreCache.updateScores(ImmutableList.of(network)); - - assertThat(mScoreCache.getMeteredHint(VALID_SCAN_RESULT)).isTrue(); - } - - @Test - public void updateScoresShouldInvokeCacheListener_networkCacheUpdated() { - mScoreCache = new WifiNetworkScoreCache(mockContext, mCacheListener); - initializeCacheWithValidScoredNetwork(); - - try { - mLatch.await(1, TimeUnit.SECONDS); // wait for listener to be executed - } catch (InterruptedException e) { - fail("Interrupted Exception while waiting for listener to be invoked."); - } - // One network should be updated. - assertThat(mUpdatedNetworksCaptor.size()).isEqualTo(1); - assertThat(mUpdatedNetworksCaptor.get(0)).isEqualTo(mValidScoredNetwork); - } - - @Test - public void leastRecentlyUsedScore_shouldBeEvictedFromCache() { - mScoreCache = new WifiNetworkScoreCache(mockContext, mCacheListener, 2 /* maxCacheSize */); - - ScoredNetwork network1 = mValidScoredNetwork; - ScoredNetwork network2 = buildScoredNetwork( - new WifiKey(FORMATTED_SSID2, BSSID), mockRssiCurve); - ScoredNetwork network3 = buildScoredNetwork( - new WifiKey(FORMATTED_SSID3, BSSID), mockRssiCurve); - mScoreCache.updateScores(ImmutableList.of(network1)); - mScoreCache.updateScores(ImmutableList.of(network2)); - - // First score should be evicted because max cache size has been reached. - mScoreCache.updateScores(ImmutableList.of(network3)); - - assertThat(mScoreCache.hasScoreCurve(buildScanResult(SSID2, BSSID))).isTrue(); - assertThat(mScoreCache.hasScoreCurve(buildScanResult(SSID3, BSSID))).isTrue(); - assertThat(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT)).isFalse(); - } -} diff --git a/wifi/non-updatable/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java deleted file mode 100644 index 7b900fec70a8..000000000000 --- a/wifi/non-updatable/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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/non-updatable/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java deleted file mode 100644 index 8ddd1899179a..000000000000 --- a/wifi/non-updatable/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java +++ /dev/null @@ -1,87 +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.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/non-updatable/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java deleted file mode 100644 index dec1db8d274e..000000000000 --- a/wifi/non-updatable/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java +++ /dev/null @@ -1,112 +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.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 map = new HashMap<>(); - map.put(pnoSettings1, TEST_VALUE); - - assertEquals(TEST_VALUE, map.get(pnoSettings2).intValue()); - } -} diff --git a/wifi/non-updatable/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java deleted file mode 100644 index 905920888012..000000000000 --- a/wifi/non-updatable/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java +++ /dev/null @@ -1,116 +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.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 map = new HashMap<>(); - map.put(scanSettings1, TEST_VALUE); - - assertEquals(TEST_VALUE, map.get(scanSettings2).intValue()); - } -} diff --git a/wifi/non-updatable/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java deleted file mode 100644 index 9ee0acbfbaa2..000000000000 --- a/wifi/non-updatable/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java +++ /dev/null @@ -1,1265 +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.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 SCAN_FREQ_SET = - new HashSet() {{ - 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 SCAN_HIDDEN_NETWORK_SSID_LIST = - new ArrayList() {{ - 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 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 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 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 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 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 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 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 sendMgmtFrameEventCaptor = - ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); - - doThrow(new RemoteException()).when(mClientInterface) - .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); - - final ArgumentCaptor alarmListenerCaptor = - ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); - final ArgumentCaptor 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 sendMgmtFrameEventCaptor = - ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); - doNothing().when(mClientInterface) - .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); - final ArgumentCaptor alarmListenerCaptor = - ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); - final ArgumentCaptor 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 sendMgmtFrameEventCaptor = - ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); - doNothing().when(mClientInterface) - .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); - final ArgumentCaptor alarmListenerCaptor = - ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); - final ArgumentCaptor 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 sendMgmtFrameEventCaptor = - ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); - doNothing().when(mClientInterface) - .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); - final ArgumentCaptor alarmListenerCaptor = - ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); - final ArgumentCaptor 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 sendMgmtFrameEventCaptor = - ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); - doNothing().when(mClientInterface) - .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); - final ArgumentCaptor alarmListenerCaptor = - ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); - final ArgumentCaptor 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 sendMgmtFrameEventCaptor = - ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); - doNothing().when(mClientInterface) - .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); - final ArgumentCaptor alarmListenerCaptor = - ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); - final ArgumentCaptor 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 { - int mExpectedScanType; - private final Set mExpectedFreqs; - private final List mExpectedSsids; - - ScanMatcher(int expectedScanType, Set expectedFreqs, List expectedSsids) { - this.mExpectedScanType = expectedScanType; - this.mExpectedFreqs = expectedFreqs; - this.mExpectedSsids = expectedSsids; - } - - @Override - public boolean matches(SingleScanSettings settings) { - if (settings.scanType != mExpectedScanType) { - return false; - } - ArrayList channelSettings = settings.channelSettings; - ArrayList hiddenNetworks = settings.hiddenNetworks; - if (mExpectedFreqs != null) { - Set freqSet = new HashSet(); - 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 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 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 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 decodeSsid(String ssidStr) { - ArrayList 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 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 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 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 byteArrayToArrayList(byte[] bytes) { - ArrayList 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/Android.bp b/wifi/tests/Android.bp new file mode 100644 index 000000000000..3f5cacff017f --- /dev/null +++ b/wifi/tests/Android.bp @@ -0,0 +1,44 @@ +// 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. + +android_test { + name: "FrameworksWifiNonUpdatableApiTests", + + defaults: ["framework-wifi-test-defaults"], + + srcs: ["src/**/*.java"], + + jacoco: { + include_filter: ["android.net.wifi.*"], + // TODO(b/147521214) need to exclude test classes + exclude_filter: [], + }, + + static_libs: [ + "androidx.test.rules", + "frameworks-base-testutils", + "guava", + "mockito-target-minus-junit4", + "truth-prebuilt", + ], + + libs: [ + "android.test.runner", + "android.test.base", + ], + + test_suites: [ + "general-tests", + ], +} diff --git a/wifi/tests/AndroidManifest.xml b/wifi/tests/AndroidManifest.xml new file mode 100644 index 000000000000..b4b6b2d7997a --- /dev/null +++ b/wifi/tests/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml new file mode 100644 index 000000000000..5f3fdd46556e --- /dev/null +++ b/wifi/tests/AndroidTest.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/wifi/tests/README.md b/wifi/tests/README.md new file mode 100644 index 000000000000..ad535f4ed86a --- /dev/null +++ b/wifi/tests/README.md @@ -0,0 +1,32 @@ +# Wifi Non-Updatable Framework Unit Tests +This package contains unit tests for the non-updatable part (i.e. outside the Wifi module) of the +Android Wifi framework APIs based on the +[Android Testing Support Library](http://developer.android.com/tools/testing-support-library/index.html). +The test cases are built using the [JUnit](http://junit.org/) and [Mockito](http://mockito.org/) +libraries. + +## Running Tests +The easiest way to run tests is simply run + +``` +atest android.net.wifi +``` + +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' +3. restart framework on the device 'adb shell stop' then 'adb shell start' + +To enable syncing data to the device for first time after clean reflash: +1. adb disable-verity +2. adb reboot +3. adb remount + +## 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`. + +## Debugging Tests +If you are trying to debug why tests are not doing what you expected, you can add android log +statements and use logcat to view them. The beginning and end of every tests is automatically logged +with the tag `TestRunner`. 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 = + "\n" + + "\n" + + "\n" + + "\n" + + "" + TEST_SSID + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "" + TEST_PASSPHRASE + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\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/WifiNetworkScoreCacheTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java new file mode 100644 index 000000000000..c4967ebf1736 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java @@ -0,0 +1,225 @@ +/* + * 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 static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.NetworkKey; +import android.net.RssiCurve; +import android.net.ScoredNetwork; +import android.net.WifiKey; +import android.net.wifi.WifiNetworkScoreCache.CacheListener; +import android.os.Handler; +import android.os.HandlerThread; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.google.common.collect.ImmutableList; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** Unit tests for {@link WifiNetworkScoreCache}. */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class WifiNetworkScoreCacheTest { + + public static final String SSID = "ssid"; + public static final String SSID2 = "ssid2"; + public static final String SSID3 = "ssid3"; + public static final String FORMATTED_SSID = "\"" + SSID + "\""; + public static final String FORMATTED_SSID2 = "\"" + SSID2 + "\""; + public static final String FORMATTED_SSID3 = "\"" + SSID3 + "\""; + public static final String BSSID = "AA:AA:AA:AA:AA:AA"; + + public static final WifiKey VALID_KEY = new WifiKey(FORMATTED_SSID, BSSID); + + public static final ScanResult VALID_SCAN_RESULT = buildScanResult(SSID, BSSID); + + @Mock private Context mockApplicationContext; + @Mock private Context mockContext; // isn't used, can be null + @Mock private RssiCurve mockRssiCurve; + + + private CacheListener mCacheListener; + private CountDownLatch mLatch; + private Handler mHandler; + private List mUpdatedNetworksCaptor; + private ScoredNetwork mValidScoredNetwork; + private WifiNetworkScoreCache mScoreCache; + + private static ScanResult buildScanResult(String ssid, String bssid) { + return new ScanResult( + WifiSsid.createFromAsciiEncoded(ssid), + bssid, + "" /* caps */, + 0 /* level */, + 0 /* frequency */, + 0 /* tsf */, + 0 /* distCm */, + 0 /* distSdCm*/); + } + + private static ScoredNetwork buildScoredNetwork(WifiKey key, RssiCurve curve) { + return new ScoredNetwork(new NetworkKey(key), curve); + } + + // Called from setup + private void initializeCacheWithValidScoredNetwork() { + mScoreCache.updateScores(ImmutableList.of(mValidScoredNetwork)); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mockContext.getApplicationContext()).thenReturn(mockApplicationContext); + + mValidScoredNetwork = buildScoredNetwork(VALID_KEY, mockRssiCurve); + mScoreCache = new WifiNetworkScoreCache(mockContext); + initializeCacheWithValidScoredNetwork(); + + HandlerThread thread = new HandlerThread("WifiNetworkScoreCacheTest Handler Thread"); + thread.start(); + mHandler = new Handler(thread.getLooper()); + mLatch = new CountDownLatch(1); + mCacheListener = new CacheListener(mHandler) { + @Override + public void networkCacheUpdated(List updatedNetworks) { + mUpdatedNetworksCaptor = updatedNetworks; + mLatch.countDown(); + } + }; + } + + + @Test + public void isScoredNetworkShouldReturnTrueAfterUpdateScoresIsCalled() { + assertThat(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT)).isTrue(); + } + + @Test + public void isScoredNetworkShouldReturnFalseAfterClearScoresIsCalled() { + mScoreCache.clearScores(); + assertThat(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT)).isFalse(); + } + + @Test + public void updateScoresShouldAddNewNetwork() { + WifiKey key2 = new WifiKey("\"ssid2\"", BSSID); + ScoredNetwork network2 = buildScoredNetwork(key2, mockRssiCurve); + ScanResult result2 = buildScanResult("ssid2", BSSID); + + mScoreCache.updateScores(ImmutableList.of(network2)); + + assertThat(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT)).isTrue(); + assertThat(mScoreCache.isScoredNetwork(result2)).isTrue(); + } + + @Test + public void hasScoreCurveShouldReturnTrue() { + assertThat(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT)).isTrue(); + } + + @Test + public void hasScoreCurveShouldReturnFalseWhenNoCachedNetwork() { + ScanResult unscored = buildScanResult("fake", BSSID); + assertThat(mScoreCache.hasScoreCurve(unscored)).isFalse(); + } + + @Test + public void hasScoreCurveShouldReturnFalseWhenScoredNetworkHasNoCurve() { + ScoredNetwork noCurve = buildScoredNetwork(VALID_KEY, null /* rssiCurve */); + mScoreCache.updateScores(ImmutableList.of(noCurve)); + + assertThat(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT)).isFalse(); + } + + @Test + public void getNetworkScoreShouldReturnScore() { + final byte score = 50; + final int rssi = -70; + ScanResult result = new ScanResult(VALID_SCAN_RESULT); + result.level = rssi; + + when(mockRssiCurve.lookupScore(rssi)).thenReturn(score); + + assertThat(mScoreCache.getNetworkScore(result)).isEqualTo(score); + } + + @Test + public void getMeteredHintShouldReturnFalse() { + assertThat(mScoreCache.getMeteredHint(VALID_SCAN_RESULT)).isFalse(); + } + + @Test + public void getMeteredHintShouldReturnTrue() { + ScoredNetwork network = + new ScoredNetwork( + new NetworkKey(VALID_KEY), mockRssiCurve, true /* metered Hint */); + mScoreCache.updateScores(ImmutableList.of(network)); + + assertThat(mScoreCache.getMeteredHint(VALID_SCAN_RESULT)).isTrue(); + } + + @Test + public void updateScoresShouldInvokeCacheListener_networkCacheUpdated() { + mScoreCache = new WifiNetworkScoreCache(mockContext, mCacheListener); + initializeCacheWithValidScoredNetwork(); + + try { + mLatch.await(1, TimeUnit.SECONDS); // wait for listener to be executed + } catch (InterruptedException e) { + fail("Interrupted Exception while waiting for listener to be invoked."); + } + // One network should be updated. + assertThat(mUpdatedNetworksCaptor.size()).isEqualTo(1); + assertThat(mUpdatedNetworksCaptor.get(0)).isEqualTo(mValidScoredNetwork); + } + + @Test + public void leastRecentlyUsedScore_shouldBeEvictedFromCache() { + mScoreCache = new WifiNetworkScoreCache(mockContext, mCacheListener, 2 /* maxCacheSize */); + + ScoredNetwork network1 = mValidScoredNetwork; + ScoredNetwork network2 = buildScoredNetwork( + new WifiKey(FORMATTED_SSID2, BSSID), mockRssiCurve); + ScoredNetwork network3 = buildScoredNetwork( + new WifiKey(FORMATTED_SSID3, BSSID), mockRssiCurve); + mScoreCache.updateScores(ImmutableList.of(network1)); + mScoreCache.updateScores(ImmutableList.of(network2)); + + // First score should be evicted because max cache size has been reached. + mScoreCache.updateScores(ImmutableList.of(network3)); + + assertThat(mScoreCache.hasScoreCurve(buildScanResult(SSID2, BSSID))).isTrue(); + assertThat(mScoreCache.hasScoreCurve(buildScanResult(SSID3, BSSID))).isTrue(); + assertThat(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT)).isFalse(); + } +} 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 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 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 SCAN_FREQ_SET = + new HashSet() {{ + 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 SCAN_HIDDEN_NETWORK_SSID_LIST = + new ArrayList() {{ + 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 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 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 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 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 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 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 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 sendMgmtFrameEventCaptor = + ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); + + doThrow(new RemoteException()).when(mClientInterface) + .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); + + final ArgumentCaptor alarmListenerCaptor = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + final ArgumentCaptor 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 sendMgmtFrameEventCaptor = + ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); + doNothing().when(mClientInterface) + .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); + final ArgumentCaptor alarmListenerCaptor = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + final ArgumentCaptor 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 sendMgmtFrameEventCaptor = + ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); + doNothing().when(mClientInterface) + .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); + final ArgumentCaptor alarmListenerCaptor = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + final ArgumentCaptor 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 sendMgmtFrameEventCaptor = + ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); + doNothing().when(mClientInterface) + .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); + final ArgumentCaptor alarmListenerCaptor = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + final ArgumentCaptor 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 sendMgmtFrameEventCaptor = + ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); + doNothing().when(mClientInterface) + .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); + final ArgumentCaptor alarmListenerCaptor = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + final ArgumentCaptor 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 sendMgmtFrameEventCaptor = + ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); + doNothing().when(mClientInterface) + .SendMgmtFrame(any(), sendMgmtFrameEventCaptor.capture(), anyInt()); + final ArgumentCaptor alarmListenerCaptor = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + final ArgumentCaptor 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 { + int mExpectedScanType; + private final Set mExpectedFreqs; + private final List mExpectedSsids; + + ScanMatcher(int expectedScanType, Set expectedFreqs, List expectedSsids) { + this.mExpectedScanType = expectedScanType; + this.mExpectedFreqs = expectedFreqs; + this.mExpectedSsids = expectedSsids; + } + + @Override + public boolean matches(SingleScanSettings settings) { + if (settings.scanType != mExpectedScanType) { + return false; + } + ArrayList channelSettings = settings.channelSettings; + ArrayList hiddenNetworks = settings.hiddenNetworks; + if (mExpectedFreqs != null) { + Set freqSet = new HashSet(); + 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 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 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 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 decodeSsid(String ssidStr) { + ArrayList 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 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 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 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 byteArrayToArrayList(byte[] bytes) { + ArrayList 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); + } + } +} -- cgit v1.2.3