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.Build;
35 import android.os.ParcelFileDescriptor;
36 import android.os.UserHandle;
37 import android.provider.Settings;
38 import android.provider.SettingsValidators.Validator;
39 import android.util.ArrayMap;
40 import android.util.ArraySet;
41 import android.util.BackupUtils;
42 import android.util.Log;
43 
44 import com.android.internal.widget.LockPatternUtils;
45 
46 import java.io.BufferedOutputStream;
47 import java.io.ByteArrayInputStream;
48 import java.io.ByteArrayOutputStream;
49 import java.io.DataInputStream;
50 import java.io.DataOutputStream;
51 import java.io.EOFException;
52 import java.io.FileInputStream;
53 import java.io.FileOutputStream;
54 import java.io.IOException;
55 import java.time.DateTimeException;
56 import java.util.Arrays;
57 import java.util.HashSet;
58 import java.util.Map;
59 import java.util.zip.CRC32;
60 
61 /**
62  * Performs backup and restore of the System and Secure settings.
63  * List of settings that are backed up are stored in the Settings.java file
64  */
65 public class SettingsBackupAgent extends BackupAgentHelper {
66     private static final boolean DEBUG = false;
67     private static final boolean DEBUG_BACKUP = DEBUG || false;
68 
69     private static final byte[] NULL_VALUE = new byte[0];
70     private static final int NULL_SIZE = -1;
71 
72     private static final String KEY_SYSTEM = "system";
73     private static final String KEY_SECURE = "secure";
74     private static final String KEY_GLOBAL = "global";
75     private static final String KEY_LOCALE = "locale";
76     private static final String KEY_LOCK_SETTINGS = "lock_settings";
77     private static final String KEY_SOFTAP_CONFIG = "softap_config";
78     private static final String KEY_NETWORK_POLICIES = "network_policies";
79     private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
80 
81     // Versioning of the state file.  Increment this version
82     // number any time the set of state items is altered.
83     private static final int STATE_VERSION = 7;
84 
85     // Versioning of the Network Policies backup payload.
86     private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
87 
88 
89     // Slots in the checksum array.  Never insert new items in the middle
90     // of this array; new slots must be appended.
91     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;
96     private static final int STATE_GLOBAL           = 5;
97     private static final int STATE_LOCK_SETTINGS    = 6;
98     private static final int STATE_SOFTAP_CONFIG    = 7;
99     private static final int STATE_NETWORK_POLICIES = 8;
100     private static final int STATE_WIFI_NEW_CONFIG  = 9;
101 
102     private static final int STATE_SIZE             = 10; // The current number of state items
103 
104     // Number of entries in the checksum array at various version numbers
105     private static final int STATE_SIZES[] = {
106             0,
107             4,              // version 1
108             5,              // version 2 added STATE_WIFI_CONFIG
109             6,              // version 3 added STATE_GLOBAL
110             7,              // version 4 added STATE_LOCK_SETTINGS
111             8,              // version 5 added STATE_SOFTAP_CONFIG
112             9,              // version 6 added STATE_NETWORK_POLICIES
113             STATE_SIZE      // version 7 added STATE_WIFI_NEW_CONFIG
114     };
115 
116     // Versioning of the 'full backup' format
117     // Increment this version any time a new item is added
118     private static final int FULL_BACKUP_VERSION = 6;
119     private static final int FULL_BACKUP_ADDED_GLOBAL = 2;  // added the "global" entry
120     private static final int FULL_BACKUP_ADDED_LOCK_SETTINGS = 3; // added the "lock_settings" entry
121     private static final int FULL_BACKUP_ADDED_SOFTAP_CONF = 4; //added the "softap_config" entry
122     private static final int FULL_BACKUP_ADDED_NETWORK_POLICIES = 5; //added "network_policies"
123     private static final int FULL_BACKUP_ADDED_WIFI_NEW = 6; // added "wifi_new_config" entry
124 
125     private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
126 
127     private static final byte[] EMPTY_DATA = new byte[0];
128 
129     private static final String TAG = "SettingsBackupAgent";
130 
131     private static final String[] PROJECTION = {
132             Settings.NameValueTable.NAME,
133             Settings.NameValueTable.VALUE
134     };
135 
136     // the key to store the WIFI data under, should be sorted as last, so restore happens last.
137     // use very late unicode character to quasi-guarantee last sort position.
138     private static final String KEY_WIFI_SUPPLICANT = "\uffedWIFI";
139     private static final String KEY_WIFI_CONFIG = "\uffedCONFIG_WIFI";
140 
141     // Keys within the lock settings section
142     private static final String KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED = "owner_info_enabled";
143     private static final String KEY_LOCK_SETTINGS_OWNER_INFO = "owner_info";
144     private static final String KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED =
145             "visible_pattern_enabled";
146     private static final String KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS =
147             "power_button_instantly_locks";
148 
149     // Name of the temporary file we use during full backup/restore.  This is
150     // stored in the full-backup tarfile as well, so should not be changed.
151     private static final String STAGE_FILE = "flattened-data";
152 
153     // List of keys that support restore to lower version of the SDK, introduced in Android P
154     private static final ArraySet<String> RESTORE_FROM_HIGHER_SDK_INT_SUPPORTED_KEYS =
155             new ArraySet<String>(Arrays.asList(new String[] {
156                 KEY_NETWORK_POLICIES,
157                 KEY_WIFI_NEW_CONFIG,
158                 KEY_SYSTEM,
159                 KEY_SECURE,
160                 KEY_GLOBAL,
161             }));
162 
163     private SettingsHelper mSettingsHelper;
164 
165     private WifiManager mWifiManager;
166 
167     // Version of the SDK that com.android.providers.settings package has been restored from.
168     // Populated in onRestore().
169     private int mRestoredFromSdkInt;
170 
171     @Override
onCreate()172     public void onCreate() {
173         if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
174 
175         mSettingsHelper = new SettingsHelper(this);
176         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
177         super.onCreate();
178     }
179 
180     @Override
onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)181     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
182             ParcelFileDescriptor newState) throws IOException {
183 
184         byte[] systemSettingsData = getSystemSettings();
185         byte[] secureSettingsData = getSecureSettings();
186         byte[] globalSettingsData = getGlobalSettings();
187         byte[] lockSettingsData   = getLockSettings(UserHandle.myUserId());
188         byte[] locale = mSettingsHelper.getLocaleData();
189         byte[] softApConfigData = getSoftAPConfiguration();
190         byte[] netPoliciesData = getNetworkPolicies();
191         byte[] wifiFullConfigData = getNewWifiConfigData();
192 
193         long[] stateChecksums = readOldChecksums(oldState);
194 
195         stateChecksums[STATE_SYSTEM] =
196                 writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
197         stateChecksums[STATE_SECURE] =
198                 writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
199         stateChecksums[STATE_GLOBAL] =
200                 writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, globalSettingsData, data);
201         stateChecksums[STATE_LOCALE] =
202                 writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
203         stateChecksums[STATE_WIFI_SUPPLICANT] = 0;
204         stateChecksums[STATE_WIFI_CONFIG] = 0;
205         stateChecksums[STATE_LOCK_SETTINGS] =
206                 writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
207                         lockSettingsData, data);
208         stateChecksums[STATE_SOFTAP_CONFIG] =
209                 writeIfChanged(stateChecksums[STATE_SOFTAP_CONFIG], KEY_SOFTAP_CONFIG,
210                         softApConfigData, data);
211         stateChecksums[STATE_NETWORK_POLICIES] =
212                 writeIfChanged(stateChecksums[STATE_NETWORK_POLICIES], KEY_NETWORK_POLICIES,
213                         netPoliciesData, data);
214         stateChecksums[STATE_WIFI_NEW_CONFIG] =
215                 writeIfChanged(stateChecksums[STATE_WIFI_NEW_CONFIG], KEY_WIFI_NEW_CONFIG,
216                         wifiFullConfigData, data);
217 
218         writeNewChecksums(stateChecksums, newState);
219     }
220 
221     @Override
onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)222     public void onRestore(BackupDataInput data, int appVersionCode,
223             ParcelFileDescriptor newState) throws IOException {
224 
225         if (DEBUG) {
226             Log.d(TAG, "onRestore(): appVersionCode: " + appVersionCode
227                     + "; Build.VERSION.SDK_INT: " + Build.VERSION.SDK_INT);
228         }
229 
230         boolean overrideRestoreAnyVersion = Settings.Global.getInt(getContentResolver(),
231                 Settings.Global.OVERRIDE_SETTINGS_PROVIDER_RESTORE_ANY_VERSION, 0) == 1;
232         if ((appVersionCode > Build.VERSION.SDK_INT) && overrideRestoreAnyVersion) {
233             Log.w(TAG, "Ignoring restore from API" + appVersionCode + " to API"
234                     + Build.VERSION.SDK_INT + " due to settings flag override.");
235             return;
236         }
237 
238         // versionCode of com.android.providers.settings corresponds to SDK_INT
239         mRestoredFromSdkInt = appVersionCode;
240 
241         HashSet<String> movedToGlobal = new HashSet<String>();
242         Settings.System.getMovedToGlobalSettings(movedToGlobal);
243         Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
244         byte[] restoredWifiSupplicantData = null;
245         byte[] restoredWifiIpConfigData = null;
246 
247         while (data.readNextHeader()) {
248             final String key = data.getKey();
249             final int size = data.getDataSize();
250 
251             // bail out of restoring from higher SDK_INT version for unsupported keys
252             if (appVersionCode > Build.VERSION.SDK_INT
253                     && !RESTORE_FROM_HIGHER_SDK_INT_SUPPORTED_KEYS.contains(key)) {
254                 Log.w(TAG, "Not restoring unrecognized key '"
255                         + key + "' from future version " + appVersionCode);
256                 data.skipEntityData();
257                 continue;
258             }
259 
260             switch (key) {
261                 case KEY_SYSTEM :
262                     restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal);
263                     mSettingsHelper.applyAudioSettings();
264                     break;
265 
266                 case KEY_SECURE :
267                     restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal);
268                     break;
269 
270                 case KEY_GLOBAL :
271                     restoreSettings(data, Settings.Global.CONTENT_URI, null);
272                     break;
273 
274                 case KEY_WIFI_SUPPLICANT :
275                     restoredWifiSupplicantData = new byte[size];
276                     data.readEntityData(restoredWifiSupplicantData, 0, size);
277                     break;
278 
279                 case KEY_LOCALE :
280                     byte[] localeData = new byte[size];
281                     data.readEntityData(localeData, 0, size);
282                     mSettingsHelper.setLocaleData(localeData, size);
283                     break;
284 
285                 case KEY_WIFI_CONFIG :
286                     restoredWifiIpConfigData = new byte[size];
287                     data.readEntityData(restoredWifiIpConfigData, 0, size);
288                     break;
289 
290                 case KEY_LOCK_SETTINGS :
291                     restoreLockSettings(UserHandle.myUserId(), data);
292                     break;
293 
294                 case KEY_SOFTAP_CONFIG :
295                     byte[] softapData = new byte[size];
296                     data.readEntityData(softapData, 0, size);
297                     restoreSoftApConfiguration(softapData);
298                     break;
299 
300                 case KEY_NETWORK_POLICIES:
301                     byte[] netPoliciesData = new byte[size];
302                     data.readEntityData(netPoliciesData, 0, size);
303                     restoreNetworkPolicies(netPoliciesData);
304                     break;
305 
306                 case KEY_WIFI_NEW_CONFIG:
307                     byte[] restoredWifiNewConfigData = new byte[size];
308                     data.readEntityData(restoredWifiNewConfigData, 0, size);
309                     restoreNewWifiConfigData(restoredWifiNewConfigData);
310                     break;
311 
312                 default :
313                     data.skipEntityData();
314 
315             }
316         }
317 
318         // Do this at the end so that we also pull in the ipconfig data.
319         if (restoredWifiSupplicantData != null) {
320             restoreSupplicantWifiConfigData(
321                     restoredWifiSupplicantData, restoredWifiIpConfigData);
322         }
323     }
324 
325     @Override
onFullBackup(FullBackupDataOutput data)326     public void onFullBackup(FullBackupDataOutput data)  throws IOException {
327         // Full backup of SettingsBackupAgent support was removed in Android P. If you want to adb
328         // backup com.android.providers.settings package use \"-keyvalue\" flag.
329         // Full restore of SettingsBackupAgent is still available for backwards compatibility.
330     }
331 
332     @Override
onRestoreFile(ParcelFileDescriptor data, long size, int type, String domain, String relpath, long mode, long mtime)333     public void onRestoreFile(ParcelFileDescriptor data, long size,
334             int type, String domain, String relpath, long mode, long mtime)
335             throws IOException {
336         if (DEBUG_BACKUP) Log.d(TAG, "onRestoreFile() invoked");
337         // Our data is actually a blob of flattened settings data identical to that
338         // produced during incremental backups.  Just unpack and apply it all in
339         // turn.
340         FileInputStream instream = new FileInputStream(data.getFileDescriptor());
341         DataInputStream in = new DataInputStream(instream);
342 
343         int version = in.readInt();
344         if (DEBUG_BACKUP) Log.d(TAG, "Flattened data version " + version);
345         if (version <= FULL_BACKUP_VERSION) {
346             // Generate the moved-to-global lookup table
347             HashSet<String> movedToGlobal = new HashSet<String>();
348             Settings.System.getMovedToGlobalSettings(movedToGlobal);
349             Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
350 
351             // system settings data first
352             int nBytes = in.readInt();
353             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of settings data");
354             byte[] buffer = new byte[nBytes];
355             in.readFully(buffer, 0, nBytes);
356             restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal);
357 
358             // secure settings
359             nBytes = in.readInt();
360             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data");
361             if (nBytes > buffer.length) buffer = new byte[nBytes];
362             in.readFully(buffer, 0, nBytes);
363             restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal);
364 
365             // Global only if sufficiently new
366             if (version >= FULL_BACKUP_ADDED_GLOBAL) {
367                 nBytes = in.readInt();
368                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of global settings data");
369                 if (nBytes > buffer.length) buffer = new byte[nBytes];
370                 in.readFully(buffer, 0, nBytes);
371                 movedToGlobal.clear();  // no redirection; this *is* the global namespace
372                 restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal);
373             }
374 
375             // locale
376             nBytes = in.readInt();
377             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of locale data");
378             if (nBytes > buffer.length) buffer = new byte[nBytes];
379             in.readFully(buffer, 0, nBytes);
380             mSettingsHelper.setLocaleData(buffer, nBytes);
381 
382             // Restore older backups performing the necessary migrations.
383             if (version < FULL_BACKUP_ADDED_WIFI_NEW) {
384                 // wifi supplicant
385                 int supplicant_size = in.readInt();
386                 if (DEBUG_BACKUP) Log.d(TAG, supplicant_size + " bytes of wifi supplicant data");
387                 byte[] supplicant_buffer = new byte[supplicant_size];
388                 in.readFully(supplicant_buffer, 0, supplicant_size);
389 
390                 // ip config
391                 int ipconfig_size = in.readInt();
392                 if (DEBUG_BACKUP) Log.d(TAG, ipconfig_size + " bytes of ip config data");
393                 byte[] ipconfig_buffer = new byte[ipconfig_size];
394                 in.readFully(ipconfig_buffer, 0, nBytes);
395                 restoreSupplicantWifiConfigData(supplicant_buffer, ipconfig_buffer);
396             }
397 
398             if (version >= FULL_BACKUP_ADDED_LOCK_SETTINGS) {
399                 nBytes = in.readInt();
400                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of lock settings data");
401                 if (nBytes > buffer.length) buffer = new byte[nBytes];
402                 if (nBytes > 0) {
403                     in.readFully(buffer, 0, nBytes);
404                     restoreLockSettings(UserHandle.myUserId(), buffer, nBytes);
405                 }
406             }
407             // softap config
408             if (version >= FULL_BACKUP_ADDED_SOFTAP_CONF) {
409                 nBytes = in.readInt();
410                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of softap config data");
411                 if (nBytes > buffer.length) buffer = new byte[nBytes];
412                 if (nBytes > 0) {
413                     in.readFully(buffer, 0, nBytes);
414                     restoreSoftApConfiguration(buffer);
415                 }
416             }
417             // network policies
418             if (version >= FULL_BACKUP_ADDED_NETWORK_POLICIES) {
419                 nBytes = in.readInt();
420                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of network policies data");
421                 if (nBytes > buffer.length) buffer = new byte[nBytes];
422                 if (nBytes > 0) {
423                     in.readFully(buffer, 0, nBytes);
424                     restoreNetworkPolicies(buffer);
425                 }
426             }
427             // Restore full wifi config data
428             if (version >= FULL_BACKUP_ADDED_WIFI_NEW) {
429                 nBytes = in.readInt();
430                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of full wifi config data");
431                 if (nBytes > buffer.length) buffer = new byte[nBytes];
432                 in.readFully(buffer, 0, nBytes);
433                 restoreNewWifiConfigData(buffer);
434             }
435 
436             if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
437         } else {
438             data.close();
439             throw new IOException("Invalid file schema");
440         }
441     }
442 
readOldChecksums(ParcelFileDescriptor oldState)443     private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException {
444         long[] stateChecksums = new long[STATE_SIZE];
445 
446         DataInputStream dataInput = new DataInputStream(
447                 new FileInputStream(oldState.getFileDescriptor()));
448 
449         try {
450             int stateVersion = dataInput.readInt();
451             if (stateVersion > STATE_VERSION) {
452                 // Constrain the maximum state version this backup agent
453                 // can handle in case a newer or corrupt backup set existed
454                 stateVersion = STATE_VERSION;
455             }
456             for (int i = 0; i < STATE_SIZES[stateVersion]; i++) {
457                 stateChecksums[i] = dataInput.readLong();
458             }
459         } catch (EOFException eof) {
460             // With the default 0 checksum we'll wind up forcing a backup of
461             // any unhandled data sets, which is appropriate.
462         }
463         dataInput.close();
464         return stateChecksums;
465     }
466 
writeNewChecksums(long[] checksums, ParcelFileDescriptor newState)467     private void writeNewChecksums(long[] checksums, ParcelFileDescriptor newState)
468             throws IOException {
469         DataOutputStream dataOutput = new DataOutputStream(
470                 new BufferedOutputStream(new FileOutputStream(newState.getFileDescriptor())));
471 
472         dataOutput.writeInt(STATE_VERSION);
473         for (int i = 0; i < STATE_SIZE; i++) {
474             dataOutput.writeLong(checksums[i]);
475         }
476         dataOutput.close();
477     }
478 
writeIfChanged(long oldChecksum, String key, byte[] data, BackupDataOutput output)479     private long writeIfChanged(long oldChecksum, String key, byte[] data,
480             BackupDataOutput output) {
481         CRC32 checkSummer = new CRC32();
482         checkSummer.update(data);
483         long newChecksum = checkSummer.getValue();
484         if (oldChecksum == newChecksum) {
485             return oldChecksum;
486         }
487         try {
488             if (DEBUG_BACKUP) {
489                 Log.v(TAG, "Writing entity " + key + " of size " + data.length);
490             }
491             output.writeEntityHeader(key, data.length);
492             output.writeEntityData(data, data.length);
493         } catch (IOException ioe) {
494             // Bail
495         }
496         return newChecksum;
497     }
498 
getSystemSettings()499     private byte[] getSystemSettings() {
500         Cursor cursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION, null,
501                 null, null);
502         try {
503             return extractRelevantValues(cursor, Settings.System.SETTINGS_TO_BACKUP);
504         } finally {
505             cursor.close();
506         }
507     }
508 
getSecureSettings()509     private byte[] getSecureSettings() {
510         Cursor cursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION, null,
511                 null, null);
512         try {
513             return extractRelevantValues(cursor, Settings.Secure.SETTINGS_TO_BACKUP);
514         } finally {
515             cursor.close();
516         }
517     }
518 
getGlobalSettings()519     private byte[] getGlobalSettings() {
520         Cursor cursor = getContentResolver().query(Settings.Global.CONTENT_URI, PROJECTION, null,
521                 null, null);
522         try {
523             return extractRelevantValues(cursor, Settings.Global.SETTINGS_TO_BACKUP);
524         } finally {
525             cursor.close();
526         }
527     }
528 
529     /**
530      * Serialize the owner info and other lock settings
531      */
getLockSettings(@serIdInt int userId)532     private byte[] getLockSettings(@UserIdInt int userId) {
533         final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
534         final boolean ownerInfoEnabled = lockPatternUtils.isOwnerInfoEnabled(userId);
535         final String ownerInfo = lockPatternUtils.getOwnerInfo(userId);
536         final boolean lockPatternEnabled = lockPatternUtils.isLockPatternEnabled(userId);
537         final boolean visiblePatternEnabled = lockPatternUtils.isVisiblePatternEnabled(userId);
538         final boolean powerButtonInstantlyLocks =
539                 lockPatternUtils.getPowerButtonInstantlyLocks(userId);
540 
541         ByteArrayOutputStream baos = new ByteArrayOutputStream();
542         DataOutputStream out = new DataOutputStream(baos);
543         try {
544             out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED);
545             out.writeUTF(ownerInfoEnabled ? "1" : "0");
546             if (ownerInfo != null) {
547                 out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO);
548                 out.writeUTF(ownerInfo != null ? ownerInfo : "");
549             }
550             if (lockPatternUtils.isVisiblePatternEverChosen(userId)) {
551                 out.writeUTF(KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED);
552                 out.writeUTF(visiblePatternEnabled ? "1" : "0");
553             }
554             if (lockPatternUtils.isPowerButtonInstantlyLocksEverChosen(userId)) {
555                 out.writeUTF(KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS);
556                 out.writeUTF(powerButtonInstantlyLocks ? "1" : "0");
557             }
558             // End marker
559             out.writeUTF("");
560             out.flush();
561         } catch (IOException ioe) {
562         }
563         return baos.toByteArray();
564     }
565 
restoreSettings(BackupDataInput data, Uri contentUri, HashSet<String> movedToGlobal)566     private void restoreSettings(BackupDataInput data, Uri contentUri,
567             HashSet<String> movedToGlobal) {
568         byte[] settings = new byte[data.getDataSize()];
569         try {
570             data.readEntityData(settings, 0, settings.length);
571         } catch (IOException ioe) {
572             Log.e(TAG, "Couldn't read entity data");
573             return;
574         }
575         restoreSettings(settings, settings.length, contentUri, movedToGlobal);
576     }
577 
restoreSettings(byte[] settings, int bytes, Uri contentUri, HashSet<String> movedToGlobal)578     private void restoreSettings(byte[] settings, int bytes, Uri contentUri,
579             HashSet<String> movedToGlobal) {
580         if (DEBUG) {
581             Log.i(TAG, "restoreSettings: " + contentUri);
582         }
583 
584         // Figure out the white list and redirects to the global table.  We restore anything
585         // in either the backup whitelist or the legacy-restore whitelist for this table.
586         final String[] whitelist;
587         Map<String, Validator> validators = null;
588         if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
589             whitelist = concat(Settings.Secure.SETTINGS_TO_BACKUP,
590                     Settings.Secure.LEGACY_RESTORE_SETTINGS);
591             validators = Settings.Secure.VALIDATORS;
592         } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
593             whitelist = concat(Settings.System.SETTINGS_TO_BACKUP,
594                     Settings.System.LEGACY_RESTORE_SETTINGS);
595             validators = Settings.System.VALIDATORS;
596         } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
597             whitelist = concat(Settings.Global.SETTINGS_TO_BACKUP,
598                     Settings.Global.LEGACY_RESTORE_SETTINGS);
599             validators = Settings.Global.VALIDATORS;
600         } else {
601             throw new IllegalArgumentException("Unknown URI: " + contentUri);
602         }
603 
604         // Restore only the white list data.
605         int pos = 0;
606         final ArrayMap<String, String> cachedEntries = new ArrayMap<>();
607         ContentValues contentValues = new ContentValues(2);
608         SettingsHelper settingsHelper = mSettingsHelper;
609         ContentResolver cr = getContentResolver();
610 
611         final int whiteListSize = whitelist.length;
612         for (int i = 0; i < whiteListSize; i++) {
613             String key = whitelist[i];
614 
615             String value = null;
616             boolean hasValueToRestore = false;
617             if (cachedEntries.indexOfKey(key) >= 0) {
618                 value = cachedEntries.remove(key);
619                 hasValueToRestore = true;
620             } else {
621                 // If the value not cached, let us look it up.
622                 while (pos < bytes) {
623                     int length = readInt(settings, pos);
624                     pos += INTEGER_BYTE_COUNT;
625                     String dataKey = length >= 0 ? new String(settings, pos, length) : null;
626                     pos += length;
627                     length = readInt(settings, pos);
628                     pos += INTEGER_BYTE_COUNT;
629                     String dataValue = null;
630                     if (length >= 0) {
631                         dataValue = new String(settings, pos, length);
632                         pos += length;
633                     }
634                     if (key.equals(dataKey)) {
635                         value = dataValue;
636                         hasValueToRestore = true;
637                         break;
638                     }
639                     cachedEntries.put(dataKey, dataValue);
640                 }
641             }
642 
643             if (!hasValueToRestore) {
644                 continue;
645             }
646 
647             // only restore the settings that have valid values
648             if (!isValidSettingValue(key, value, validators)) {
649                 Log.w(TAG, "Attempted restore of " + key + " setting, but its value didn't pass"
650                         + " validation, value: " + value);
651                 continue;
652             }
653 
654             final Uri destination = (movedToGlobal != null && movedToGlobal.contains(key))
655                     ? Settings.Global.CONTENT_URI
656                     : contentUri;
657             settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
658                     mRestoredFromSdkInt);
659 
660             if (DEBUG) {
661                 Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
662             }
663         }
664     }
665 
isValidSettingValue(String key, String value, Map<String, Validator> validators)666     private boolean isValidSettingValue(String key, String value,
667             Map<String, Validator> validators) {
668         if (key == null || validators == null) {
669             return false;
670         }
671         Validator validator = validators.get(key);
672         return (validator != null) && validator.validate(value);
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             int originalApBand = config.apBand;
845             mWifiManager.setWifiApConfiguration(config);
846 
847             // Depending on device hardware, we may need to notify the user of a setting change for
848             // the apBand preference
849             boolean dualMode = mWifiManager.isDualModeSupported();
850             int storedApBand = mWifiManager.getWifiApConfiguration().apBand;
851             if (dualMode) {
852                 if (storedApBand != originalApBand) {
853                     Log.d(TAG, "restored ap configuration requires a conversion, notify the user");
854                     mWifiManager.notifyUserOfApBandConversion();
855                 }
856             }
857         } catch (IOException | BackupUtils.BadVersionException e) {
858             Log.e(TAG, "Failed to unMarshal SoftAPConfiguration " + e.getMessage());
859         }
860     }
861 
getNetworkPolicies()862     private byte[] getNetworkPolicies() {
863         NetworkPolicyManager networkPolicyManager =
864                 (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE);
865         NetworkPolicy[] policies = networkPolicyManager.getNetworkPolicies();
866         ByteArrayOutputStream baos = new ByteArrayOutputStream();
867         if (policies != null && policies.length != 0) {
868             DataOutputStream out = new DataOutputStream(baos);
869             try {
870                 out.writeInt(NETWORK_POLICIES_BACKUP_VERSION);
871                 out.writeInt(policies.length);
872                 for (NetworkPolicy policy : policies) {
873                     // We purposefully only backup policies that the user has
874                     // defined; any inferred policies might include
875                     // carrier-protected data that we can't export.
876                     if (policy != null && !policy.inferred) {
877                         byte[] marshaledPolicy = policy.getBytesForBackup();
878                         out.writeByte(BackupUtils.NOT_NULL);
879                         out.writeInt(marshaledPolicy.length);
880                         out.write(marshaledPolicy);
881                     } else {
882                         out.writeByte(BackupUtils.NULL);
883                     }
884                 }
885             } catch (IOException ioe) {
886                 Log.e(TAG, "Failed to convert NetworkPolicies to byte array " + ioe.getMessage());
887                 baos.reset();
888             }
889         }
890         return baos.toByteArray();
891     }
892 
getNewWifiConfigData()893     private byte[] getNewWifiConfigData() {
894         return mWifiManager.retrieveBackupData();
895     }
896 
restoreNewWifiConfigData(byte[] bytes)897     private void restoreNewWifiConfigData(byte[] bytes) {
898         if (DEBUG_BACKUP) {
899             Log.v(TAG, "Applying restored wifi data");
900         }
901         mWifiManager.restoreBackupData(bytes);
902     }
903 
restoreNetworkPolicies(byte[] data)904     private void restoreNetworkPolicies(byte[] data) {
905         NetworkPolicyManager networkPolicyManager =
906                 (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE);
907         if (data != null && data.length != 0) {
908             DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
909             try {
910                 int version = in.readInt();
911                 if (version < 1 || version > NETWORK_POLICIES_BACKUP_VERSION) {
912                     throw new BackupUtils.BadVersionException(
913                             "Unknown Backup Serialization Version");
914                 }
915                 int length = in.readInt();
916                 NetworkPolicy[] policies = new NetworkPolicy[length];
917                 for (int i = 0; i < length; i++) {
918                     byte isNull = in.readByte();
919                     if (isNull == BackupUtils.NULL) continue;
920                     int byteLength = in.readInt();
921                     byte[] policyData = new byte[byteLength];
922                     in.read(policyData, 0, byteLength);
923                     policies[i] = NetworkPolicy.getNetworkPolicyFromBackup(
924                             new DataInputStream(new ByteArrayInputStream(policyData)));
925                 }
926                 // Only set the policies if there was no error in the restore operation
927                 networkPolicyManager.setNetworkPolicies(policies);
928             } catch (NullPointerException | IOException | BackupUtils.BadVersionException
929                     | DateTimeException e) {
930                 // NPE can be thrown when trying to instantiate a NetworkPolicy
931                 Log.e(TAG, "Failed to convert byte array to NetworkPolicies " + e.getMessage());
932             }
933         }
934     }
935 
936     /**
937      * Write an int in BigEndian into the byte array.
938      * @param out byte array
939      * @param pos current pos in array
940      * @param value integer to write
941      * @return the index after adding the size of an int (4) in bytes.
942      */
writeInt(byte[] out, int pos, int value)943     private int writeInt(byte[] out, int pos, int value) {
944         out[pos + 0] = (byte) ((value >> 24) & 0xFF);
945         out[pos + 1] = (byte) ((value >> 16) & 0xFF);
946         out[pos + 2] = (byte) ((value >>  8) & 0xFF);
947         out[pos + 3] = (byte) ((value >>  0) & 0xFF);
948         return pos + INTEGER_BYTE_COUNT;
949     }
950 
writeBytes(byte[] out, int pos, byte[] value)951     private int writeBytes(byte[] out, int pos, byte[] value) {
952         System.arraycopy(value, 0, out, pos, value.length);
953         return pos + value.length;
954     }
955 
readInt(byte[] in, int pos)956     private int readInt(byte[] in, int pos) {
957         int result = ((in[pos] & 0xFF) << 24)
958                 | ((in[pos + 1] & 0xFF) << 16)
959                 | ((in[pos + 2] & 0xFF) <<  8)
960                 | ((in[pos + 3] & 0xFF) <<  0);
961         return result;
962     }
963 }
964