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.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.UserIdInt;
22 import android.app.backup.BackupAgentHelper;
23 import android.app.backup.BackupDataInput;
24 import android.app.backup.BackupDataOutput;
25 import android.app.backup.FullBackupDataOutput;
26 import android.content.ContentResolver;
27 import android.content.ContentValues;
28 import android.content.Context;
29 import android.content.pm.PackageManager;
30 import android.database.Cursor;
31 import android.net.NetworkPolicy;
32 import android.net.NetworkPolicyManager;
33 import android.net.Uri;
34 import android.net.wifi.SoftApConfiguration;
35 import android.net.wifi.WifiManager;
36 import android.os.Build;
37 import android.os.ParcelFileDescriptor;
38 import android.os.UserHandle;
39 import android.provider.Settings;
40 import android.provider.settings.backup.DeviceSpecificSettings;
41 import android.provider.settings.backup.GlobalSettings;
42 import android.provider.settings.backup.LargeScreenSettings;
43 import android.provider.settings.backup.SecureSettings;
44 import android.provider.settings.backup.SystemSettings;
45 import android.provider.settings.validators.GlobalSettingsValidators;
46 import android.provider.settings.validators.SecureSettingsValidators;
47 import android.provider.settings.validators.SystemSettingsValidators;
48 import android.provider.settings.validators.Validator;
49 import android.telephony.SubscriptionManager;
50 import android.util.ArrayMap;
51 import android.util.ArraySet;
52 import android.util.BackupUtils;
53 import android.util.FeatureFlagUtils;
54 import android.util.Log;
55 import android.util.Slog;
56 import android.view.Display;
57 
58 import com.android.internal.annotations.VisibleForTesting;
59 import com.android.internal.util.ArrayUtils;
60 import com.android.internal.widget.LockPatternUtils;
61 import com.android.settingslib.display.DisplayDensityConfiguration;
62 import com.android.window.flags.Flags;
63 
64 import java.io.BufferedOutputStream;
65 import java.io.ByteArrayInputStream;
66 import java.io.ByteArrayOutputStream;
67 import java.io.DataInputStream;
68 import java.io.DataOutputStream;
69 import java.io.EOFException;
70 import java.io.FileInputStream;
71 import java.io.FileOutputStream;
72 import java.io.IOException;
73 import java.io.OutputStream;
74 import java.time.DateTimeException;
75 import java.util.Arrays;
76 import java.util.Collections;
77 import java.util.HashSet;
78 import java.util.Map;
79 import java.util.Objects;
80 import java.util.Set;
81 import java.util.concurrent.CountDownLatch;
82 import java.util.concurrent.TimeUnit;
83 import java.util.concurrent.atomic.AtomicInteger;
84 import java.util.function.Consumer;
85 import java.util.zip.CRC32;
86 
87 /**
88  * Performs backup and restore of the System and Secure settings.
89  * List of settings that are backed up are stored in the Settings.java file
90  */
91 public class SettingsBackupAgent extends BackupAgentHelper {
92     private static final boolean DEBUG = false;
93     private static final boolean DEBUG_BACKUP = DEBUG || false;
94 
95     private static final byte[] NULL_VALUE = new byte[0];
96     private static final int NULL_SIZE = -1;
97     private static final float FONT_SCALE_DEF_VALUE = 1.0f;
98 
99     private static final String KEY_SYSTEM = "system";
100     private static final String KEY_SECURE = "secure";
101     private static final String KEY_GLOBAL = "global";
102     private static final String KEY_LOCALE = "locale";
103     private static final String KEY_LOCK_SETTINGS = "lock_settings";
104     private static final String KEY_SOFTAP_CONFIG = "softap_config";
105     private static final String KEY_NETWORK_POLICIES = "network_policies";
106     private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
107     private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
108     private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
109     // Restoring sim-specific data backed up from newer Android version to Android 12 was causing a
110     // fatal crash. Creating a backup with a different key will prevent Android 12 versions from
111     // restoring this data.
112     private static final String KEY_SIM_SPECIFIC_SETTINGS_2 = "sim_specific_settings_2";
113     private static final String KEY_WIFI_SETTINGS_BACKUP_DATA = "wifi_settings_backup_data";
114 
115     // Versioning of the state file.  Increment this version
116     // number any time the set of state items is altered.
117     private static final int STATE_VERSION = 9;
118 
119     // Versioning of the Network Policies backup payload.
120     private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
121 
122     // Slots in the checksum array.  Never insert new items in the middle
123     // of this array; new slots must be appended.
124     private static final int STATE_SYSTEM                = 0;
125     private static final int STATE_SECURE                = 1;
126     private static final int STATE_LOCALE                = 2;
127     private static final int STATE_WIFI_SUPPLICANT       = 3;
128     private static final int STATE_WIFI_CONFIG           = 4;
129     private static final int STATE_GLOBAL                = 5;
130     private static final int STATE_LOCK_SETTINGS         = 6;
131     private static final int STATE_SOFTAP_CONFIG         = 7;
132     private static final int STATE_NETWORK_POLICIES      = 8;
133     private static final int STATE_WIFI_NEW_CONFIG       = 9;
134     private static final int STATE_DEVICE_CONFIG         = 10;
135     private static final int STATE_SIM_SPECIFIC_SETTINGS = 11;
136     private static final int STATE_WIFI_SETTINGS         = 12;
137 
138     private static final int STATE_SIZE                  = 13; // The current number of state items
139 
140     // Number of entries in the checksum array at various version numbers
141     private static final int STATE_SIZES[] = {
142             0,
143             4,              // version 1
144             5,              // version 2 added STATE_WIFI_CONFIG
145             6,              // version 3 added STATE_GLOBAL
146             7,              // version 4 added STATE_LOCK_SETTINGS
147             8,              // version 5 added STATE_SOFTAP_CONFIG
148             9,              // version 6 added STATE_NETWORK_POLICIES
149             10,             // version 7 added STATE_WIFI_NEW_CONFIG
150             11,             // version 8 added STATE_DEVICE_CONFIG
151             12,             // version 9 added STATE_SIM_SPECIFIC_SETTINGS
152             STATE_SIZE      // version 10 added STATE_WIFI_SETTINGS
153     };
154 
155     private static final int FULL_BACKUP_ADDED_GLOBAL = 2;  // added the "global" entry
156     private static final int FULL_BACKUP_ADDED_LOCK_SETTINGS = 3; // added the "lock_settings" entry
157     private static final int FULL_BACKUP_ADDED_SOFTAP_CONF = 4; //added the "softap_config" entry
158     private static final int FULL_BACKUP_ADDED_NETWORK_POLICIES = 5; //added "network_policies"
159     private static final int FULL_BACKUP_ADDED_WIFI_NEW = 6; // added "wifi_new_config" entry
160     private static final int FULL_BACKUP_ADDED_DEVICE_SPECIFIC = 7; // added "device specific" entry
161     // Versioning of the 'full backup' format
162     // Increment this version any time a new item is added
163     private static final int FULL_BACKUP_VERSION = FULL_BACKUP_ADDED_DEVICE_SPECIFIC;
164 
165     private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
166 
167     private static final byte[] EMPTY_DATA = new byte[0];
168 
169     private static final String TAG = "SettingsBackupAgent";
170 
171     @VisibleForTesting
172     static final String[] PROJECTION = {
173             Settings.NameValueTable.NAME,
174             Settings.NameValueTable.VALUE
175     };
176 
177     // Versioning of the 'device specific' section of a backup
178     // Increment this any time the format is changed or data added.
179     @VisibleForTesting
180     static final int DEVICE_SPECIFIC_VERSION = 1;
181 
182     // the key to store the WIFI data under, should be sorted as last, so restore happens last.
183     // use very late unicode character to quasi-guarantee last sort position.
184     private static final String KEY_WIFI_SUPPLICANT = "\uffedWIFI";
185     private static final String KEY_WIFI_CONFIG = "\uffedCONFIG_WIFI";
186 
187     // Keys within the lock settings section
188     private static final String KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED = "owner_info_enabled";
189     private static final String KEY_LOCK_SETTINGS_OWNER_INFO = "owner_info";
190     private static final String KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED =
191             "visible_pattern_enabled";
192     private static final String KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS =
193             "power_button_instantly_locks";
194     private static final String KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY =
195             "pin_enhanced_privacy";
196 
197     // Name of the temporary file we use during full backup/restore.  This is
198     // stored in the full-backup tarfile as well, so should not be changed.
199     private static final String STAGE_FILE = "flattened-data";
200 
201     // List of keys that support restore to lower version of the SDK, introduced in Android P
202     private static final ArraySet<String> RESTORE_FROM_HIGHER_SDK_INT_SUPPORTED_KEYS =
203             new ArraySet<String>(Arrays.asList(new String[] {
204                 KEY_NETWORK_POLICIES,
205                 KEY_WIFI_NEW_CONFIG,
206                 KEY_SYSTEM,
207                 KEY_SECURE,
208                 KEY_GLOBAL,
209             }));
210 
211     @VisibleForTesting
212     SettingsHelper mSettingsHelper;
213 
214     private WifiManager mWifiManager;
215 
216     // Version of the SDK that com.android.providers.settings package has been restored from.
217     // Populated in onRestore().
218     private int mRestoredFromSdkInt;
219 
220     // The available font scale for the current device
221     @Nullable
222     private String[] mAvailableFontScales;
223 
224     // The font_scale default value for this device.
225     private float mDefaultFontScale;
226 
227     @Override
onCreate()228     public void onCreate() {
229         if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
230         mDefaultFontScale = getBaseContext().getResources().getFloat(R.dimen.def_device_font_scale);
231         mAvailableFontScales = getBaseContext().getResources()
232                 .getStringArray(R.array.entryvalues_font_size);
233         mSettingsHelper = new SettingsHelper(this);
234         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
235         super.onCreate();
236     }
237 
238     @Override
onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)239     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
240             ParcelFileDescriptor newState) throws IOException {
241         byte[] systemSettingsData = getSystemSettings();
242         byte[] secureSettingsData = getSecureSettings();
243         byte[] globalSettingsData = getGlobalSettings();
244         byte[] lockSettingsData   = getLockSettings(UserHandle.myUserId());
245         byte[] locale = mSettingsHelper.getLocaleData();
246         byte[] softApConfigData = getSoftAPConfiguration();
247         byte[] netPoliciesData = getNetworkPolicies();
248         byte[] wifiFullConfigData = getNewWifiConfigData();
249         byte[] deviceSpecificInformation = getDeviceSpecificConfiguration();
250         byte[] simSpecificSettingsData = getSimSpecificSettingsData();
251         byte[] wifiSettingsData = getWifiSettingsBackupData();
252 
253         long[] stateChecksums = readOldChecksums(oldState);
254 
255         stateChecksums[STATE_SYSTEM] =
256                 writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
257         stateChecksums[STATE_SECURE] =
258                 writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
259         stateChecksums[STATE_GLOBAL] =
260                 writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, globalSettingsData, data);
261         stateChecksums[STATE_LOCALE] =
262                 writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
263         stateChecksums[STATE_WIFI_SUPPLICANT] = 0;
264         stateChecksums[STATE_WIFI_CONFIG] = 0;
265         stateChecksums[STATE_LOCK_SETTINGS] =
266                 writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
267                         lockSettingsData, data);
268         if (isWatch()) {
269             stateChecksums[STATE_SOFTAP_CONFIG] = 0;
270         } else {
271             stateChecksums[STATE_SOFTAP_CONFIG] =
272                     writeIfChanged(stateChecksums[STATE_SOFTAP_CONFIG], KEY_SOFTAP_CONFIG,
273                             softApConfigData, data);
274         }
275         stateChecksums[STATE_NETWORK_POLICIES] =
276                 writeIfChanged(stateChecksums[STATE_NETWORK_POLICIES], KEY_NETWORK_POLICIES,
277                         netPoliciesData, data);
278         stateChecksums[STATE_WIFI_NEW_CONFIG] =
279                 writeIfChanged(stateChecksums[STATE_WIFI_NEW_CONFIG], KEY_WIFI_NEW_CONFIG,
280                         wifiFullConfigData, data);
281         stateChecksums[STATE_DEVICE_CONFIG] =
282                 writeIfChanged(stateChecksums[STATE_DEVICE_CONFIG], KEY_DEVICE_SPECIFIC_CONFIG,
283                         deviceSpecificInformation, data);
284         stateChecksums[STATE_SIM_SPECIFIC_SETTINGS] =
285                 writeIfChanged(stateChecksums[STATE_SIM_SPECIFIC_SETTINGS],
286                         KEY_SIM_SPECIFIC_SETTINGS_2, simSpecificSettingsData, data);
287         stateChecksums[STATE_WIFI_SETTINGS] =
288                 writeIfChanged(stateChecksums[STATE_WIFI_SETTINGS],
289                         KEY_WIFI_SETTINGS_BACKUP_DATA, wifiSettingsData, data);
290 
291         writeNewChecksums(stateChecksums, newState);
292     }
293 
isWatch()294     private boolean isWatch() {
295         return getBaseContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
296     }
297 
298     @Override
onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)299     public void onRestore(BackupDataInput data, int appVersionCode,
300             ParcelFileDescriptor newState) {
301         throw new RuntimeException("SettingsBackupAgent has been migrated to use key exclusion");
302     }
303 
304     @Override
onRestore(BackupDataInput data, long appVersionCode, ParcelFileDescriptor newState, Set<String> dynamicBlockList)305     public void onRestore(BackupDataInput data, long appVersionCode,
306             ParcelFileDescriptor newState, Set<String> dynamicBlockList) throws IOException {
307 
308         if (DEBUG) {
309             Log.d(TAG, "onRestore(): appVersionCode: " + appVersionCode
310                     + "; Build.VERSION.SDK_INT: " + Build.VERSION.SDK_INT);
311         }
312 
313         boolean overrideRestoreAnyVersion = Settings.Global.getInt(getContentResolver(),
314                 Settings.Global.OVERRIDE_SETTINGS_PROVIDER_RESTORE_ANY_VERSION, 0) == 1;
315         if ((appVersionCode > Build.VERSION.SDK_INT) && overrideRestoreAnyVersion) {
316             Log.w(TAG, "Ignoring restore from API" + appVersionCode + " to API"
317                     + Build.VERSION.SDK_INT + " due to settings flag override.");
318             return;
319         }
320 
321         // versionCode of com.android.providers.settings corresponds to SDK_INT
322         mRestoredFromSdkInt = (int) appVersionCode;
323 
324         Set<String> movedToGlobal = getMovedToGlobalSettings();
325         Set<String> movedToSecure = getMovedToSecureSettings();
326         Set<String> movedToSystem = getMovedToSystemSettings();
327 
328         Set<String> preservedGlobalSettings = getSettingsToPreserveInRestore(
329                 Settings.Global.CONTENT_URI);
330         Set<String> preservedSecureSettings = getSettingsToPreserveInRestore(
331                 Settings.Secure.CONTENT_URI);
332         Set<String> preservedSystemSettings = getSettingsToPreserveInRestore(
333                 Settings.System.CONTENT_URI);
334         Set<String> preservedSettings = new HashSet<>(preservedGlobalSettings);
335         preservedSettings.addAll(preservedSecureSettings);
336         preservedSettings.addAll(preservedSystemSettings);
337 
338         byte[] restoredWifiSupplicantData = null;
339         byte[] restoredWifiIpConfigData = null;
340 
341         while (data.readNextHeader()) {
342             final String key = data.getKey();
343             final int size = data.getDataSize();
344 
345             // bail out of restoring from higher SDK_INT version for unsupported keys
346             if (appVersionCode > Build.VERSION.SDK_INT
347                     && !RESTORE_FROM_HIGHER_SDK_INT_SUPPORTED_KEYS.contains(key)) {
348                 Log.w(TAG, "Not restoring unrecognized key '"
349                         + key + "' from future version " + appVersionCode);
350                 data.skipEntityData();
351                 continue;
352             }
353 
354             switch (key) {
355                 case KEY_SYSTEM :
356                     restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal,
357                             movedToSecure, /* movedToSystem= */ null,
358                             R.array.restore_blocked_system_settings, dynamicBlockList,
359                             preservedSystemSettings);
360                     mSettingsHelper.applyAudioSettings();
361                     break;
362 
363                 case KEY_SECURE :
364                     restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal,
365                             /* movedToSecure= */ null, movedToSystem,
366                             R.array.restore_blocked_secure_settings, dynamicBlockList,
367                             preservedSecureSettings);
368                     break;
369 
370                 case KEY_GLOBAL :
371                     restoreSettings(data, Settings.Global.CONTENT_URI, /* movedToGlobal= */ null,
372                             movedToSecure, movedToSystem, R.array.restore_blocked_global_settings,
373                             dynamicBlockList, preservedGlobalSettings);
374                     break;
375 
376                 case KEY_WIFI_SUPPLICANT :
377                     restoredWifiSupplicantData = new byte[size];
378                     data.readEntityData(restoredWifiSupplicantData, 0, size);
379                     break;
380 
381                 case KEY_LOCALE :
382                     byte[] localeData = new byte[size];
383                     data.readEntityData(localeData, 0, size);
384                     mSettingsHelper.setLocaleData(localeData, size);
385                     break;
386 
387                 case KEY_WIFI_CONFIG :
388                     restoredWifiIpConfigData = new byte[size];
389                     data.readEntityData(restoredWifiIpConfigData, 0, size);
390                     break;
391 
392                 case KEY_LOCK_SETTINGS :
393                     restoreLockSettings(UserHandle.myUserId(), data);
394                     break;
395 
396                 case KEY_SOFTAP_CONFIG :
397                     byte[] softapData = new byte[size];
398                     data.readEntityData(softapData, 0, size);
399                     if (!isWatch()) {
400                         restoreSoftApConfiguration(softapData);
401                     }
402                     break;
403 
404                 case KEY_NETWORK_POLICIES:
405                     byte[] netPoliciesData = new byte[size];
406                     data.readEntityData(netPoliciesData, 0, size);
407                     if (!isWatch()) {
408                         restoreNetworkPolicies(netPoliciesData);
409                     }
410                     break;
411 
412                 case KEY_WIFI_NEW_CONFIG:
413                     byte[] restoredWifiNewConfigData = new byte[size];
414                     data.readEntityData(restoredWifiNewConfigData, 0, size);
415                     if (!isWatch()) {
416                         restoreNewWifiConfigData(restoredWifiNewConfigData);
417                     }
418                     break;
419 
420                 case KEY_DEVICE_SPECIFIC_CONFIG:
421                     byte[] restoredDeviceSpecificConfig = new byte[size];
422                     data.readEntityData(restoredDeviceSpecificConfig, 0, size);
423                     restoreDeviceSpecificConfig(
424                             restoredDeviceSpecificConfig,
425                             R.array.restore_blocked_device_specific_settings,
426                             dynamicBlockList,
427                             preservedSettings);
428                     break;
429 
430                 case KEY_SIM_SPECIFIC_SETTINGS:
431                     // Intentional fall through so that sim-specific backups from Android 12 will
432                     // also be restored on newer Android versions.
433                 case KEY_SIM_SPECIFIC_SETTINGS_2:
434                     byte[] restoredSimSpecificSettings = new byte[size];
435                     data.readEntityData(restoredSimSpecificSettings, 0, size);
436                     restoreSimSpecificSettings(restoredSimSpecificSettings);
437                     break;
438                 case KEY_WIFI_SETTINGS_BACKUP_DATA:
439                     byte[] restoredWifiData = new byte[size];
440                     data.readEntityData(restoredWifiData, 0, size);
441                     if (!isWatch()) {
442                         restoreWifiData(restoredWifiData);
443                     }
444                     break;
445                 default :
446                     data.skipEntityData();
447 
448             }
449         }
450 
451         // Do this at the end so that we also pull in the ipconfig data.
452         if (restoredWifiSupplicantData != null && !isWatch()) {
453             restoreSupplicantWifiConfigData(
454                     restoredWifiSupplicantData, restoredWifiIpConfigData);
455         }
456     }
457 
458     @Override
onFullBackup(FullBackupDataOutput data)459     public void onFullBackup(FullBackupDataOutput data)  throws IOException {
460         // Full backup of SettingsBackupAgent support was removed in Android P. If you want to adb
461         // backup com.android.providers.settings package use \"-keyvalue\" flag.
462         // Full restore of SettingsBackupAgent is still available for backwards compatibility.
463     }
464 
465     @Override
onRestoreFile(ParcelFileDescriptor data, long size, int type, String domain, String relpath, long mode, long mtime)466     public void onRestoreFile(ParcelFileDescriptor data, long size,
467             int type, String domain, String relpath, long mode, long mtime)
468             throws IOException {
469         if (DEBUG_BACKUP) Log.d(TAG, "onRestoreFile() invoked");
470         // Our data is actually a blob of flattened settings data identical to that
471         // produced during incremental backups.  Just unpack and apply it all in
472         // turn.
473         FileInputStream instream = new FileInputStream(data.getFileDescriptor());
474         DataInputStream in = new DataInputStream(instream);
475 
476         int version = in.readInt();
477         if (DEBUG_BACKUP) Log.d(TAG, "Flattened data version " + version);
478         if (version <= FULL_BACKUP_VERSION) {
479             // Generate the moved-to-global lookup table
480             Set<String> movedToGlobal = getMovedToGlobalSettings();
481             Set<String> movedToSecure = getMovedToSecureSettings();
482             Set<String> movedToSystem = getMovedToSystemSettings();
483 
484             // system settings data first
485             int nBytes = in.readInt();
486             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of settings data");
487             byte[] buffer = new byte[nBytes];
488             in.readFully(buffer, 0, nBytes);
489             restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal,
490                     movedToSecure, /* movedToSystem= */ null,
491                     R.array.restore_blocked_system_settings, Collections.emptySet(),
492                     Collections.emptySet());
493 
494             // secure settings
495             nBytes = in.readInt();
496             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data");
497             if (nBytes > buffer.length) buffer = new byte[nBytes];
498             in.readFully(buffer, 0, nBytes);
499             restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal,
500                     /* movedToSecure= */ null, movedToSystem,
501                     R.array.restore_blocked_secure_settings, Collections.emptySet(),
502                     Collections.emptySet());
503 
504             // Global only if sufficiently new
505             if (version >= FULL_BACKUP_ADDED_GLOBAL) {
506                 nBytes = in.readInt();
507                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of global settings data");
508                 if (nBytes > buffer.length) buffer = new byte[nBytes];
509                 in.readFully(buffer, 0, nBytes);
510                 restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI,
511                         /* movedToGlobal= */ null, movedToSecure, movedToSystem,
512                         R.array.restore_blocked_global_settings, Collections.emptySet(),
513                         Collections.emptySet());
514             }
515 
516             // locale
517             nBytes = in.readInt();
518             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of locale data");
519             if (nBytes > buffer.length) buffer = new byte[nBytes];
520             in.readFully(buffer, 0, nBytes);
521             mSettingsHelper.setLocaleData(buffer, nBytes);
522 
523             // Restore older backups performing the necessary migrations.
524             if (version < FULL_BACKUP_ADDED_WIFI_NEW) {
525                 // wifi supplicant
526                 int supplicant_size = in.readInt();
527                 if (DEBUG_BACKUP) Log.d(TAG, supplicant_size + " bytes of wifi supplicant data");
528                 byte[] supplicant_buffer = new byte[supplicant_size];
529                 in.readFully(supplicant_buffer, 0, supplicant_size);
530 
531                 // ip config
532                 int ipconfig_size = in.readInt();
533                 if (DEBUG_BACKUP) Log.d(TAG, ipconfig_size + " bytes of ip config data");
534                 byte[] ipconfig_buffer = new byte[ipconfig_size];
535                 in.readFully(ipconfig_buffer, 0, nBytes);
536                 if (!isWatch()) {
537                     restoreSupplicantWifiConfigData(supplicant_buffer, ipconfig_buffer);
538                 }
539             }
540 
541             if (version >= FULL_BACKUP_ADDED_LOCK_SETTINGS) {
542                 nBytes = in.readInt();
543                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of lock settings data");
544                 if (nBytes > buffer.length) buffer = new byte[nBytes];
545                 if (nBytes > 0) {
546                     in.readFully(buffer, 0, nBytes);
547                     restoreLockSettings(UserHandle.myUserId(), buffer, nBytes);
548                 }
549             }
550             // softap config
551             if (version >= FULL_BACKUP_ADDED_SOFTAP_CONF) {
552                 nBytes = in.readInt();
553                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of softap config data");
554                 if (nBytes > buffer.length) buffer = new byte[nBytes];
555                 if (nBytes > 0) {
556                     in.readFully(buffer, 0, nBytes);
557                     if (!isWatch()) {
558                         restoreSoftApConfiguration(buffer);
559                     }
560                 }
561             }
562             // network policies
563             if (version >= FULL_BACKUP_ADDED_NETWORK_POLICIES) {
564                 nBytes = in.readInt();
565                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of network policies data");
566                 if (nBytes > buffer.length) buffer = new byte[nBytes];
567                 if (nBytes > 0) {
568                     in.readFully(buffer, 0, nBytes);
569                     if (!isWatch()) {
570                         restoreNetworkPolicies(buffer);
571                     }
572                 }
573             }
574             // Restore full wifi config data
575             if (version >= FULL_BACKUP_ADDED_WIFI_NEW) {
576                 nBytes = in.readInt();
577                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of full wifi config data");
578                 if (nBytes > buffer.length) buffer = new byte[nBytes];
579                 in.readFully(buffer, 0, nBytes);
580                 if (!isWatch()) {
581                     restoreNewWifiConfigData(buffer);
582                 }
583             }
584 
585             if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
586         } else {
587             data.close();
588             throw new IOException("Invalid file schema");
589         }
590     }
591 
getMovedToGlobalSettings()592     private Set<String> getMovedToGlobalSettings() {
593         HashSet<String> movedToGlobalSettings = new HashSet<String>();
594         Settings.System.getMovedToGlobalSettings(movedToGlobalSettings);
595         Settings.Secure.getMovedToGlobalSettings(movedToGlobalSettings);
596         return movedToGlobalSettings;
597     }
598 
getMovedToSecureSettings()599     private Set<String> getMovedToSecureSettings() {
600         Set<String> movedToSecureSettings = new HashSet<>();
601         Settings.Global.getMovedToSecureSettings(movedToSecureSettings);
602         Settings.System.getMovedToSecureSettings(movedToSecureSettings);
603         return movedToSecureSettings;
604     }
605 
getMovedToSystemSettings()606     private Set<String> getMovedToSystemSettings() {
607         Set<String> movedToSystemSettings = new HashSet<>();
608         Settings.Global.getMovedToSystemSettings(movedToSystemSettings);
609         Settings.Secure.getMovedToSystemSettings(movedToSystemSettings);
610         return movedToSystemSettings;
611     }
612 
readOldChecksums(ParcelFileDescriptor oldState)613     private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException {
614         long[] stateChecksums = new long[STATE_SIZE];
615 
616         DataInputStream dataInput = new DataInputStream(
617                 new FileInputStream(oldState.getFileDescriptor()));
618 
619         try {
620             int stateVersion = dataInput.readInt();
621             if (stateVersion > STATE_VERSION) {
622                 // Constrain the maximum state version this backup agent
623                 // can handle in case a newer or corrupt backup set existed
624                 stateVersion = STATE_VERSION;
625             }
626             for (int i = 0; i < STATE_SIZES[stateVersion]; i++) {
627                 stateChecksums[i] = dataInput.readLong();
628             }
629         } catch (EOFException eof) {
630             // With the default 0 checksum we'll wind up forcing a backup of
631             // any unhandled data sets, which is appropriate.
632         }
633         dataInput.close();
634         return stateChecksums;
635     }
636 
writeNewChecksums(long[] checksums, ParcelFileDescriptor newState)637     private void writeNewChecksums(long[] checksums, ParcelFileDescriptor newState)
638             throws IOException {
639         DataOutputStream dataOutput = new DataOutputStream(
640                 new BufferedOutputStream(new FileOutputStream(newState.getFileDescriptor())));
641 
642         dataOutput.writeInt(STATE_VERSION);
643         for (int i = 0; i < STATE_SIZE; i++) {
644             dataOutput.writeLong(checksums[i]);
645         }
646         dataOutput.close();
647     }
648 
writeIfChanged(long oldChecksum, String key, byte[] data, BackupDataOutput output)649     private long writeIfChanged(long oldChecksum, String key, byte[] data,
650             BackupDataOutput output) {
651         CRC32 checkSummer = new CRC32();
652         checkSummer.update(data);
653         long newChecksum = checkSummer.getValue();
654         if (oldChecksum == newChecksum) {
655             return oldChecksum;
656         }
657         try {
658             if (DEBUG_BACKUP) {
659                 Log.v(TAG, "Writing entity " + key + " of size " + data.length);
660             }
661             output.writeEntityHeader(key, data.length);
662             output.writeEntityData(data, data.length);
663         } catch (IOException ioe) {
664             // Bail
665         }
666         return newChecksum;
667     }
668 
getSystemSettings()669     private byte[] getSystemSettings() {
670         Cursor cursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION, null,
671                 null, null);
672         try {
673             return extractRelevantValues(cursor, SystemSettings.SETTINGS_TO_BACKUP);
674         } finally {
675             cursor.close();
676         }
677     }
678 
getSecureSettings()679     private byte[] getSecureSettings() {
680         Cursor cursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION, null,
681                 null, null);
682         try {
683             return extractRelevantValues(cursor, SecureSettings.SETTINGS_TO_BACKUP);
684         } finally {
685             cursor.close();
686         }
687     }
688 
getGlobalSettings()689     private byte[] getGlobalSettings() {
690         Cursor cursor = getContentResolver().query(Settings.Global.CONTENT_URI, PROJECTION, null,
691                 null, null);
692         try {
693             return extractRelevantValues(cursor, GlobalSettings.SETTINGS_TO_BACKUP);
694         } finally {
695             cursor.close();
696         }
697     }
698 
699     /**
700      * Get names of the settings for which the current value should be preserved during restore.
701      */
getSettingsToPreserveInRestore(Uri settingsUri)702     private Set<String> getSettingsToPreserveInRestore(Uri settingsUri) {
703         if (!FeatureFlagUtils.isEnabled(getBaseContext(),
704                 FeatureFlagUtils.SETTINGS_DO_NOT_RESTORE_PRESERVED)) {
705             return Collections.emptySet();
706         }
707 
708         try (Cursor cursor = getContentResolver().query(settingsUri, new String[]{
709                         Settings.NameValueTable.NAME,
710                         Settings.NameValueTable.IS_PRESERVED_IN_RESTORE},
711                 /* selection */ null, /* selectionArgs */ null, /* sortOrder */ null)) {
712 
713             if (!cursor.moveToFirst()) {
714                 Slog.i(TAG, "No settings to be preserved in restore");
715                 return Collections.emptySet();
716             }
717 
718             int nameIndex = cursor.getColumnIndex(Settings.NameValueTable.NAME);
719             int isPreservedIndex = cursor.getColumnIndex(
720                     Settings.NameValueTable.IS_PRESERVED_IN_RESTORE);
721 
722             Set<String> preservedSettings = new HashSet<>();
723             while (!cursor.isAfterLast()) {
724                 if (Boolean.parseBoolean(cursor.getString(isPreservedIndex))) {
725                     preservedSettings.add(getQualifiedKeyForSetting(cursor.getString(nameIndex),
726                             settingsUri));
727                 }
728                 cursor.moveToNext();
729             }
730 
731             return preservedSettings;
732         }
733     }
734 
735     /**
736      * Serialize the owner info and other lock settings
737      */
getLockSettings(@serIdInt int userId)738     private byte[] getLockSettings(@UserIdInt int userId) {
739         final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
740         final boolean ownerInfoEnabled = lockPatternUtils.isOwnerInfoEnabled(userId);
741         final String ownerInfo = lockPatternUtils.getOwnerInfo(userId);
742         final boolean lockPatternEnabled = lockPatternUtils.isLockPatternEnabled(userId);
743         final boolean visiblePatternEnabled = lockPatternUtils.isVisiblePatternEnabled(userId);
744         final boolean powerButtonInstantlyLocks =
745                 lockPatternUtils.getPowerButtonInstantlyLocks(userId);
746 
747         ByteArrayOutputStream baos = new ByteArrayOutputStream();
748         DataOutputStream out = new DataOutputStream(baos);
749         try {
750             out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED);
751             out.writeUTF(ownerInfoEnabled ? "1" : "0");
752             if (ownerInfo != null) {
753                 out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO);
754                 out.writeUTF(ownerInfo != null ? ownerInfo : "");
755             }
756             if (lockPatternUtils.isVisiblePatternEverChosen(userId)) {
757                 out.writeUTF(KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED);
758                 out.writeUTF(visiblePatternEnabled ? "1" : "0");
759             }
760             if (lockPatternUtils.isPowerButtonInstantlyLocksEverChosen(userId)) {
761                 out.writeUTF(KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS);
762                 out.writeUTF(powerButtonInstantlyLocks ? "1" : "0");
763             }
764             if (lockPatternUtils.isPinEnhancedPrivacyEverChosen(userId)) {
765                 out.writeUTF(KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY);
766                 out.writeUTF(lockPatternUtils.isPinEnhancedPrivacyEnabled(userId) ? "1" : "0");
767             }
768             // End marker
769             out.writeUTF("");
770             out.flush();
771         } catch (IOException ioe) {
772         }
773         return baos.toByteArray();
774     }
775 
restoreSettings( BackupDataInput data, Uri contentUri, Set<String> movedToGlobal, Set<String> movedToSecure, Set<String> movedToSystem, int blockedSettingsArrayId, Set<String> dynamicBlockList, Set<String> settingsToPreserve)776     private void restoreSettings(
777             BackupDataInput data,
778             Uri contentUri,
779             Set<String> movedToGlobal,
780             Set<String> movedToSecure,
781             Set<String> movedToSystem,
782             int blockedSettingsArrayId,
783             Set<String> dynamicBlockList,
784             Set<String> settingsToPreserve) {
785         byte[] settings = new byte[data.getDataSize()];
786         try {
787             data.readEntityData(settings, 0, settings.length);
788         } catch (IOException ioe) {
789             Log.e(TAG, "Couldn't read entity data");
790             return;
791         }
792         restoreSettings(
793                 settings,
794                 settings.length,
795                 contentUri,
796                 movedToGlobal,
797                 movedToSecure,
798                 movedToSystem,
799                 blockedSettingsArrayId,
800                 dynamicBlockList,
801                 settingsToPreserve);
802     }
803 
restoreSettings( byte[] settings, int bytes, Uri contentUri, Set<String> movedToGlobal, Set<String> movedToSecure, Set<String> movedToSystem, int blockedSettingsArrayId, Set<String> dynamicBlockList, Set<String> settingsToPreserve)804     private void restoreSettings(
805             byte[] settings,
806             int bytes,
807             Uri contentUri,
808             Set<String> movedToGlobal,
809             Set<String> movedToSecure,
810             Set<String> movedToSystem,
811             int blockedSettingsArrayId,
812             Set<String> dynamicBlockList,
813             Set<String> settingsToPreserve) {
814         restoreSettings(
815                 settings,
816                 0,
817                 bytes,
818                 contentUri,
819                 movedToGlobal,
820                 movedToSecure,
821                 movedToSystem,
822                 blockedSettingsArrayId,
823                 dynamicBlockList,
824                 settingsToPreserve);
825     }
826 
827     @VisibleForTesting
restoreSettings( byte[] settings, int pos, int bytes, Uri contentUri, Set<String> movedToGlobal, Set<String> movedToSecure, Set<String> movedToSystem, int blockedSettingsArrayId, Set<String> dynamicBlockList, Set<String> settingsToPreserve)828     void restoreSettings(
829             byte[] settings,
830             int pos,
831             int bytes,
832             Uri contentUri,
833             Set<String> movedToGlobal,
834             Set<String> movedToSecure,
835             Set<String> movedToSystem,
836             int blockedSettingsArrayId,
837             Set<String> dynamicBlockList,
838             Set<String> settingsToPreserve) {
839         if (DEBUG) {
840             Log.i(TAG, "restoreSettings: " + contentUri);
841         }
842 
843         SettingsBackupWhitelist whitelist = getBackupWhitelist(contentUri);
844 
845         // Restore only the white list data.
846         final ArrayMap<String, String> cachedEntries = new ArrayMap<>();
847         ContentValues contentValues = new ContentValues(2);
848         SettingsHelper settingsHelper = mSettingsHelper;
849         ContentResolver cr = getContentResolver();
850 
851         Set<String> blockedSettings = getBlockedSettings(blockedSettingsArrayId);
852 
853         for (String key : whitelist.mSettingsWhitelist) {
854             boolean isBlockedBySystem = blockedSettings != null && blockedSettings.contains(key);
855             if (isBlockedBySystem || isBlockedByDynamicList(dynamicBlockList, contentUri,  key)) {
856                 Log.i(
857                         TAG,
858                         "Key "
859                                 + key
860                                 + " removed from restore by "
861                                 + (isBlockedBySystem ? "system" : "dynamic")
862                                 + " block list");
863                 continue;
864             }
865 
866             // Filter out Settings.Secure.NAVIGATION_MODE from modified preserve settings.
867             // Let it take part in restore process. See also b/244532342.
868             boolean isSettingPreserved = settingsToPreserve.contains(
869                     getQualifiedKeyForSetting(key, contentUri));
870             if (isSettingPreserved && !Settings.Secure.NAVIGATION_MODE.equals(key)) {
871                 Log.i(TAG, "Skipping restore for setting " + key + " as it is marked as "
872                         + "preserved");
873                 continue;
874             }
875 
876             if (LargeScreenSettings.doNotRestoreIfLargeScreenSetting(key, getBaseContext())) {
877                 Log.i(TAG, "Skipping restore for setting " + key + " as the target device "
878                         + "is a large screen (i.e tablet or foldable in unfolded state)");
879                 continue;
880             }
881 
882             String value = null;
883             boolean hasValueToRestore = false;
884             if (cachedEntries.indexOfKey(key) >= 0) {
885                 value = cachedEntries.remove(key);
886                 hasValueToRestore = true;
887             } else {
888                 // If the value not cached, let us look it up.
889                 while (pos < bytes) {
890                     int length = readInt(settings, pos);
891                     pos += INTEGER_BYTE_COUNT;
892                     String dataKey = length >= 0 ? new String(settings, pos, length) : null;
893                     pos += length;
894                     length = readInt(settings, pos);
895                     pos += INTEGER_BYTE_COUNT;
896                     String dataValue = null;
897                     if (length >= 0) {
898                         dataValue = new String(settings, pos, length);
899                         pos += length;
900                     }
901                     if (key.equals(dataKey)) {
902                         value = dataValue;
903                         hasValueToRestore = true;
904                         break;
905                     }
906                     cachedEntries.put(dataKey, dataValue);
907                 }
908             }
909 
910             if (!hasValueToRestore) {
911                 continue;
912             }
913 
914             // only restore the settings that have valid values
915             if (!isValidSettingValue(key, value, whitelist.mSettingsValidators)) {
916                 Log.w(TAG, "Attempted restore of " + key + " setting, but its value didn't pass"
917                         + " validation, value: " + value);
918                 continue;
919             }
920 
921             final Uri destination;
922             if (movedToGlobal != null && movedToGlobal.contains(key)) {
923                 destination = Settings.Global.CONTENT_URI;
924             } else if (movedToSecure != null && movedToSecure.contains(key)) {
925                 destination = Settings.Secure.CONTENT_URI;
926             } else if (movedToSystem != null && movedToSystem.contains(key)) {
927                 destination = Settings.System.CONTENT_URI;
928             } else {
929                 destination = contentUri;
930             }
931 
932             // Value is written to NAVIGATION_MODE_RESTORE to mark navigation mode
933             // has been set before on source device.
934             // See also: b/244532342.
935             if (Settings.Secure.NAVIGATION_MODE.equals(key)) {
936                 contentValues.clear();
937                 contentValues.put(Settings.NameValueTable.NAME,
938                         Settings.Secure.NAVIGATION_MODE_RESTORE);
939                 contentValues.put(Settings.NameValueTable.VALUE, value);
940                 cr.insert(destination, contentValues);
941                 // Avoid restore original setting if it has been preserved.
942                 if (isSettingPreserved) {
943                     Log.i(TAG, "Skipping restore for setting navigation_mode "
944                         + "as it is marked as preserved");
945                     continue;
946                 }
947             }
948 
949             if (Settings.System.FONT_SCALE.equals(key)) {
950                 // If the current value is different from the default it means that it's been
951                 // already changed for a11y reason. In that case we don't need to restore
952                 // the new value.
953                 final float currentValue = Settings.System.getFloat(cr, Settings.System.FONT_SCALE,
954                         mDefaultFontScale);
955                 if (currentValue != mDefaultFontScale) {
956                     Log.d(TAG, "Font scale not restored because changed for a11y reason.");
957                     continue;
958                 }
959                 final String toRestore = value;
960                 value = findClosestAllowedFontScale(value, mAvailableFontScales);
961                 Log.d(TAG, "Restored font scale from: " + toRestore + " to " + value);
962             }
963 
964 
965             settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
966                     mRestoredFromSdkInt);
967 
968             Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
969         }
970     }
971 
972 
973     @VisibleForTesting
findClosestAllowedFontScale(@onNull String requestedFontScale, @NonNull String[] availableFontScales)974     static String findClosestAllowedFontScale(@NonNull String requestedFontScale,
975             @NonNull String[] availableFontScales) {
976         if (Flags.configurableFontScaleDefault()) {
977             final float requestedValue = Float.parseFloat(requestedFontScale);
978             // Whatever is the requested value, we search the closest allowed value which is
979             // equals or larger. Note that if the requested value is the previous default,
980             // and this is still available, the value will be preserved.
981             float candidate = 0.0f;
982             boolean fontScaleFound = false;
983             for (int i = 0; !fontScaleFound && i < availableFontScales.length; i++) {
984                 final float fontScale = Float.parseFloat(availableFontScales[i]);
985                 if (fontScale >= requestedValue) {
986                     candidate = fontScale;
987                     fontScaleFound = true;
988                 }
989             }
990             // If the current value is greater than all the allowed ones, we return the
991             // largest possible.
992             return fontScaleFound ? String.valueOf(candidate) : String.valueOf(
993                     availableFontScales[availableFontScales.length - 1]);
994         }
995         return requestedFontScale;
996     }
997 
998     @VisibleForTesting
getBackupWhitelist(Uri contentUri)999     SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) {
1000         // Figure out the white list and redirects to the global table.  We restore anything
1001         // in either the backup allowlist or the legacy-restore allowlist for this table.
1002         String[] whitelist;
1003         Map<String, Validator> validators = null;
1004         if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
1005             whitelist = ArrayUtils.concat(String.class, SecureSettings.SETTINGS_TO_BACKUP,
1006                     Settings.Secure.LEGACY_RESTORE_SETTINGS,
1007                     DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
1008             validators = SecureSettingsValidators.VALIDATORS;
1009         } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
1010             whitelist = ArrayUtils.concat(String.class, SystemSettings.SETTINGS_TO_BACKUP,
1011                     Settings.System.LEGACY_RESTORE_SETTINGS);
1012             validators = SystemSettingsValidators.VALIDATORS;
1013         } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
1014             whitelist = ArrayUtils.concat(String.class, GlobalSettings.SETTINGS_TO_BACKUP,
1015                     Settings.Global.LEGACY_RESTORE_SETTINGS);
1016             validators = GlobalSettingsValidators.VALIDATORS;
1017         } else {
1018             throw new IllegalArgumentException("Unknown URI: " + contentUri);
1019         }
1020 
1021         return new SettingsBackupWhitelist(whitelist, validators);
1022     }
1023 
isBlockedByDynamicList(Set<String> dynamicBlockList, Uri areaUri, String key)1024     private boolean isBlockedByDynamicList(Set<String> dynamicBlockList, Uri areaUri, String key) {
1025         String contentKey = Uri.withAppendedPath(areaUri, key).toString();
1026         return dynamicBlockList.contains(contentKey);
1027     }
1028 
1029     @VisibleForTesting
getQualifiedKeyForSetting(String settingName, Uri settingUri)1030     static String getQualifiedKeyForSetting(String settingName, Uri settingUri) {
1031         return Uri.withAppendedPath(settingUri, settingName).toString();
1032     }
1033 
1034     // There may be other sources of blocked settings, so I'm separating out this
1035     // code to make it easy to modify in the future.
1036     @VisibleForTesting
getBlockedSettings(int blockedSettingsArrayId)1037     protected Set<String> getBlockedSettings(int blockedSettingsArrayId) {
1038         String[] blockedSettings = getResources().getStringArray(blockedSettingsArrayId);
1039         return new HashSet<>(Arrays.asList(blockedSettings));
1040     }
1041 
isValidSettingValue(String key, String value, Map<String, Validator> validators)1042     private boolean isValidSettingValue(String key, String value,
1043             Map<String, Validator> validators) {
1044         if (key == null || validators == null) {
1045             return false;
1046         }
1047         Validator validator = validators.get(key);
1048         return (validator != null) && validator.validate(value);
1049     }
1050 
1051     /**
1052      * Restores the owner info enabled and other settings in LockSettings.
1053      *
1054      * @param buffer
1055      * @param nBytes
1056      */
restoreLockSettings(@serIdInt int userId, byte[] buffer, int nBytes)1057     private void restoreLockSettings(@UserIdInt int userId, byte[] buffer, int nBytes) {
1058         final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
1059 
1060         ByteArrayInputStream bais = new ByteArrayInputStream(buffer, 0, nBytes);
1061         DataInputStream in = new DataInputStream(bais);
1062         try {
1063             String key;
1064             // Read until empty string marker
1065             while ((key = in.readUTF()).length() > 0) {
1066                 final String value = in.readUTF();
1067                 if (DEBUG_BACKUP) {
1068                     Log.v(TAG, "Restoring lock_settings " + key + " = " + value);
1069                 }
1070                 switch (key) {
1071                     case KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED:
1072                         lockPatternUtils.setOwnerInfoEnabled("1".equals(value), userId);
1073                         break;
1074                     case KEY_LOCK_SETTINGS_OWNER_INFO:
1075                         lockPatternUtils.setOwnerInfo(value, userId);
1076                         break;
1077                     case KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED:
1078                         lockPatternUtils.setVisiblePatternEnabled("1".equals(value), userId);
1079                         break;
1080                     case KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS:
1081                         lockPatternUtils.setPowerButtonInstantlyLocks("1".equals(value), userId);
1082                         break;
1083                     case KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY:
1084                         lockPatternUtils.setPinEnhancedPrivacyEnabled("1".equals(value), userId);
1085                         break;
1086                 }
1087             }
1088             in.close();
1089         } catch (IOException ioe) {
1090         }
1091     }
1092 
restoreLockSettings(@serIdInt int userId, BackupDataInput data)1093     private void restoreLockSettings(@UserIdInt int userId, BackupDataInput data) {
1094         final byte[] settings = new byte[data.getDataSize()];
1095         try {
1096             data.readEntityData(settings, 0, settings.length);
1097         } catch (IOException ioe) {
1098             Log.e(TAG, "Couldn't read entity data");
1099             return;
1100         }
1101         restoreLockSettings(userId, settings, settings.length);
1102     }
1103 
1104     /**
1105      * Given a cursor and a set of keys, extract the required keys and
1106      * values and write them to a byte array.
1107      *
1108      * @param cursor A cursor with settings data.
1109      * @param settings The settings to extract.
1110      * @return The byte array of extracted values.
1111      */
extractRelevantValues(Cursor cursor, String[] settings)1112     private byte[] extractRelevantValues(Cursor cursor, String[] settings) {
1113         if (!cursor.moveToFirst()) {
1114             Log.e(TAG, "Couldn't read from the cursor");
1115             return new byte[0];
1116         }
1117 
1118         final int nameColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.NAME);
1119         final int valueColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.VALUE);
1120 
1121         // Obtain the relevant data in a temporary array.
1122         int totalSize = 0;
1123         int backedUpSettingIndex = 0;
1124         final int settingsCount = settings.length;
1125         final byte[][] values = new byte[settingsCount * 2][]; // keys and values
1126         final ArrayMap<String, String> cachedEntries = new ArrayMap<>();
1127         for (int i = 0; i < settingsCount; i++) {
1128             final String key = settings[i];
1129 
1130             // If the value not cached, let us look it up.
1131             String value = null;
1132             boolean hasValueToBackup = false;
1133             if (cachedEntries.indexOfKey(key) >= 0) {
1134                 value = cachedEntries.remove(key);
1135                 hasValueToBackup = true;
1136             } else {
1137                 while (!cursor.isAfterLast()) {
1138                     final String cursorKey = cursor.getString(nameColumnIndex);
1139                     final String cursorValue = cursor.getString(valueColumnIndex);
1140                     cursor.moveToNext();
1141                     if (key.equals(cursorKey)) {
1142                         value = cursorValue;
1143                         hasValueToBackup = true;
1144                         break;
1145                     }
1146                     cachedEntries.put(cursorKey, cursorValue);
1147                 }
1148             }
1149 
1150             if (!hasValueToBackup) {
1151                 continue;
1152             }
1153 
1154             // Intercept the keys and see if they need special handling
1155             value = mSettingsHelper.onBackupValue(key, value);
1156 
1157             // Write the key and value in the intermediary array.
1158             final byte[] keyBytes = key.getBytes();
1159             totalSize += INTEGER_BYTE_COUNT + keyBytes.length;
1160             values[backedUpSettingIndex * 2] = keyBytes;
1161 
1162             final byte[] valueBytes = (value != null) ? value.getBytes() : NULL_VALUE;
1163             totalSize += INTEGER_BYTE_COUNT + valueBytes.length;
1164             values[backedUpSettingIndex * 2 + 1] = valueBytes;
1165 
1166             backedUpSettingIndex++;
1167 
1168             if (DEBUG) {
1169                 Log.d(TAG, "Backed up setting: " + key + "=" + value);
1170             }
1171         }
1172 
1173         // Aggregate the result.
1174         byte[] result = new byte[totalSize];
1175         int pos = 0;
1176         final int keyValuePairCount = backedUpSettingIndex * 2;
1177         for (int i = 0; i < keyValuePairCount; i++) {
1178             final byte[] value = values[i];
1179             if (value != NULL_VALUE) {
1180                 pos = writeInt(result, pos, value.length);
1181                 pos = writeBytes(result, pos, value);
1182             } else {
1183                 pos = writeInt(result, pos, NULL_SIZE);
1184             }
1185         }
1186         return result;
1187     }
1188 
restoreSupplicantWifiConfigData(byte[] supplicant_bytes, byte[] ipconfig_bytes)1189     private void restoreSupplicantWifiConfigData(byte[] supplicant_bytes, byte[] ipconfig_bytes) {
1190         if (DEBUG_BACKUP) {
1191             Log.v(TAG, "Applying restored supplicant wifi data");
1192         }
1193         mWifiManager.restoreSupplicantBackupData(supplicant_bytes, ipconfig_bytes);
1194     }
1195 
getSoftAPConfiguration()1196     private byte[] getSoftAPConfiguration() {
1197         return mWifiManager.retrieveSoftApBackupData();
1198     }
1199 
restoreSoftApConfiguration(byte[] data)1200     private void restoreSoftApConfiguration(byte[] data) {
1201         SoftApConfiguration configInCloud = mWifiManager.restoreSoftApBackupData(data);
1202         if (configInCloud != null) {
1203             if (DEBUG) Log.d(TAG, "Successfully unMarshaled SoftApConfiguration ");
1204             // Depending on device hardware, we may need to notify the user of a setting change
1205             SoftApConfiguration storedConfig = mWifiManager.getSoftApConfiguration();
1206 
1207             if (isConfigurationHasChanged(configInCloud, storedConfig)) {
1208                 Log.d(TAG, "restored ap configuration requires a conversion: "
1209                         + ", configInCloud is " + configInCloud + " but storedConfig is "
1210                         + storedConfig);
1211             }
1212         }
1213     }
1214 
isConfigurationHasChanged(SoftApConfiguration configInCloud, SoftApConfiguration storedConfig)1215     private boolean isConfigurationHasChanged(SoftApConfiguration configInCloud,
1216             SoftApConfiguration storedConfig) {
1217         // Check if the cloud configuration was modified when restored to the device.
1218         // All elements of the configuration are compared except:
1219         // 1. Persistent randomized MAC address (which is per device)
1220         // 2. The flag indicating whether the configuration is "user modified"
1221         return !(Objects.equals(configInCloud.getWifiSsid(), storedConfig.getWifiSsid())
1222                 && Objects.equals(configInCloud.getBssid(), storedConfig.getBssid())
1223                 && Objects.equals(configInCloud.getPassphrase(), storedConfig.getPassphrase())
1224                 && configInCloud.isHiddenSsid() == storedConfig.isHiddenSsid()
1225                 && configInCloud.getChannels().toString().equals(
1226                         storedConfig.getChannels().toString())
1227                 && configInCloud.getSecurityType() == storedConfig.getSecurityType()
1228                 && configInCloud.getMaxNumberOfClients() == storedConfig.getMaxNumberOfClients()
1229                 && configInCloud.isAutoShutdownEnabled() == storedConfig.isAutoShutdownEnabled()
1230                 && configInCloud.getShutdownTimeoutMillis()
1231                         == storedConfig.getShutdownTimeoutMillis()
1232                 && configInCloud.isClientControlByUserEnabled()
1233                         == storedConfig.isClientControlByUserEnabled()
1234                 && Objects.equals(configInCloud.getBlockedClientList(),
1235                         storedConfig.getBlockedClientList())
1236                 && Objects.equals(configInCloud.getAllowedClientList(),
1237                         storedConfig.getAllowedClientList())
1238                 && configInCloud.getMacRandomizationSetting()
1239                         == storedConfig.getMacRandomizationSetting()
1240                 && configInCloud.isBridgedModeOpportunisticShutdownEnabled()
1241                         == storedConfig.isBridgedModeOpportunisticShutdownEnabled()
1242                 && configInCloud.isIeee80211axEnabled() == storedConfig.isIeee80211axEnabled()
1243                 && configInCloud.isIeee80211beEnabled() == storedConfig.isIeee80211beEnabled()
1244                 && configInCloud.getBridgedModeOpportunisticShutdownTimeoutMillis()
1245                         == storedConfig.getBridgedModeOpportunisticShutdownTimeoutMillis()
1246                 && Objects.equals(configInCloud.getVendorElements(),
1247                         storedConfig.getVendorElements())
1248                 && Arrays.equals(configInCloud.getAllowedAcsChannels(
1249                         SoftApConfiguration.BAND_2GHZ),
1250                         storedConfig.getAllowedAcsChannels(SoftApConfiguration.BAND_2GHZ))
1251                 && Arrays.equals(configInCloud.getAllowedAcsChannels(
1252                         SoftApConfiguration.BAND_5GHZ),
1253                         storedConfig.getAllowedAcsChannels(SoftApConfiguration.BAND_5GHZ))
1254                 && Arrays.equals(configInCloud.getAllowedAcsChannels(
1255                         SoftApConfiguration.BAND_6GHZ),
1256                         storedConfig.getAllowedAcsChannels(SoftApConfiguration.BAND_6GHZ))
1257                 && configInCloud.getMaxChannelBandwidth() == storedConfig.getMaxChannelBandwidth()
1258                         );
1259     }
1260 
getNetworkPolicies()1261     private byte[] getNetworkPolicies() {
1262         NetworkPolicyManager networkPolicyManager =
1263                 (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE);
1264         NetworkPolicy[] policies = networkPolicyManager.getNetworkPolicies();
1265         ByteArrayOutputStream baos = new ByteArrayOutputStream();
1266         if (policies != null && policies.length != 0) {
1267             DataOutputStream out = new DataOutputStream(baos);
1268             try {
1269                 out.writeInt(NETWORK_POLICIES_BACKUP_VERSION);
1270                 out.writeInt(policies.length);
1271                 for (NetworkPolicy policy : policies) {
1272                     // We purposefully only backup policies that the user has
1273                     // defined; any inferred policies might include
1274                     // carrier-protected data that we can't export.
1275                     if (policy != null && !policy.inferred) {
1276                         byte[] marshaledPolicy = policy.getBytesForBackup();
1277                         out.writeByte(BackupUtils.NOT_NULL);
1278                         out.writeInt(marshaledPolicy.length);
1279                         out.write(marshaledPolicy);
1280                     } else {
1281                         out.writeByte(BackupUtils.NULL);
1282                     }
1283                 }
1284             } catch (IOException ioe) {
1285                 Log.e(TAG, "Failed to convert NetworkPolicies to byte array " + ioe.getMessage());
1286                 baos.reset();
1287             }
1288         }
1289         return baos.toByteArray();
1290     }
1291 
getNewWifiConfigData()1292     private byte[] getNewWifiConfigData() {
1293         return mWifiManager.retrieveBackupData();
1294     }
1295 
restoreNewWifiConfigData(byte[] bytes)1296     private void restoreNewWifiConfigData(byte[] bytes) {
1297         if (DEBUG_BACKUP) {
1298             Log.v(TAG, "Applying restored wifi data");
1299         }
1300         mWifiManager.restoreBackupData(bytes);
1301     }
1302 
restoreNetworkPolicies(byte[] data)1303     private void restoreNetworkPolicies(byte[] data) {
1304         NetworkPolicyManager networkPolicyManager =
1305                 (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE);
1306         if (data != null && data.length != 0) {
1307             DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
1308             try {
1309                 int version = in.readInt();
1310                 if (version < 1 || version > NETWORK_POLICIES_BACKUP_VERSION) {
1311                     throw new BackupUtils.BadVersionException(
1312                             "Unknown Backup Serialization Version");
1313                 }
1314                 int length = in.readInt();
1315                 NetworkPolicy[] policies = new NetworkPolicy[length];
1316                 for (int i = 0; i < length; i++) {
1317                     byte isNull = in.readByte();
1318                     if (isNull == BackupUtils.NULL) continue;
1319                     int byteLength = in.readInt();
1320                     byte[] policyData = new byte[byteLength];
1321                     in.read(policyData, 0, byteLength);
1322                     policies[i] = NetworkPolicy.getNetworkPolicyFromBackup(
1323                             new DataInputStream(new ByteArrayInputStream(policyData)));
1324                 }
1325                 // Only set the policies if there was no error in the restore operation
1326                 networkPolicyManager.setNetworkPolicies(policies);
1327             } catch (NullPointerException | IOException | BackupUtils.BadVersionException
1328                     | DateTimeException e) {
1329                 // NPE can be thrown when trying to instantiate a NetworkPolicy
1330                 Log.e(TAG, "Failed to convert byte array to NetworkPolicies " + e.getMessage());
1331             }
1332         }
1333     }
1334 
1335     @VisibleForTesting
getDeviceSpecificConfiguration()1336     byte[] getDeviceSpecificConfiguration() throws IOException {
1337         try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
1338             writeHeader(os);
1339             os.write(getDeviceSpecificSettings());
1340             return os.toByteArray();
1341         }
1342     }
1343 
1344     @VisibleForTesting
writeHeader(OutputStream os)1345     void writeHeader(OutputStream os) throws IOException {
1346         os.write(toByteArray(DEVICE_SPECIFIC_VERSION));
1347         os.write(toByteArray(Build.MANUFACTURER));
1348         os.write(toByteArray(Build.PRODUCT));
1349     }
1350 
getDeviceSpecificSettings()1351     private byte[] getDeviceSpecificSettings() {
1352         try (Cursor cursor =
1353                      getContentResolver()
1354                              .query(Settings.Secure.CONTENT_URI, PROJECTION, null, null, null)) {
1355             return extractRelevantValues(
1356                     cursor, DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
1357         }
1358     }
1359 
1360     /**
1361      * Restore the device specific settings.
1362      *
1363      * @param data The byte array holding a backed up version of another devices settings.
1364      * @param blockedSettingsArrayId The string array resource holding the settings not to restore.
1365      * @param dynamicBlocklist The dynamic list of settings not to restore fed into this agent.
1366      * @return true if the restore succeeded, false if it was stopped.
1367      */
1368     @VisibleForTesting
restoreDeviceSpecificConfig(byte[] data, int blockedSettingsArrayId, Set<String> dynamicBlocklist, Set<String> preservedSettings)1369     boolean restoreDeviceSpecificConfig(byte[] data, int blockedSettingsArrayId,
1370             Set<String> dynamicBlocklist, Set<String> preservedSettings) {
1371         // We're using an AtomicInteger to wrap the position int and allow called methods to
1372         // modify it.
1373         AtomicInteger pos = new AtomicInteger(0);
1374         if (!isSourceAcceptable(data, pos)) {
1375             return false;
1376         }
1377 
1378         Integer originalDensity = getPreviousDensity();
1379 
1380         int dataStart = pos.get();
1381         restoreSettings(
1382                 data,
1383                 dataStart,
1384                 data.length,
1385                 Settings.Secure.CONTENT_URI,
1386                 null,
1387                 null,
1388                 null,
1389                 blockedSettingsArrayId,
1390                 dynamicBlocklist,
1391                 preservedSettings);
1392 
1393         updateWindowManagerIfNeeded(originalDensity);
1394 
1395         return true;
1396     }
1397 
getSimSpecificSettingsData()1398     private byte[] getSimSpecificSettingsData() {
1399         byte[] simSpecificData = new byte[0];
1400         PackageManager packageManager = getBaseContext().getPackageManager();
1401         if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
1402             SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
1403             simSpecificData = subManager.getAllSimSpecificSettingsForBackup();
1404             Log.i(TAG, "sim specific data of length + " + simSpecificData.length
1405                 + " successfully retrieved");
1406         }
1407 
1408         return simSpecificData;
1409     }
1410 
restoreSimSpecificSettings(byte[] data)1411     private void restoreSimSpecificSettings(byte[] data) {
1412         PackageManager packageManager = getBaseContext().getPackageManager();
1413         boolean hasTelephony = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
1414         if (hasTelephony) {
1415             SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
1416             subManager.restoreAllSimSpecificSettingsFromBackup(data);
1417         }
1418     }
1419 
1420     private static final class Mutable<E> {
1421         public volatile E value;
1422 
Mutable()1423         Mutable() {
1424             value = null;
1425         }
1426     }
1427 
getWifiSettingsBackupData()1428     private byte[] getWifiSettingsBackupData() {
1429         final CountDownLatch latch = new CountDownLatch(1);
1430         final Mutable<byte[]> backupWifiData = new Mutable<byte[]>();
1431 
1432         try {
1433             mWifiManager.retrieveWifiBackupData(getBaseContext().getMainExecutor(),
1434                     new Consumer<byte[]>() {
1435                         @Override
1436                         public void accept(byte[] value) {
1437                             backupWifiData.value = value;
1438                             latch.countDown();
1439                         }
1440                     });
1441             // cts requires B&R with 10 seconds
1442             if (latch.await(10, TimeUnit.SECONDS) && backupWifiData.value != null) {
1443                 return backupWifiData.value;
1444             }
1445         } catch (InterruptedException ie) {
1446             Log.e(TAG, "fail to retrieveWifiBackupData, " + ie);
1447         }
1448         Log.e(TAG, "fail to retrieveWifiBackupData");
1449         return new byte[0];
1450     }
1451 
restoreWifiData(byte[] data)1452     private void restoreWifiData(byte[] data) {
1453         if (DEBUG_BACKUP) {
1454             Log.v(TAG, "Applying restored all wifi data");
1455         }
1456         mWifiManager.restoreWifiBackupData(data);
1457     }
1458 
updateWindowManagerIfNeeded(Integer previousDensity)1459     private void updateWindowManagerIfNeeded(Integer previousDensity) {
1460         int newDensity;
1461         try {
1462             newDensity = getForcedDensity();
1463         } catch (Settings.SettingNotFoundException e) {
1464             // If there's not density setting we can't perform a change.
1465             return;
1466         }
1467 
1468         if (previousDensity == null || previousDensity != newDensity) {
1469             // From nothing to something is a change.
1470             DisplayDensityConfiguration.setForcedDisplayDensity(
1471                     Display.DEFAULT_DISPLAY, newDensity);
1472         }
1473     }
1474 
getPreviousDensity()1475     private Integer getPreviousDensity() {
1476         try {
1477             return getForcedDensity();
1478         } catch (Settings.SettingNotFoundException e) {
1479             return null;
1480         }
1481     }
1482 
getForcedDensity()1483     private int getForcedDensity() throws Settings.SettingNotFoundException {
1484         return Settings.Secure.getInt(getContentResolver(), Settings.Secure.DISPLAY_DENSITY_FORCED);
1485     }
1486 
1487     @VisibleForTesting
isSourceAcceptable(byte[] data, AtomicInteger pos)1488     boolean isSourceAcceptable(byte[] data, AtomicInteger pos) {
1489         int version = readInt(data, pos);
1490         if (version > DEVICE_SPECIFIC_VERSION) {
1491             Slog.w(TAG, "Unable to restore device specific information; Backup is too new");
1492             return false;
1493         }
1494 
1495         String sourceManufacturer = readString(data, pos);
1496         if (!Objects.equals(Build.MANUFACTURER, sourceManufacturer)) {
1497             Log.w(
1498                     TAG,
1499                     "Unable to restore device specific information; Manufacturer mismatch "
1500                             + "(\'"
1501                             + Build.MANUFACTURER
1502                             + "\' and \'"
1503                             + sourceManufacturer
1504                             + "\')");
1505             return false;
1506         }
1507 
1508         String sourceProduct = readString(data, pos);
1509         if (!Objects.equals(Build.PRODUCT, sourceProduct)) {
1510             Log.w(
1511                     TAG,
1512                     "Unable to restore device specific information; Product mismatch (\'"
1513                             + Build.PRODUCT
1514                             + "\' and \'"
1515                             + sourceProduct
1516                             + "\')");
1517             return false;
1518         }
1519 
1520         return true;
1521     }
1522 
1523     @VisibleForTesting
toByteArray(String value)1524     static byte[] toByteArray(String value) {
1525         if (value == null) {
1526             return toByteArray(NULL_SIZE);
1527         }
1528 
1529         byte[] stringBytes = value.getBytes();
1530         byte[] sizeAndString = new byte[stringBytes.length + INTEGER_BYTE_COUNT];
1531         writeInt(sizeAndString, 0, stringBytes.length);
1532         writeBytes(sizeAndString, INTEGER_BYTE_COUNT, stringBytes);
1533         return sizeAndString;
1534     }
1535 
1536     @VisibleForTesting
toByteArray(int value)1537     static byte[] toByteArray(int value) {
1538         byte[] result = new byte[INTEGER_BYTE_COUNT];
1539         writeInt(result, 0, value);
1540         return result;
1541     }
1542 
readString(byte[] data, AtomicInteger pos)1543     private String readString(byte[] data, AtomicInteger pos) {
1544         int byteCount = readInt(data, pos);
1545         if (byteCount == NULL_SIZE) {
1546             return null;
1547         }
1548 
1549         int stringStart = pos.getAndAdd(byteCount);
1550         return new String(data, stringStart, byteCount);
1551     }
1552 
1553     /**
1554      * Write an int in BigEndian into the byte array.
1555      * @param out byte array
1556      * @param pos current pos in array
1557      * @param value integer to write
1558      * @return the index after adding the size of an int (4) in bytes.
1559      */
writeInt(byte[] out, int pos, int value)1560     private static int writeInt(byte[] out, int pos, int value) {
1561         out[pos + 0] = (byte) ((value >> 24) & 0xFF);
1562         out[pos + 1] = (byte) ((value >> 16) & 0xFF);
1563         out[pos + 2] = (byte) ((value >>  8) & 0xFF);
1564         out[pos + 3] = (byte) ((value >>  0) & 0xFF);
1565         return pos + INTEGER_BYTE_COUNT;
1566     }
1567 
writeBytes(byte[] out, int pos, byte[] value)1568     private static int writeBytes(byte[] out, int pos, byte[] value) {
1569         System.arraycopy(value, 0, out, pos, value.length);
1570         return pos + value.length;
1571     }
1572 
readInt(byte[] in, AtomicInteger pos)1573     private int readInt(byte[] in, AtomicInteger pos) {
1574         return readInt(in, pos.getAndAdd(INTEGER_BYTE_COUNT));
1575     }
1576 
readInt(byte[] in, int pos)1577     private int readInt(byte[] in, int pos) {
1578         int result = ((in[pos] & 0xFF) << 24)
1579                 | ((in[pos + 1] & 0xFF) << 16)
1580                 | ((in[pos + 2] & 0xFF) <<  8)
1581                 | ((in[pos + 3] & 0xFF) <<  0);
1582         return result;
1583     }
1584 
1585     /**
1586      * Store the allowlist of settings to be backed up and validators for them.
1587      */
1588     @VisibleForTesting
1589     static class SettingsBackupWhitelist {
1590         final String[] mSettingsWhitelist;
1591         final Map<String, Validator> mSettingsValidators;
1592 
1593 
SettingsBackupWhitelist(String[] settingsWhitelist, Map<String, Validator> settingsValidators)1594         SettingsBackupWhitelist(String[] settingsWhitelist,
1595                 Map<String, Validator> settingsValidators) {
1596             mSettingsWhitelist = settingsWhitelist;
1597             mSettingsValidators = settingsValidators;
1598         }
1599     }
1600 }
1601