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
17package com.android.providers.settings;
18
19import android.annotation.Nullable;
20import android.annotation.UserIdInt;
21import android.app.backup.BackupAgentHelper;
22import android.app.backup.BackupDataInput;
23import android.app.backup.BackupDataOutput;
24import android.app.backup.FullBackupDataOutput;
25import android.content.ContentResolver;
26import android.content.ContentValues;
27import android.content.Context;
28import android.database.Cursor;
29import android.net.NetworkPolicy;
30import android.net.NetworkPolicyManager;
31import android.net.Uri;
32import android.net.wifi.WifiConfiguration;
33import android.net.wifi.WifiManager;
34import android.os.ParcelFileDescriptor;
35import android.os.UserHandle;
36import android.provider.Settings;
37import android.util.ArrayMap;
38import android.util.BackupUtils;
39import android.util.Log;
40
41import com.android.internal.widget.LockPatternUtils;
42
43import java.io.BufferedOutputStream;
44import java.io.ByteArrayInputStream;
45import java.io.ByteArrayOutputStream;
46import java.io.DataInputStream;
47import java.io.DataOutputStream;
48import java.io.EOFException;
49import java.io.File;
50import java.io.FileInputStream;
51import java.io.FileOutputStream;
52import java.io.IOException;
53import java.util.HashMap;
54import java.util.HashSet;
55import java.util.Map;
56import 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 */
62public 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