Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a571a583 authored by Svetoslav Ganov's avatar Svetoslav Ganov
Browse files

Settings are restored in alphabetical order rather in order of dependency.

Settings were restore in alphabetical order and capturing dependency
among them required keys to be chosen in such a way that after sorting
they apprear in dependency order. Now settings are exported and restored
in the order they are declared in the arrays of settings to backup.
Hence, the order in this array will capture the dependency order.

bug:5343351

Change-Id: I93a40bcdd194943cd6f85aa18f1557d546e38274
parent aff24a52
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -1882,6 +1882,11 @@ public final class Settings {
        /**
         * Settings to backup. This is here so that it's in the same place as the settings
         * keys and easy to update.
         *
         * NOTE: Settings are backed up and restored in the order they appear
         *       in this array. If you have one setting depending on another,
         *       make sure that they are ordered appropriately.
         *
         * @hide
         */
        public static final String[] SETTINGS_TO_BACKUP = {
@@ -4048,6 +4053,12 @@ public final class Settings {
        public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";

        /**
         * This are the settings to be backed up.
         *
         * NOTE: Settings are backed up and restored in the order they appear
         *       in this array. If you have one setting depending on another,
         *       make sure that they are ordered appropriately.
         *
         * @hide
         */
        public static final String[] SETTINGS_TO_BACKUP = {
@@ -4056,11 +4067,11 @@ public final class Settings {
            PARENTAL_CONTROL_ENABLED,
            PARENTAL_CONTROL_REDIRECT_URL,
            USB_MASS_STORAGE_ENABLED,
            ACCESSIBILITY_ENABLED,
            ACCESSIBILITY_SCRIPT_INJECTION,
            BACKUP_AUTO_RESTORE,
            ENABLED_ACCESSIBILITY_SERVICES,
            TOUCH_EXPLORATION_ENABLED,
            ACCESSIBILITY_ENABLED,
            TTS_USE_DEFAULTS,
            TTS_DEFAULT_RATE,
            TTS_DEFAULT_PITCH,
+125 −121
Original line number Diff line number Diff line
@@ -16,6 +16,21 @@

package com.android.providers.settings;

import android.app.backup.BackupAgentHelper;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.FullBackupDataOutput;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.provider.Settings;
import android.util.Log;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -27,28 +42,13 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.CRC32;

import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupAgentHelper;
import android.app.backup.FullBackupDataOutput;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;

/**
 * Performs backup and restore of the System and Secure settings.
 * List of settings that are backed up are stored in the Settings.java file
@@ -79,8 +79,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
    // Versioning of the 'full backup' format
    private static final int FULL_BACKUP_VERSION = 1;

    private static String[] sortedSystemKeys = null;
    private static String[] sortedSecureKeys = null;
    private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;

    private static final byte[] EMPTY_DATA = new byte[0];

@@ -112,6 +111,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
    private WifiManager mWfm;
    private static String mWifiConfigFile;

    @Override
    public void onCreate() {
        if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");

@@ -348,26 +348,17 @@ public class SettingsBackupAgent extends BackupAgentHelper {
    }

    private byte[] getSystemSettings() {
        Cursor sortedCursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION,
                null, null, Settings.NameValueTable.NAME);
        // Copy and sort the array
        if (sortedSystemKeys == null) {
            sortedSystemKeys = copyAndSort(Settings.System.SETTINGS_TO_BACKUP);
        }
        byte[] result = extractRelevantValues(sortedCursor, sortedSystemKeys);
        sortedCursor.close();
        Cursor cursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION, null,
                null, null);
        byte[] result = extractRelevantValues(cursor, Settings.System.SETTINGS_TO_BACKUP);
        cursor.close();
        return result;
    }

    private byte[] getSecureSettings() {
        Cursor sortedCursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION,
                null, null, Settings.NameValueTable.NAME);
        // Copy and sort the array
        if (sortedSecureKeys == null) {
            sortedSecureKeys = copyAndSort(Settings.Secure.SETTINGS_TO_BACKUP);
        }
        byte[] result = extractRelevantValues(sortedCursor, sortedSecureKeys);
        sortedCursor.close();
        Cursor cursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION, null,
                null, null);
        byte[] result = extractRelevantValues(cursor, Settings.Secure.SETTINGS_TO_BACKUP);
        return result;
    }

@@ -383,120 +374,133 @@ public class SettingsBackupAgent extends BackupAgentHelper {
    }

    private void restoreSettings(byte[] settings, int bytes, Uri contentUri) {
        if (DEBUG) Log.i(TAG, "restoreSettings: " + contentUri);
        if (DEBUG) {
            Log.i(TAG, "restoreSettings: " + contentUri);
        }

        // Figure out the white list.
        String[] whitelist = null;
        if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
            whitelist = Settings.Secure.SETTINGS_TO_BACKUP;
        } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
            whitelist = Settings.System.SETTINGS_TO_BACKUP;
        } else {
            throw new IllegalArgumentException("Unknown URI: " + contentUri);
        }

        ContentValues cv = new ContentValues(2);
        // Restore only the white list data.
        int pos = 0;
        Map<String, String> cachedEntries = new HashMap<String, String>();
        ContentValues contentValues = new ContentValues(2);
        SettingsHelper settingsHelper = mSettingsHelper;

        final int whiteListSize = whitelist.length;
        for (int i = 0; i < whiteListSize; i++) {
            String key = whitelist[i];
            String value = cachedEntries.remove(key);

            // If the value not cached, let us look it up.
            if (value == null) {
                while (pos < bytes) {
                    int length = readInt(settings, pos);
            pos += 4;
            String settingName = length > 0? new String(settings, pos, length) : null;
                    pos += INTEGER_BYTE_COUNT;
                    String dataKey = length > 0 ? new String(settings, pos, length) : null;
                    pos += length;
                    length = readInt(settings, pos);
            pos += 4;
            String settingValue = length > 0? new String(settings, pos, length) : null;
                    pos += INTEGER_BYTE_COUNT;
                    String dataValue = length > 0 ? new String(settings, pos, length) : null;
                    pos += length;
            if (!TextUtils.isEmpty(settingName) && !TextUtils.isEmpty(settingValue)) {
                //Log.i(TAG, "Restore " + settingName + " = " + settingValue);

                // Only restore settings in our list of known-acceptable data
                if (invalidSavedSetting(whitelist, settingName)) {
                    continue;
                }

                if (mSettingsHelper.restoreValue(settingName, settingValue)) {
                    cv.clear();
                    cv.put(Settings.NameValueTable.NAME, settingName);
                    cv.put(Settings.NameValueTable.VALUE, settingValue);
                    getContentResolver().insert(contentUri, cv);
                }
                    if (key.equals(dataKey)) {
                        value = dataValue;
                        break;
                    }
                    cachedEntries.put(dataKey, dataValue);
                }
            }

    // Returns 'true' if the given setting is one that we refuse to restore
    private boolean invalidSavedSetting(String[] knownNames, String candidate) {
        // no filter? allow everything
        if (knownNames == null) {
            return false;
            if (value == null) {
                continue;
            }

        // whitelisted setting?  allow it
        for (String name : knownNames) {
            if (name.equals(candidate)) {
                return false;
            }
            if (settingsHelper.restoreValue(key, value)) {
                contentValues.clear();
                contentValues.put(Settings.NameValueTable.NAME, key);
                contentValues.put(Settings.NameValueTable.VALUE, value);
                getContentResolver().insert(contentUri, contentValues);
            }

        // refuse everything else
        if (DEBUG) Log.v(TAG, "Ignoring restore datum: " + candidate);
        return true;
            if (DEBUG) {
                Log.d(TAG, "Restored setting: " + key + "=" + value);
            }
        }

    private String[] copyAndSort(String[] keys) {
        String[] sortedKeys = new String[keys.length];
        System.arraycopy(keys, 0, sortedKeys, 0, keys.length);
        Arrays.sort(sortedKeys);
        return sortedKeys;
    }

    /**
     * Given a cursor sorted by key name and a set of keys sorted by name,
     * extract the required keys and values and write them to a byte array.
     * @param sortedCursor
     * @param sortedKeys
     * @return
     * Given a cursor and a set of keys, extract the required keys and
     * values and write them to a byte array.
     *
     * @param cursor A cursor with settings data.
     * @param settings The settings to extract.
     * @return The byte array of extracted values.
     */
    byte[] extractRelevantValues(Cursor sortedCursor, String[] sortedKeys) {
        byte[][] values = new byte[sortedKeys.length * 2][]; // keys and values
        if (!sortedCursor.moveToFirst()) {
    private byte[] extractRelevantValues(Cursor cursor, String[] settings) {
        final int settingsCount = settings.length;
        byte[][] values = new byte[settingsCount * 2][]; // keys and values
        if (!cursor.moveToFirst()) {
            Log.e(TAG, "Couldn't read from the cursor");
            return new byte[0];
        }
        int keyIndex = 0;

        // Obtain the relevant data in a temporary array.
        int totalSize = 0;
        while (!sortedCursor.isAfterLast()) {
            String name = sortedCursor.getString(COLUMN_NAME);
            while (sortedKeys[keyIndex].compareTo(name.toString()) < 0) {
                keyIndex++;
                if (keyIndex == sortedKeys.length) break;
            }
            if (keyIndex < sortedKeys.length && name.equals(sortedKeys[keyIndex])) {
                String value = sortedCursor.getString(COLUMN_VALUE);
                byte[] nameBytes = name.toString().getBytes();
                totalSize += 4 + nameBytes.length;
                values[keyIndex * 2] = nameBytes;
                byte[] valueBytes;
                if (TextUtils.isEmpty(value)) {
                    valueBytes = null;
                    totalSize += 4;
                } else {
                    valueBytes = value.toString().getBytes();
                    totalSize += 4 + valueBytes.length;
                    //Log.i(TAG, "Backing up " + name + " = " + value);
        int backedUpSettingIndex = 0;
        Map<String, String> cachedEntries = new HashMap<String, String>();
        for (int i = 0; i < settingsCount; i++) {
            String key = settings[i];
            String value = cachedEntries.remove(key);

            // If the value not cached, let us look it up.
            if (value == null) {
                while (!cursor.isAfterLast()) {
                    String cursorKey = cursor.getString(COLUMN_NAME);
                    String cursorValue = cursor.getString(COLUMN_VALUE);
                    cursor.moveToNext();
                    if (key.equals(cursorKey)) {
                        value = cursorValue;
                        break;
                    }
                values[keyIndex * 2 + 1] = valueBytes;
                keyIndex++;
                    cachedEntries.put(cursorKey, cursorValue);
                }
            if (keyIndex == sortedKeys.length || !sortedCursor.moveToNext()) {
                break;
            }

            if (value == null) {
                continue;
            }

            // Write the key and value in the intermediary array.
            byte[] keyBytes = key.getBytes();
            totalSize += INTEGER_BYTE_COUNT + keyBytes.length;
            values[backedUpSettingIndex * 2] = keyBytes;

            byte[] valueBytes = value.getBytes();
            totalSize += INTEGER_BYTE_COUNT + valueBytes.length;
            values[backedUpSettingIndex * 2 + 1] = valueBytes;

            backedUpSettingIndex++;

            if (DEBUG) {
                Log.d(TAG, "Backed up setting: " + key + "=" + value);
            }
        }

        // Aggregate the result.
        byte[] result = new byte[totalSize];
        int pos = 0;
        for (int i = 0; i < sortedKeys.length * 2; i++) {
            if (values[i] != null) {
        final int keyValuePairCount = backedUpSettingIndex * 2;
        for (int i = 0; i < keyValuePairCount; i++) {
            pos = writeInt(result, pos, values[i].length);
            pos = writeBytes(result, pos, values[i]);
        }
        }
        return result;
    }

@@ -647,14 +651,14 @@ public class SettingsBackupAgent extends BackupAgentHelper {
     * @param out byte array
     * @param pos current pos in array
     * @param value integer to write
     * @return the index after adding the size of an int (4)
     * @return the index after adding the size of an int (4) in bytes.
     */
    private int writeInt(byte[] out, int pos, int value) {
        out[pos + 0] = (byte) ((value >> 24) & 0xFF);
        out[pos + 1] = (byte) ((value >> 16) & 0xFF);
        out[pos + 2] = (byte) ((value >>  8) & 0xFF);
        out[pos + 3] = (byte) ((value >>  0) & 0xFF);
        return pos + 4;
        return pos + INTEGER_BYTE_COUNT;
    }

    private int writeBytes(byte[] out, int pos, byte[] value) {