1 /* 2 * Copyright (C) 2019 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 com.android.server.wifi; 18 19 import android.content.Context; 20 import android.net.MacAddress; 21 import android.net.wifi.SoftApConfiguration; 22 import android.net.wifi.WifiConfiguration; 23 import android.net.wifi.WifiMigration; 24 import android.util.BackupUtils; 25 import android.util.Log; 26 import android.util.SparseIntArray; 27 28 import com.android.modules.utils.build.SdkLevel; 29 import com.android.server.wifi.util.ApConfigUtil; 30 import com.android.server.wifi.util.SettingsMigrationDataHolder; 31 32 import java.io.ByteArrayInputStream; 33 import java.io.ByteArrayOutputStream; 34 import java.io.DataInputStream; 35 import java.io.DataOutputStream; 36 import java.io.IOException; 37 import java.util.ArrayList; 38 import java.util.Iterator; 39 import java.util.List; 40 41 /** 42 * Class used to backup/restore data using the SettingsBackupAgent. 43 * There are 2 symmetric API's exposed here: 44 * 1. retrieveBackupDataFromSoftApConfiguration: Retrieve the configuration data to be backed up. 45 * 2. retrieveSoftApConfigurationFromBackupData: Restore the configuration using the provided data. 46 * The byte stream to be backed up is versioned to migrate the data easily across 47 * revisions. 48 */ 49 public class SoftApBackupRestore { 50 private static final String TAG = "SoftApBackupRestore"; 51 52 /** 53 * Current backup data version. 54 */ 55 private static final int CURRENT_SAP_BACKUP_DATA_VERSION = 8; 56 private static final int LAST_SAP_BACKUP_DATA_VERSION_IN_R = 7; 57 58 private static final int ETHER_ADDR_LEN = 6; // Byte array size of MacAddress 59 60 private final Context mContext; 61 private final SettingsMigrationDataHolder mSettingsMigrationDataHolder; 62 SoftApBackupRestore(Context context, SettingsMigrationDataHolder settingsMigrationDataHolder)63 public SoftApBackupRestore(Context context, 64 SettingsMigrationDataHolder settingsMigrationDataHolder) { 65 mContext = context; 66 mSettingsMigrationDataHolder = settingsMigrationDataHolder; 67 } 68 69 /** 70 * Retrieve a byte stream representing the data that needs to be backed up from the 71 * provided softap configuration. 72 * 73 * @param config saved soft ap config that needs to be backed up. 74 * @return Raw byte stream that needs to be backed up. 75 */ retrieveBackupDataFromSoftApConfiguration(SoftApConfiguration config)76 public byte[] retrieveBackupDataFromSoftApConfiguration(SoftApConfiguration config) { 77 if (config == null) { 78 Log.e(TAG, "Invalid configuration received"); 79 return new byte[0]; 80 } 81 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 82 try { 83 DataOutputStream out = new DataOutputStream(baos); 84 if (SdkLevel.isAtLeastS()) { 85 out.writeInt(CURRENT_SAP_BACKUP_DATA_VERSION); 86 } else { 87 out.writeInt(LAST_SAP_BACKUP_DATA_VERSION_IN_R); 88 } 89 BackupUtils.writeString(out, config.getSsid()); 90 out.writeInt(config.getBand()); 91 out.writeInt(config.getChannel()); 92 BackupUtils.writeString(out, config.getPassphrase()); 93 out.writeInt(config.getSecurityType()); 94 out.writeBoolean(config.isHiddenSsid()); 95 out.writeInt(config.getMaxNumberOfClients()); 96 out.writeLong(config.getShutdownTimeoutMillis()); 97 out.writeBoolean(config.isClientControlByUserEnabled()); 98 writeMacAddressList(out, config.getBlockedClientList()); 99 writeMacAddressList(out, config.getAllowedClientList()); 100 out.writeBoolean(config.isAutoShutdownEnabled()); 101 if (SdkLevel.isAtLeastS()) { 102 out.writeBoolean(config.isBridgedModeOpportunisticShutdownEnabled()); 103 out.writeInt(config.getMacRandomizationSetting()); 104 SparseIntArray channels = config.getChannels(); 105 int numOfChannels = channels.size(); 106 out.writeInt(numOfChannels); 107 for (int i = 0; i < numOfChannels; i++) { 108 out.writeInt(channels.keyAt(i)); 109 out.writeInt(channels.valueAt(i)); 110 } 111 out.writeBoolean(config.isIeee80211axEnabled()); 112 } 113 114 } catch (IOException io) { 115 Log.e(TAG, "Invalid configuration received, IOException " + io); 116 return new byte[0]; 117 } 118 return baos.toByteArray(); 119 } 120 121 /** 122 * Parse out the configurations from the back up data. 123 * 124 * @param data raw byte stream representing the data. 125 * @return Soft ap config retrieved from the backed up data. 126 */ retrieveSoftApConfigurationFromBackupData(byte[] data)127 public SoftApConfiguration retrieveSoftApConfigurationFromBackupData(byte[] data) { 128 if (data == null || data.length == 0) { 129 Log.e(TAG, "Invalid backup data received"); 130 return null; 131 } 132 SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); 133 try { 134 DataInputStream in = new DataInputStream(new ByteArrayInputStream(data)); 135 int version = in.readInt(); 136 if (version < 1 || version > CURRENT_SAP_BACKUP_DATA_VERSION) { 137 throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version"); 138 } 139 140 if (version == 1) return null; // Version 1 is a bad dataset. 141 142 configBuilder.setSsid(BackupUtils.readString(in)); 143 144 int band; 145 if (version < 4) { 146 band = ApConfigUtil.convertWifiConfigBandToSoftApConfigBand(in.readInt()); 147 } else { 148 band = in.readInt(); 149 } 150 int channel = in.readInt(); 151 152 if (channel == 0) { 153 configBuilder.setBand(band); 154 } else { 155 configBuilder.setChannel(channel, band); 156 } 157 String passphrase = BackupUtils.readString(in); 158 int securityType = in.readInt(); 159 if (version < 4 && securityType == WifiConfiguration.KeyMgmt.WPA2_PSK) { 160 configBuilder.setPassphrase(passphrase, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); 161 } else if (version >= 4 && securityType != SoftApConfiguration.SECURITY_TYPE_OPEN) { 162 configBuilder.setPassphrase(passphrase, securityType); 163 } 164 if (version >= 3) { 165 configBuilder.setHiddenSsid(in.readBoolean()); 166 } 167 if (version >= 5) { 168 configBuilder.setMaxNumberOfClients(in.readInt()); 169 if (version >= 7) { 170 configBuilder.setShutdownTimeoutMillis(in.readLong()); 171 } else { 172 configBuilder.setShutdownTimeoutMillis(Long.valueOf(in.readInt())); 173 } 174 configBuilder.setClientControlByUserEnabled(in.readBoolean()); 175 int numberOfBlockedClient = in.readInt(); 176 List<MacAddress> blockedList = new ArrayList<>( 177 macAddressListFromByteArray(in, numberOfBlockedClient)); 178 int numberOfAllowedClient = in.readInt(); 179 List<MacAddress> allowedList = new ArrayList<>( 180 macAddressListFromByteArray(in, numberOfAllowedClient)); 181 configBuilder.setBlockedClientList(blockedList); 182 configBuilder.setAllowedClientList(allowedList); 183 } 184 if (version >= 6) { 185 configBuilder.setAutoShutdownEnabled(in.readBoolean()); 186 } else { 187 // Migrate data out of settings. 188 WifiMigration.SettingsMigrationData migrationData = 189 mSettingsMigrationDataHolder.retrieveData(); 190 if (migrationData == null) { 191 Log.e(TAG, "No migration data present"); 192 } else { 193 configBuilder.setAutoShutdownEnabled(migrationData.isSoftApTimeoutEnabled()); 194 } 195 } 196 if (version >= 8 && SdkLevel.isAtLeastS()) { 197 configBuilder.setBridgedModeOpportunisticShutdownEnabled(in.readBoolean()); 198 configBuilder.setMacRandomizationSetting(in.readInt()); 199 int numOfChannels = in.readInt(); 200 SparseIntArray channels = new SparseIntArray(numOfChannels); 201 for (int i = 0; i < numOfChannels; i++) { 202 channels.put(in.readInt(), in.readInt()); 203 } 204 configBuilder.setChannels(channels); 205 configBuilder.setIeee80211axEnabled(in.readBoolean()); 206 } 207 } catch (IOException io) { 208 Log.e(TAG, "Invalid backup data received, IOException: " + io); 209 return null; 210 } catch (BackupUtils.BadVersionException badVersion) { 211 Log.e(TAG, "Invalid backup data received, BadVersionException: " + badVersion); 212 return null; 213 } catch (IllegalArgumentException ie) { 214 Log.e(TAG, "Invalid backup data received, IllegalArgumentException " + ie); 215 return null; 216 } 217 return configBuilder.build(); 218 } 219 writeMacAddressList(DataOutputStream out, List<MacAddress> macList)220 private void writeMacAddressList(DataOutputStream out, List<MacAddress> macList) 221 throws IOException { 222 out.writeInt(macList.size()); 223 Iterator<MacAddress> iterator = macList.iterator(); 224 while (iterator.hasNext()) { 225 byte[] mac = iterator.next().toByteArray(); 226 out.write(mac, 0, ETHER_ADDR_LEN); 227 } 228 } 229 macAddressListFromByteArray(DataInputStream in, int numberOfClients)230 private List<MacAddress> macAddressListFromByteArray(DataInputStream in, int numberOfClients) 231 throws IOException { 232 List<MacAddress> macList = new ArrayList<>(); 233 for (int i = 0; i < numberOfClients; i++) { 234 byte[] mac = new byte[ETHER_ADDR_LEN]; 235 in.read(mac, 0, ETHER_ADDR_LEN); 236 macList.add(MacAddress.fromBytes(mac)); 237 } 238 return macList; 239 } 240 } 241