285 lines
11 KiB
Java
285 lines
11 KiB
Java
![]() |
/*
|
||
|
* 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();
|
||
|
}
|
||
|
}
|