/* * 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(); } }