1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.wifi; 18 19 import static android.os.Environment.getDataMiscDirectory; 20 21 import android.annotation.Nullable; 22 import android.net.MacAddress; 23 import android.util.Log; 24 25 import com.android.internal.annotations.VisibleForTesting; 26 import com.android.internal.util.FastXmlSerializer; 27 import com.android.internal.util.XmlUtils; 28 29 import org.xmlpull.v1.XmlPullParserException; 30 import org.xmlpull.v1.XmlSerializer; 31 32 import java.io.BufferedInputStream; 33 import java.io.ByteArrayInputStream; 34 import java.io.ByteArrayOutputStream; 35 import java.io.DataInputStream; 36 import java.io.File; 37 import java.io.FileInputStream; 38 import java.io.FileNotFoundException; 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.nio.charset.StandardCharsets; 42 43 /** 44 * Utility class to convert the legacy softap.conf file format to the new XML format. 45 * Note: 46 * <li>This should be modified by the OEM if they want to migrate configuration for existing 47 * devices for new softap features supported by AOSP in Android 11. 48 * For ex: client allowlist/blocklist feature was already supported by some OEM's before Android 10 49 * while AOSP only supported it in Android 11. </li> 50 * <li>Most of this class was copied over from WifiApConfigStore class in Android 10 and 51 * SoftApStoreData class in Android 11</li> 52 * @hide 53 */ 54 public final class SoftApConfToXmlMigrationUtil { 55 private static final String TAG = "SoftApConfToXmlMigrationUtil"; 56 57 /** 58 * Directory to read the wifi config store files from under. 59 */ 60 private static final String LEGACY_WIFI_STORE_DIRECTORY_NAME = "wifi"; 61 /** 62 * The legacy Softap config file which contained key/value pairs. 63 */ 64 private static final String LEGACY_AP_CONFIG_FILE = "softap.conf"; 65 66 /** 67 * Pre-apex wifi shared folder. 68 */ getLegacyWifiSharedDirectory()69 private static File getLegacyWifiSharedDirectory() { 70 return new File(getDataMiscDirectory(), LEGACY_WIFI_STORE_DIRECTORY_NAME); 71 } 72 73 /* @hide constants copied from WifiConfiguration */ 74 /** 75 * 2GHz band. 76 */ 77 private static final int WIFICONFIG_AP_BAND_2GHZ = 0; 78 /** 79 * 5GHz band. 80 */ 81 private static final int WIFICONFIG_AP_BAND_5GHZ = 1; 82 /** 83 * Device is allowed to choose the optimal band (2Ghz or 5Ghz) based on device capability, 84 * operating country code and current radio conditions. 85 */ 86 private static final int WIFICONFIG_AP_BAND_ANY = -1; 87 /** 88 * Convert band from WifiConfiguration into SoftApConfiguration 89 * 90 * @param wifiConfigBand band encoded as WIFICONFIG_AP_BAND_xxxx 91 * @return band as encoded as SoftApConfiguration.BAND_xxx 92 */ 93 @VisibleForTesting convertWifiConfigBandToSoftApConfigBand(int wifiConfigBand)94 public static int convertWifiConfigBandToSoftApConfigBand(int wifiConfigBand) { 95 switch (wifiConfigBand) { 96 case WIFICONFIG_AP_BAND_2GHZ: 97 return SoftApConfiguration.BAND_2GHZ; 98 case WIFICONFIG_AP_BAND_5GHZ: 99 return SoftApConfiguration.BAND_5GHZ; 100 case WIFICONFIG_AP_BAND_ANY: 101 return SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ; 102 default: 103 return SoftApConfiguration.BAND_2GHZ; 104 } 105 } 106 107 /** 108 * Load AP configuration from legacy persistent storage. 109 * Note: This is deprecated and only used for migrating data once on reboot. 110 */ loadFromLegacyFile(InputStream fis)111 private static SoftApConfiguration loadFromLegacyFile(InputStream fis) { 112 SoftApConfiguration config = null; 113 DataInputStream in = null; 114 try { 115 SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); 116 in = new DataInputStream(new BufferedInputStream(fis)); 117 118 int version = in.readInt(); 119 if (version < 1 || version > 3) { 120 Log.e(TAG, "Bad version on hotspot configuration file"); 121 return null; 122 } 123 configBuilder.setSsid(in.readUTF()); 124 125 if (version >= 2) { 126 int band = in.readInt(); 127 int channel = in.readInt(); 128 if (channel == 0) { 129 configBuilder.setBand( 130 convertWifiConfigBandToSoftApConfigBand(band)); 131 } else { 132 configBuilder.setChannel(channel, 133 convertWifiConfigBandToSoftApConfigBand(band)); 134 } 135 } 136 if (version >= 3) { 137 configBuilder.setHiddenSsid(in.readBoolean()); 138 } 139 int authType = in.readInt(); 140 if (authType == WifiConfiguration.KeyMgmt.WPA2_PSK) { 141 configBuilder.setPassphrase(in.readUTF(), 142 SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); 143 } 144 config = configBuilder.build(); 145 } catch (IOException e) { 146 Log.e(TAG, "Error reading hotspot configuration ", e); 147 config = null; 148 } catch (IllegalArgumentException ie) { 149 Log.e(TAG, "Invalid hotspot configuration ", ie); 150 config = null; 151 } finally { 152 if (in != null) { 153 try { 154 in.close(); 155 } catch (IOException e) { 156 Log.e(TAG, "Error closing hotspot configuration during read", e); 157 } 158 } 159 } 160 // NOTE: OEM's should add their customized parsing code here. 161 return config; 162 } 163 164 // This is the version that Android 11 released with. 165 private static final int CONFIG_STORE_DATA_VERSION = 3; 166 167 private static final String XML_TAG_DOCUMENT_HEADER = "WifiConfigStoreData"; 168 private static final String XML_TAG_VERSION = "Version"; 169 private static final String XML_TAG_SECTION_HEADER_SOFTAP = "SoftAp"; 170 private static final String XML_TAG_SSID = "SSID"; 171 private static final String XML_TAG_BSSID = "Bssid"; 172 private static final String XML_TAG_CHANNEL = "Channel"; 173 private static final String XML_TAG_HIDDEN_SSID = "HiddenSSID"; 174 private static final String XML_TAG_SECURITY_TYPE = "SecurityType"; 175 private static final String XML_TAG_AP_BAND = "ApBand"; 176 private static final String XML_TAG_PASSPHRASE = "Passphrase"; 177 private static final String XML_TAG_MAX_NUMBER_OF_CLIENTS = "MaxNumberOfClients"; 178 private static final String XML_TAG_AUTO_SHUTDOWN_ENABLED = "AutoShutdownEnabled"; 179 private static final String XML_TAG_SHUTDOWN_TIMEOUT_MILLIS = "ShutdownTimeoutMillis"; 180 private static final String XML_TAG_CLIENT_CONTROL_BY_USER = "ClientControlByUser"; 181 private static final String XML_TAG_BLOCKED_CLIENT_LIST = "BlockedClientList"; 182 private static final String XML_TAG_ALLOWED_CLIENT_LIST = "AllowedClientList"; 183 public static final String XML_TAG_CLIENT_MACADDRESS = "ClientMacAddress"; 184 convertConfToXml(SoftApConfiguration softApConf)185 private static byte[] convertConfToXml(SoftApConfiguration softApConf) { 186 try { 187 final XmlSerializer out = new FastXmlSerializer(); 188 final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 189 out.setOutput(outputStream, StandardCharsets.UTF_8.name()); 190 191 // Header for the XML file. 192 out.startDocument(null, true); 193 out.startTag(null, XML_TAG_DOCUMENT_HEADER); 194 XmlUtils.writeValueXml(CONFIG_STORE_DATA_VERSION, XML_TAG_VERSION, out); 195 out.startTag(null, XML_TAG_SECTION_HEADER_SOFTAP); 196 197 // SoftAp conf 198 XmlUtils.writeValueXml(softApConf.getSsid(), XML_TAG_SSID, out); 199 if (softApConf.getBssid() != null) { 200 XmlUtils.writeValueXml(softApConf.getBssid().toString(), XML_TAG_BSSID, out); 201 } 202 XmlUtils.writeValueXml(softApConf.getBand(), XML_TAG_AP_BAND, out); 203 XmlUtils.writeValueXml(softApConf.getChannel(), XML_TAG_CHANNEL, out); 204 XmlUtils.writeValueXml(softApConf.isHiddenSsid(), XML_TAG_HIDDEN_SSID, out); 205 XmlUtils.writeValueXml(softApConf.getSecurityType(), XML_TAG_SECURITY_TYPE, out); 206 if (softApConf.getSecurityType() != SoftApConfiguration.SECURITY_TYPE_OPEN) { 207 XmlUtils.writeValueXml(softApConf.getPassphrase(), XML_TAG_PASSPHRASE, out); 208 } 209 XmlUtils.writeValueXml(softApConf.getMaxNumberOfClients(), 210 XML_TAG_MAX_NUMBER_OF_CLIENTS, out); 211 XmlUtils.writeValueXml(softApConf.isClientControlByUserEnabled(), 212 XML_TAG_CLIENT_CONTROL_BY_USER, out); 213 XmlUtils.writeValueXml(softApConf.isAutoShutdownEnabled(), 214 XML_TAG_AUTO_SHUTDOWN_ENABLED, out); 215 XmlUtils.writeValueXml(softApConf.getShutdownTimeoutMillis(), 216 XML_TAG_SHUTDOWN_TIMEOUT_MILLIS, out); 217 out.startTag(null, XML_TAG_BLOCKED_CLIENT_LIST); 218 for (MacAddress mac: softApConf.getBlockedClientList()) { 219 XmlUtils.writeValueXml(mac.toString(), XML_TAG_CLIENT_MACADDRESS, out); 220 } 221 out.endTag(null, XML_TAG_BLOCKED_CLIENT_LIST); 222 out.startTag(null, XML_TAG_ALLOWED_CLIENT_LIST); 223 for (MacAddress mac: softApConf.getAllowedClientList()) { 224 XmlUtils.writeValueXml(mac.toString(), XML_TAG_CLIENT_MACADDRESS, out); 225 } 226 out.endTag(null, XML_TAG_ALLOWED_CLIENT_LIST); 227 228 // Footer for the XML file. 229 out.endTag(null, XML_TAG_SECTION_HEADER_SOFTAP); 230 out.endTag(null, XML_TAG_DOCUMENT_HEADER); 231 out.endDocument(); 232 233 return outputStream.toByteArray(); 234 } catch (IOException | XmlPullParserException e) { 235 Log.e(TAG, "Failed to convert softap conf to XML", e); 236 return null; 237 } 238 } 239 SoftApConfToXmlMigrationUtil()240 private SoftApConfToXmlMigrationUtil() { } 241 242 /** 243 * Read the legacy /data/misc/wifi/softap.conf file format and convert to the new XML 244 * format understood by WifiConfigStore. 245 * Note: Used for unit testing. 246 */ 247 @VisibleForTesting 248 @Nullable convert(InputStream fis)249 public static InputStream convert(InputStream fis) { 250 SoftApConfiguration softApConf = loadFromLegacyFile(fis); 251 if (softApConf == null) return null; 252 253 byte[] xmlBytes = convertConfToXml(softApConf); 254 if (xmlBytes == null) return null; 255 256 return new ByteArrayInputStream(xmlBytes); 257 } 258 259 /** 260 * Read the legacy /data/misc/wifi/softap.conf file format and convert to the new XML 261 * format understood by WifiConfigStore. 262 */ 263 @Nullable convert()264 public static InputStream convert() { 265 File file = new File(getLegacyWifiSharedDirectory(), LEGACY_AP_CONFIG_FILE); 266 FileInputStream fis = null; 267 try { 268 fis = new FileInputStream(file); 269 } catch (FileNotFoundException e) { 270 return null; 271 } 272 if (fis == null) return null; 273 return convert(fis); 274 } 275 276 /** 277 * Remove the legacy /data/misc/wifi/softap.conf file. 278 */ 279 @Nullable remove()280 public static void remove() { 281 File file = new File(getLegacyWifiSharedDirectory(), LEGACY_AP_CONFIG_FILE); 282 file.delete(); 283 } 284 } 285