Loading wifi/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ filegroup { // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache // to a separate package. "java/android/net/wifi/SoftApConfToXmlMigrationUtil.java", "java/android/net/wifi/WifiNetworkScoreCache.java", "java/android/net/wifi/WifiMigration.java", "java/android/net/wifi/nl80211/*.java", Loading wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java 0 → 100755 +284 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.wifi; import static android.os.Environment.getDataMiscDirectory; import android.annotation.Nullable; import android.net.MacAddress; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; /** * Utility class to convert the legacy softap.conf file format to the new XML format. * Note: * <li>This should be modified by the OEM if they want to migrate configuration for existing * devices for new softap features supported by AOSP in Android 11. * For ex: client allowlist/blocklist feature was already supported by some OEM's before Android 10 * while AOSP only supported it in Android 11. </li> * <li>Most of this class was copied over from WifiApConfigStore class in Android 10 and * SoftApStoreData class in Android 11</li> * @hide */ public final class SoftApConfToXmlMigrationUtil { private static final String TAG = "SoftApConfToXmlMigrationUtil"; /** * Directory to read the wifi config store files from under. */ private static final String LEGACY_WIFI_STORE_DIRECTORY_NAME = "wifi"; /** * The legacy Softap config file which contained key/value pairs. */ private static final String LEGACY_AP_CONFIG_FILE = "softap.conf"; /** * Pre-apex wifi shared folder. */ private static File getLegacyWifiSharedDirectory() { return new File(getDataMiscDirectory(), LEGACY_WIFI_STORE_DIRECTORY_NAME); } /* @hide constants copied from WifiConfiguration */ /** * 2GHz band. */ private static final int WIFICONFIG_AP_BAND_2GHZ = 0; /** * 5GHz band. */ private static final int WIFICONFIG_AP_BAND_5GHZ = 1; /** * Device is allowed to choose the optimal band (2Ghz or 5Ghz) based on device capability, * operating country code and current radio conditions. */ private static final int WIFICONFIG_AP_BAND_ANY = -1; /** * Convert band from WifiConfiguration into SoftApConfiguration * * @param wifiConfigBand band encoded as WIFICONFIG_AP_BAND_xxxx * @return band as encoded as SoftApConfiguration.BAND_xxx */ @VisibleForTesting public static int convertWifiConfigBandToSoftApConfigBand(int wifiConfigBand) { switch (wifiConfigBand) { case WIFICONFIG_AP_BAND_2GHZ: return SoftApConfiguration.BAND_2GHZ; case WIFICONFIG_AP_BAND_5GHZ: return SoftApConfiguration.BAND_5GHZ; case WIFICONFIG_AP_BAND_ANY: return SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ; default: return SoftApConfiguration.BAND_2GHZ; } } /** * Load AP configuration from legacy persistent storage. * Note: This is deprecated and only used for migrating data once on reboot. */ private static SoftApConfiguration loadFromLegacyFile(InputStream fis) { SoftApConfiguration config = null; DataInputStream in = null; try { SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); in = new DataInputStream(new BufferedInputStream(fis)); int version = in.readInt(); if (version < 1 || version > 3) { Log.e(TAG, "Bad version on hotspot configuration file"); return null; } configBuilder.setSsid(in.readUTF()); if (version >= 2) { int band = in.readInt(); int channel = in.readInt(); if (channel == 0) { configBuilder.setBand( convertWifiConfigBandToSoftApConfigBand(band)); } else { configBuilder.setChannel(channel, convertWifiConfigBandToSoftApConfigBand(band)); } } if (version >= 3) { configBuilder.setHiddenSsid(in.readBoolean()); } int authType = in.readInt(); if (authType == WifiConfiguration.KeyMgmt.WPA2_PSK) { configBuilder.setPassphrase(in.readUTF(), SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); } config = configBuilder.build(); } catch (IOException e) { Log.e(TAG, "Error reading hotspot configuration ", e); config = null; } catch (IllegalArgumentException ie) { Log.e(TAG, "Invalid hotspot configuration ", ie); config = null; } finally { if (in != null) { try { in.close(); } catch (IOException e) { Log.e(TAG, "Error closing hotspot configuration during read", e); } } } // NOTE: OEM's should add their customized parsing code here. return config; } // This is the version that Android 11 released with. private static final int CONFIG_STORE_DATA_VERSION = 3; private static final String XML_TAG_DOCUMENT_HEADER = "WifiConfigStoreData"; private static final String XML_TAG_VERSION = "Version"; private static final String XML_TAG_SECTION_HEADER_SOFTAP = "SoftAp"; private static final String XML_TAG_SSID = "SSID"; private static final String XML_TAG_BSSID = "Bssid"; private static final String XML_TAG_CHANNEL = "Channel"; private static final String XML_TAG_HIDDEN_SSID = "HiddenSSID"; private static final String XML_TAG_SECURITY_TYPE = "SecurityType"; private static final String XML_TAG_AP_BAND = "ApBand"; private static final String XML_TAG_PASSPHRASE = "Passphrase"; private static final String XML_TAG_MAX_NUMBER_OF_CLIENTS = "MaxNumberOfClients"; private static final String XML_TAG_AUTO_SHUTDOWN_ENABLED = "AutoShutdownEnabled"; private static final String XML_TAG_SHUTDOWN_TIMEOUT_MILLIS = "ShutdownTimeoutMillis"; private static final String XML_TAG_CLIENT_CONTROL_BY_USER = "ClientControlByUser"; private static final String XML_TAG_BLOCKED_CLIENT_LIST = "BlockedClientList"; private static final String XML_TAG_ALLOWED_CLIENT_LIST = "AllowedClientList"; public static final String XML_TAG_CLIENT_MACADDRESS = "ClientMacAddress"; private static byte[] convertConfToXml(SoftApConfiguration softApConf) { try { final XmlSerializer out = new FastXmlSerializer(); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); out.setOutput(outputStream, StandardCharsets.UTF_8.name()); // Header for the XML file. out.startDocument(null, true); out.startTag(null, XML_TAG_DOCUMENT_HEADER); XmlUtils.writeValueXml(CONFIG_STORE_DATA_VERSION, XML_TAG_VERSION, out); out.startTag(null, XML_TAG_SECTION_HEADER_SOFTAP); // SoftAp conf XmlUtils.writeValueXml(softApConf.getSsid(), XML_TAG_SSID, out); if (softApConf.getBssid() != null) { XmlUtils.writeValueXml(softApConf.getBssid().toString(), XML_TAG_BSSID, out); } XmlUtils.writeValueXml(softApConf.getBand(), XML_TAG_AP_BAND, out); XmlUtils.writeValueXml(softApConf.getChannel(), XML_TAG_CHANNEL, out); XmlUtils.writeValueXml(softApConf.isHiddenSsid(), XML_TAG_HIDDEN_SSID, out); XmlUtils.writeValueXml(softApConf.getSecurityType(), XML_TAG_SECURITY_TYPE, out); if (softApConf.getSecurityType() != SoftApConfiguration.SECURITY_TYPE_OPEN) { XmlUtils.writeValueXml(softApConf.getPassphrase(), XML_TAG_PASSPHRASE, out); } XmlUtils.writeValueXml(softApConf.getMaxNumberOfClients(), XML_TAG_MAX_NUMBER_OF_CLIENTS, out); XmlUtils.writeValueXml(softApConf.isClientControlByUserEnabled(), XML_TAG_CLIENT_CONTROL_BY_USER, out); XmlUtils.writeValueXml(softApConf.isAutoShutdownEnabled(), XML_TAG_AUTO_SHUTDOWN_ENABLED, out); XmlUtils.writeValueXml(softApConf.getShutdownTimeoutMillis(), XML_TAG_SHUTDOWN_TIMEOUT_MILLIS, out); out.startTag(null, XML_TAG_BLOCKED_CLIENT_LIST); for (MacAddress mac: softApConf.getBlockedClientList()) { XmlUtils.writeValueXml(mac.toString(), XML_TAG_CLIENT_MACADDRESS, out); } out.endTag(null, XML_TAG_BLOCKED_CLIENT_LIST); out.startTag(null, XML_TAG_ALLOWED_CLIENT_LIST); for (MacAddress mac: softApConf.getAllowedClientList()) { XmlUtils.writeValueXml(mac.toString(), XML_TAG_CLIENT_MACADDRESS, out); } out.endTag(null, XML_TAG_ALLOWED_CLIENT_LIST); // Footer for the XML file. out.endTag(null, XML_TAG_SECTION_HEADER_SOFTAP); out.endTag(null, XML_TAG_DOCUMENT_HEADER); out.endDocument(); return outputStream.toByteArray(); } catch (IOException | XmlPullParserException e) { Log.e(TAG, "Failed to convert softap conf to XML", e); return null; } } private SoftApConfToXmlMigrationUtil() { } /** * Read the legacy /data/misc/wifi/softap.conf file format and convert to the new XML * format understood by WifiConfigStore. * Note: Used for unit testing. */ @VisibleForTesting @Nullable public static InputStream convert(InputStream fis) { SoftApConfiguration softApConf = loadFromLegacyFile(fis); if (softApConf == null) return null; byte[] xmlBytes = convertConfToXml(softApConf); if (xmlBytes == null) return null; return new ByteArrayInputStream(xmlBytes); } /** * Read the legacy /data/misc/wifi/softap.conf file format and convert to the new XML * format understood by WifiConfigStore. */ @Nullable public static InputStream convert() { File file = new File(getLegacyWifiSharedDirectory(), LEGACY_AP_CONFIG_FILE); FileInputStream fis = null; try { fis = new FileInputStream(file); } catch (FileNotFoundException e) { return null; } if (fis == null) return null; return convert(fis); } /** * Remove the legacy /data/misc/wifi/softap.conf file. */ @Nullable public static void remove() { File file = new File(getLegacyWifiSharedDirectory(), LEGACY_AP_CONFIG_FILE); file.delete(); } } wifi/java/android/net/wifi/WifiMigration.java +24 −3 Original line number Diff line number Diff line Loading @@ -95,7 +95,7 @@ public final class WifiMigration { private static final SparseArray<String> STORE_ID_TO_FILE_NAME = new SparseArray<String>() {{ put(STORE_FILE_SHARED_GENERAL, "WifiConfigStore.xml"); put(STORE_FILE_SHARED_SOFTAP, "softap.conf"); put(STORE_FILE_SHARED_SOFTAP, "WifiConfigStoreSoftAp.xml"); put(STORE_FILE_USER_GENERAL, "WifiConfigStore.xml"); put(STORE_FILE_USER_NETWORK_SUGGESTIONS, "WifiConfigStoreNetworkSuggestions.xml"); }}; Loading Loading @@ -176,6 +176,13 @@ public final class WifiMigration { // 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; } } Loading @@ -191,7 +198,18 @@ public final class WifiMigration { if (storeFileId != STORE_FILE_SHARED_GENERAL && storeFileId != STORE_FILE_SHARED_SOFTAP) { throw new IllegalArgumentException("Invalid shared store file id"); } getSharedAtomicFile(storeFileId).delete(); 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(); } } /** Loading Loading @@ -258,7 +276,10 @@ public final class WifiMigration { throw new IllegalArgumentException("Invalid user store file id"); } Objects.requireNonNull(userHandle); getUserAtomicFile(storeFileId, userHandle.getIdentifier()).delete(); AtomicFile file = getUserAtomicFile(storeFileId, userHandle.getIdentifier()); if (file.exists()) { file.delete(); } } /** Loading wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java 0 → 100644 +199 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.wifi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import androidx.test.filters.SmallTest; import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; /** * Unit tests for {@link android.net.wifi.SoftApConfToXmlMigrationUtilTest}. */ @SmallTest public class SoftApConfToXmlMigrationUtilTest { private static final String TEST_SSID = "SSID"; private static final String TEST_PASSPHRASE = "TestPassphrase"; private static final int TEST_CHANNEL = 0; private static final boolean TEST_HIDDEN = false; private static final int TEST_BAND = SoftApConfiguration.BAND_5GHZ; private static final int TEST_SECURITY = SoftApConfiguration.SECURITY_TYPE_WPA2_PSK; private static final String TEST_EXPECTED_XML_STRING = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + "<WifiConfigStoreData>\n" + "<int name=\"Version\" value=\"3\" />\n" + "<SoftAp>\n" + "<string name=\"SSID\">" + TEST_SSID + "</string>\n" + "<int name=\"ApBand\" value=\"" + TEST_BAND + "\" />\n" + "<int name=\"Channel\" value=\"" + TEST_CHANNEL + "\" />\n" + "<boolean name=\"HiddenSSID\" value=\"" + TEST_HIDDEN + "\" />\n" + "<int name=\"SecurityType\" value=\"" + TEST_SECURITY + "\" />\n" + "<string name=\"Passphrase\">" + TEST_PASSPHRASE + "</string>\n" + "<int name=\"MaxNumberOfClients\" value=\"0\" />\n" + "<boolean name=\"ClientControlByUser\" value=\"false\" />\n" + "<boolean name=\"AutoShutdownEnabled\" value=\"true\" />\n" + "<long name=\"ShutdownTimeoutMillis\" value=\"0\" />\n" + "<BlockedClientList />\n" + "<AllowedClientList />\n" + "</SoftAp>\n" + "</WifiConfigStoreData>\n"; private byte[] createLegacyApConfFile(WifiConfiguration config) throws Exception { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(outputStream); out.writeInt(3); out.writeUTF(config.SSID); out.writeInt(config.apBand); out.writeInt(config.apChannel); out.writeBoolean(config.hiddenSSID); int authType = config.getAuthType(); out.writeInt(authType); if (authType != WifiConfiguration.KeyMgmt.NONE) { out.writeUTF(config.preSharedKey); } out.close(); return outputStream.toByteArray(); } /** * Generate a SoftApConfiguration based on the specified parameters. */ private SoftApConfiguration setupApConfig( String ssid, String preSharedKey, int keyManagement, int band, int channel, boolean hiddenSSID) { SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); configBuilder.setSsid(ssid); configBuilder.setPassphrase(preSharedKey, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); if (channel == 0) { configBuilder.setBand(band); } else { configBuilder.setChannel(channel, band); } configBuilder.setHiddenSsid(hiddenSSID); return configBuilder.build(); } /** * Generate a WifiConfiguration based on the specified parameters. */ private WifiConfiguration setupWifiConfigurationApConfig( String ssid, String preSharedKey, int keyManagement, int band, int channel, boolean hiddenSSID) { WifiConfiguration config = new WifiConfiguration(); config.SSID = ssid; config.preSharedKey = preSharedKey; config.allowedKeyManagement.set(keyManagement); config.apBand = band; config.apChannel = channel; config.hiddenSSID = hiddenSSID; return config; } /** * Asserts that the WifiConfigurations equal to SoftApConfiguration. * This only compares the elements saved * for softAp used. */ public static void assertWifiConfigurationEqualSoftApConfiguration( WifiConfiguration backup, SoftApConfiguration restore) { assertEquals(backup.SSID, restore.getSsid()); assertEquals(backup.BSSID, restore.getBssid()); assertEquals(SoftApConfToXmlMigrationUtil.convertWifiConfigBandToSoftApConfigBand( backup.apBand), restore.getBand()); assertEquals(backup.apChannel, restore.getChannel()); assertEquals(backup.preSharedKey, restore.getPassphrase()); if (backup.getAuthType() == WifiConfiguration.KeyMgmt.WPA2_PSK) { assertEquals(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK, restore.getSecurityType()); } else { assertEquals(SoftApConfiguration.SECURITY_TYPE_OPEN, restore.getSecurityType()); } assertEquals(backup.hiddenSSID, restore.isHiddenSsid()); } /** * Note: This is a copy of {@link AtomicFile#readFully()} modified to use the passed in * {@link InputStream} which was returned using {@link AtomicFile#openRead()}. */ private static byte[] readFully(InputStream stream) throws IOException { try { int pos = 0; int avail = stream.available(); byte[] data = new byte[avail]; while (true) { int amt = stream.read(data, pos, data.length - pos); if (amt <= 0) { return data; } pos += amt; avail = stream.available(); if (avail > data.length - pos) { byte[] newData = new byte[pos + avail]; System.arraycopy(data, 0, newData, 0, pos); data = newData; } } } finally { stream.close(); } } /** * Tests conversion from legacy .conf file to XML file format. */ @Test public void testConversion() throws Exception { WifiConfiguration backupConfig = setupWifiConfigurationApConfig( TEST_SSID, /* SSID */ TEST_PASSPHRASE, /* preshared key */ WifiConfiguration.KeyMgmt.WPA2_PSK, /* key management */ 1, /* AP band (5GHz) */ TEST_CHANNEL, /* AP channel */ TEST_HIDDEN /* Hidden SSID */); SoftApConfiguration expectedConfig = setupApConfig( TEST_SSID, /* SSID */ TEST_PASSPHRASE, /* preshared key */ SoftApConfiguration.SECURITY_TYPE_WPA2_PSK, /* security type */ SoftApConfiguration.BAND_5GHZ, /* AP band (5GHz) */ TEST_CHANNEL, /* AP channel */ TEST_HIDDEN /* Hidden SSID */); assertWifiConfigurationEqualSoftApConfiguration(backupConfig, expectedConfig); byte[] confBytes = createLegacyApConfFile(backupConfig); assertNotNull(confBytes); InputStream xmlStream = SoftApConfToXmlMigrationUtil.convert( new ByteArrayInputStream(confBytes)); byte[] xmlBytes = readFully(xmlStream); assertNotNull(xmlBytes); assertEquals(TEST_EXPECTED_XML_STRING, new String(xmlBytes)); } } Loading
wifi/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ filegroup { // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache // to a separate package. "java/android/net/wifi/SoftApConfToXmlMigrationUtil.java", "java/android/net/wifi/WifiNetworkScoreCache.java", "java/android/net/wifi/WifiMigration.java", "java/android/net/wifi/nl80211/*.java", Loading
wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java 0 → 100755 +284 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.wifi; import static android.os.Environment.getDataMiscDirectory; import android.annotation.Nullable; import android.net.MacAddress; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; /** * Utility class to convert the legacy softap.conf file format to the new XML format. * Note: * <li>This should be modified by the OEM if they want to migrate configuration for existing * devices for new softap features supported by AOSP in Android 11. * For ex: client allowlist/blocklist feature was already supported by some OEM's before Android 10 * while AOSP only supported it in Android 11. </li> * <li>Most of this class was copied over from WifiApConfigStore class in Android 10 and * SoftApStoreData class in Android 11</li> * @hide */ public final class SoftApConfToXmlMigrationUtil { private static final String TAG = "SoftApConfToXmlMigrationUtil"; /** * Directory to read the wifi config store files from under. */ private static final String LEGACY_WIFI_STORE_DIRECTORY_NAME = "wifi"; /** * The legacy Softap config file which contained key/value pairs. */ private static final String LEGACY_AP_CONFIG_FILE = "softap.conf"; /** * Pre-apex wifi shared folder. */ private static File getLegacyWifiSharedDirectory() { return new File(getDataMiscDirectory(), LEGACY_WIFI_STORE_DIRECTORY_NAME); } /* @hide constants copied from WifiConfiguration */ /** * 2GHz band. */ private static final int WIFICONFIG_AP_BAND_2GHZ = 0; /** * 5GHz band. */ private static final int WIFICONFIG_AP_BAND_5GHZ = 1; /** * Device is allowed to choose the optimal band (2Ghz or 5Ghz) based on device capability, * operating country code and current radio conditions. */ private static final int WIFICONFIG_AP_BAND_ANY = -1; /** * Convert band from WifiConfiguration into SoftApConfiguration * * @param wifiConfigBand band encoded as WIFICONFIG_AP_BAND_xxxx * @return band as encoded as SoftApConfiguration.BAND_xxx */ @VisibleForTesting public static int convertWifiConfigBandToSoftApConfigBand(int wifiConfigBand) { switch (wifiConfigBand) { case WIFICONFIG_AP_BAND_2GHZ: return SoftApConfiguration.BAND_2GHZ; case WIFICONFIG_AP_BAND_5GHZ: return SoftApConfiguration.BAND_5GHZ; case WIFICONFIG_AP_BAND_ANY: return SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ; default: return SoftApConfiguration.BAND_2GHZ; } } /** * Load AP configuration from legacy persistent storage. * Note: This is deprecated and only used for migrating data once on reboot. */ private static SoftApConfiguration loadFromLegacyFile(InputStream fis) { SoftApConfiguration config = null; DataInputStream in = null; try { SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); in = new DataInputStream(new BufferedInputStream(fis)); int version = in.readInt(); if (version < 1 || version > 3) { Log.e(TAG, "Bad version on hotspot configuration file"); return null; } configBuilder.setSsid(in.readUTF()); if (version >= 2) { int band = in.readInt(); int channel = in.readInt(); if (channel == 0) { configBuilder.setBand( convertWifiConfigBandToSoftApConfigBand(band)); } else { configBuilder.setChannel(channel, convertWifiConfigBandToSoftApConfigBand(band)); } } if (version >= 3) { configBuilder.setHiddenSsid(in.readBoolean()); } int authType = in.readInt(); if (authType == WifiConfiguration.KeyMgmt.WPA2_PSK) { configBuilder.setPassphrase(in.readUTF(), SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); } config = configBuilder.build(); } catch (IOException e) { Log.e(TAG, "Error reading hotspot configuration ", e); config = null; } catch (IllegalArgumentException ie) { Log.e(TAG, "Invalid hotspot configuration ", ie); config = null; } finally { if (in != null) { try { in.close(); } catch (IOException e) { Log.e(TAG, "Error closing hotspot configuration during read", e); } } } // NOTE: OEM's should add their customized parsing code here. return config; } // This is the version that Android 11 released with. private static final int CONFIG_STORE_DATA_VERSION = 3; private static final String XML_TAG_DOCUMENT_HEADER = "WifiConfigStoreData"; private static final String XML_TAG_VERSION = "Version"; private static final String XML_TAG_SECTION_HEADER_SOFTAP = "SoftAp"; private static final String XML_TAG_SSID = "SSID"; private static final String XML_TAG_BSSID = "Bssid"; private static final String XML_TAG_CHANNEL = "Channel"; private static final String XML_TAG_HIDDEN_SSID = "HiddenSSID"; private static final String XML_TAG_SECURITY_TYPE = "SecurityType"; private static final String XML_TAG_AP_BAND = "ApBand"; private static final String XML_TAG_PASSPHRASE = "Passphrase"; private static final String XML_TAG_MAX_NUMBER_OF_CLIENTS = "MaxNumberOfClients"; private static final String XML_TAG_AUTO_SHUTDOWN_ENABLED = "AutoShutdownEnabled"; private static final String XML_TAG_SHUTDOWN_TIMEOUT_MILLIS = "ShutdownTimeoutMillis"; private static final String XML_TAG_CLIENT_CONTROL_BY_USER = "ClientControlByUser"; private static final String XML_TAG_BLOCKED_CLIENT_LIST = "BlockedClientList"; private static final String XML_TAG_ALLOWED_CLIENT_LIST = "AllowedClientList"; public static final String XML_TAG_CLIENT_MACADDRESS = "ClientMacAddress"; private static byte[] convertConfToXml(SoftApConfiguration softApConf) { try { final XmlSerializer out = new FastXmlSerializer(); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); out.setOutput(outputStream, StandardCharsets.UTF_8.name()); // Header for the XML file. out.startDocument(null, true); out.startTag(null, XML_TAG_DOCUMENT_HEADER); XmlUtils.writeValueXml(CONFIG_STORE_DATA_VERSION, XML_TAG_VERSION, out); out.startTag(null, XML_TAG_SECTION_HEADER_SOFTAP); // SoftAp conf XmlUtils.writeValueXml(softApConf.getSsid(), XML_TAG_SSID, out); if (softApConf.getBssid() != null) { XmlUtils.writeValueXml(softApConf.getBssid().toString(), XML_TAG_BSSID, out); } XmlUtils.writeValueXml(softApConf.getBand(), XML_TAG_AP_BAND, out); XmlUtils.writeValueXml(softApConf.getChannel(), XML_TAG_CHANNEL, out); XmlUtils.writeValueXml(softApConf.isHiddenSsid(), XML_TAG_HIDDEN_SSID, out); XmlUtils.writeValueXml(softApConf.getSecurityType(), XML_TAG_SECURITY_TYPE, out); if (softApConf.getSecurityType() != SoftApConfiguration.SECURITY_TYPE_OPEN) { XmlUtils.writeValueXml(softApConf.getPassphrase(), XML_TAG_PASSPHRASE, out); } XmlUtils.writeValueXml(softApConf.getMaxNumberOfClients(), XML_TAG_MAX_NUMBER_OF_CLIENTS, out); XmlUtils.writeValueXml(softApConf.isClientControlByUserEnabled(), XML_TAG_CLIENT_CONTROL_BY_USER, out); XmlUtils.writeValueXml(softApConf.isAutoShutdownEnabled(), XML_TAG_AUTO_SHUTDOWN_ENABLED, out); XmlUtils.writeValueXml(softApConf.getShutdownTimeoutMillis(), XML_TAG_SHUTDOWN_TIMEOUT_MILLIS, out); out.startTag(null, XML_TAG_BLOCKED_CLIENT_LIST); for (MacAddress mac: softApConf.getBlockedClientList()) { XmlUtils.writeValueXml(mac.toString(), XML_TAG_CLIENT_MACADDRESS, out); } out.endTag(null, XML_TAG_BLOCKED_CLIENT_LIST); out.startTag(null, XML_TAG_ALLOWED_CLIENT_LIST); for (MacAddress mac: softApConf.getAllowedClientList()) { XmlUtils.writeValueXml(mac.toString(), XML_TAG_CLIENT_MACADDRESS, out); } out.endTag(null, XML_TAG_ALLOWED_CLIENT_LIST); // Footer for the XML file. out.endTag(null, XML_TAG_SECTION_HEADER_SOFTAP); out.endTag(null, XML_TAG_DOCUMENT_HEADER); out.endDocument(); return outputStream.toByteArray(); } catch (IOException | XmlPullParserException e) { Log.e(TAG, "Failed to convert softap conf to XML", e); return null; } } private SoftApConfToXmlMigrationUtil() { } /** * Read the legacy /data/misc/wifi/softap.conf file format and convert to the new XML * format understood by WifiConfigStore. * Note: Used for unit testing. */ @VisibleForTesting @Nullable public static InputStream convert(InputStream fis) { SoftApConfiguration softApConf = loadFromLegacyFile(fis); if (softApConf == null) return null; byte[] xmlBytes = convertConfToXml(softApConf); if (xmlBytes == null) return null; return new ByteArrayInputStream(xmlBytes); } /** * Read the legacy /data/misc/wifi/softap.conf file format and convert to the new XML * format understood by WifiConfigStore. */ @Nullable public static InputStream convert() { File file = new File(getLegacyWifiSharedDirectory(), LEGACY_AP_CONFIG_FILE); FileInputStream fis = null; try { fis = new FileInputStream(file); } catch (FileNotFoundException e) { return null; } if (fis == null) return null; return convert(fis); } /** * Remove the legacy /data/misc/wifi/softap.conf file. */ @Nullable public static void remove() { File file = new File(getLegacyWifiSharedDirectory(), LEGACY_AP_CONFIG_FILE); file.delete(); } }
wifi/java/android/net/wifi/WifiMigration.java +24 −3 Original line number Diff line number Diff line Loading @@ -95,7 +95,7 @@ public final class WifiMigration { private static final SparseArray<String> STORE_ID_TO_FILE_NAME = new SparseArray<String>() {{ put(STORE_FILE_SHARED_GENERAL, "WifiConfigStore.xml"); put(STORE_FILE_SHARED_SOFTAP, "softap.conf"); put(STORE_FILE_SHARED_SOFTAP, "WifiConfigStoreSoftAp.xml"); put(STORE_FILE_USER_GENERAL, "WifiConfigStore.xml"); put(STORE_FILE_USER_NETWORK_SUGGESTIONS, "WifiConfigStoreNetworkSuggestions.xml"); }}; Loading Loading @@ -176,6 +176,13 @@ public final class WifiMigration { // 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; } } Loading @@ -191,7 +198,18 @@ public final class WifiMigration { if (storeFileId != STORE_FILE_SHARED_GENERAL && storeFileId != STORE_FILE_SHARED_SOFTAP) { throw new IllegalArgumentException("Invalid shared store file id"); } getSharedAtomicFile(storeFileId).delete(); 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(); } } /** Loading Loading @@ -258,7 +276,10 @@ public final class WifiMigration { throw new IllegalArgumentException("Invalid user store file id"); } Objects.requireNonNull(userHandle); getUserAtomicFile(storeFileId, userHandle.getIdentifier()).delete(); AtomicFile file = getUserAtomicFile(storeFileId, userHandle.getIdentifier()); if (file.exists()) { file.delete(); } } /** Loading
wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java 0 → 100644 +199 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.wifi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import androidx.test.filters.SmallTest; import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; /** * Unit tests for {@link android.net.wifi.SoftApConfToXmlMigrationUtilTest}. */ @SmallTest public class SoftApConfToXmlMigrationUtilTest { private static final String TEST_SSID = "SSID"; private static final String TEST_PASSPHRASE = "TestPassphrase"; private static final int TEST_CHANNEL = 0; private static final boolean TEST_HIDDEN = false; private static final int TEST_BAND = SoftApConfiguration.BAND_5GHZ; private static final int TEST_SECURITY = SoftApConfiguration.SECURITY_TYPE_WPA2_PSK; private static final String TEST_EXPECTED_XML_STRING = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + "<WifiConfigStoreData>\n" + "<int name=\"Version\" value=\"3\" />\n" + "<SoftAp>\n" + "<string name=\"SSID\">" + TEST_SSID + "</string>\n" + "<int name=\"ApBand\" value=\"" + TEST_BAND + "\" />\n" + "<int name=\"Channel\" value=\"" + TEST_CHANNEL + "\" />\n" + "<boolean name=\"HiddenSSID\" value=\"" + TEST_HIDDEN + "\" />\n" + "<int name=\"SecurityType\" value=\"" + TEST_SECURITY + "\" />\n" + "<string name=\"Passphrase\">" + TEST_PASSPHRASE + "</string>\n" + "<int name=\"MaxNumberOfClients\" value=\"0\" />\n" + "<boolean name=\"ClientControlByUser\" value=\"false\" />\n" + "<boolean name=\"AutoShutdownEnabled\" value=\"true\" />\n" + "<long name=\"ShutdownTimeoutMillis\" value=\"0\" />\n" + "<BlockedClientList />\n" + "<AllowedClientList />\n" + "</SoftAp>\n" + "</WifiConfigStoreData>\n"; private byte[] createLegacyApConfFile(WifiConfiguration config) throws Exception { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(outputStream); out.writeInt(3); out.writeUTF(config.SSID); out.writeInt(config.apBand); out.writeInt(config.apChannel); out.writeBoolean(config.hiddenSSID); int authType = config.getAuthType(); out.writeInt(authType); if (authType != WifiConfiguration.KeyMgmt.NONE) { out.writeUTF(config.preSharedKey); } out.close(); return outputStream.toByteArray(); } /** * Generate a SoftApConfiguration based on the specified parameters. */ private SoftApConfiguration setupApConfig( String ssid, String preSharedKey, int keyManagement, int band, int channel, boolean hiddenSSID) { SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); configBuilder.setSsid(ssid); configBuilder.setPassphrase(preSharedKey, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); if (channel == 0) { configBuilder.setBand(band); } else { configBuilder.setChannel(channel, band); } configBuilder.setHiddenSsid(hiddenSSID); return configBuilder.build(); } /** * Generate a WifiConfiguration based on the specified parameters. */ private WifiConfiguration setupWifiConfigurationApConfig( String ssid, String preSharedKey, int keyManagement, int band, int channel, boolean hiddenSSID) { WifiConfiguration config = new WifiConfiguration(); config.SSID = ssid; config.preSharedKey = preSharedKey; config.allowedKeyManagement.set(keyManagement); config.apBand = band; config.apChannel = channel; config.hiddenSSID = hiddenSSID; return config; } /** * Asserts that the WifiConfigurations equal to SoftApConfiguration. * This only compares the elements saved * for softAp used. */ public static void assertWifiConfigurationEqualSoftApConfiguration( WifiConfiguration backup, SoftApConfiguration restore) { assertEquals(backup.SSID, restore.getSsid()); assertEquals(backup.BSSID, restore.getBssid()); assertEquals(SoftApConfToXmlMigrationUtil.convertWifiConfigBandToSoftApConfigBand( backup.apBand), restore.getBand()); assertEquals(backup.apChannel, restore.getChannel()); assertEquals(backup.preSharedKey, restore.getPassphrase()); if (backup.getAuthType() == WifiConfiguration.KeyMgmt.WPA2_PSK) { assertEquals(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK, restore.getSecurityType()); } else { assertEquals(SoftApConfiguration.SECURITY_TYPE_OPEN, restore.getSecurityType()); } assertEquals(backup.hiddenSSID, restore.isHiddenSsid()); } /** * Note: This is a copy of {@link AtomicFile#readFully()} modified to use the passed in * {@link InputStream} which was returned using {@link AtomicFile#openRead()}. */ private static byte[] readFully(InputStream stream) throws IOException { try { int pos = 0; int avail = stream.available(); byte[] data = new byte[avail]; while (true) { int amt = stream.read(data, pos, data.length - pos); if (amt <= 0) { return data; } pos += amt; avail = stream.available(); if (avail > data.length - pos) { byte[] newData = new byte[pos + avail]; System.arraycopy(data, 0, newData, 0, pos); data = newData; } } } finally { stream.close(); } } /** * Tests conversion from legacy .conf file to XML file format. */ @Test public void testConversion() throws Exception { WifiConfiguration backupConfig = setupWifiConfigurationApConfig( TEST_SSID, /* SSID */ TEST_PASSPHRASE, /* preshared key */ WifiConfiguration.KeyMgmt.WPA2_PSK, /* key management */ 1, /* AP band (5GHz) */ TEST_CHANNEL, /* AP channel */ TEST_HIDDEN /* Hidden SSID */); SoftApConfiguration expectedConfig = setupApConfig( TEST_SSID, /* SSID */ TEST_PASSPHRASE, /* preshared key */ SoftApConfiguration.SECURITY_TYPE_WPA2_PSK, /* security type */ SoftApConfiguration.BAND_5GHZ, /* AP band (5GHz) */ TEST_CHANNEL, /* AP channel */ TEST_HIDDEN /* Hidden SSID */); assertWifiConfigurationEqualSoftApConfiguration(backupConfig, expectedConfig); byte[] confBytes = createLegacyApConfFile(backupConfig); assertNotNull(confBytes); InputStream xmlStream = SoftApConfToXmlMigrationUtil.convert( new ByteArrayInputStream(confBytes)); byte[] xmlBytes = readFully(xmlStream); assertNotNull(xmlBytes); assertEquals(TEST_EXPECTED_XML_STRING, new String(xmlBytes)); } }