blob: 2e96f18bedb2191ad7c24484dd57e685ab7df721 [file] [log] [blame]
Amith Yamasani220f4d62009-07-02 02:34:14 -07001/*
2 * Copyright (C) 2008 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
17package com.android.providers.settings;
18
Svetoslav Ganova571a582011-09-20 18:32:20 -070019import android.app.backup.BackupAgentHelper;
20import android.app.backup.BackupDataInput;
21import android.app.backup.BackupDataOutput;
22import android.app.backup.FullBackupDataOutput;
Christopher Tate87bc7e72013-06-10 16:55:02 -070023import android.content.ContentResolver;
Svetoslav Ganova571a582011-09-20 18:32:20 -070024import android.content.ContentValues;
25import android.content.Context;
26import android.database.Cursor;
27import android.net.Uri;
Christopher Tatecab915a2015-01-08 12:50:00 -080028import android.net.wifi.WifiConfiguration;
29import android.net.wifi.WifiConfiguration.KeyMgmt;
Svetoslav Ganova571a582011-09-20 18:32:20 -070030import android.net.wifi.WifiManager;
31import android.os.FileUtils;
Christopher Tated488bc02012-10-09 14:06:30 -070032import android.os.Handler;
Svetoslav Ganova571a582011-09-20 18:32:20 -070033import android.os.ParcelFileDescriptor;
34import android.os.Process;
Amith Yamasani072543f2015-02-13 11:09:45 -080035import android.os.UserHandle;
Svetoslav Ganova571a582011-09-20 18:32:20 -070036import android.provider.Settings;
37import android.util.Log;
38
Amith Yamasani072543f2015-02-13 11:09:45 -080039import com.android.internal.widget.LockPatternUtils;
40
Christopher Tatecab915a2015-01-08 12:50:00 -080041import libcore.io.IoUtils;
42
Brad Fitzpatrick70787892010-11-17 11:31:12 -080043import java.io.BufferedOutputStream;
Amith Yamasani2cfab842009-09-09 18:27:31 -070044import java.io.BufferedReader;
45import java.io.BufferedWriter;
Amith Yamasani072543f2015-02-13 11:09:45 -080046import java.io.ByteArrayInputStream;
Christopher Tatecab915a2015-01-08 12:50:00 -080047import java.io.ByteArrayOutputStream;
Christopher Tate8dfe2b92012-05-15 15:05:04 -070048import java.io.CharArrayReader;
Amith Yamasanid1582142009-07-08 20:04:55 -070049import java.io.DataInputStream;
50import java.io.DataOutputStream;
Amith Yamasani2cfab842009-09-09 18:27:31 -070051import java.io.EOFException;
Amith Yamasani220f4d62009-07-02 02:34:14 -070052import java.io.File;
53import java.io.FileInputStream;
54import java.io.FileOutputStream;
Amith Yamasani2cfab842009-09-09 18:27:31 -070055import java.io.FileReader;
56import java.io.FileWriter;
Amith Yamasani220f4d62009-07-02 02:34:14 -070057import java.io.IOException;
Svetoslav Ganova571a582011-09-20 18:32:20 -070058import java.io.InputStream;
Brad Fitzpatrick70787892010-11-17 11:31:12 -080059import java.io.OutputStream;
Christopher Tatecab915a2015-01-08 12:50:00 -080060import java.io.OutputStreamWriter;
Christopher Tate8dfe2b92012-05-15 15:05:04 -070061import java.io.Writer;
62import java.util.ArrayList;
Christopher Tatecab915a2015-01-08 12:50:00 -080063import java.util.BitSet;
Svetoslav Ganova571a582011-09-20 18:32:20 -070064import java.util.HashMap;
Christopher Tate8dfe2b92012-05-15 15:05:04 -070065import java.util.HashSet;
Christopher Tatecab915a2015-01-08 12:50:00 -080066import java.util.List;
Svetoslav Ganova571a582011-09-20 18:32:20 -070067import java.util.Map;
Christopher Tatecab915a2015-01-08 12:50:00 -080068import java.util.Objects;
Amith Yamasanid1582142009-07-08 20:04:55 -070069import java.util.zip.CRC32;
Amith Yamasani220f4d62009-07-02 02:34:14 -070070
Amith Yamasani220f4d62009-07-02 02:34:14 -070071/**
72 * Performs backup and restore of the System and Secure settings.
73 * List of settings that are backed up are stored in the Settings.java file
74 */
Christopher Tatecc84c692010-03-29 14:54:02 -070075public class SettingsBackupAgent extends BackupAgentHelper {
Christopher Tate436344a2009-09-30 16:17:37 -070076 private static final boolean DEBUG = false;
Christopher Tate8dfe2b92012-05-15 15:05:04 -070077 private static final boolean DEBUG_BACKUP = DEBUG || false;
Amith Yamasani220f4d62009-07-02 02:34:14 -070078
79 private static final String KEY_SYSTEM = "system";
80 private static final String KEY_SECURE = "secure";
Christopher Tate66488d62012-10-02 11:58:01 -070081 private static final String KEY_GLOBAL = "global";
Amith Yamasani8823c0a82009-07-07 14:30:17 -070082 private static final String KEY_LOCALE = "locale";
Amith Yamasani072543f2015-02-13 11:09:45 -080083 private static final String KEY_LOCK_SETTINGS = "lock_settings";
Amith Yamasani220f4d62009-07-02 02:34:14 -070084
Christopher Tatea286f412009-09-18 15:51:15 -070085 // Versioning of the state file. Increment this version
86 // number any time the set of state items is altered.
Amith Yamasani072543f2015-02-13 11:09:45 -080087 private static final int STATE_VERSION = 4;
Christopher Tatea286f412009-09-18 15:51:15 -070088
Christopher Tate66488d62012-10-02 11:58:01 -070089 // Slots in the checksum array. Never insert new items in the middle
90 // of this array; new slots must be appended.
Irfan Sheriff4aeca7c52011-03-10 16:53:33 -080091 private static final int STATE_SYSTEM = 0;
92 private static final int STATE_SECURE = 1;
93 private static final int STATE_LOCALE = 2;
94 private static final int STATE_WIFI_SUPPLICANT = 3;
95 private static final int STATE_WIFI_CONFIG = 4;
Christopher Tate66488d62012-10-02 11:58:01 -070096 private static final int STATE_GLOBAL = 5;
Amith Yamasani072543f2015-02-13 11:09:45 -080097 private static final int STATE_LOCK_SETTINGS = 6;
Christopher Tate66488d62012-10-02 11:58:01 -070098
Amith Yamasani072543f2015-02-13 11:09:45 -080099 private static final int STATE_SIZE = 7; // The current number of state items
Christopher Tate66488d62012-10-02 11:58:01 -0700100
101 // Number of entries in the checksum array at various version numbers
102 private static final int STATE_SIZES[] = {
103 0,
104 4, // version 1
105 5, // version 2 added STATE_WIFI_CONFIG
Amith Yamasani072543f2015-02-13 11:09:45 -0800106 6, // version 3 added STATE_GLOBAL
107 STATE_SIZE // version 4 added STATE_LOCK_SETTINGS
Christopher Tate66488d62012-10-02 11:58:01 -0700108 };
Amith Yamasanid1582142009-07-08 20:04:55 -0700109
Christopher Tate75a99702011-05-18 16:28:19 -0700110 // Versioning of the 'full backup' format
Amith Yamasani072543f2015-02-13 11:09:45 -0800111 private static final int FULL_BACKUP_VERSION = 3;
Christopher Tate66488d62012-10-02 11:58:01 -0700112 private static final int FULL_BACKUP_ADDED_GLOBAL = 2; // added the "global" entry
Amith Yamasani072543f2015-02-13 11:09:45 -0800113 private static final int FULL_BACKUP_ADDED_LOCK_SETTINGS = 3; // added the "lock_settings" entry
Christopher Tate75a99702011-05-18 16:28:19 -0700114
Svetoslav Ganova571a582011-09-20 18:32:20 -0700115 private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
Amith Yamasani220f4d62009-07-02 02:34:14 -0700116
117 private static final byte[] EMPTY_DATA = new byte[0];
118
119 private static final String TAG = "SettingsBackupAgent";
120
Amith Yamasani220f4d62009-07-02 02:34:14 -0700121 private static final String[] PROJECTION = {
Amith Yamasani220f4d62009-07-02 02:34:14 -0700122 Settings.NameValueTable.NAME,
123 Settings.NameValueTable.VALUE
124 };
125
126 private static final String FILE_WIFI_SUPPLICANT = "/data/misc/wifi/wpa_supplicant.conf";
Amith Yamasani2cfab842009-09-09 18:27:31 -0700127 private static final String FILE_WIFI_SUPPLICANT_TEMPLATE =
128 "/system/etc/wifi/wpa_supplicant.conf";
Christian Sonntag92c17522009-08-07 15:16:17 -0700129
130 // the key to store the WIFI data under, should be sorted as last, so restore happens last.
131 // use very late unicode character to quasi-guarantee last sort position.
Amith Yamasani2cfab842009-09-09 18:27:31 -0700132 private static final String KEY_WIFI_SUPPLICANT = "\uffedWIFI";
Irfan Sheriff4aeca7c52011-03-10 16:53:33 -0800133 private static final String KEY_WIFI_CONFIG = "\uffedCONFIG_WIFI";
Amith Yamasani220f4d62009-07-02 02:34:14 -0700134
Amith Yamasani072543f2015-02-13 11:09:45 -0800135 // Keys within the lock settings section
136 private static final String KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED = "owner_info_enabled";
137 private static final String KEY_LOCK_SETTINGS_OWNER_INFO = "owner_info";
138
Christopher Tate4a627c72011-04-01 14:43:32 -0700139 // Name of the temporary file we use during full backup/restore. This is
140 // stored in the full-backup tarfile as well, so should not be changed.
141 private static final String STAGE_FILE = "flattened-data";
142
Christopher Tated488bc02012-10-09 14:06:30 -0700143 // Delay in milliseconds between the restore operation and when we will bounce
144 // wifi in order to rewrite the supplicant config etc.
145 private static final long WIFI_BOUNCE_DELAY_MILLIS = 60 * 1000; // one minute
146
Amith Yamasani220f4d62009-07-02 02:34:14 -0700147 private SettingsHelper mSettingsHelper;
Irfan Sheriff4aeca7c52011-03-10 16:53:33 -0800148 private WifiManager mWfm;
149 private static String mWifiConfigFile;
Amith Yamasani220f4d62009-07-02 02:34:14 -0700150
Christopher Tate45dc0d02015-08-07 19:20:05 -0700151 // Chain of asynchronous operations used when rewriting the wifi supplicant config file
152 WifiDisableRunnable mWifiDisable = null;
Christopher Tated488bc02012-10-09 14:06:30 -0700153 WifiRestoreRunnable mWifiRestore = null;
Christopher Tate45dc0d02015-08-07 19:20:05 -0700154 int mRetainedWifiState; // used only during config file rewrite
Christopher Tated488bc02012-10-09 14:06:30 -0700155
Christopher Tate8dfe2b92012-05-15 15:05:04 -0700156 // Class for capturing a network definition from the wifi supplicant config file
157 static class Network {
158 String ssid = ""; // equals() and hashCode() need these to be non-null
159 String key_mgmt = "";
Vinit Deshapnde1d1319e2013-09-06 17:12:41 -0700160 boolean certUsed = false;
Christopher Tatecab915a2015-01-08 12:50:00 -0800161 boolean hasWepKey = false;
Christopher Tatea89f9d92015-11-17 18:01:52 -0800162 boolean isEap = false;
Christopher Tate8dfe2b92012-05-15 15:05:04 -0700163 final ArrayList<String> rawLines = new ArrayList<String>();
164
165 public static Network readFromStream(BufferedReader in) {
166 final Network n = new Network();
167 String line;
168 try {
169 while (in.ready()) {
170 line = in.readLine();
171 if (line == null || line.startsWith("}")) {
172 break;
173 }
174 n.rememberLine(line);
175 }
176 } catch (IOException e) {
177 return null;
178 }
179 return n;
180 }
181
182 void rememberLine(String line) {
183 // can't rely on particular whitespace patterns so strip leading/trailing
184 line = line.trim();
185 if (line.isEmpty()) return; // only whitespace; drop the line
186 rawLines.add(line);
187
188 // remember the ssid and key_mgmt lines for duplicate culling
Christopher Tatecab915a2015-01-08 12:50:00 -0800189 if (line.startsWith("ssid=")) {
Christopher Tate8dfe2b92012-05-15 15:05:04 -0700190 ssid = line;
Christopher Tatecab915a2015-01-08 12:50:00 -0800191 } else if (line.startsWith("key_mgmt=")) {
Christopher Tate8dfe2b92012-05-15 15:05:04 -0700192 key_mgmt = line;
Christopher Tatea89f9d92015-11-17 18:01:52 -0800193 if (line.contains("EAP")) {
194 isEap = true;
195 }
Vinit Deshapnde1d1319e2013-09-06 17:12:41 -0700196 } else if (line.startsWith("client_cert=")) {
197 certUsed = true;
198 } else if (line.startsWith("ca_cert=")) {
199 certUsed = true;
200 } else if (line.startsWith("ca_path=")) {
201 certUsed = true;
Christopher Tatecab915a2015-01-08 12:50:00 -0800202 } else if (line.startsWith("wep_")) {
203 hasWepKey = true;
Christopher Tatea89f9d92015-11-17 18:01:52 -0800204 } else if (line.startsWith("eap=")) {
205 isEap = true;
Christopher Tate8dfe2b92012-05-15 15:05:04 -0700206 }
207 }
208
209 public void write(Writer w) throws IOException {
210 w.write("\nnetwork={\n");
211 for (String line : rawLines) {
212 w.write("\t" + line + "\n");
213 }
214 w.write("}\n");
215 }
216
217 public void dump() {
218 Log.v(TAG, "network={");
219 for (String line : rawLines) {
220 Log.v(TAG, " " + line);
221 }
222 Log.v(TAG, "}");
223 }
224
Christopher Tatecab915a2015-01-08 12:50:00 -0800225 // Calculate the equivalent of WifiConfiguration's configKey()
226 public String configKey() {
227 if (ssid == null) {
228 // No SSID => malformed network definition
229 return null;
230 }
231
232 final String bareSsid = ssid.substring(ssid.indexOf('=') + 1);
233
234 final BitSet types = new BitSet();
235 if (key_mgmt == null) {
236 // no key_mgmt specified; this is defined as equivalent to "WPA-PSK WPA-EAP"
237 types.set(KeyMgmt.WPA_PSK);
238 types.set(KeyMgmt.WPA_EAP);
239 } else {
240 // Need to parse the key_mgmt line
241 final String bareKeyMgmt = key_mgmt.substring(key_mgmt.indexOf('=') + 1);
242 String[] typeStrings = bareKeyMgmt.split("\\s+");
243
244 // Parse out all the key management regimes permitted for this network. The literal
245 // strings here are the standard values permitted in wpa_supplicant.conf.
246 for (int i = 0; i < typeStrings.length; i++) {
247 final String ktype = typeStrings[i];
248 if (ktype.equals("WPA-PSK")) {
249 Log.v(TAG, " + setting WPA_PSK bit");
250 types.set(KeyMgmt.WPA_PSK);
251 } else if (ktype.equals("WPA-EAP")) {
252 Log.v(TAG, " + setting WPA_EAP bit");
253 types.set(KeyMgmt.WPA_EAP);
254 } else if (ktype.equals("IEEE8021X")) {
255 Log.v(TAG, " + setting IEEE8021X bit");
256 types.set(KeyMgmt.IEEE8021X);
257 }
258 }
259 }
260
261 // Now build the canonical config key paralleling the WifiConfiguration semantics
262 final String key;
263 if (types.get(KeyMgmt.WPA_PSK)) {
264 key = bareSsid + KeyMgmt.strings[KeyMgmt.WPA_PSK];
265 } else if (types.get(KeyMgmt.WPA_EAP) || types.get(KeyMgmt.IEEE8021X)) {
266 key = bareSsid + KeyMgmt.strings[KeyMgmt.WPA_EAP];
267 } else if (hasWepKey) {
268 key = bareSsid + "WEP"; // hardcoded this way in WifiConfiguration
269 } else {
270 key = bareSsid + KeyMgmt.strings[KeyMgmt.NONE];
271 }
272 return key;
273 }
274
Christopher Tate8dfe2b92012-05-15 15:05:04 -0700275 // Same approach as Pair.equals() and Pair.hashCode()
276 @Override
277 public boolean equals(Object o) {
278 if (o == this) return true;
279 if (!(o instanceof Network)) return false;
280 final Network other;
281 try {
282 other = (Network) o;
283 } catch (ClassCastException e) {
284 return false;
285 }
286 return ssid.equals(other.ssid) && key_mgmt.equals(other.key_mgmt);
287 }
288
289 @Override
290 public int hashCode() {
291 int result = 17;
292 result = 31 * result + ssid.hashCode();
293 result = 31 * result + key_mgmt.hashCode();
294 return result;
295 }
296 }
297
Christopher Tatecab915a2015-01-08 12:50:00 -0800298 boolean networkInWhitelist(Network net, List<WifiConfiguration> whitelist) {
299 final String netConfigKey = net.configKey();
300 final int N = whitelist.size();
301 for (int i = 0; i < N; i++) {
302 if (Objects.equals(netConfigKey, whitelist.get(i).configKey(true))) {
303 return true;
304 }
305 }
306 return false;
307 }
308
Christopher Tate8dfe2b92012-05-15 15:05:04 -0700309 // Ingest multiple wifi config file fragments, looking for network={} blocks
310 // and eliminating duplicates
311 class WifiNetworkSettings {
312 // One for fast lookup, one for maintaining ordering
313 final HashSet<Network> mKnownNetworks = new HashSet<Network>();
314 final ArrayList<Network> mNetworks = new ArrayList<Network>(8);
315
Christopher Tatecab915a2015-01-08 12:50:00 -0800316 public void readNetworks(BufferedReader in, List<WifiConfiguration> whitelist) {
Christopher Tate8dfe2b92012-05-15 15:05:04 -0700317 try {
318 String line;
319 while (in.ready()) {
320 line = in.readLine();
321 if (line != null) {
322 // Parse out 'network=' decls so we can ignore duplicates
323 if (line.startsWith("network")) {
324 Network net = Network.readFromStream(in);
Christopher Tatecab915a2015-01-08 12:50:00 -0800325 if (whitelist != null) {
326 if (!networkInWhitelist(net, whitelist)) {
327 if (DEBUG_BACKUP) {
328 Log.v(TAG, "Network not in whitelist, skipping: "
329 + net.ssid + " / " + net.key_mgmt);
330 }
331 continue;
332 }
333 }
Christopher Tatea89f9d92015-11-17 18:01:52 -0800334 // Don't propagate EAP network definitions
335 if (net.isEap) {
336 if (DEBUG_BACKUP) {
337 Log.v(TAG, "Skipping EAP network " + net.ssid + " / " + net.key_mgmt);
338 }
339 continue;
340 }
Christopher Tate8dfe2b92012-05-15 15:05:04 -0700341 if (! mKnownNetworks.contains(net)) {
342 if (DEBUG_BACKUP) {
343 Log.v(TAG, "Adding " + net.ssid + " / " + net.key_mgmt);
344 }
345 mKnownNetworks.add(net);
346 mNetworks.add(net);
347 } else {
348 if (DEBUG_BACKUP) {
349 Log.v(TAG, "Dupe; skipped " + net.ssid + " / " + net.key_mgmt);
350 }
351 }
352 }
353 }
354 }
355 } catch (IOException e) {
356 // whatever happened, we're done now
357 }
358 }
359
360 public void write(Writer w) throws IOException {
361 for (Network net : mNetworks) {
Vinit Deshapnde1d1319e2013-09-06 17:12:41 -0700362 if (net.certUsed) {
363 // Networks that use certificates for authentication can't be restored
364 // because the certificates they need don't get restored (because they
365 // are stored in keystore, and can't be restored)
366 continue;
367 }
368
Christopher Tatea89f9d92015-11-17 18:01:52 -0800369 if (net.isEap) {
370 // Similarly, omit EAP network definitions to avoid propagating
371 // controlled enterprise network definitions.
372 continue;
373 }
374
Christopher Tate8dfe2b92012-05-15 15:05:04 -0700375 net.write(w);
376 }
377 }
378
379 public void dump() {
380 for (Network net : mNetworks) {
381 net.dump();
382 }
383 }
384 }
385
Svetoslav Ganova571a582011-09-20 18:32:20 -0700386 @Override
Amith Yamasani220f4d62009-07-02 02:34:14 -0700387 public void onCreate() {
Christopher Tate75a99702011-05-18 16:28:19 -0700388 if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
389
Amith Yamasani220f4d62009-07-02 02:34:14 -0700390 mSettingsHelper = new SettingsHelper(this);
391 super.onCreate();
Irfan Sheriff4aeca7c52011-03-10 16:53:33 -0800392
393 WifiManager mWfm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
394 if (mWfm != null) mWifiConfigFile = mWfm.getConfigFile();
Amith Yamasani220f4d62009-07-02 02:34:14 -0700395 }
396
397 @Override
398 public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
399 ParcelFileDescriptor newState) throws IOException {
400
401 byte[] systemSettingsData = getSystemSettings();
402 byte[] secureSettingsData = getSecureSettings();
Christopher Tate66488d62012-10-02 11:58:01 -0700403 byte[] globalSettingsData = getGlobalSettings();
Amith Yamasani072543f2015-02-13 11:09:45 -0800404 byte[] lockSettingsData = getLockSettings();
Amith Yamasani8823c0a82009-07-07 14:30:17 -0700405 byte[] locale = mSettingsHelper.getLocaleData();
Irfan Sheriff4aeca7c52011-03-10 16:53:33 -0800406 byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
407 byte[] wifiConfigData = getFileData(mWifiConfigFile);
Amith Yamasani220f4d62009-07-02 02:34:14 -0700408
Christopher Tate79ec80d2011-06-24 14:58:49 -0700409 long[] stateChecksums = readOldChecksums(oldState);
Amith Yamasani220f4d62009-07-02 02:34:14 -0700410
Christopher Tate79ec80d2011-06-24 14:58:49 -0700411 stateChecksums[STATE_SYSTEM] =
412 writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
413 stateChecksums[STATE_SECURE] =
414 writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
Christopher Tate66488d62012-10-02 11:58:01 -0700415 stateChecksums[STATE_GLOBAL] =
Christopher Tate3543beb2012-10-05 13:35:12 -0700416 writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, globalSettingsData, data);
Christopher Tate79ec80d2011-06-24 14:58:49 -0700417 stateChecksums[STATE_LOCALE] =
418 writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
419 stateChecksums[STATE_WIFI_SUPPLICANT] =
420 writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT,
421 wifiSupplicantData, data);
422 stateChecksums[STATE_WIFI_CONFIG] =
423 writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData,
424 data);
Amith Yamasani072543f2015-02-13 11:09:45 -0800425 stateChecksums[STATE_LOCK_SETTINGS] =
426 writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
427 lockSettingsData, data);
Amith Yamasani8823c0a82009-07-07 14:30:17 -0700428
Christopher Tate79ec80d2011-06-24 14:58:49 -0700429 writeNewChecksums(stateChecksums, newState);
Amith Yamasani220f4d62009-07-02 02:34:14 -0700430 }
431
Christopher Tate45dc0d02015-08-07 19:20:05 -0700432 class WifiDisableRunnable implements Runnable {
433 final WifiRestoreRunnable mNextPhase;
434
435 public WifiDisableRunnable(WifiRestoreRunnable next) {
436 mNextPhase = next;
437 }
438
439 @Override
440 public void run() {
441 if (DEBUG_BACKUP) {
442 Log.v(TAG, "Disabling wifi during restore");
443 }
444 final ContentResolver cr = getContentResolver();
445 final int scanAlways = Settings.Global.getInt(cr,
446 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
447 final int retainedWifiState = enableWifi(false);
448 if (scanAlways != 0) {
449 Settings.Global.putInt(cr,
450 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
451 }
452
453 // Tell the final stage how to clean up after itself
454 mNextPhase.setPriorState(retainedWifiState, scanAlways);
455
456 // And run it after a modest pause to give broadcasts and content
457 // observers time an opportunity to run on this looper thread, so
458 // that the wifi stack actually goes all the way down.
459 new Handler(getMainLooper()).postDelayed(mNextPhase, 2500);
460 }
461 }
462
Christopher Tated488bc02012-10-09 14:06:30 -0700463 class WifiRestoreRunnable implements Runnable {
464 private byte[] restoredSupplicantData;
465 private byte[] restoredWifiConfigFile;
Christopher Tate45dc0d02015-08-07 19:20:05 -0700466 private int retainedWifiState; // provided by disable stage
467 private int scanAlways; // provided by disable stage
468
469 void setPriorState(int retainedState, int always) {
470 retainedWifiState = retainedState;
471 scanAlways = always;
472 }
Christopher Tated488bc02012-10-09 14:06:30 -0700473
474 void incorporateWifiSupplicant(BackupDataInput data) {
475 restoredSupplicantData = new byte[data.getDataSize()];
476 if (restoredSupplicantData.length <= 0) return;
477 try {
478 data.readEntityData(restoredSupplicantData, 0, data.getDataSize());
479 } catch (IOException e) {
480 Log.w(TAG, "Unable to read supplicant data");
481 restoredSupplicantData = null;
482 }
483 }
484
485 void incorporateWifiConfigFile(BackupDataInput data) {
486 restoredWifiConfigFile = new byte[data.getDataSize()];
487 if (restoredWifiConfigFile.length <= 0) return;
488 try {
489 data.readEntityData(restoredWifiConfigFile, 0, data.getDataSize());
490 } catch (IOException e) {
491 Log.w(TAG, "Unable to read config file");
492 restoredWifiConfigFile = null;
493 }
494 }
495
496 @Override
497 public void run() {
498 if (restoredSupplicantData != null || restoredWifiConfigFile != null) {
499 if (DEBUG_BACKUP) {
Christopher Tate45dc0d02015-08-07 19:20:05 -0700500 Log.v(TAG, "Applying restored wifi data");
Christopher Tated488bc02012-10-09 14:06:30 -0700501 }
Christopher Tated488bc02012-10-09 14:06:30 -0700502 if (restoredSupplicantData != null) {
503 restoreWifiSupplicant(FILE_WIFI_SUPPLICANT,
504 restoredSupplicantData, restoredSupplicantData.length);
505 FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
506 FileUtils.S_IRUSR | FileUtils.S_IWUSR |
507 FileUtils.S_IRGRP | FileUtils.S_IWGRP,
508 Process.myUid(), Process.WIFI_UID);
509 }
510 if (restoredWifiConfigFile != null) {
511 restoreFileData(mWifiConfigFile,
512 restoredWifiConfigFile, restoredWifiConfigFile.length);
513 }
514 // restore the previous WIFI state.
Christopher Tate87bc7e72013-06-10 16:55:02 -0700515 if (scanAlways != 0) {
Christopher Tate45dc0d02015-08-07 19:20:05 -0700516 Settings.Global.putInt(getContentResolver(),
Christopher Tate87bc7e72013-06-10 16:55:02 -0700517 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, scanAlways);
518 }
Christopher Tated488bc02012-10-09 14:06:30 -0700519 enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
520 retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
521 }
522 }
523 }
524
525 // Instantiate the wifi-config restore runnable, scheduling it for execution
526 // a minute hence
527 void initWifiRestoreIfNecessary() {
528 if (mWifiRestore == null) {
529 mWifiRestore = new WifiRestoreRunnable();
Christopher Tate45dc0d02015-08-07 19:20:05 -0700530 mWifiDisable = new WifiDisableRunnable(mWifiRestore);
Christopher Tated488bc02012-10-09 14:06:30 -0700531 }
532 }
533
Amith Yamasani220f4d62009-07-02 02:34:14 -0700534 @Override
535 public void onRestore(BackupDataInput data, int appVersionCode,
536 ParcelFileDescriptor newState) throws IOException {
537
Christopher Tate66488d62012-10-02 11:58:01 -0700538 HashSet<String> movedToGlobal = new HashSet<String>();
Svetoslav683914b2015-01-15 14:22:26 -0800539 Settings.System.getMovedToGlobalSettings(movedToGlobal);
540 Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
Christopher Tate66488d62012-10-02 11:58:01 -0700541
Amith Yamasani220f4d62009-07-02 02:34:14 -0700542 while (data.readNextHeader()) {
543 final String key = data.getKey();
Amith Yamasani8823c0a82009-07-07 14:30:17 -0700544 final int size = data.getDataSize();
Amith Yamasani220f4d62009-07-02 02:34:14 -0700545 if (KEY_SYSTEM.equals(key)) {
Christopher Tate66488d62012-10-02 11:58:01 -0700546 restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal);
Amith Yamasanid1582142009-07-08 20:04:55 -0700547 mSettingsHelper.applyAudioSettings();
Amith Yamasani220f4d62009-07-02 02:34:14 -0700548 } else if (KEY_SECURE.equals(key)) {
Christopher Tate66488d62012-10-02 11:58:01 -0700549 restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal);
Christopher Tate3543beb2012-10-05 13:35:12 -0700550 } else if (KEY_GLOBAL.equals(key)) {
551 restoreSettings(data, Settings.Global.CONTENT_URI, null);
Christopher Tated488bc02012-10-09 14:06:30 -0700552 } else if (KEY_WIFI_SUPPLICANT.equals(key)) {
553 initWifiRestoreIfNecessary();
554 mWifiRestore.incorporateWifiSupplicant(data);
Amith Yamasani8823c0a82009-07-07 14:30:17 -0700555 } else if (KEY_LOCALE.equals(key)) {
556 byte[] localeData = new byte[size];
557 data.readEntityData(localeData, 0, size);
Christopher Tate75a99702011-05-18 16:28:19 -0700558 mSettingsHelper.setLocaleData(localeData, size);
Christopher Tated488bc02012-10-09 14:06:30 -0700559 } else if (KEY_WIFI_CONFIG.equals(key)) {
560 initWifiRestoreIfNecessary();
561 mWifiRestore.incorporateWifiConfigFile(data);
Amith Yamasani072543f2015-02-13 11:09:45 -0800562 } else if (KEY_LOCK_SETTINGS.equals(key)) {
563 restoreLockSettings(data);
Irfan Sheriff4aeca7c52011-03-10 16:53:33 -0800564 } else {
Amith Yamasani220f4d62009-07-02 02:34:14 -0700565 data.skipEntityData();
566 }
567 }
Christopher Tated488bc02012-10-09 14:06:30 -0700568
569 // If we have wifi data to restore, post a runnable to perform the
Christopher Tate45dc0d02015-08-07 19:20:05 -0700570 // bounce-and-update operation a little ways in the future. The
571 // 'disable' runnable brings down the stack and remembers its state,
572 // and in turn schedules the 'restore' runnable to do the rewrite
573 // and cleanup operations.
Christopher Tated488bc02012-10-09 14:06:30 -0700574 if (mWifiRestore != null) {
Evan Charltoncc7b0432014-01-14 14:47:11 -0800575 long wifiBounceDelayMillis = Settings.Global.getLong(
576 getContentResolver(),
577 Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS,
578 WIFI_BOUNCE_DELAY_MILLIS);
Christopher Tate45dc0d02015-08-07 19:20:05 -0700579 new Handler(getMainLooper()).postDelayed(mWifiDisable, wifiBounceDelayMillis);
Christopher Tated488bc02012-10-09 14:06:30 -0700580 }
Amith Yamasani220f4d62009-07-02 02:34:14 -0700581 }
582
Christopher Tate75a99702011-05-18 16:28:19 -0700583 @Override
Christopher Tate79ec80d2011-06-24 14:58:49 -0700584 public void onFullBackup(FullBackupDataOutput data) throws IOException {
585 byte[] systemSettingsData = getSystemSettings();
586 byte[] secureSettingsData = getSecureSettings();
Christopher Tate66488d62012-10-02 11:58:01 -0700587 byte[] globalSettingsData = getGlobalSettings();
Amith Yamasani072543f2015-02-13 11:09:45 -0800588 byte[] lockSettingsData = getLockSettings();
Christopher Tate79ec80d2011-06-24 14:58:49 -0700589 byte[] locale = mSettingsHelper.getLocaleData();
590 byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
591 byte[] wifiConfigData = getFileData(mWifiConfigFile);
592
593 // Write the data to the staging file, then emit that as our tarfile
594 // representation of the backed-up settings.
595 String root = getFilesDir().getAbsolutePath();
596 File stage = new File(root, STAGE_FILE);
597 try {
598 FileOutputStream filestream = new FileOutputStream(stage);
599 BufferedOutputStream bufstream = new BufferedOutputStream(filestream);
600 DataOutputStream out = new DataOutputStream(bufstream);
601
Christopher Tate2efd2db2011-07-19 16:32:49 -0700602 if (DEBUG_BACKUP) Log.d(TAG, "Writing flattened data version " + FULL_BACKUP_VERSION);
Christopher Tate79ec80d2011-06-24 14:58:49 -0700603 out.writeInt(FULL_BACKUP_VERSION);
604
Christopher Tate2efd2db2011-07-19 16:32:49 -0700605 if (DEBUG_BACKUP) Log.d(TAG, systemSettingsData.length + " bytes of settings data");
Christopher Tate79ec80d2011-06-24 14:58:49 -0700606 out.writeInt(systemSettingsData.length);
607 out.write(systemSettingsData);
Christopher Tate2efd2db2011-07-19 16:32:49 -0700608 if (DEBUG_BACKUP) Log.d(TAG, secureSettingsData.length + " bytes of secure settings data");
Christopher Tate79ec80d2011-06-24 14:58:49 -0700609 out.writeInt(secureSettingsData.length);
610 out.write(secureSettingsData);
Christopher Tate66488d62012-10-02 11:58:01 -0700611 if (DEBUG_BACKUP) Log.d(TAG, globalSettingsData.length + " bytes of global settings data");
612 out.writeInt(globalSettingsData.length);
613 out.write(globalSettingsData);
Christopher Tate2efd2db2011-07-19 16:32:49 -0700614 if (DEBUG_BACKUP) Log.d(TAG, locale.length + " bytes of locale data");
Christopher Tate79ec80d2011-06-24 14:58:49 -0700615 out.writeInt(locale.length);
616 out.write(locale);
Christopher Tate2efd2db2011-07-19 16:32:49 -0700617 if (DEBUG_BACKUP) Log.d(TAG, wifiSupplicantData.length + " bytes of wifi supplicant data");
Christopher Tate79ec80d2011-06-24 14:58:49 -0700618 out.writeInt(wifiSupplicantData.length);
619 out.write(wifiSupplicantData);
Christopher Tate2efd2db2011-07-19 16:32:49 -0700620 if (DEBUG_BACKUP) Log.d(TAG, wifiConfigData.length + " bytes of wifi config data");
Christopher Tate79ec80d2011-06-24 14:58:49 -0700621 out.writeInt(wifiConfigData.length);
622 out.write(wifiConfigData);
Amith Yamasani072543f2015-02-13 11:09:45 -0800623 if (DEBUG_BACKUP) Log.d(TAG, lockSettingsData.length + " bytes of lock settings data");
624 out.writeInt(lockSettingsData.length);
625 out.write(lockSettingsData);
Christopher Tate79ec80d2011-06-24 14:58:49 -0700626
627 out.flush(); // also flushes downstream
628
629 // now we're set to emit the tar stream
630 fullBackupFile(stage, data);
631 } finally {
632 stage.delete();
633 }
634 }
635
636 @Override
Christopher Tate75a99702011-05-18 16:28:19 -0700637 public void onRestoreFile(ParcelFileDescriptor data, long size,
638 int type, String domain, String relpath, long mode, long mtime)
639 throws IOException {
640 if (DEBUG_BACKUP) Log.d(TAG, "onRestoreFile() invoked");
641 // Our data is actually a blob of flattened settings data identical to that
642 // produced during incremental backups. Just unpack and apply it all in
643 // turn.
644 FileInputStream instream = new FileInputStream(data.getFileDescriptor());
645 DataInputStream in = new DataInputStream(instream);
646
647 int version = in.readInt();
648 if (DEBUG_BACKUP) Log.d(TAG, "Flattened data version " + version);
Christopher Tate66488d62012-10-02 11:58:01 -0700649 if (version <= FULL_BACKUP_VERSION) {
650 // Generate the moved-to-global lookup table
651 HashSet<String> movedToGlobal = new HashSet<String>();
Svetoslav683914b2015-01-15 14:22:26 -0800652 Settings.System.getMovedToGlobalSettings(movedToGlobal);
653 Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
Christopher Tate66488d62012-10-02 11:58:01 -0700654
Christopher Tate75a99702011-05-18 16:28:19 -0700655 // system settings data first
656 int nBytes = in.readInt();
657 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of settings data");
658 byte[] buffer = new byte[nBytes];
Christopher Tate2efd2db2011-07-19 16:32:49 -0700659 in.readFully(buffer, 0, nBytes);
Christopher Tate66488d62012-10-02 11:58:01 -0700660 restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal);
Christopher Tate75a99702011-05-18 16:28:19 -0700661
662 // secure settings
663 nBytes = in.readInt();
664 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data");
665 if (nBytes > buffer.length) buffer = new byte[nBytes];
Christopher Tate2efd2db2011-07-19 16:32:49 -0700666 in.readFully(buffer, 0, nBytes);
Christopher Tate66488d62012-10-02 11:58:01 -0700667 restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal);
668
669 // Global only if sufficiently new
670 if (version >= FULL_BACKUP_ADDED_GLOBAL) {
671 nBytes = in.readInt();
672 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of global settings data");
673 if (nBytes > buffer.length) buffer = new byte[nBytes];
674 in.readFully(buffer, 0, nBytes);
675 movedToGlobal.clear(); // no redirection; this *is* the global namespace
676 restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal);
677 }
Christopher Tate75a99702011-05-18 16:28:19 -0700678
679 // locale
680 nBytes = in.readInt();
681 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of locale data");
682 if (nBytes > buffer.length) buffer = new byte[nBytes];
Christopher Tate2efd2db2011-07-19 16:32:49 -0700683 in.readFully(buffer, 0, nBytes);
Christopher Tate75a99702011-05-18 16:28:19 -0700684 mSettingsHelper.setLocaleData(buffer, nBytes);
685
686 // wifi supplicant
687 nBytes = in.readInt();
688 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of wifi supplicant data");
689 if (nBytes > buffer.length) buffer = new byte[nBytes];
Christopher Tate2efd2db2011-07-19 16:32:49 -0700690 in.readFully(buffer, 0, nBytes);
Christopher Tate75a99702011-05-18 16:28:19 -0700691 int retainedWifiState = enableWifi(false);
692 restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, buffer, nBytes);
693 FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
694 FileUtils.S_IRUSR | FileUtils.S_IWUSR |
695 FileUtils.S_IRGRP | FileUtils.S_IWGRP,
696 Process.myUid(), Process.WIFI_UID);
697 // retain the previous WIFI state.
698 enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
699 retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
700
701 // wifi config
702 nBytes = in.readInt();
703 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of wifi config data");
704 if (nBytes > buffer.length) buffer = new byte[nBytes];
Christopher Tate2efd2db2011-07-19 16:32:49 -0700705 in.readFully(buffer, 0, nBytes);
Christopher Tate75a99702011-05-18 16:28:19 -0700706 restoreFileData(mWifiConfigFile, buffer, nBytes);
707
Amith Yamasani072543f2015-02-13 11:09:45 -0800708 if (version >= FULL_BACKUP_ADDED_LOCK_SETTINGS) {
709 nBytes = in.readInt();
710 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of lock settings data");
711 if (nBytes > buffer.length) buffer = new byte[nBytes];
712 if (nBytes > 0) {
713 in.readFully(buffer, 0, nBytes);
714 restoreLockSettings(buffer, nBytes);
715 }
716 }
717
Christopher Tate75a99702011-05-18 16:28:19 -0700718 if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
719 } else {
720 data.close();
721 throw new IOException("Invalid file schema");
722 }
723 }
724
Amith Yamasanid1582142009-07-08 20:04:55 -0700725 private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException {
726 long[] stateChecksums = new long[STATE_SIZE];
727
728 DataInputStream dataInput = new DataInputStream(
729 new FileInputStream(oldState.getFileDescriptor()));
Christopher Tatea286f412009-09-18 15:51:15 -0700730
731 try {
732 int stateVersion = dataInput.readInt();
Christopher Tate66488d62012-10-02 11:58:01 -0700733 for (int i = 0; i < STATE_SIZES[stateVersion]; i++) {
734 stateChecksums[i] = dataInput.readLong();
Amith Yamasanid1582142009-07-08 20:04:55 -0700735 }
Christopher Tatea286f412009-09-18 15:51:15 -0700736 } catch (EOFException eof) {
737 // With the default 0 checksum we'll wind up forcing a backup of
738 // any unhandled data sets, which is appropriate.
Amith Yamasanid1582142009-07-08 20:04:55 -0700739 }
740 dataInput.close();
741 return stateChecksums;
742 }
743
744 private void writeNewChecksums(long[] checksums, ParcelFileDescriptor newState)
745 throws IOException {
746 DataOutputStream dataOutput = new DataOutputStream(
Marvin Paul68795552014-12-16 14:12:33 -0800747 new BufferedOutputStream(new FileOutputStream(newState.getFileDescriptor())));
Christopher Tatea286f412009-09-18 15:51:15 -0700748
749 dataOutput.writeInt(STATE_VERSION);
Amith Yamasanid1582142009-07-08 20:04:55 -0700750 for (int i = 0; i < STATE_SIZE; i++) {
751 dataOutput.writeLong(checksums[i]);
752 }
753 dataOutput.close();
754 }
755
756 private long writeIfChanged(long oldChecksum, String key, byte[] data,
757 BackupDataOutput output) {
758 CRC32 checkSummer = new CRC32();
759 checkSummer.update(data);
760 long newChecksum = checkSummer.getValue();
761 if (oldChecksum == newChecksum) {
762 return oldChecksum;
763 }
764 try {
Amith Yamasani072543f2015-02-13 11:09:45 -0800765 if (DEBUG_BACKUP) {
766 Log.v(TAG, "Writing entity " + key + " of size " + data.length);
767 }
Amith Yamasanid1582142009-07-08 20:04:55 -0700768 output.writeEntityHeader(key, data.length);
769 output.writeEntityData(data, data.length);
770 } catch (IOException ioe) {
771 // Bail
772 }
773 return newChecksum;
774 }
775
Amith Yamasani220f4d62009-07-02 02:34:14 -0700776 private byte[] getSystemSettings() {
Svetoslav Ganova571a582011-09-20 18:32:20 -0700777 Cursor cursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION, null,
778 null, null);
Jeff Brown1d8e7d62011-10-09 15:24:53 -0700779 try {
780 return extractRelevantValues(cursor, Settings.System.SETTINGS_TO_BACKUP);
781 } finally {
782 cursor.close();
783 }
Amith Yamasani220f4d62009-07-02 02:34:14 -0700784 }
785
786 private byte[] getSecureSettings() {
Svetoslav Ganova571a582011-09-20 18:32:20 -0700787 Cursor cursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION, null,
788 null, null);
Jeff Brown1d8e7d62011-10-09 15:24:53 -0700789 try {
790 return extractRelevantValues(cursor, Settings.Secure.SETTINGS_TO_BACKUP);
791 } finally {
792 cursor.close();
793 }
Amith Yamasani220f4d62009-07-02 02:34:14 -0700794 }
795
Christopher Tate66488d62012-10-02 11:58:01 -0700796 private byte[] getGlobalSettings() {
797 Cursor cursor = getContentResolver().query(Settings.Global.CONTENT_URI, PROJECTION, null,
798 null, null);
799 try {
800 return extractRelevantValues(cursor, Settings.Global.SETTINGS_TO_BACKUP);
801 } finally {
802 cursor.close();
803 }
804 }
805
Amith Yamasani072543f2015-02-13 11:09:45 -0800806 /**
807 * Serialize the owner info settings
808 */
809 private byte[] getLockSettings() {
810 final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
Adrian Roos8150d2a2015-04-16 17:11:20 -0700811 final boolean ownerInfoEnabled = lockPatternUtils.isOwnerInfoEnabled(UserHandle.myUserId());
Amith Yamasani072543f2015-02-13 11:09:45 -0800812 final String ownerInfo = lockPatternUtils.getOwnerInfo(UserHandle.myUserId());
813
814 ByteArrayOutputStream baos = new ByteArrayOutputStream();
815 DataOutputStream out = new DataOutputStream(baos);
816 try {
817 out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED);
818 out.writeUTF(ownerInfoEnabled ? "1" : "0");
819 if (ownerInfo != null) {
820 out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO);
821 out.writeUTF(ownerInfo != null ? ownerInfo : "");
822 }
823 // End marker
824 out.writeUTF("");
825 out.flush();
826 } catch (IOException ioe) {
827 }
828 return baos.toByteArray();
829 }
830
Christopher Tate66488d62012-10-02 11:58:01 -0700831 private void restoreSettings(BackupDataInput data, Uri contentUri,
832 HashSet<String> movedToGlobal) {
Christopher Tate75a99702011-05-18 16:28:19 -0700833 byte[] settings = new byte[data.getDataSize()];
834 try {
835 data.readEntityData(settings, 0, settings.length);
836 } catch (IOException ioe) {
837 Log.e(TAG, "Couldn't read entity data");
838 return;
839 }
Christopher Tate66488d62012-10-02 11:58:01 -0700840 restoreSettings(settings, settings.length, contentUri, movedToGlobal);
Christopher Tate75a99702011-05-18 16:28:19 -0700841 }
842
Christopher Tate66488d62012-10-02 11:58:01 -0700843 private void restoreSettings(byte[] settings, int bytes, Uri contentUri,
844 HashSet<String> movedToGlobal) {
Svetoslav Ganova571a582011-09-20 18:32:20 -0700845 if (DEBUG) {
846 Log.i(TAG, "restoreSettings: " + contentUri);
847 }
848
Christopher Tate66488d62012-10-02 11:58:01 -0700849 // Figure out the white list and redirects to the global table.
Christopher Tate6597e342015-02-17 12:15:25 -0800850 final String[] whitelist;
Christopher Tate796e0f02009-09-22 11:57:58 -0700851 if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
852 whitelist = Settings.Secure.SETTINGS_TO_BACKUP;
853 } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
854 whitelist = Settings.System.SETTINGS_TO_BACKUP;
Christopher Tate66488d62012-10-02 11:58:01 -0700855 } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
856 whitelist = Settings.Global.SETTINGS_TO_BACKUP;
Svetoslav Ganova571a582011-09-20 18:32:20 -0700857 } else {
858 throw new IllegalArgumentException("Unknown URI: " + contentUri);
Christopher Tate796e0f02009-09-22 11:57:58 -0700859 }
860
Svetoslav Ganova571a582011-09-20 18:32:20 -0700861 // Restore only the white list data.
Amith Yamasani220f4d62009-07-02 02:34:14 -0700862 int pos = 0;
Svetoslav Ganova571a582011-09-20 18:32:20 -0700863 Map<String, String> cachedEntries = new HashMap<String, String>();
864 ContentValues contentValues = new ContentValues(2);
865 SettingsHelper settingsHelper = mSettingsHelper;
Christopher Tate6597e342015-02-17 12:15:25 -0800866 ContentResolver cr = getContentResolver();
Christopher Tate0738e882009-09-11 16:35:39 -0700867
Svetoslav Ganova571a582011-09-20 18:32:20 -0700868 final int whiteListSize = whitelist.length;
869 for (int i = 0; i < whiteListSize; i++) {
870 String key = whitelist[i];
871 String value = cachedEntries.remove(key);
Christopher Tate0738e882009-09-11 16:35:39 -0700872
Svetoslav Ganova571a582011-09-20 18:32:20 -0700873 // If the value not cached, let us look it up.
874 if (value == null) {
875 while (pos < bytes) {
876 int length = readInt(settings, pos);
877 pos += INTEGER_BYTE_COUNT;
878 String dataKey = length > 0 ? new String(settings, pos, length) : null;
879 pos += length;
880 length = readInt(settings, pos);
881 pos += INTEGER_BYTE_COUNT;
882 String dataValue = length > 0 ? new String(settings, pos, length) : null;
883 pos += length;
884 if (key.equals(dataKey)) {
885 value = dataValue;
886 break;
887 }
888 cachedEntries.put(dataKey, dataValue);
Amith Yamasani70c874b2009-07-06 14:53:25 -0700889 }
Amith Yamasani220f4d62009-07-02 02:34:14 -0700890 }
Amith Yamasani220f4d62009-07-02 02:34:14 -0700891
Svetoslav Ganova571a582011-09-20 18:32:20 -0700892 if (value == null) {
893 continue;
894 }
Christopher Tate796e0f02009-09-22 11:57:58 -0700895
Christopher Tate3543beb2012-10-05 13:35:12 -0700896 final Uri destination = (movedToGlobal != null && movedToGlobal.contains(key))
Christopher Tate66488d62012-10-02 11:58:01 -0700897 ? Settings.Global.CONTENT_URI
898 : contentUri;
Christopher Tate6597e342015-02-17 12:15:25 -0800899 settingsHelper.restoreValue(this, cr, contentValues, destination, key, value);
Svetoslav Ganova571a582011-09-20 18:32:20 -0700900
Christopher Tated488bc02012-10-09 14:06:30 -0700901 if (DEBUG) {
Christopher Tate66488d62012-10-02 11:58:01 -0700902 Log.d(TAG, "Restored setting: " + destination + " : "+ key + "=" + value);
Christopher Tate0738e882009-09-11 16:35:39 -0700903 }
904 }
Amith Yamasani220f4d62009-07-02 02:34:14 -0700905 }
906
907 /**
Amith Yamasani072543f2015-02-13 11:09:45 -0800908 * Restores the owner info enabled and owner info settings in LockSettings.
909 *
910 * @param buffer
911 * @param nBytes
912 */
913 private void restoreLockSettings(byte[] buffer, int nBytes) {
914 final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
915
916 ByteArrayInputStream bais = new ByteArrayInputStream(buffer, 0, nBytes);
917 DataInputStream in = new DataInputStream(bais);
918 try {
919 String key;
920 // Read until empty string marker
921 while ((key = in.readUTF()).length() > 0) {
922 final String value = in.readUTF();
923 if (DEBUG_BACKUP) {
924 Log.v(TAG, "Restoring lock_settings " + key + " = " + value);
925 }
926 switch (key) {
927 case KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED:
Adrian Roos8150d2a2015-04-16 17:11:20 -0700928 lockPatternUtils.setOwnerInfoEnabled("1".equals(value),
929 UserHandle.myUserId());
Amith Yamasani072543f2015-02-13 11:09:45 -0800930 break;
931 case KEY_LOCK_SETTINGS_OWNER_INFO:
932 lockPatternUtils.setOwnerInfo(value, UserHandle.myUserId());
933 break;
934 }
935 }
936 in.close();
937 } catch (IOException ioe) {
938 }
939 }
940
941 private void restoreLockSettings(BackupDataInput data) {
942 final byte[] settings = new byte[data.getDataSize()];
943 try {
944 data.readEntityData(settings, 0, settings.length);
945 } catch (IOException ioe) {
946 Log.e(TAG, "Couldn't read entity data");
947 return;
948 }
949 restoreLockSettings(settings, settings.length);
950 }
951
952 /**
Svetoslav Ganova571a582011-09-20 18:32:20 -0700953 * Given a cursor and a set of keys, extract the required keys and
954 * values and write them to a byte array.
955 *
956 * @param cursor A cursor with settings data.
957 * @param settings The settings to extract.
958 * @return The byte array of extracted values.
Amith Yamasani220f4d62009-07-02 02:34:14 -0700959 */
Svetoslav Ganova571a582011-09-20 18:32:20 -0700960 private byte[] extractRelevantValues(Cursor cursor, String[] settings) {
961 final int settingsCount = settings.length;
962 byte[][] values = new byte[settingsCount * 2][]; // keys and values
963 if (!cursor.moveToFirst()) {
Amith Yamasani220f4d62009-07-02 02:34:14 -0700964 Log.e(TAG, "Couldn't read from the cursor");
965 return new byte[0];
966 }
Svetoslav Ganova571a582011-09-20 18:32:20 -0700967
968 // Obtain the relevant data in a temporary array.
Amith Yamasani220f4d62009-07-02 02:34:14 -0700969 int totalSize = 0;
Svetoslav Ganova571a582011-09-20 18:32:20 -0700970 int backedUpSettingIndex = 0;
971 Map<String, String> cachedEntries = new HashMap<String, String>();
972 for (int i = 0; i < settingsCount; i++) {
973 String key = settings[i];
974 String value = cachedEntries.remove(key);
975
Svetoslav683914b2015-01-15 14:22:26 -0800976 final int nameColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.NAME);
977 final int valueColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.VALUE);
978
Svetoslav Ganova571a582011-09-20 18:32:20 -0700979 // If the value not cached, let us look it up.
980 if (value == null) {
981 while (!cursor.isAfterLast()) {
Svetoslav683914b2015-01-15 14:22:26 -0800982 String cursorKey = cursor.getString(nameColumnIndex);
983 String cursorValue = cursor.getString(valueColumnIndex);
Svetoslav Ganova571a582011-09-20 18:32:20 -0700984 cursor.moveToNext();
985 if (key.equals(cursorKey)) {
986 value = cursorValue;
987 break;
988 }
989 cachedEntries.put(cursorKey, cursorValue);
Amith Yamasani220f4d62009-07-02 02:34:14 -0700990 }
Amith Yamasani220f4d62009-07-02 02:34:14 -0700991 }
Svetoslav Ganova571a582011-09-20 18:32:20 -0700992
Amith Yamasani622bf2222013-09-06 13:54:28 -0700993 // Intercept the keys and see if they need special handling
994 value = mSettingsHelper.onBackupValue(key, value);
995
Svetoslav Ganova571a582011-09-20 18:32:20 -0700996 if (value == null) {
997 continue;
998 }
Svetoslav Ganova571a582011-09-20 18:32:20 -0700999 // Write the key and value in the intermediary array.
1000 byte[] keyBytes = key.getBytes();
1001 totalSize += INTEGER_BYTE_COUNT + keyBytes.length;
1002 values[backedUpSettingIndex * 2] = keyBytes;
1003
1004 byte[] valueBytes = value.getBytes();
1005 totalSize += INTEGER_BYTE_COUNT + valueBytes.length;
1006 values[backedUpSettingIndex * 2 + 1] = valueBytes;
1007
1008 backedUpSettingIndex++;
1009
1010 if (DEBUG) {
1011 Log.d(TAG, "Backed up setting: " + key + "=" + value);
Amith Yamasani220f4d62009-07-02 02:34:14 -07001012 }
1013 }
1014
Svetoslav Ganova571a582011-09-20 18:32:20 -07001015 // Aggregate the result.
Amith Yamasani220f4d62009-07-02 02:34:14 -07001016 byte[] result = new byte[totalSize];
1017 int pos = 0;
Svetoslav Ganova571a582011-09-20 18:32:20 -07001018 final int keyValuePairCount = backedUpSettingIndex * 2;
1019 for (int i = 0; i < keyValuePairCount; i++) {
1020 pos = writeInt(result, pos, values[i].length);
1021 pos = writeBytes(result, pos, values[i]);
Amith Yamasani220f4d62009-07-02 02:34:14 -07001022 }
1023 return result;
1024 }
1025
Irfan Sheriff4aeca7c52011-03-10 16:53:33 -08001026 private byte[] getFileData(String filename) {
1027 InputStream is = null;
1028 try {
1029 File file = new File(filename);
1030 is = new FileInputStream(file);
1031
1032 //Will truncate read on a very long file,
1033 //should not happen for a config file
1034 byte[] bytes = new byte[(int)file.length()];
1035
1036 int offset = 0;
1037 int numRead = 0;
1038 while (offset < bytes.length
1039 && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
1040 offset += numRead;
1041 }
1042
1043 //read failure
1044 if (offset < bytes.length) {
1045 Log.w(TAG, "Couldn't backup " + filename);
1046 return EMPTY_DATA;
1047 }
1048 return bytes;
1049 } catch (IOException ioe) {
1050 Log.w(TAG, "Couldn't backup " + filename);
1051 return EMPTY_DATA;
1052 } finally {
1053 if (is != null) {
1054 try {
1055 is.close();
1056 } catch (IOException e) {
1057 }
1058 }
1059 }
1060
1061 }
1062
Christopher Tate75a99702011-05-18 16:28:19 -07001063 private void restoreFileData(String filename, byte[] bytes, int size) {
Irfan Sheriff4aeca7c52011-03-10 16:53:33 -08001064 try {
Irfan Sheriff4aeca7c52011-03-10 16:53:33 -08001065 File file = new File(filename);
1066 if (file.exists()) file.delete();
1067
1068 OutputStream os = new BufferedOutputStream(new FileOutputStream(filename, true));
Christopher Tate75a99702011-05-18 16:28:19 -07001069 os.write(bytes, 0, size);
Irfan Sheriff4aeca7c52011-03-10 16:53:33 -08001070 os.close();
1071 } catch (IOException ioe) {
1072 Log.w(TAG, "Couldn't restore " + filename);
1073 }
1074 }
1075
1076
Amith Yamasani2cfab842009-09-09 18:27:31 -07001077 private byte[] getWifiSupplicant(String filename) {
Brad Fitzpatrick70787892010-11-17 11:31:12 -08001078 BufferedReader br = null;
Amith Yamasani220f4d62009-07-02 02:34:14 -07001079 try {
1080 File file = new File(filename);
Christopher Tatecab915a2015-01-08 12:50:00 -08001081 if (!file.exists()) {
1082 return EMPTY_DATA;
1083 }
1084
1085 WifiManager wifi = (WifiManager) getSystemService(WIFI_SERVICE);
1086 List<WifiConfiguration> configs = wifi.getConfiguredNetworks();
1087
1088 WifiNetworkSettings fromFile = new WifiNetworkSettings();
1089 br = new BufferedReader(new FileReader(file));
1090 fromFile.readNetworks(br, configs);
1091
1092 // Write the parsed networks into a packed byte array
1093 if (fromFile.mKnownNetworks.size() > 0) {
1094 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1095 OutputStreamWriter out = new OutputStreamWriter(bos);
1096 fromFile.write(out);
Christopher Tate129ea762015-02-17 17:04:01 -08001097 out.flush();
Christopher Tatecab915a2015-01-08 12:50:00 -08001098 return bos.toByteArray();
Amith Yamasani220f4d62009-07-02 02:34:14 -07001099 } else {
Amith Yamasanid1582142009-07-08 20:04:55 -07001100 return EMPTY_DATA;
Amith Yamasani220f4d62009-07-02 02:34:14 -07001101 }
1102 } catch (IOException ioe) {
1103 Log.w(TAG, "Couldn't backup " + filename);
Amith Yamasanid1582142009-07-08 20:04:55 -07001104 return EMPTY_DATA;
Brad Fitzpatrick70787892010-11-17 11:31:12 -08001105 } finally {
Christopher Tatecab915a2015-01-08 12:50:00 -08001106 IoUtils.closeQuietly(br);
Amith Yamasani220f4d62009-07-02 02:34:14 -07001107 }
1108 }
1109
Christopher Tate75a99702011-05-18 16:28:19 -07001110 private void restoreWifiSupplicant(String filename, byte[] bytes, int size) {
Amith Yamasani220f4d62009-07-02 02:34:14 -07001111 try {
Christopher Tate8dfe2b92012-05-15 15:05:04 -07001112 WifiNetworkSettings supplicantImage = new WifiNetworkSettings();
Amith Yamasani2cfab842009-09-09 18:27:31 -07001113
Christopher Tate8dfe2b92012-05-15 15:05:04 -07001114 File supplicantFile = new File(FILE_WIFI_SUPPLICANT);
1115 if (supplicantFile.exists()) {
1116 // Retain the existing APs; we'll append the restored ones to them
1117 BufferedReader in = new BufferedReader(new FileReader(FILE_WIFI_SUPPLICANT));
Christopher Tatecab915a2015-01-08 12:50:00 -08001118 supplicantImage.readNetworks(in, null);
Christopher Tate8dfe2b92012-05-15 15:05:04 -07001119 in.close();
1120
1121 supplicantFile.delete();
1122 }
1123
1124 // Incorporate the restore AP information
1125 if (size > 0) {
1126 char[] restoredAsBytes = new char[size];
1127 for (int i = 0; i < size; i++) restoredAsBytes[i] = (char) bytes[i];
1128 BufferedReader in = new BufferedReader(new CharArrayReader(restoredAsBytes));
Christopher Tatecab915a2015-01-08 12:50:00 -08001129 supplicantImage.readNetworks(in, null);
Christopher Tate8dfe2b92012-05-15 15:05:04 -07001130
1131 if (DEBUG_BACKUP) {
1132 Log.v(TAG, "Final AP list:");
1133 supplicantImage.dump();
1134 }
1135 }
1136
1137 // Install the correct default template
1138 BufferedWriter bw = new BufferedWriter(new FileWriter(FILE_WIFI_SUPPLICANT));
1139 copyWifiSupplicantTemplate(bw);
1140
1141 // Write the restored supplicant config and we're done
1142 supplicantImage.write(bw);
1143 bw.close();
Amith Yamasani220f4d62009-07-02 02:34:14 -07001144 } catch (IOException ioe) {
1145 Log.w(TAG, "Couldn't restore " + filename);
1146 }
1147 }
1148
Christopher Tate8dfe2b92012-05-15 15:05:04 -07001149 private void copyWifiSupplicantTemplate(BufferedWriter bw) {
Amith Yamasani2cfab842009-09-09 18:27:31 -07001150 try {
1151 BufferedReader br = new BufferedReader(new FileReader(FILE_WIFI_SUPPLICANT_TEMPLATE));
Amith Yamasani2cfab842009-09-09 18:27:31 -07001152 char[] temp = new char[1024];
1153 int size;
1154 while ((size = br.read(temp)) > 0) {
1155 bw.write(temp, 0, size);
1156 }
Amith Yamasani2cfab842009-09-09 18:27:31 -07001157 br.close();
1158 } catch (IOException ioe) {
1159 Log.w(TAG, "Couldn't copy wpa_supplicant file");
1160 }
1161 }
1162
Amith Yamasani220f4d62009-07-02 02:34:14 -07001163 /**
1164 * Write an int in BigEndian into the byte array.
1165 * @param out byte array
1166 * @param pos current pos in array
1167 * @param value integer to write
Svetoslav Ganova571a582011-09-20 18:32:20 -07001168 * @return the index after adding the size of an int (4) in bytes.
Amith Yamasani220f4d62009-07-02 02:34:14 -07001169 */
1170 private int writeInt(byte[] out, int pos, int value) {
1171 out[pos + 0] = (byte) ((value >> 24) & 0xFF);
1172 out[pos + 1] = (byte) ((value >> 16) & 0xFF);
1173 out[pos + 2] = (byte) ((value >> 8) & 0xFF);
1174 out[pos + 3] = (byte) ((value >> 0) & 0xFF);
Svetoslav Ganova571a582011-09-20 18:32:20 -07001175 return pos + INTEGER_BYTE_COUNT;
Amith Yamasani220f4d62009-07-02 02:34:14 -07001176 }
1177
1178 private int writeBytes(byte[] out, int pos, byte[] value) {
1179 System.arraycopy(value, 0, out, pos, value.length);
1180 return pos + value.length;
1181 }
1182
1183 private int readInt(byte[] in, int pos) {
1184 int result =
1185 ((in[pos ] & 0xFF) << 24) |
1186 ((in[pos + 1] & 0xFF) << 16) |
1187 ((in[pos + 2] & 0xFF) << 8) |
1188 ((in[pos + 3] & 0xFF) << 0);
1189 return result;
1190 }
1191
Christian Sonntagc5b5b0f2009-08-07 10:00:21 -07001192 private int enableWifi(boolean enable) {
Irfan Sheriff4f4f5162012-04-27 14:42:42 -07001193 if (mWfm == null) {
1194 mWfm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
1195 }
Irfan Sheriff4aeca7c52011-03-10 16:53:33 -08001196 if (mWfm != null) {
1197 int state = mWfm.getWifiState();
1198 mWfm.setWifiEnabled(enable);
Christian Sonntagc5b5b0f2009-08-07 10:00:21 -07001199 return state;
Irfan Sheriff4f4f5162012-04-27 14:42:42 -07001200 } else {
1201 Log.e(TAG, "Failed to fetch WifiManager instance");
Amith Yamasani220f4d62009-07-02 02:34:14 -07001202 }
Christian Sonntagc5b5b0f2009-08-07 10:00:21 -07001203 return WifiManager.WIFI_STATE_UNKNOWN;
Amith Yamasani220f4d62009-07-02 02:34:14 -07001204 }
Amith Yamasani220f4d62009-07-02 02:34:14 -07001205}