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