OSDN Git Service

Introduce legacy settings restore whitelists
[android-x86/frameworks-base.git] / packages / SettingsProvider / src / com / android / providers / settings / SettingsBackupAgent.java
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.BackupUtils;
38 import android.util.Log;
39
40 import com.android.internal.widget.LockPatternUtils;
41
42 import java.io.BufferedOutputStream;
43 import java.io.ByteArrayInputStream;
44 import java.io.ByteArrayOutputStream;
45 import java.io.DataInputStream;
46 import java.io.DataOutputStream;
47 import java.io.EOFException;
48 import java.io.File;
49 import java.io.FileInputStream;
50 import java.io.FileOutputStream;
51 import java.io.IOException;
52 import java.util.HashMap;
53 import java.util.HashSet;
54 import java.util.Map;
55 import java.util.zip.CRC32;
56
57 /**
58  * Performs backup and restore of the System and Secure settings.
59  * List of settings that are backed up are stored in the Settings.java file
60  */
61 public class SettingsBackupAgent extends BackupAgentHelper {
62     private static final boolean DEBUG = false;
63     private static final boolean DEBUG_BACKUP = DEBUG || false;
64
65     private static final String KEY_SYSTEM = "system";
66     private static final String KEY_SECURE = "secure";
67     private static final String KEY_GLOBAL = "global";
68     private static final String KEY_LOCALE = "locale";
69     private static final String KEY_LOCK_SETTINGS = "lock_settings";
70     private static final String KEY_SOFTAP_CONFIG = "softap_config";
71     private static final String KEY_NETWORK_POLICIES = "network_policies";
72     private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
73
74     // Versioning of the state file.  Increment this version
75     // number any time the set of state items is altered.
76     private static final int STATE_VERSION = 7;
77
78     // Versioning of the Network Policies backup payload.
79     private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
80
81
82     // Slots in the checksum array.  Never insert new items in the middle
83     // of this array; new slots must be appended.
84     private static final int STATE_SYSTEM           = 0;
85     private static final int STATE_SECURE           = 1;
86     private static final int STATE_LOCALE           = 2;
87     private static final int STATE_WIFI_SUPPLICANT  = 3;
88     private static final int STATE_WIFI_CONFIG      = 4;
89     private static final int STATE_GLOBAL           = 5;
90     private static final int STATE_LOCK_SETTINGS    = 6;
91     private static final int STATE_SOFTAP_CONFIG    = 7;
92     private static final int STATE_NETWORK_POLICIES = 8;
93     private static final int STATE_WIFI_NEW_CONFIG  = 9;
94
95     private static final int STATE_SIZE             = 10; // The current number of state items
96
97     // Number of entries in the checksum array at various version numbers
98     private static final int STATE_SIZES[] = {
99             0,
100             4,              // version 1
101             5,              // version 2 added STATE_WIFI_CONFIG
102             6,              // version 3 added STATE_GLOBAL
103             7,              // version 4 added STATE_LOCK_SETTINGS
104             8,              // version 5 added STATE_SOFTAP_CONFIG
105             9,              // version 6 added STATE_NETWORK_POLICIES
106             STATE_SIZE      // version 7 added STATE_WIFI_NEW_CONFIG
107     };
108
109     // Versioning of the 'full backup' format
110     // Increment this version any time a new item is added
111     private static final int FULL_BACKUP_VERSION = 6;
112     private static final int FULL_BACKUP_ADDED_GLOBAL = 2;  // added the "global" entry
113     private static final int FULL_BACKUP_ADDED_LOCK_SETTINGS = 3; // added the "lock_settings" entry
114     private static final int FULL_BACKUP_ADDED_SOFTAP_CONF = 4; //added the "softap_config" entry
115     private static final int FULL_BACKUP_ADDED_NETWORK_POLICIES = 5; //added "network_policies"
116     private static final int FULL_BACKUP_ADDED_WIFI_NEW = 6; // added "wifi_new_config" entry
117
118     private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
119
120     private static final byte[] EMPTY_DATA = new byte[0];
121
122     private static final String TAG = "SettingsBackupAgent";
123
124     private static final String[] PROJECTION = {
125             Settings.NameValueTable.NAME,
126             Settings.NameValueTable.VALUE
127     };
128
129     // the key to store the WIFI data under, should be sorted as last, so restore happens last.
130     // use very late unicode character to quasi-guarantee last sort position.
131     private static final String KEY_WIFI_SUPPLICANT = "\uffedWIFI";
132     private static final String KEY_WIFI_CONFIG = "\uffedCONFIG_WIFI";
133
134     // Keys within the lock settings section
135     private static final String KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED = "owner_info_enabled";
136     private static final String KEY_LOCK_SETTINGS_OWNER_INFO = "owner_info";
137     private static final String KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED =
138             "visible_pattern_enabled";
139     private static final String KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS =
140             "power_button_instantly_locks";
141
142     // Name of the temporary file we use during full backup/restore.  This is
143     // stored in the full-backup tarfile as well, so should not be changed.
144     private static final String STAGE_FILE = "flattened-data";
145
146     private SettingsHelper mSettingsHelper;
147
148     private WifiManager mWifiManager;
149
150     @Override
151     public void onCreate() {
152         if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
153
154         mSettingsHelper = new SettingsHelper(this);
155         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
156         super.onCreate();
157     }
158
159     @Override
160     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
161             ParcelFileDescriptor newState) throws IOException {
162
163         byte[] systemSettingsData = getSystemSettings();
164         byte[] secureSettingsData = getSecureSettings();
165         byte[] globalSettingsData = getGlobalSettings();
166         byte[] lockSettingsData   = getLockSettings(UserHandle.myUserId());
167         byte[] locale = mSettingsHelper.getLocaleData();
168         byte[] softApConfigData = getSoftAPConfiguration();
169         byte[] netPoliciesData = getNetworkPolicies();
170         byte[] wifiFullConfigData = getNewWifiConfigData();
171
172         long[] stateChecksums = readOldChecksums(oldState);
173
174         stateChecksums[STATE_SYSTEM] =
175                 writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
176         stateChecksums[STATE_SECURE] =
177                 writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
178         stateChecksums[STATE_GLOBAL] =
179                 writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, globalSettingsData, data);
180         stateChecksums[STATE_LOCALE] =
181                 writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
182         stateChecksums[STATE_WIFI_SUPPLICANT] = 0;
183         stateChecksums[STATE_WIFI_CONFIG] = 0;
184         stateChecksums[STATE_LOCK_SETTINGS] =
185                 writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
186                         lockSettingsData, data);
187         stateChecksums[STATE_SOFTAP_CONFIG] =
188                 writeIfChanged(stateChecksums[STATE_SOFTAP_CONFIG], KEY_SOFTAP_CONFIG,
189                         softApConfigData, data);
190         stateChecksums[STATE_NETWORK_POLICIES] =
191                 writeIfChanged(stateChecksums[STATE_NETWORK_POLICIES], KEY_NETWORK_POLICIES,
192                         netPoliciesData, data);
193         stateChecksums[STATE_WIFI_NEW_CONFIG] =
194                 writeIfChanged(stateChecksums[STATE_WIFI_NEW_CONFIG], KEY_WIFI_NEW_CONFIG,
195                         wifiFullConfigData, data);
196
197         writeNewChecksums(stateChecksums, newState);
198     }
199
200     @Override
201     public void onRestore(BackupDataInput data, int appVersionCode,
202             ParcelFileDescriptor newState) throws IOException {
203
204         HashSet<String> movedToGlobal = new HashSet<String>();
205         Settings.System.getMovedToGlobalSettings(movedToGlobal);
206         Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
207         byte[] restoredWifiSupplicantData = null;
208         byte[] restoredWifiIpConfigData = null;
209
210         while (data.readNextHeader()) {
211             final String key = data.getKey();
212             final int size = data.getDataSize();
213             switch (key) {
214                 case KEY_SYSTEM :
215                     restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal);
216                     mSettingsHelper.applyAudioSettings();
217                     break;
218
219                 case KEY_SECURE :
220                     restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal);
221                     break;
222
223                 case KEY_GLOBAL :
224                     restoreSettings(data, Settings.Global.CONTENT_URI, null);
225                     break;
226
227                 case KEY_WIFI_SUPPLICANT :
228                     restoredWifiSupplicantData = new byte[size];
229                     data.readEntityData(restoredWifiSupplicantData, 0, size);
230                     break;
231
232                 case KEY_LOCALE :
233                     byte[] localeData = new byte[size];
234                     data.readEntityData(localeData, 0, size);
235                     mSettingsHelper.setLocaleData(localeData, size);
236                     break;
237
238                 case KEY_WIFI_CONFIG :
239                     restoredWifiIpConfigData = new byte[size];
240                     data.readEntityData(restoredWifiIpConfigData, 0, size);
241                     break;
242
243                 case KEY_LOCK_SETTINGS :
244                     restoreLockSettings(UserHandle.myUserId(), data);
245                     break;
246
247                 case KEY_SOFTAP_CONFIG :
248                     byte[] softapData = new byte[size];
249                     data.readEntityData(softapData, 0, size);
250                     restoreSoftApConfiguration(softapData);
251                     break;
252
253                 case KEY_NETWORK_POLICIES:
254                     byte[] netPoliciesData = new byte[size];
255                     data.readEntityData(netPoliciesData, 0, size);
256                     restoreNetworkPolicies(netPoliciesData);
257                     break;
258
259                 case KEY_WIFI_NEW_CONFIG:
260                     byte[] restoredWifiNewConfigData = new byte[size];
261                     data.readEntityData(restoredWifiNewConfigData, 0, size);
262                     restoreNewWifiConfigData(restoredWifiNewConfigData);
263                     break;
264
265                 default :
266                     data.skipEntityData();
267
268             }
269         }
270
271         // Do this at the end so that we also pull in the ipconfig data.
272         if (restoredWifiSupplicantData != null) {
273             restoreSupplicantWifiConfigData(
274                     restoredWifiSupplicantData, restoredWifiIpConfigData);
275         }
276     }
277
278     @Override
279     public void onFullBackup(FullBackupDataOutput data)  throws IOException {
280         byte[] systemSettingsData = getSystemSettings();
281         byte[] secureSettingsData = getSecureSettings();
282         byte[] globalSettingsData = getGlobalSettings();
283         byte[] lockSettingsData   = getLockSettings(UserHandle.myUserId());
284         byte[] locale = mSettingsHelper.getLocaleData();
285         byte[] softApConfigData = getSoftAPConfiguration();
286         byte[] netPoliciesData = getNetworkPolicies();
287         byte[] wifiFullConfigData = getNewWifiConfigData();
288
289         // Write the data to the staging file, then emit that as our tarfile
290         // representation of the backed-up settings.
291         String root = getFilesDir().getAbsolutePath();
292         File stage = new File(root, STAGE_FILE);
293         try {
294             FileOutputStream filestream = new FileOutputStream(stage);
295             BufferedOutputStream bufstream = new BufferedOutputStream(filestream);
296             DataOutputStream out = new DataOutputStream(bufstream);
297
298             if (DEBUG_BACKUP) Log.d(TAG, "Writing flattened data version " + FULL_BACKUP_VERSION);
299             out.writeInt(FULL_BACKUP_VERSION);
300
301             if (DEBUG_BACKUP) Log.d(TAG, systemSettingsData.length + " bytes of settings data");
302             out.writeInt(systemSettingsData.length);
303             out.write(systemSettingsData);
304             if (DEBUG_BACKUP) {
305                 Log.d(TAG, secureSettingsData.length + " bytes of secure settings data");
306             }
307             out.writeInt(secureSettingsData.length);
308             out.write(secureSettingsData);
309             if (DEBUG_BACKUP) {
310                 Log.d(TAG, globalSettingsData.length + " bytes of global settings data");
311             }
312             out.writeInt(globalSettingsData.length);
313             out.write(globalSettingsData);
314             if (DEBUG_BACKUP) Log.d(TAG, locale.length + " bytes of locale data");
315             out.writeInt(locale.length);
316             out.write(locale);
317             if (DEBUG_BACKUP) Log.d(TAG, lockSettingsData.length + " bytes of lock settings data");
318             out.writeInt(lockSettingsData.length);
319             out.write(lockSettingsData);
320             if (DEBUG_BACKUP) Log.d(TAG, softApConfigData.length + " bytes of softap config data");
321             out.writeInt(softApConfigData.length);
322             out.write(softApConfigData);
323             if (DEBUG_BACKUP) Log.d(TAG, netPoliciesData.length + " bytes of net policies data");
324             out.writeInt(netPoliciesData.length);
325             out.write(netPoliciesData);
326             if (DEBUG_BACKUP) {
327                 Log.d(TAG, wifiFullConfigData.length + " bytes of wifi config data");
328             }
329             out.writeInt(wifiFullConfigData.length);
330             out.write(wifiFullConfigData);
331
332             out.flush();    // also flushes downstream
333
334             // now we're set to emit the tar stream
335             fullBackupFile(stage, data);
336         } finally {
337             stage.delete();
338         }
339     }
340
341     @Override
342     public void onRestoreFile(ParcelFileDescriptor data, long size,
343             int type, String domain, String relpath, long mode, long mtime)
344             throws IOException {
345         if (DEBUG_BACKUP) Log.d(TAG, "onRestoreFile() invoked");
346         // Our data is actually a blob of flattened settings data identical to that
347         // produced during incremental backups.  Just unpack and apply it all in
348         // turn.
349         FileInputStream instream = new FileInputStream(data.getFileDescriptor());
350         DataInputStream in = new DataInputStream(instream);
351
352         int version = in.readInt();
353         if (DEBUG_BACKUP) Log.d(TAG, "Flattened data version " + version);
354         if (version <= FULL_BACKUP_VERSION) {
355             // Generate the moved-to-global lookup table
356             HashSet<String> movedToGlobal = new HashSet<String>();
357             Settings.System.getMovedToGlobalSettings(movedToGlobal);
358             Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
359
360             // system settings data first
361             int nBytes = in.readInt();
362             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of settings data");
363             byte[] buffer = new byte[nBytes];
364             in.readFully(buffer, 0, nBytes);
365             restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal);
366
367             // secure settings
368             nBytes = in.readInt();
369             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data");
370             if (nBytes > buffer.length) buffer = new byte[nBytes];
371             in.readFully(buffer, 0, nBytes);
372             restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal);
373
374             // Global only if sufficiently new
375             if (version >= FULL_BACKUP_ADDED_GLOBAL) {
376                 nBytes = in.readInt();
377                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of global settings data");
378                 if (nBytes > buffer.length) buffer = new byte[nBytes];
379                 in.readFully(buffer, 0, nBytes);
380                 movedToGlobal.clear();  // no redirection; this *is* the global namespace
381                 restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal);
382             }
383
384             // locale
385             nBytes = in.readInt();
386             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of locale data");
387             if (nBytes > buffer.length) buffer = new byte[nBytes];
388             in.readFully(buffer, 0, nBytes);
389             mSettingsHelper.setLocaleData(buffer, nBytes);
390
391             // Restore older backups performing the necessary migrations.
392             if (version < FULL_BACKUP_ADDED_WIFI_NEW) {
393                 // wifi supplicant
394                 int supplicant_size = in.readInt();
395                 if (DEBUG_BACKUP) Log.d(TAG, supplicant_size + " bytes of wifi supplicant data");
396                 byte[] supplicant_buffer = new byte[supplicant_size];
397                 in.readFully(supplicant_buffer, 0, supplicant_size);
398
399                 // ip config
400                 int ipconfig_size = in.readInt();
401                 if (DEBUG_BACKUP) Log.d(TAG, ipconfig_size + " bytes of ip config data");
402                 byte[] ipconfig_buffer = new byte[ipconfig_size];
403                 in.readFully(ipconfig_buffer, 0, nBytes);
404                 restoreSupplicantWifiConfigData(supplicant_buffer, ipconfig_buffer);
405             }
406
407             if (version >= FULL_BACKUP_ADDED_LOCK_SETTINGS) {
408                 nBytes = in.readInt();
409                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of lock settings data");
410                 if (nBytes > buffer.length) buffer = new byte[nBytes];
411                 if (nBytes > 0) {
412                     in.readFully(buffer, 0, nBytes);
413                     restoreLockSettings(UserHandle.myUserId(), buffer, nBytes);
414                 }
415             }
416             // softap config
417             if (version >= FULL_BACKUP_ADDED_SOFTAP_CONF) {
418                 nBytes = in.readInt();
419                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of softap config data");
420                 if (nBytes > buffer.length) buffer = new byte[nBytes];
421                 if (nBytes > 0) {
422                     in.readFully(buffer, 0, nBytes);
423                     restoreSoftApConfiguration(buffer);
424                 }
425             }
426             // network policies
427             if (version >= FULL_BACKUP_ADDED_NETWORK_POLICIES) {
428                 nBytes = in.readInt();
429                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of network policies data");
430                 if (nBytes > buffer.length) buffer = new byte[nBytes];
431                 if (nBytes > 0) {
432                     in.readFully(buffer, 0, nBytes);
433                     restoreNetworkPolicies(buffer);
434                 }
435             }
436             // Restore full wifi config data
437             if (version >= FULL_BACKUP_ADDED_WIFI_NEW) {
438                 nBytes = in.readInt();
439                 if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of full wifi config data");
440                 if (nBytes > buffer.length) buffer = new byte[nBytes];
441                 in.readFully(buffer, 0, nBytes);
442                 restoreNewWifiConfigData(buffer);
443             }
444
445             if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
446         } else {
447             data.close();
448             throw new IOException("Invalid file schema");
449         }
450     }
451
452     private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException {
453         long[] stateChecksums = new long[STATE_SIZE];
454
455         DataInputStream dataInput = new DataInputStream(
456                 new FileInputStream(oldState.getFileDescriptor()));
457
458         try {
459             int stateVersion = dataInput.readInt();
460             if (stateVersion > STATE_VERSION) {
461                 // Constrain the maximum state version this backup agent
462                 // can handle in case a newer or corrupt backup set existed
463                 stateVersion = STATE_VERSION;
464             }
465             for (int i = 0; i < STATE_SIZES[stateVersion]; i++) {
466                 stateChecksums[i] = dataInput.readLong();
467             }
468         } catch (EOFException eof) {
469             // With the default 0 checksum we'll wind up forcing a backup of
470             // any unhandled data sets, which is appropriate.
471         }
472         dataInput.close();
473         return stateChecksums;
474     }
475
476     private void writeNewChecksums(long[] checksums, ParcelFileDescriptor newState)
477             throws IOException {
478         DataOutputStream dataOutput = new DataOutputStream(
479                 new BufferedOutputStream(new FileOutputStream(newState.getFileDescriptor())));
480
481         dataOutput.writeInt(STATE_VERSION);
482         for (int i = 0; i < STATE_SIZE; i++) {
483             dataOutput.writeLong(checksums[i]);
484         }
485         dataOutput.close();
486     }
487
488     private long writeIfChanged(long oldChecksum, String key, byte[] data,
489             BackupDataOutput output) {
490         CRC32 checkSummer = new CRC32();
491         checkSummer.update(data);
492         long newChecksum = checkSummer.getValue();
493         if (oldChecksum == newChecksum) {
494             return oldChecksum;
495         }
496         try {
497             if (DEBUG_BACKUP) {
498                 Log.v(TAG, "Writing entity " + key + " of size " + data.length);
499             }
500             output.writeEntityHeader(key, data.length);
501             output.writeEntityData(data, data.length);
502         } catch (IOException ioe) {
503             // Bail
504         }
505         return newChecksum;
506     }
507
508     private byte[] getSystemSettings() {
509         Cursor cursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION, null,
510                 null, null);
511         try {
512             return extractRelevantValues(cursor, Settings.System.SETTINGS_TO_BACKUP);
513         } finally {
514             cursor.close();
515         }
516     }
517
518     private byte[] getSecureSettings() {
519         Cursor cursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION, null,
520                 null, null);
521         try {
522             return extractRelevantValues(cursor, Settings.Secure.SETTINGS_TO_BACKUP);
523         } finally {
524             cursor.close();
525         }
526     }
527
528     private byte[] getGlobalSettings() {
529         Cursor cursor = getContentResolver().query(Settings.Global.CONTENT_URI, PROJECTION, null,
530                 null, null);
531         try {
532             return extractRelevantValues(cursor, Settings.Global.SETTINGS_TO_BACKUP);
533         } finally {
534             cursor.close();
535         }
536     }
537
538     /**
539      * Serialize the owner info and other lock settings
540      */
541     private byte[] getLockSettings(@UserIdInt int userId) {
542         final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
543         final boolean ownerInfoEnabled = lockPatternUtils.isOwnerInfoEnabled(userId);
544         final String ownerInfo = lockPatternUtils.getOwnerInfo(userId);
545         final boolean lockPatternEnabled = lockPatternUtils.isLockPatternEnabled(userId);
546         final boolean visiblePatternEnabled = lockPatternUtils.isVisiblePatternEnabled(userId);
547         final boolean powerButtonInstantlyLocks =
548                 lockPatternUtils.getPowerButtonInstantlyLocks(userId);
549
550         ByteArrayOutputStream baos = new ByteArrayOutputStream();
551         DataOutputStream out = new DataOutputStream(baos);
552         try {
553             out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED);
554             out.writeUTF(ownerInfoEnabled ? "1" : "0");
555             if (ownerInfo != null) {
556                 out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO);
557                 out.writeUTF(ownerInfo != null ? ownerInfo : "");
558             }
559             if (lockPatternUtils.isVisiblePatternEverChosen(userId)) {
560                 out.writeUTF(KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED);
561                 out.writeUTF(visiblePatternEnabled ? "1" : "0");
562             }
563             if (lockPatternUtils.isPowerButtonInstantlyLocksEverChosen(userId)) {
564                 out.writeUTF(KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS);
565                 out.writeUTF(powerButtonInstantlyLocks ? "1" : "0");
566             }
567             // End marker
568             out.writeUTF("");
569             out.flush();
570         } catch (IOException ioe) {
571         }
572         return baos.toByteArray();
573     }
574
575     private void restoreSettings(BackupDataInput data, Uri contentUri,
576             HashSet<String> movedToGlobal) {
577         byte[] settings = new byte[data.getDataSize()];
578         try {
579             data.readEntityData(settings, 0, settings.length);
580         } catch (IOException ioe) {
581             Log.e(TAG, "Couldn't read entity data");
582             return;
583         }
584         restoreSettings(settings, settings.length, contentUri, movedToGlobal);
585     }
586
587     private void restoreSettings(byte[] settings, int bytes, Uri contentUri,
588             HashSet<String> movedToGlobal) {
589         if (DEBUG) {
590             Log.i(TAG, "restoreSettings: " + contentUri);
591         }
592
593         // Figure out the white list and redirects to the global table.  We restore anything
594         // in either the backup whitelist or the legacy-restore whitelist for this table.
595         final String[] whitelist;
596         if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
597             whitelist = concat(Settings.Secure.SETTINGS_TO_BACKUP,
598                     Settings.Secure.LEGACY_RESTORE_SETTINGS);
599         } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
600             whitelist = concat(Settings.System.SETTINGS_TO_BACKUP,
601                     Settings.System.LEGACY_RESTORE_SETTINGS);
602         } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
603             whitelist = concat(Settings.Global.SETTINGS_TO_BACKUP,
604                     Settings.Global.LEGACY_RESTORE_SETTINGS);
605         } else {
606             throw new IllegalArgumentException("Unknown URI: " + contentUri);
607         }
608
609         // Restore only the white list data.
610         int pos = 0;
611         Map<String, String> cachedEntries = new HashMap<String, String>();
612         ContentValues contentValues = new ContentValues(2);
613         SettingsHelper settingsHelper = mSettingsHelper;
614         ContentResolver cr = getContentResolver();
615
616         final int whiteListSize = whitelist.length;
617         for (int i = 0; i < whiteListSize; i++) {
618             String key = whitelist[i];
619             String value = cachedEntries.remove(key);
620
621             // If the value not cached, let us look it up.
622             if (value == null) {
623                 while (pos < bytes) {
624                     int length = readInt(settings, pos);
625                     pos += INTEGER_BYTE_COUNT;
626                     String dataKey = length > 0 ? new String(settings, pos, length) : null;
627                     pos += length;
628                     length = readInt(settings, pos);
629                     pos += INTEGER_BYTE_COUNT;
630                     String dataValue = length > 0 ? new String(settings, pos, length) : null;
631                     pos += length;
632                     if (key.equals(dataKey)) {
633                         value = dataValue;
634                         break;
635                     }
636                     cachedEntries.put(dataKey, dataValue);
637                 }
638             }
639
640             if (value == null) {
641                 continue;
642             }
643
644             final Uri destination = (movedToGlobal != null && movedToGlobal.contains(key))
645                     ? Settings.Global.CONTENT_URI
646                     : contentUri;
647             settingsHelper.restoreValue(this, cr, contentValues, destination, key, value);
648
649             if (DEBUG) {
650                 Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
651             }
652         }
653     }
654
655     private final String[] concat(String[] first, @Nullable String[] second) {
656         if (second == null || second.length == 0) {
657             return first;
658         }
659         final int firstLen = first.length;
660         final int secondLen = second.length;
661         String[] both = new String[firstLen + secondLen];
662         System.arraycopy(first, 0, both, 0, firstLen);
663         System.arraycopy(second, 0, both, firstLen, secondLen);
664         return both;
665     }
666
667     /**
668      * Restores the owner info enabled and other settings in LockSettings.
669      *
670      * @param buffer
671      * @param nBytes
672      */
673     private void restoreLockSettings(@UserIdInt int userId, byte[] buffer, int nBytes) {
674         final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
675
676         ByteArrayInputStream bais = new ByteArrayInputStream(buffer, 0, nBytes);
677         DataInputStream in = new DataInputStream(bais);
678         try {
679             String key;
680             // Read until empty string marker
681             while ((key = in.readUTF()).length() > 0) {
682                 final String value = in.readUTF();
683                 if (DEBUG_BACKUP) {
684                     Log.v(TAG, "Restoring lock_settings " + key + " = " + value);
685                 }
686                 switch (key) {
687                     case KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED:
688                         lockPatternUtils.setOwnerInfoEnabled("1".equals(value), userId);
689                         break;
690                     case KEY_LOCK_SETTINGS_OWNER_INFO:
691                         lockPatternUtils.setOwnerInfo(value, userId);
692                         break;
693                     case KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED:
694                         lockPatternUtils.reportPatternWasChosen(userId);
695                         lockPatternUtils.setVisiblePatternEnabled("1".equals(value), userId);
696                         break;
697                     case KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS:
698                         lockPatternUtils.setPowerButtonInstantlyLocks("1".equals(value), userId);
699                         break;
700                 }
701             }
702             in.close();
703         } catch (IOException ioe) {
704         }
705     }
706
707     private void restoreLockSettings(@UserIdInt int userId, BackupDataInput data) {
708         final byte[] settings = new byte[data.getDataSize()];
709         try {
710             data.readEntityData(settings, 0, settings.length);
711         } catch (IOException ioe) {
712             Log.e(TAG, "Couldn't read entity data");
713             return;
714         }
715         restoreLockSettings(userId, settings, settings.length);
716     }
717
718     /**
719      * Given a cursor and a set of keys, extract the required keys and
720      * values and write them to a byte array.
721      *
722      * @param cursor A cursor with settings data.
723      * @param settings The settings to extract.
724      * @return The byte array of extracted values.
725      */
726     private byte[] extractRelevantValues(Cursor cursor, String[] settings) {
727         final int settingsCount = settings.length;
728         byte[][] values = new byte[settingsCount * 2][]; // keys and values
729         if (!cursor.moveToFirst()) {
730             Log.e(TAG, "Couldn't read from the cursor");
731             return new byte[0];
732         }
733
734         // Obtain the relevant data in a temporary array.
735         int totalSize = 0;
736         int backedUpSettingIndex = 0;
737         Map<String, String> cachedEntries = new HashMap<String, String>();
738         for (int i = 0; i < settingsCount; i++) {
739             String key = settings[i];
740             String value = cachedEntries.remove(key);
741
742             final int nameColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.NAME);
743             final int valueColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.VALUE);
744
745             // If the value not cached, let us look it up.
746             if (value == null) {
747                 while (!cursor.isAfterLast()) {
748                     String cursorKey = cursor.getString(nameColumnIndex);
749                     String cursorValue = cursor.getString(valueColumnIndex);
750                     cursor.moveToNext();
751                     if (key.equals(cursorKey)) {
752                         value = cursorValue;
753                         break;
754                     }
755                     cachedEntries.put(cursorKey, cursorValue);
756                 }
757             }
758
759             // Intercept the keys and see if they need special handling
760             value = mSettingsHelper.onBackupValue(key, value);
761
762             if (value == null) {
763                 continue;
764             }
765             // Write the key and value in the intermediary array.
766             byte[] keyBytes = key.getBytes();
767             totalSize += INTEGER_BYTE_COUNT + keyBytes.length;
768             values[backedUpSettingIndex * 2] = keyBytes;
769
770             byte[] valueBytes = value.getBytes();
771             totalSize += INTEGER_BYTE_COUNT + valueBytes.length;
772             values[backedUpSettingIndex * 2 + 1] = valueBytes;
773
774             backedUpSettingIndex++;
775
776             if (DEBUG) {
777                 Log.d(TAG, "Backed up setting: " + key + "=" + value);
778             }
779         }
780
781         // Aggregate the result.
782         byte[] result = new byte[totalSize];
783         int pos = 0;
784         final int keyValuePairCount = backedUpSettingIndex * 2;
785         for (int i = 0; i < keyValuePairCount; i++) {
786             pos = writeInt(result, pos, values[i].length);
787             pos = writeBytes(result, pos, values[i]);
788         }
789         return result;
790     }
791
792     private void restoreSupplicantWifiConfigData(byte[] supplicant_bytes, byte[] ipconfig_bytes) {
793         if (DEBUG_BACKUP) {
794             Log.v(TAG, "Applying restored supplicant wifi data");
795         }
796         mWifiManager.restoreSupplicantBackupData(supplicant_bytes, ipconfig_bytes);
797     }
798
799     private byte[] getSoftAPConfiguration() {
800         try {
801             return mWifiManager.getWifiApConfiguration().getBytesForBackup();
802         } catch (IOException ioe) {
803             Log.e(TAG, "Failed to marshal SoftAPConfiguration" + ioe.getMessage());
804             return new byte[0];
805         }
806     }
807
808     private void restoreSoftApConfiguration(byte[] data) {
809         try {
810             WifiConfiguration config = WifiConfiguration
811                     .getWifiConfigFromBackup(new DataInputStream(new ByteArrayInputStream(data)));
812             if (DEBUG) Log.d(TAG, "Successfully unMarshaled WifiConfiguration ");
813             mWifiManager.setWifiApConfiguration(config);
814         } catch (IOException | BackupUtils.BadVersionException e) {
815             Log.e(TAG, "Failed to unMarshal SoftAPConfiguration " + e.getMessage());
816         }
817     }
818
819     private byte[] getNetworkPolicies() {
820         NetworkPolicyManager networkPolicyManager =
821                 (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE);
822         NetworkPolicy[] policies = networkPolicyManager.getNetworkPolicies();
823         ByteArrayOutputStream baos = new ByteArrayOutputStream();
824         if (policies != null && policies.length != 0) {
825             DataOutputStream out = new DataOutputStream(baos);
826             try {
827                 out.writeInt(NETWORK_POLICIES_BACKUP_VERSION);
828                 out.writeInt(policies.length);
829                 for (NetworkPolicy policy : policies) {
830                     if (policy != null) {
831                         byte[] marshaledPolicy = policy.getBytesForBackup();
832                         out.writeByte(BackupUtils.NOT_NULL);
833                         out.writeInt(marshaledPolicy.length);
834                         out.write(marshaledPolicy);
835                     } else {
836                         out.writeByte(BackupUtils.NULL);
837                     }
838                 }
839             } catch (IOException ioe) {
840                 Log.e(TAG, "Failed to convert NetworkPolicies to byte array " + ioe.getMessage());
841                 baos.reset();
842             }
843         }
844         return baos.toByteArray();
845     }
846
847     private byte[] getNewWifiConfigData() {
848         return mWifiManager.retrieveBackupData();
849     }
850
851     private void restoreNewWifiConfigData(byte[] bytes) {
852         if (DEBUG_BACKUP) {
853             Log.v(TAG, "Applying restored wifi data");
854         }
855         mWifiManager.restoreBackupData(bytes);
856     }
857
858     private void restoreNetworkPolicies(byte[] data) {
859         NetworkPolicyManager networkPolicyManager =
860                 (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE);
861         if (data != null && data.length != 0) {
862             DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
863             try {
864                 int version = in.readInt();
865                 if (version < 1 || version > NETWORK_POLICIES_BACKUP_VERSION) {
866                     throw new BackupUtils.BadVersionException(
867                             "Unknown Backup Serialization Version");
868                 }
869                 int length = in.readInt();
870                 NetworkPolicy[] policies = new NetworkPolicy[length];
871                 for (int i = 0; i < length; i++) {
872                     byte isNull = in.readByte();
873                     if (isNull == BackupUtils.NULL) continue;
874                     int byteLength = in.readInt();
875                     byte[] policyData = new byte[byteLength];
876                     in.read(policyData, 0, byteLength);
877                     policies[i] = NetworkPolicy.getNetworkPolicyFromBackup(
878                             new DataInputStream(new ByteArrayInputStream(policyData)));
879                 }
880                 // Only set the policies if there was no error in the restore operation
881                 networkPolicyManager.setNetworkPolicies(policies);
882             } catch (NullPointerException | IOException | BackupUtils.BadVersionException e) {
883                 // NPE can be thrown when trying to instantiate a NetworkPolicy
884                 Log.e(TAG, "Failed to convert byte array to NetworkPolicies " + e.getMessage());
885             }
886         }
887     }
888
889     /**
890      * Write an int in BigEndian into the byte array.
891      * @param out byte array
892      * @param pos current pos in array
893      * @param value integer to write
894      * @return the index after adding the size of an int (4) in bytes.
895      */
896     private int writeInt(byte[] out, int pos, int value) {
897         out[pos + 0] = (byte) ((value >> 24) & 0xFF);
898         out[pos + 1] = (byte) ((value >> 16) & 0xFF);
899         out[pos + 2] = (byte) ((value >>  8) & 0xFF);
900         out[pos + 3] = (byte) ((value >>  0) & 0xFF);
901         return pos + INTEGER_BYTE_COUNT;
902     }
903
904     private int writeBytes(byte[] out, int pos, byte[] value) {
905         System.arraycopy(value, 0, out, pos, value.length);
906         return pos + value.length;
907     }
908
909     private int readInt(byte[] in, int pos) {
910         int result = ((in[pos] & 0xFF) << 24)
911                 | ((in[pos + 1] & 0xFF) << 16)
912                 | ((in[pos + 2] & 0xFF) <<  8)
913                 | ((in[pos + 3] & 0xFF) <<  0);
914         return result;
915     }
916 }