• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.providers.settings;
18 
19 import android.annotation.Nullable;
20 import android.annotation.UserIdInt;
21 import android.app.backup.BackupAgentHelper;
22 import android.app.backup.BackupDataInput;
23 import android.app.backup.BackupDataOutput;
24 import android.app.backup.FullBackupDataOutput;
25 import android.content.ContentResolver;
26 import android.content.ContentValues;
27 import android.content.Context;
28 import android.database.Cursor;
29 import android.net.NetworkPolicy;
30 import android.net.NetworkPolicyManager;
31 import android.net.Uri;
32 import android.net.wifi.WifiConfiguration;
33 import android.net.wifi.WifiManager;
34 import android.os.ParcelFileDescriptor;
35 import android.os.UserHandle;
36 import android.provider.Settings;
37 import android.util.ArrayMap;
38 import android.util.BackupUtils;
39 import android.util.Log;
40 
41 import com.android.internal.widget.LockPatternUtils;
42 
43 import java.io.BufferedOutputStream;
44 import java.io.ByteArrayInputStream;
45 import java.io.ByteArrayOutputStream;
46 import java.io.DataInputStream;
47 import java.io.DataOutputStream;
48 import java.io.EOFException;
49 import java.io.File;
50 import java.io.FileInputStream;
51 import java.io.FileOutputStream;
52 import java.io.IOException;
53 import java.util.HashMap;
54 import java.util.HashSet;
55 import java.util.Map;
56 import java.util.zip.CRC32;
57 
58 /**
59  * Performs backup and restore of the System and Secure settings.
60  * List of settings that are backed up are stored in the Settings.java file
61  */
62 public class SettingsBackupAgent extends BackupAgentHelper {
63     private static final boolean DEBUG = false;
64     private static final boolean DEBUG_BACKUP = DEBUG || false;
65 
66     private static final byte[] NULL_VALUE = new byte[0];
67     private static final int NULL_SIZE = -1;
68 
69     private static final String KEY_SYSTEM = "system";
70     private static final String KEY_SECURE = "secure";
71     private static final String KEY_GLOBAL = "global";
72     private static final String KEY_LOCALE = "locale";
73     private static final String KEY_LOCK_SETTINGS = "lock_settings";
74     private static final String KEY_SOFTAP_CONFIG = "softap_config";
75     private static final String KEY_NETWORK_POLICIES = "network_policies";
76     private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
77 
78     // Versioning of the state file.  Increment this version
79     // number any time the set of state items is altered.
80     private static final int STATE_VERSION = 7;
81 
82     // Versioning of the Network Policies backup payload.
83     private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
84 
85 
86     // Slots in the checksum array.  Never insert new items in the middle
87     // of this array; new slots must be appended.
88     private static final int STATE_SYSTEM           = 0;
89     private static final int STATE_SECURE           = 1;
90     private static final int STATE_LOCALE           = 2;
91     private static final int STATE_WIFI_SUPPLICANT  = 3;
92     private static final int STATE_WIFI_CONFIG      = 4;
93     private static final int STATE_GLOBAL           = 5;
94     private static final int STATE_LOCK_SETTINGS    = 6;
95     private static final int STATE_SOFTAP_CONFIG    = 7;
96     private static final int STATE_NETWORK_POLICIES = 8;
97     private static final int STATE_WIFI_NEW_CONFIG  = 9;
98 
99     private static final int STATE_SIZE             = 10; // The current number of state items
100 
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
106             6,              // version 3 added STATE_GLOBAL
107             7,              // version 4 added STATE_LOCK_SETTINGS
108             8,              // version 5 added STATE_SOFTAP_CONFIG
109             9,              // version 6 added STATE_NETWORK_POLICIES
110             STATE_SIZE      // version 7 added STATE_WIFI_NEW_CONFIG
111     };
112 
113     // Versioning of the 'full backup' format
114     // Increment this version any time a new item is added
115     private static final int FULL_BACKUP_VERSION = 6;
116     private static final int FULL_BACKUP_ADDED_GLOBAL = 2;  // added the "global" entry
117     private static final int FULL_BACKUP_ADDED_LOCK_SETTINGS = 3; // added the "lock_settings" entry
118     private static final int FULL_BACKUP_ADDED_SOFTAP_CONF = 4; //added the "softap_config" entry
119     private static final int FULL_BACKUP_ADDED_NETWORK_POLICIES = 5; //added "network_policies"
120     private static final int FULL_BACKUP_ADDED_WIFI_NEW = 6; // added "wifi_new_config" entry
121 
122     private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
123 
124     private static final byte[] EMPTY_DATA = new byte[0];
125 
126     private static final String TAG = "SettingsBackupAgent";
127 
128     private static final String[] PROJECTION = {
129             Settings.NameValueTable.NAME,
130             Settings.NameValueTable.VALUE
131     };
132 
133     // the key to store the WIFI data under, should be sorted as last, so restore happens last.
134     // use very late unicode character to quasi-guarantee last sort position.
135     private static final String KEY_WIFI_SUPPLICANT = "\uffedWIFI";
136     private static final String KEY_WIFI_CONFIG = "\uffedCONFIG_WIFI";
137 
138     // Keys within the lock settings section
139     private static final String KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED = "owner_info_enabled";
140     private static final String KEY_LOCK_SETTINGS_OWNER_INFO = "owner_info";
141     private static final String KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED =
142             "visible_pattern_enabled";
143     private static final String KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS =
144             "power_button_instantly_locks";
145 
146     // Name of the temporary file we use during full backup/restore.  This is
147     // stored in the full-backup tarfile as well, so should not be changed.
148     private static final String STAGE_FILE = "flattened-data";
149 
150     private SettingsHelper mSettingsHelper;
151 
152     private WifiManager mWifiManager;
153 
154     // Version of the SDK that com.android.providers.settings package has been restored from.
155     // Populated in onRestore().
156     private int mRestoredFromSdkInt;
157 
158     @Override
onCreate()159     public void onCreate() {
160         if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
161 
162         mSettingsHelper = new SettingsHelper(this);
163         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
164         super.onCreate();
165     }
166 
167     @Override
onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)168     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
169             ParcelFileDescriptor newState) throws IOException {
170 
171         byte[] systemSettingsData = getSystemSettings();
172         byte[] secureSettingsData = getSecureSettings();
173         byte[] globalSettingsData = getGlobalSettings();
174         byte[] lockSettingsData   = getLockSettings(UserHandle.myUserId());
175         byte[] locale = mSettingsHelper.getLocaleData();
176         byte[] softApConfigData = getSoftAPConfiguration();
177         byte[] netPoliciesData = getNetworkPolicies();
178         byte[] wifiFullConfigData = getNewWifiConfigData();
179 
180         long[] stateChecksums = readOldChecksums(oldState);
181 
182         stateChecksums[STATE_SYSTEM] =
183                 writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
184         stateChecksums[STATE_SECURE] =
185                 writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
186         stateChecksums[STATE_GLOBAL] =
187                 writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, globalSettingsData, data);
188         stateChecksums[STATE_LOCALE] =
189                 writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
190         stateChecksums[STATE_WIFI_SUPPLICANT] = 0;
191         stateChecksums[STATE_WIFI_CONFIG] = 0;
192         stateChecksums[STATE_LOCK_SETTINGS] =
193                 writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
194                         lockSettingsData, data);
195         stateChecksums[STATE_SOFTAP_CONFIG] =
196                 writeIfChanged(stateChecksums[STATE_SOFTAP_CONFIG], KEY_SOFTAP_CONFIG,
197                         softApConfigData, data);
198         stateChecksums[STATE_NETWORK_POLICIES] =
199                 writeIfChanged(stateChecksums[STATE_NETWORK_POLICIES], KEY_NETWORK_POLICIES,
200                         netPoliciesData, data);
201         stateChecksums[STATE_WIFI_NEW_CONFIG] =
202                 writeIfChanged(stateChecksums[STATE_WIFI_NEW_CONFIG], KEY_WIFI_NEW_CONFIG,
203                         wifiFullConfigData, data);
204 
205         writeNewChecksums(stateChecksums, newState);
206     }
207 
208     @Override
onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)209     public void onRestore(BackupDataInput data, int appVersionCode,
210             ParcelFileDescriptor newState) throws IOException {
211 
212         // versionCode of com.android.providers.settings corresponds to SDK_INT
213         mRestoredFromSdkInt = appVersionCode;
214 
215         HashSet<String> movedToGlobal = new HashSet<String>();
216         Settings.System.getMovedToGlobalSettings(movedToGlobal);
217         Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
218         byte[] restoredWifiSupplicantData = null;
219         byte[] restoredWifiIpConfigData = null;
220 
221         while (data.readNextHeader()) {
222             final String key = data.getKey();
223             final int size = data.getDataSize();
224             switch (key) {
225                 case KEY_SYSTEM :
226                     restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal);
227                     mSettingsHelper.applyAudioSettings();
228                     break;
229 
230                 case KEY_SECURE :
231                     restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal);
232                     break;
233 
234                 case KEY_GLOBAL :
235                     restoreSettings(data, Settings.Global.CONTENT_URI, null);
236                     break;
237 
238                 case KEY_WIFI_SUPPLICANT :
239                     restoredWifiSupplicantData = new byte[size];
240                     data.readEntityData(restoredWifiSupplicantData, 0, size);
241                     break;
242 
243                 case KEY_LOCALE :
244                     byte[] localeData = new byte[size];
245                     data.readEntityData(localeData, 0, size);
246                     mSettingsHelper.setLocaleData(localeData, size);
247                     break;
248 
249                 case KEY_WIFI_CONFIG :
250                     restoredWifiIpConfigData = new byte[size];
251                     data.readEntityData(restoredWifiIpConfigData, 0, size);
252                     break;
253 
254                 case KEY_LOCK_SETTINGS :
255                     restoreLockSettings(UserHandle.myUserId(), data);
256                     break;
257 
258                 case KEY_SOFTAP_CONFIG :
259                     byte[] softapData = new byte[size];
260                     data.readEntityData(softapData, 0, size);
261                     restoreSoftApConfiguration(softapData);
262                     break;
263 
264                 case KEY_NETWORK_POLICIES:
265                     byte[] netPoliciesData = new byte[size];
266                     data.readEntityData(netPoliciesData, 0, size);
267                     restoreNetworkPolicies(netPoliciesData);
268                     break;
269 
270                 case KEY_WIFI_NEW_CONFIG:
271                     byte[] restoredWifiNewConfigData = new byte[size];
272                     data.readEntityData(restoredWifiNewConfigData, 0, size);
273                     restoreNewWifiConfigData(restoredWifiNewConfigData);
274                     break;
275 
276                 default :
277                     data.skipEntityData();
278 
279             }
280         }
281 
282         // Do this at the end so that we also pull in the ipconfig data.
283         if (restoredWifiSupplicantData != null) {
284             restoreSupplicantWifiConfigData(
285                     restoredWifiSupplicantData, restoredWifiIpConfigData);
286         }
287     }
288 
289     @Override
onFullBackup(FullBackupDataOutput data)290     public void onFullBackup(FullBackupDataOutput data)  throws IOException {
291         byte[] systemSettingsData = getSystemSettings();
292         byte[] secureSettingsData = getSecureSettings();
293         byte[] globalSettingsData = getGlobalSettings();
294         byte[] lockSettingsData   = getLockSettings(UserHandle.myUserId());
295         byte[] locale = mSettingsHelper.getLocaleData();
296         byte[] softApConfigData = getSoftAPConfiguration();
297         byte[] netPoliciesData = getNetworkPolicies();
298         byte[] wifiFullConfigData = getNewWifiConfigData();
299 
300         // Write the data to the staging file, then emit that as our tarfile
301         // representation of the backed-up settings.
302         String root = getFilesDir().getAbsolutePath();
303         File stage = new File(root, STAGE_FILE);
304         try {
305             FileOutputStream filestream = new FileOutputStream(stage);
306             BufferedOutputStream bufstream = new BufferedOutputStream(filestream);
307             DataOutputStream out = new DataOutputStream(bufstream);
308 
309             if (DEBUG_BACKUP) Log.d(TAG, "Writing flattened data version " + FULL_BACKUP_VERSION);
310             out.writeInt(FULL_BACKUP_VERSION);
311 
312             if (DEBUG_BACKUP) Log.d(TAG, systemSettingsData.length + " bytes of settings data");
313             out.writeInt(systemSettingsData.length);
314             out.write(systemSettingsData);
315             if (DEBUG_BACKUP) {
316                 Log.d(TAG, secureSettingsData.length + " bytes of secure settings data");
317             }
318             out.writeInt(secureSettingsData.length);
319             out.write(secureSettingsData);
320             if (DEBUG_BACKUP) {
321                 Log.d(TAG, globalSettingsData.length + " bytes of global settings data");
322             }
323             out.writeInt(globalSettingsData.length);
324             out.write(globalSettingsData);
325             if (DEBUG_BACKUP) Log.d(TAG, locale.length + " bytes of locale data");
326             out.writeInt(locale.length);
327             out.write(locale);
328             if (DEBUG_BACKUP) Log.d(TAG, lockSettingsData.length + " bytes of lock settings data");
329             out.writeInt(lockSettingsData.length);
330             out.write(lockSettingsData);
331             if (DEBUG_BACKUP) Log.d(TAG, softApConfigData.length + " bytes of softap config data");
332             out.writeInt(softApConfigData.length);
333             out.write(softApConfigData);
334             if (DEBUG_BACKUP) Log.d(TAG, netPoliciesData.length + " bytes of net policies data");
335             out.writeInt(netPoliciesData.length);
336             out.write(netPoliciesData);
337             if (DEBUG_BACKUP) {
338                 Log.d(TAG, wifiFullConfigData.length + " bytes of wifi config data");
339             }
340             out.writeInt(wifiFullConfigData.length);
341             out.write(wifiFullConfigData);
342 
343             out.flush();    // also flushes downstream
344 
345             // now we're set to emit the tar stream
346             fullBackupFile(stage, data);
347         } finally {
348             stage.delete();
349         }
350     }
351 
352     @Override
onRestoreFile(ParcelFileDescriptor data, long size, int type, String domain, String relpath, long mode, long mtime)353     public void onRestoreFile(ParcelFileDescriptor data, long size,
354             int type, String domain, String relpath, long mode, long mtime)
355             throws IOException {
356         if (DEBUG_BACKUP) Log.d(TAG, "onRestoreFile() invoked");
357         // Our data is actually a blob of flattened settings data identical to that
358         // produced during incremental backups.  Just unpack and apply it all in
359         // turn.
360         FileInputStream instream = new FileInputStream(data.getFileDescriptor());
361         DataInputStream in = new DataInputStream(instream);
362 
363         int version = in.readInt();
364         if (DEBUG_BACKUP) Log.d(TAG, "Flattened data version " + version);
365         if (version <= FULL_BACKUP_VERSION) {
366             // Generate the moved-to-global lookup table
367             HashSet<String> movedToGlobal = new HashSet<String>();
368             Settings.System.getMovedToGlobalSettings(movedToGlobal);
369             Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
370 
371             // system settings data first
372             int nBytes = in.readInt();
373             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of settings data");
374             byte[] buffer = new byte[nBytes];
375             in.readFully(buffer, 0, nBytes);
376             restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal);
377 
378             // secure settings
379             nBytes = in.readInt();
380             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data");
381             if (nBytes > buffer.length) buffer = new byte[nBytes];
382             in.readFully(buffer, 0, nBytes);
383             restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal);
384 
385             // Global only if sufficiently new
386             if (version >= FULL_BACKUP_ADDED_GLOBAL) {
387                 nBytes = in.readInt();
388                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of global settings data");
389                 if (nBytes > buffer.length) buffer = new byte[nBytes];
390                 in.readFully(buffer, 0, nBytes);
391                 movedToGlobal.clear();  // no redirection; this *is* the global namespace
392                 restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal);
393             }
394 
395             // locale
396             nBytes = in.readInt();
397             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of locale data");
398             if (nBytes > buffer.length) buffer = new byte[nBytes];
399             in.readFully(buffer, 0, nBytes);
400             mSettingsHelper.setLocaleData(buffer, nBytes);
401 
402             // Restore older backups performing the necessary migrations.
403             if (version < FULL_BACKUP_ADDED_WIFI_NEW) {
404                 // wifi supplicant
405                 int supplicant_size = in.readInt();
406                 if (DEBUG_BACKUP) Log.d(TAG, supplicant_size + " bytes of wifi supplicant data");
407                 byte[] supplicant_buffer = new byte[supplicant_size];
408                 in.readFully(supplicant_buffer, 0, supplicant_size);
409 
410                 // ip config
411                 int ipconfig_size = in.readInt();
412                 if (DEBUG_BACKUP) Log.d(TAG, ipconfig_size + " bytes of ip config data");
413                 byte[] ipconfig_buffer = new byte[ipconfig_size];
414                 in.readFully(ipconfig_buffer, 0, nBytes);
415                 restoreSupplicantWifiConfigData(supplicant_buffer, ipconfig_buffer);
416             }
417 
418             if (version >= FULL_BACKUP_ADDED_LOCK_SETTINGS) {
419                 nBytes = in.readInt();
420                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of lock settings data");
421                 if (nBytes > buffer.length) buffer = new byte[nBytes];
422                 if (nBytes > 0) {
423                     in.readFully(buffer, 0, nBytes);
424                     restoreLockSettings(UserHandle.myUserId(), buffer, nBytes);
425                 }
426             }
427             // softap config
428             if (version >= FULL_BACKUP_ADDED_SOFTAP_CONF) {
429                 nBytes = in.readInt();
430                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of softap config data");
431                 if (nBytes > buffer.length) buffer = new byte[nBytes];
432                 if (nBytes > 0) {
433                     in.readFully(buffer, 0, nBytes);
434                     restoreSoftApConfiguration(buffer);
435                 }
436             }
437             // network policies
438             if (version >= FULL_BACKUP_ADDED_NETWORK_POLICIES) {
439                 nBytes = in.readInt();
440                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of network policies data");
441                 if (nBytes > buffer.length) buffer = new byte[nBytes];
442                 if (nBytes > 0) {
443                     in.readFully(buffer, 0, nBytes);
444                     restoreNetworkPolicies(buffer);
445                 }
446             }
447             // Restore full wifi config data
448             if (version >= FULL_BACKUP_ADDED_WIFI_NEW) {
449                 nBytes = in.readInt();
450                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of full wifi config data");
451                 if (nBytes > buffer.length) buffer = new byte[nBytes];
452                 in.readFully(buffer, 0, nBytes);
453                 restoreNewWifiConfigData(buffer);
454             }
455 
456             if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
457         } else {
458             data.close();
459             throw new IOException("Invalid file schema");
460         }
461     }
462 
readOldChecksums(ParcelFileDescriptor oldState)463     private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException {
464         long[] stateChecksums = new long[STATE_SIZE];
465 
466         DataInputStream dataInput = new DataInputStream(
467                 new FileInputStream(oldState.getFileDescriptor()));
468 
469         try {
470             int stateVersion = dataInput.readInt();
471             if (stateVersion > STATE_VERSION) {
472                 // Constrain the maximum state version this backup agent
473                 // can handle in case a newer or corrupt backup set existed
474                 stateVersion = STATE_VERSION;
475             }
476             for (int i = 0; i < STATE_SIZES[stateVersion]; i++) {
477                 stateChecksums[i] = dataInput.readLong();
478             }
479         } catch (EOFException eof) {
480             // With the default 0 checksum we'll wind up forcing a backup of
481             // any unhandled data sets, which is appropriate.
482         }
483         dataInput.close();
484         return stateChecksums;
485     }
486 
writeNewChecksums(long[] checksums, ParcelFileDescriptor newState)487     private void writeNewChecksums(long[] checksums, ParcelFileDescriptor newState)
488             throws IOException {
489         DataOutputStream dataOutput = new DataOutputStream(
490                 new BufferedOutputStream(new FileOutputStream(newState.getFileDescriptor())));
491 
492         dataOutput.writeInt(STATE_VERSION);
493         for (int i = 0; i < STATE_SIZE; i++) {
494             dataOutput.writeLong(checksums[i]);
495         }
496         dataOutput.close();
497     }
498 
writeIfChanged(long oldChecksum, String key, byte[] data, BackupDataOutput output)499     private long writeIfChanged(long oldChecksum, String key, byte[] data,
500             BackupDataOutput output) {
501         CRC32 checkSummer = new CRC32();
502         checkSummer.update(data);
503         long newChecksum = checkSummer.getValue();
504         if (oldChecksum == newChecksum) {
505             return oldChecksum;
506         }
507         try {
508             if (DEBUG_BACKUP) {
509                 Log.v(TAG, "Writing entity " + key + " of size " + data.length);
510             }
511             output.writeEntityHeader(key, data.length);
512             output.writeEntityData(data, data.length);
513         } catch (IOException ioe) {
514             // Bail
515         }
516         return newChecksum;
517     }
518 
getSystemSettings()519     private byte[] getSystemSettings() {
520         Cursor cursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION, null,
521                 null, null);
522         try {
523             return extractRelevantValues(cursor, Settings.System.SETTINGS_TO_BACKUP);
524         } finally {
525             cursor.close();
526         }
527     }
528 
getSecureSettings()529     private byte[] getSecureSettings() {
530         Cursor cursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION, null,
531                 null, null);
532         try {
533             return extractRelevantValues(cursor, Settings.Secure.SETTINGS_TO_BACKUP);
534         } finally {
535             cursor.close();
536         }
537     }
538 
getGlobalSettings()539     private byte[] getGlobalSettings() {
540         Cursor cursor = getContentResolver().query(Settings.Global.CONTENT_URI, PROJECTION, null,
541                 null, null);
542         try {
543             return extractRelevantValues(cursor, Settings.Global.SETTINGS_TO_BACKUP);
544         } finally {
545             cursor.close();
546         }
547     }
548 
549     /**
550      * Serialize the owner info and other lock settings
551      */
getLockSettings(@serIdInt int userId)552     private byte[] getLockSettings(@UserIdInt int userId) {
553         final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
554         final boolean ownerInfoEnabled = lockPatternUtils.isOwnerInfoEnabled(userId);
555         final String ownerInfo = lockPatternUtils.getOwnerInfo(userId);
556         final boolean lockPatternEnabled = lockPatternUtils.isLockPatternEnabled(userId);
557         final boolean visiblePatternEnabled = lockPatternUtils.isVisiblePatternEnabled(userId);
558         final boolean powerButtonInstantlyLocks =
559                 lockPatternUtils.getPowerButtonInstantlyLocks(userId);
560 
561         ByteArrayOutputStream baos = new ByteArrayOutputStream();
562         DataOutputStream out = new DataOutputStream(baos);
563         try {
564             out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED);
565             out.writeUTF(ownerInfoEnabled ? "1" : "0");
566             if (ownerInfo != null) {
567                 out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO);
568                 out.writeUTF(ownerInfo != null ? ownerInfo : "");
569             }
570             if (lockPatternUtils.isVisiblePatternEverChosen(userId)) {
571                 out.writeUTF(KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED);
572                 out.writeUTF(visiblePatternEnabled ? "1" : "0");
573             }
574             if (lockPatternUtils.isPowerButtonInstantlyLocksEverChosen(userId)) {
575                 out.writeUTF(KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS);
576                 out.writeUTF(powerButtonInstantlyLocks ? "1" : "0");
577             }
578             // End marker
579             out.writeUTF("");
580             out.flush();
581         } catch (IOException ioe) {
582         }
583         return baos.toByteArray();
584     }
585 
restoreSettings(BackupDataInput data, Uri contentUri, HashSet<String> movedToGlobal)586     private void restoreSettings(BackupDataInput data, Uri contentUri,
587             HashSet<String> movedToGlobal) {
588         byte[] settings = new byte[data.getDataSize()];
589         try {
590             data.readEntityData(settings, 0, settings.length);
591         } catch (IOException ioe) {
592             Log.e(TAG, "Couldn't read entity data");
593             return;
594         }
595         restoreSettings(settings, settings.length, contentUri, movedToGlobal);
596     }
597 
restoreSettings(byte[] settings, int bytes, Uri contentUri, HashSet<String> movedToGlobal)598     private void restoreSettings(byte[] settings, int bytes, Uri contentUri,
599             HashSet<String> movedToGlobal) {
600         if (DEBUG) {
601             Log.i(TAG, "restoreSettings: " + contentUri);
602         }
603 
604         // Figure out the white list and redirects to the global table.  We restore anything
605         // in either the backup whitelist or the legacy-restore whitelist for this table.
606         final String[] whitelist;
607         if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
608             whitelist = concat(Settings.Secure.SETTINGS_TO_BACKUP,
609                     Settings.Secure.LEGACY_RESTORE_SETTINGS);
610         } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
611             whitelist = concat(Settings.System.SETTINGS_TO_BACKUP,
612                     Settings.System.LEGACY_RESTORE_SETTINGS);
613         } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
614             whitelist = concat(Settings.Global.SETTINGS_TO_BACKUP,
615                     Settings.Global.LEGACY_RESTORE_SETTINGS);
616         } else {
617             throw new IllegalArgumentException("Unknown URI: " + contentUri);
618         }
619 
620         // Restore only the white list data.
621         int pos = 0;
622         final ArrayMap<String, String> cachedEntries = new ArrayMap<>();
623         ContentValues contentValues = new ContentValues(2);
624         SettingsHelper settingsHelper = mSettingsHelper;
625         ContentResolver cr = getContentResolver();
626 
627         final int whiteListSize = whitelist.length;
628         for (int i = 0; i < whiteListSize; i++) {
629             String key = whitelist[i];
630 
631             String value = null;
632             boolean hasValueToRestore = false;
633             if (cachedEntries.indexOfKey(key) >= 0) {
634                 value = cachedEntries.remove(key);
635                 hasValueToRestore = true;
636             } else {
637                 // If the value not cached, let us look it up.
638                 while (pos < bytes) {
639                     int length = readInt(settings, pos);
640                     pos += INTEGER_BYTE_COUNT;
641                     String dataKey = length >= 0 ? new String(settings, pos, length) : null;
642                     pos += length;
643                     length = readInt(settings, pos);
644                     pos += INTEGER_BYTE_COUNT;
645                     String dataValue = null;
646                     if (length >= 0) {
647                         dataValue = new String(settings, pos, length);
648                         pos += length;
649                     }
650                     if (key.equals(dataKey)) {
651                         value = dataValue;
652                         hasValueToRestore = true;
653                         break;
654                     }
655                     cachedEntries.put(dataKey, dataValue);
656                 }
657             }
658 
659             if (!hasValueToRestore) {
660                 continue;
661             }
662 
663             final Uri destination = (movedToGlobal != null && movedToGlobal.contains(key))
664                     ? Settings.Global.CONTENT_URI
665                     : contentUri;
666             settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
667                     mRestoredFromSdkInt);
668 
669             if (DEBUG) {
670                 Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
671             }
672         }
673     }
674 
concat(String[] first, @Nullable String[] second)675     private final String[] concat(String[] first, @Nullable String[] second) {
676         if (second == null || second.length == 0) {
677             return first;
678         }
679         final int firstLen = first.length;
680         final int secondLen = second.length;
681         String[] both = new String[firstLen + secondLen];
682         System.arraycopy(first, 0, both, 0, firstLen);
683         System.arraycopy(second, 0, both, firstLen, secondLen);
684         return both;
685     }
686 
687     /**
688      * Restores the owner info enabled and other settings in LockSettings.
689      *
690      * @param buffer
691      * @param nBytes
692      */
restoreLockSettings(@serIdInt int userId, byte[] buffer, int nBytes)693     private void restoreLockSettings(@UserIdInt int userId, byte[] buffer, int nBytes) {
694         final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
695 
696         ByteArrayInputStream bais = new ByteArrayInputStream(buffer, 0, nBytes);
697         DataInputStream in = new DataInputStream(bais);
698         try {
699             String key;
700             // Read until empty string marker
701             while ((key = in.readUTF()).length() > 0) {
702                 final String value = in.readUTF();
703                 if (DEBUG_BACKUP) {
704                     Log.v(TAG, "Restoring lock_settings " + key + " = " + value);
705                 }
706                 switch (key) {
707                     case KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED:
708                         lockPatternUtils.setOwnerInfoEnabled("1".equals(value), userId);
709                         break;
710                     case KEY_LOCK_SETTINGS_OWNER_INFO:
711                         lockPatternUtils.setOwnerInfo(value, userId);
712                         break;
713                     case KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED:
714                         lockPatternUtils.reportPatternWasChosen(userId);
715                         lockPatternUtils.setVisiblePatternEnabled("1".equals(value), userId);
716                         break;
717                     case KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS:
718                         lockPatternUtils.setPowerButtonInstantlyLocks("1".equals(value), userId);
719                         break;
720                 }
721             }
722             in.close();
723         } catch (IOException ioe) {
724         }
725     }
726 
restoreLockSettings(@serIdInt int userId, BackupDataInput data)727     private void restoreLockSettings(@UserIdInt int userId, BackupDataInput data) {
728         final byte[] settings = new byte[data.getDataSize()];
729         try {
730             data.readEntityData(settings, 0, settings.length);
731         } catch (IOException ioe) {
732             Log.e(TAG, "Couldn't read entity data");
733             return;
734         }
735         restoreLockSettings(userId, settings, settings.length);
736     }
737 
738     /**
739      * Given a cursor and a set of keys, extract the required keys and
740      * values and write them to a byte array.
741      *
742      * @param cursor A cursor with settings data.
743      * @param settings The settings to extract.
744      * @return The byte array of extracted values.
745      */
extractRelevantValues(Cursor cursor, String[] settings)746     private byte[] extractRelevantValues(Cursor cursor, String[] settings) {
747         if (!cursor.moveToFirst()) {
748             Log.e(TAG, "Couldn't read from the cursor");
749             return new byte[0];
750         }
751 
752         final int nameColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.NAME);
753         final int valueColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.VALUE);
754 
755         // Obtain the relevant data in a temporary array.
756         int totalSize = 0;
757         int backedUpSettingIndex = 0;
758         final int settingsCount = settings.length;
759         final byte[][] values = new byte[settingsCount * 2][]; // keys and values
760         final ArrayMap<String, String> cachedEntries = new ArrayMap<>();
761         for (int i = 0; i < settingsCount; i++) {
762             final String key = settings[i];
763 
764             // If the value not cached, let us look it up.
765             String value = null;
766             boolean hasValueToBackup = false;
767             if (cachedEntries.indexOfKey(key) >= 0) {
768                 value = cachedEntries.remove(key);
769                 hasValueToBackup = true;
770             } else {
771                 while (!cursor.isAfterLast()) {
772                     final String cursorKey = cursor.getString(nameColumnIndex);
773                     final String cursorValue = cursor.getString(valueColumnIndex);
774                     cursor.moveToNext();
775                     if (key.equals(cursorKey)) {
776                         value = cursorValue;
777                         hasValueToBackup = true;
778                         break;
779                     }
780                     cachedEntries.put(cursorKey, cursorValue);
781                 }
782             }
783 
784             if (!hasValueToBackup) {
785                 continue;
786             }
787 
788             // Intercept the keys and see if they need special handling
789             value = mSettingsHelper.onBackupValue(key, value);
790 
791             // Write the key and value in the intermediary array.
792             final byte[] keyBytes = key.getBytes();
793             totalSize += INTEGER_BYTE_COUNT + keyBytes.length;
794             values[backedUpSettingIndex * 2] = keyBytes;
795 
796             final byte[] valueBytes = (value != null) ? value.getBytes() : NULL_VALUE;
797             totalSize += INTEGER_BYTE_COUNT + valueBytes.length;
798             values[backedUpSettingIndex * 2 + 1] = valueBytes;
799 
800             backedUpSettingIndex++;
801 
802             if (DEBUG) {
803                 Log.d(TAG, "Backed up setting: " + key + "=" + value);
804             }
805         }
806 
807         // Aggregate the result.
808         byte[] result = new byte[totalSize];
809         int pos = 0;
810         final int keyValuePairCount = backedUpSettingIndex * 2;
811         for (int i = 0; i < keyValuePairCount; i++) {
812             final byte[] value = values[i];
813             if (value != NULL_VALUE) {
814                 pos = writeInt(result, pos, value.length);
815                 pos = writeBytes(result, pos, value);
816             } else {
817                 pos = writeInt(result, pos, NULL_SIZE);
818             }
819         }
820         return result;
821     }
822 
restoreSupplicantWifiConfigData(byte[] supplicant_bytes, byte[] ipconfig_bytes)823     private void restoreSupplicantWifiConfigData(byte[] supplicant_bytes, byte[] ipconfig_bytes) {
824         if (DEBUG_BACKUP) {
825             Log.v(TAG, "Applying restored supplicant wifi data");
826         }
827         mWifiManager.restoreSupplicantBackupData(supplicant_bytes, ipconfig_bytes);
828     }
829 
getSoftAPConfiguration()830     private byte[] getSoftAPConfiguration() {
831         try {
832             return mWifiManager.getWifiApConfiguration().getBytesForBackup();
833         } catch (IOException ioe) {
834             Log.e(TAG, "Failed to marshal SoftAPConfiguration" + ioe.getMessage());
835             return new byte[0];
836         }
837     }
838 
restoreSoftApConfiguration(byte[] data)839     private void restoreSoftApConfiguration(byte[] data) {
840         try {
841             WifiConfiguration config = WifiConfiguration
842                     .getWifiConfigFromBackup(new DataInputStream(new ByteArrayInputStream(data)));
843             if (DEBUG) Log.d(TAG, "Successfully unMarshaled WifiConfiguration ");
844             mWifiManager.setWifiApConfiguration(config);
845         } catch (IOException | BackupUtils.BadVersionException e) {
846             Log.e(TAG, "Failed to unMarshal SoftAPConfiguration " + e.getMessage());
847         }
848     }
849 
getNetworkPolicies()850     private byte[] getNetworkPolicies() {
851         NetworkPolicyManager networkPolicyManager =
852                 (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE);
853         NetworkPolicy[] policies = networkPolicyManager.getNetworkPolicies();
854         ByteArrayOutputStream baos = new ByteArrayOutputStream();
855         if (policies != null && policies.length != 0) {
856             DataOutputStream out = new DataOutputStream(baos);
857             try {
858                 out.writeInt(NETWORK_POLICIES_BACKUP_VERSION);
859                 out.writeInt(policies.length);
860                 for (NetworkPolicy policy : policies) {
861                     if (policy != null) {
862                         byte[] marshaledPolicy = policy.getBytesForBackup();
863                         out.writeByte(BackupUtils.NOT_NULL);
864                         out.writeInt(marshaledPolicy.length);
865                         out.write(marshaledPolicy);
866                     } else {
867                         out.writeByte(BackupUtils.NULL);
868                     }
869                 }
870             } catch (IOException ioe) {
871                 Log.e(TAG, "Failed to convert NetworkPolicies to byte array " + ioe.getMessage());
872                 baos.reset();
873             }
874         }
875         return baos.toByteArray();
876     }
877 
getNewWifiConfigData()878     private byte[] getNewWifiConfigData() {
879         return mWifiManager.retrieveBackupData();
880     }
881 
restoreNewWifiConfigData(byte[] bytes)882     private void restoreNewWifiConfigData(byte[] bytes) {
883         if (DEBUG_BACKUP) {
884             Log.v(TAG, "Applying restored wifi data");
885         }
886         mWifiManager.restoreBackupData(bytes);
887     }
888 
restoreNetworkPolicies(byte[] data)889     private void restoreNetworkPolicies(byte[] data) {
890         NetworkPolicyManager networkPolicyManager =
891                 (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE);
892         if (data != null && data.length != 0) {
893             DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
894             try {
895                 int version = in.readInt();
896                 if (version < 1 || version > NETWORK_POLICIES_BACKUP_VERSION) {
897                     throw new BackupUtils.BadVersionException(
898                             "Unknown Backup Serialization Version");
899                 }
900                 int length = in.readInt();
901                 NetworkPolicy[] policies = new NetworkPolicy[length];
902                 for (int i = 0; i < length; i++) {
903                     byte isNull = in.readByte();
904                     if (isNull == BackupUtils.NULL) continue;
905                     int byteLength = in.readInt();
906                     byte[] policyData = new byte[byteLength];
907                     in.read(policyData, 0, byteLength);
908                     policies[i] = NetworkPolicy.getNetworkPolicyFromBackup(
909                             new DataInputStream(new ByteArrayInputStream(policyData)));
910                 }
911                 // Only set the policies if there was no error in the restore operation
912                 networkPolicyManager.setNetworkPolicies(policies);
913             } catch (NullPointerException | IOException | BackupUtils.BadVersionException e) {
914                 // NPE can be thrown when trying to instantiate a NetworkPolicy
915                 Log.e(TAG, "Failed to convert byte array to NetworkPolicies " + e.getMessage());
916             }
917         }
918     }
919 
920     /**
921      * Write an int in BigEndian into the byte array.
922      * @param out byte array
923      * @param pos current pos in array
924      * @param value integer to write
925      * @return the index after adding the size of an int (4) in bytes.
926      */
writeInt(byte[] out, int pos, int value)927     private int writeInt(byte[] out, int pos, int value) {
928         out[pos + 0] = (byte) ((value >> 24) & 0xFF);
929         out[pos + 1] = (byte) ((value >> 16) & 0xFF);
930         out[pos + 2] = (byte) ((value >>  8) & 0xFF);
931         out[pos + 3] = (byte) ((value >>  0) & 0xFF);
932         return pos + INTEGER_BYTE_COUNT;
933     }
934 
writeBytes(byte[] out, int pos, byte[] value)935     private int writeBytes(byte[] out, int pos, byte[] value) {
936         System.arraycopy(value, 0, out, pos, value.length);
937         return pos + value.length;
938     }
939 
readInt(byte[] in, int pos)940     private int readInt(byte[] in, int pos) {
941         int result = ((in[pos] & 0xFF) << 24)
942                 | ((in[pos + 1] & 0xFF) << 16)
943                 | ((in[pos + 2] & 0xFF) <<  8)
944                 | ((in[pos + 3] & 0xFF) <<  0);
945         return result;
946     }
947 }
948