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.app.backup.BackupAgentHelper;
20import android.app.backup.BackupDataInput;
21import android.app.backup.BackupDataOutput;
22import android.app.backup.FullBackupDataOutput;
23import android.content.ContentResolver;
24import android.content.ContentValues;
25import android.content.Context;
26import android.database.Cursor;
27import android.net.NetworkPolicy;
28import android.net.NetworkPolicyManager;
29import android.net.Uri;
30import android.net.wifi.WifiConfiguration;
31import android.net.wifi.WifiConfiguration.KeyMgmt;
32import android.net.wifi.WifiManager;
33import android.os.FileUtils;
34import android.os.Handler;
35import android.os.ParcelFileDescriptor;
36import android.os.Process;
37import android.os.UserHandle;
38import android.provider.Settings;
39import android.util.BackupUtils;
40import android.util.Log;
41
42import com.android.internal.widget.LockPatternUtils;
43
44import libcore.io.IoUtils;
45
46import java.io.BufferedOutputStream;
47import java.io.BufferedReader;
48import java.io.BufferedWriter;
49import java.io.ByteArrayInputStream;
50import java.io.ByteArrayOutputStream;
51import java.io.CharArrayReader;
52import java.io.DataInputStream;
53import java.io.DataOutputStream;
54import java.io.EOFException;
55import java.io.File;
56import java.io.FileInputStream;
57import java.io.FileOutputStream;
58import java.io.FileReader;
59import java.io.FileWriter;
60import java.io.IOException;
61import java.io.InputStream;
62import java.io.OutputStream;
63import java.io.OutputStreamWriter;
64import java.io.Writer;
65import java.util.ArrayList;
66import java.util.BitSet;
67import java.util.HashMap;
68import java.util.HashSet;
69import java.util.List;
70import java.util.Map;
71import java.util.Objects;
72import java.util.zip.CRC32;
73
74/**
75 * Performs backup and restore of the System and Secure settings.
76 * List of settings that are backed up are stored in the Settings.java file
77 */
78public class SettingsBackupAgent extends BackupAgentHelper {
79    private static final boolean DEBUG = false;
80    private static final boolean DEBUG_BACKUP = DEBUG || false;
81
82    private static final String KEY_SYSTEM = "system";
83    private static final String KEY_SECURE = "secure";
84    private static final String KEY_GLOBAL = "global";
85    private static final String KEY_LOCALE = "locale";
86    private static final String KEY_LOCK_SETTINGS = "lock_settings";
87    private static final String KEY_SOFTAP_CONFIG = "softap_config";
88    private static final String KEY_NETWORK_POLICIES = "network_policies";
89
90    // Versioning of the state file.  Increment this version
91    // number any time the set of state items is altered.
92    private static final int STATE_VERSION = 6;
93
94    // Versioning of the Network Policies backup payload.
95    private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
96
97
98    // Slots in the checksum array.  Never insert new items in the middle
99    // of this array; new slots must be appended.
100    private static final int STATE_SYSTEM           = 0;
101    private static final int STATE_SECURE           = 1;
102    private static final int STATE_LOCALE           = 2;
103    private static final int STATE_WIFI_SUPPLICANT  = 3;
104    private static final int STATE_WIFI_CONFIG      = 4;
105    private static final int STATE_GLOBAL           = 5;
106    private static final int STATE_LOCK_SETTINGS    = 6;
107    private static final int STATE_SOFTAP_CONFIG    = 7;
108    private static final int STATE_NETWORK_POLICIES = 8;
109
110    private static final int STATE_SIZE             = 9; // The current number of state items
111
112    // Number of entries in the checksum array at various version numbers
113    private static final int STATE_SIZES[] = {
114            0,
115            4,              // version 1
116            5,              // version 2 added STATE_WIFI_CONFIG
117            6,              // version 3 added STATE_GLOBAL
118            7,              // version 4 added STATE_LOCK_SETTINGS
119            8,              // version 5 added STATE_SOFTAP_CONFIG
120            STATE_SIZE      // version 6 added STATE_NETWORK_POLICIES
121    };
122
123    // Versioning of the 'full backup' format
124    // Increment this version any time a new item is added
125    private static final int FULL_BACKUP_VERSION = 5;
126    private static final int FULL_BACKUP_ADDED_GLOBAL = 2;  // added the "global" entry
127    private static final int FULL_BACKUP_ADDED_LOCK_SETTINGS = 3; // added the "lock_settings" entry
128    private static final int FULL_BACKUP_ADDED_SOFTAP_CONF = 4; //added the "softap_config" entry
129    private static final int FULL_BACKUP_ADDED_NETWORK_POLICIES = 5; //added "network_policies"
130
131    private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
132
133    private static final byte[] EMPTY_DATA = new byte[0];
134
135    private static final String TAG = "SettingsBackupAgent";
136
137    private static final String[] PROJECTION = {
138            Settings.NameValueTable.NAME,
139            Settings.NameValueTable.VALUE
140    };
141
142    private static final String FILE_WIFI_SUPPLICANT = "/data/misc/wifi/wpa_supplicant.conf";
143    private static final String FILE_WIFI_SUPPLICANT_TEMPLATE =
144            "/system/etc/wifi/wpa_supplicant.conf";
145
146    // the key to store the WIFI data under, should be sorted as last, so restore happens last.
147    // use very late unicode character to quasi-guarantee last sort position.
148    private static final String KEY_WIFI_SUPPLICANT = "\uffedWIFI";
149    private static final String KEY_WIFI_CONFIG = "\uffedCONFIG_WIFI";
150
151    // Keys within the lock settings section
152    private static final String KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED = "owner_info_enabled";
153    private static final String KEY_LOCK_SETTINGS_OWNER_INFO = "owner_info";
154
155    // Name of the temporary file we use during full backup/restore.  This is
156    // stored in the full-backup tarfile as well, so should not be changed.
157    private static final String STAGE_FILE = "flattened-data";
158
159    // Delay in milliseconds between the restore operation and when we will bounce
160    // wifi in order to rewrite the supplicant config etc.
161    private static final long WIFI_BOUNCE_DELAY_MILLIS = 60 * 1000; // one minute
162
163    private SettingsHelper mSettingsHelper;
164    private WifiManager mWfm;
165    private String mWifiConfigFile;
166
167    // Chain of asynchronous operations used when rewriting the wifi supplicant config file
168    WifiDisableRunnable mWifiDisable = null;
169    WifiRestoreRunnable mWifiRestore = null;
170    int mRetainedWifiState; // used only during config file rewrite
171
172    // Class for capturing a network definition from the wifi supplicant config file
173    static class Network {
174        String ssid = "";  // equals() and hashCode() need these to be non-null
175        String key_mgmt = "";
176        boolean certUsed = false;
177        boolean hasWepKey = false;
178        boolean isEap = false;
179        final ArrayList<String> rawLines = new ArrayList<String>();
180
181        public static Network readFromStream(BufferedReader in) {
182            final Network n = new Network();
183            String line;
184            try {
185                while (in.ready()) {
186                    line = in.readLine();
187                    if (line == null || line.startsWith("}")) {
188                        break;
189                    }
190                    n.rememberLine(line);
191                }
192            } catch (IOException e) {
193                return null;
194            }
195            return n;
196        }
197
198        void rememberLine(String line) {
199            // can't rely on particular whitespace patterns so strip leading/trailing
200            line = line.trim();
201            if (line.isEmpty()) return; // only whitespace; drop the line
202            rawLines.add(line);
203
204            // remember the ssid and key_mgmt lines for duplicate culling
205            if (line.startsWith("ssid=")) {
206                ssid = line;
207            } else if (line.startsWith("key_mgmt=")) {
208                key_mgmt = line;
209                if (line.contains("EAP")) {
210                    isEap = true;
211                }
212            } else if (line.startsWith("client_cert=")) {
213                certUsed = true;
214            } else if (line.startsWith("ca_cert=")) {
215                certUsed = true;
216            } else if (line.startsWith("ca_path=")) {
217                certUsed = true;
218            } else if (line.startsWith("wep_")) {
219                hasWepKey = true;
220            } else if (line.startsWith("eap=")) {
221                isEap = true;
222            }
223        }
224
225        public void write(Writer w) throws IOException {
226            w.write("\nnetwork={\n");
227            for (String line : rawLines) {
228                w.write("\t" + line + "\n");
229            }
230            w.write("}\n");
231        }
232
233        public void dump() {
234            Log.v(TAG, "network={");
235            for (String line : rawLines) {
236                Log.v(TAG, "   " + line);
237            }
238            Log.v(TAG, "}");
239        }
240
241        // Calculate the equivalent of WifiConfiguration's configKey()
242        public String configKey() {
243            if (ssid == null) {
244                // No SSID => malformed network definition
245                return null;
246            }
247
248            final String bareSsid = ssid.substring(ssid.indexOf('=') + 1);
249
250            final BitSet types = new BitSet();
251            if (key_mgmt == null) {
252                // no key_mgmt specified; this is defined as equivalent to "WPA-PSK WPA-EAP"
253                types.set(KeyMgmt.WPA_PSK);
254                types.set(KeyMgmt.WPA_EAP);
255            } else {
256                // Need to parse the key_mgmt line
257                final String bareKeyMgmt = key_mgmt.substring(key_mgmt.indexOf('=') + 1);
258                String[] typeStrings = bareKeyMgmt.split("\\s+");
259
260                // Parse out all the key management regimes permitted for this network.  The literal
261                // strings here are the standard values permitted in wpa_supplicant.conf.
262                for (int i = 0; i < typeStrings.length; i++) {
263                    final String ktype = typeStrings[i];
264                    if (ktype.equals("WPA-PSK")) {
265                        Log.v(TAG, "  + setting WPA_PSK bit");
266                        types.set(KeyMgmt.WPA_PSK);
267                    } else if (ktype.equals("WPA-EAP")) {
268                        Log.v(TAG, "  + setting WPA_EAP bit");
269                        types.set(KeyMgmt.WPA_EAP);
270                    } else if (ktype.equals("IEEE8021X")) {
271                        Log.v(TAG, "  + setting IEEE8021X bit");
272                        types.set(KeyMgmt.IEEE8021X);
273                    }
274                }
275            }
276
277            // Now build the canonical config key paralleling the WifiConfiguration semantics
278            final String key;
279            if (types.get(KeyMgmt.WPA_PSK)) {
280                key = bareSsid + KeyMgmt.strings[KeyMgmt.WPA_PSK];
281            } else if (types.get(KeyMgmt.WPA_EAP) || types.get(KeyMgmt.IEEE8021X)) {
282                key = bareSsid + KeyMgmt.strings[KeyMgmt.WPA_EAP];
283            } else if (hasWepKey) {
284                key = bareSsid + "WEP";  // hardcoded this way in WifiConfiguration
285            } else {
286                key = bareSsid + KeyMgmt.strings[KeyMgmt.NONE];
287            }
288            return key;
289        }
290
291        // Same approach as Pair.equals() and Pair.hashCode()
292        @Override
293        public boolean equals(Object o) {
294            if (o == this) return true;
295            if (!(o instanceof Network)) return false;
296            final Network other;
297            try {
298                other = (Network) o;
299            } catch (ClassCastException e) {
300                return false;
301            }
302            return ssid.equals(other.ssid) && key_mgmt.equals(other.key_mgmt);
303        }
304
305        @Override
306        public int hashCode() {
307            int result = 17;
308            result = 31 * result + ssid.hashCode();
309            result = 31 * result + key_mgmt.hashCode();
310            return result;
311        }
312    }
313
314    boolean networkInWhitelist(Network net, List<WifiConfiguration> whitelist) {
315        final String netConfigKey = net.configKey();
316        final int N = whitelist.size();
317        for (int i = 0; i < N; i++) {
318            if (Objects.equals(netConfigKey, whitelist.get(i).configKey(true))) {
319                return true;
320            }
321        }
322        return false;
323    }
324
325    // Ingest multiple wifi config file fragments, looking for network={} blocks
326    // and eliminating duplicates
327    class WifiNetworkSettings {
328        // One for fast lookup, one for maintaining ordering
329        final HashSet<Network> mKnownNetworks = new HashSet<Network>();
330        final ArrayList<Network> mNetworks = new ArrayList<Network>(8);
331
332        public void readNetworks(BufferedReader in, List<WifiConfiguration> whitelist,
333                boolean acceptEapNetworks) {
334            try {
335                String line;
336                while (in.ready()) {
337                    line = in.readLine();
338                    if (line != null) {
339                        // Parse out 'network=' decls so we can ignore duplicates
340                        if (line.startsWith("network")) {
341                            Network net = Network.readFromStream(in);
342                            if (whitelist != null) {
343                                if (!networkInWhitelist(net, whitelist)) {
344                                    if (DEBUG_BACKUP) {
345                                        Log.v(TAG, "Network not in whitelist, skipping: "
346                                                + net.ssid + " / " + net.key_mgmt);
347                                    }
348                                    continue;
349                                }
350                            }
351                            // Don't propagate EAP network definitions
352                            if (net.isEap && !acceptEapNetworks) {
353                                if (DEBUG_BACKUP) {
354                                    Log.v(TAG, "Skipping EAP network " + net.ssid + " / " + net.key_mgmt);
355                                }
356                                continue;
357                            }
358                            if (!mKnownNetworks.contains(net)) {
359                                if (DEBUG_BACKUP) {
360                                    Log.v(TAG, "Adding " + net.ssid + " / " + net.key_mgmt);
361                                }
362                                mKnownNetworks.add(net);
363                                mNetworks.add(net);
364                            } else {
365                                if (DEBUG_BACKUP) {
366                                    Log.v(TAG, "Dupe; skipped " + net.ssid + " / " + net.key_mgmt);
367                                }
368                            }
369                        }
370                    }
371                }
372            } catch (IOException e) {
373                // whatever happened, we're done now
374            }
375        }
376
377        public void write(Writer w) throws IOException {
378            for (Network net : mNetworks) {
379                if (net.certUsed) {
380                    // Networks that use certificates for authentication can't be restored
381                    // because the certificates they need don't get restored (because they
382                    // are stored in keystore, and can't be restored)
383                    continue;
384                }
385
386                if (net.isEap) {
387                    // Similarly, omit EAP network definitions to avoid propagating
388                    // controlled enterprise network definitions.
389                    continue;
390                }
391
392                net.write(w);
393            }
394        }
395
396        public void dump() {
397            for (Network net : mNetworks) {
398                net.dump();
399            }
400        }
401    }
402
403    @Override
404    public void onCreate() {
405        if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
406
407        mSettingsHelper = new SettingsHelper(this);
408        super.onCreate();
409
410        WifiManager mWfm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
411        if (mWfm != null) mWifiConfigFile = mWfm.getConfigFile();
412    }
413
414    @Override
415    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
416            ParcelFileDescriptor newState) throws IOException {
417
418        byte[] systemSettingsData = getSystemSettings();
419        byte[] secureSettingsData = getSecureSettings();
420        byte[] globalSettingsData = getGlobalSettings();
421        byte[] lockSettingsData   = getLockSettings();
422        byte[] locale = mSettingsHelper.getLocaleData();
423        byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
424        byte[] wifiConfigData = getFileData(mWifiConfigFile);
425        byte[] softApConfigData = getSoftAPConfiguration();
426        byte[] netPoliciesData = getNetworkPolicies();
427
428        long[] stateChecksums = readOldChecksums(oldState);
429
430        stateChecksums[STATE_SYSTEM] =
431                writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
432        stateChecksums[STATE_SECURE] =
433                writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
434        stateChecksums[STATE_GLOBAL] =
435                writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, globalSettingsData, data);
436        stateChecksums[STATE_LOCALE] =
437                writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
438        stateChecksums[STATE_WIFI_SUPPLICANT] =
439                writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT,
440                        wifiSupplicantData, data);
441        stateChecksums[STATE_WIFI_CONFIG] =
442                writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData,
443                        data);
444        stateChecksums[STATE_LOCK_SETTINGS] =
445                writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
446                        lockSettingsData, data);
447        stateChecksums[STATE_SOFTAP_CONFIG] =
448                writeIfChanged(stateChecksums[STATE_SOFTAP_CONFIG], KEY_SOFTAP_CONFIG,
449                        softApConfigData, data);
450        stateChecksums[STATE_NETWORK_POLICIES] =
451                writeIfChanged(stateChecksums[STATE_NETWORK_POLICIES], KEY_NETWORK_POLICIES,
452                        netPoliciesData, data);
453
454        writeNewChecksums(stateChecksums, newState);
455    }
456
457    class WifiDisableRunnable implements Runnable {
458        final WifiRestoreRunnable mNextPhase;
459
460        public WifiDisableRunnable(WifiRestoreRunnable next) {
461            mNextPhase = next;
462        }
463
464        @Override
465        public void run() {
466            if (DEBUG_BACKUP) {
467                Log.v(TAG, "Disabling wifi during restore");
468            }
469            final ContentResolver cr = getContentResolver();
470            final int scanAlways = Settings.Global.getInt(cr,
471                    Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
472            final int retainedWifiState = enableWifi(false);
473            if (scanAlways != 0) {
474                Settings.Global.putInt(cr,
475                        Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
476            }
477
478            // Tell the final stage how to clean up after itself
479            mNextPhase.setPriorState(retainedWifiState, scanAlways);
480
481            // And run it after a modest pause to give broadcasts and content
482            // observers time an opportunity to run on this looper thread, so
483            // that the wifi stack actually goes all the way down.
484            new Handler(getMainLooper()).postDelayed(mNextPhase, 2500);
485        }
486    }
487
488    class WifiRestoreRunnable implements Runnable {
489        private byte[] restoredSupplicantData;
490        private byte[] restoredWifiConfigFile;
491        private int retainedWifiState;  // provided by disable stage
492        private int scanAlways; // provided by disable stage
493
494        void setPriorState(int retainedState, int always) {
495            retainedWifiState = retainedState;
496            scanAlways = always;
497        }
498
499        void incorporateWifiSupplicant(BackupDataInput data) {
500            restoredSupplicantData = new byte[data.getDataSize()];
501            if (restoredSupplicantData.length <= 0) return;
502            try {
503                data.readEntityData(restoredSupplicantData, 0, data.getDataSize());
504            } catch (IOException e) {
505                Log.w(TAG, "Unable to read supplicant data");
506                restoredSupplicantData = null;
507            }
508        }
509
510        void incorporateWifiConfigFile(BackupDataInput data) {
511            restoredWifiConfigFile = new byte[data.getDataSize()];
512            if (restoredWifiConfigFile.length <= 0) return;
513            try {
514                data.readEntityData(restoredWifiConfigFile, 0, data.getDataSize());
515            } catch (IOException e) {
516                Log.w(TAG, "Unable to read config file");
517                restoredWifiConfigFile = null;
518            }
519        }
520
521        @Override
522        public void run() {
523            if (restoredSupplicantData != null || restoredWifiConfigFile != null) {
524                if (DEBUG_BACKUP) {
525                    Log.v(TAG, "Applying restored wifi data");
526                }
527                if (restoredSupplicantData != null) {
528                    restoreWifiSupplicant(FILE_WIFI_SUPPLICANT,
529                            restoredSupplicantData, restoredSupplicantData.length);
530                    FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
531                            FileUtils.S_IRUSR | FileUtils.S_IWUSR
532                            | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
533                            Process.myUid(), Process.WIFI_UID);
534                }
535                if (restoredWifiConfigFile != null) {
536                    restoreFileData(mWifiConfigFile,
537                            restoredWifiConfigFile, restoredWifiConfigFile.length);
538                }
539                // restore the previous WIFI state.
540                if (scanAlways != 0) {
541                    Settings.Global.putInt(getContentResolver(),
542                            Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, scanAlways);
543                }
544                enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED
545                        || retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
546            }
547        }
548    }
549
550    // Instantiate the wifi-config restore runnable, scheduling it for execution
551    // a minute hence
552    void initWifiRestoreIfNecessary() {
553        if (mWifiRestore == null) {
554            mWifiRestore = new WifiRestoreRunnable();
555            mWifiDisable = new WifiDisableRunnable(mWifiRestore);
556        }
557    }
558
559    @Override
560    public void onRestore(BackupDataInput data, int appVersionCode,
561            ParcelFileDescriptor newState) throws IOException {
562
563        HashSet<String> movedToGlobal = new HashSet<String>();
564        Settings.System.getMovedToGlobalSettings(movedToGlobal);
565        Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
566
567        while (data.readNextHeader()) {
568            final String key = data.getKey();
569            final int size = data.getDataSize();
570            switch (key) {
571                case KEY_SYSTEM :
572                    restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal);
573                    mSettingsHelper.applyAudioSettings();
574                    break;
575
576                case KEY_SECURE :
577                    restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal);
578                    break;
579
580                case KEY_GLOBAL :
581                    restoreSettings(data, Settings.Global.CONTENT_URI, null);
582                    break;
583
584                case KEY_WIFI_SUPPLICANT :
585                    initWifiRestoreIfNecessary();
586                    mWifiRestore.incorporateWifiSupplicant(data);
587                    break;
588
589                case KEY_LOCALE :
590                    byte[] localeData = new byte[size];
591                    data.readEntityData(localeData, 0, size);
592                    mSettingsHelper.setLocaleData(localeData, size);
593                    break;
594
595                case KEY_WIFI_CONFIG :
596                    initWifiRestoreIfNecessary();
597                    mWifiRestore.incorporateWifiConfigFile(data);
598                    break;
599
600                case KEY_LOCK_SETTINGS :
601                    restoreLockSettings(data);
602                    break;
603
604                case KEY_SOFTAP_CONFIG :
605                    byte[] softapData = new byte[size];
606                    data.readEntityData(softapData, 0, size);
607                    restoreSoftApConfiguration(softapData);
608                    break;
609
610                case KEY_NETWORK_POLICIES:
611                    byte[] netPoliciesData = new byte[size];
612                    data.readEntityData(netPoliciesData, 0, size);
613                    restoreNetworkPolicies(netPoliciesData);
614                    break;
615
616                default :
617                    data.skipEntityData();
618
619            }
620        }
621
622        // If we have wifi data to restore, post a runnable to perform the
623        // bounce-and-update operation a little ways in the future.  The
624        // 'disable' runnable brings down the stack and remembers its state,
625        // and in turn schedules the 'restore' runnable to do the rewrite
626        // and cleanup operations.
627        if (mWifiRestore != null) {
628            long wifiBounceDelayMillis = Settings.Global.getLong(
629                    getContentResolver(),
630                    Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS,
631                    WIFI_BOUNCE_DELAY_MILLIS);
632            new Handler(getMainLooper()).postDelayed(mWifiDisable, wifiBounceDelayMillis);
633        }
634    }
635
636    @Override
637    public void onFullBackup(FullBackupDataOutput data)  throws IOException {
638        byte[] systemSettingsData = getSystemSettings();
639        byte[] secureSettingsData = getSecureSettings();
640        byte[] globalSettingsData = getGlobalSettings();
641        byte[] lockSettingsData   = getLockSettings();
642        byte[] locale = mSettingsHelper.getLocaleData();
643        byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
644        byte[] wifiConfigData = getFileData(mWifiConfigFile);
645        byte[] softApConfigData = getSoftAPConfiguration();
646        byte[] netPoliciesData = getNetworkPolicies();
647
648        // Write the data to the staging file, then emit that as our tarfile
649        // representation of the backed-up settings.
650        String root = getFilesDir().getAbsolutePath();
651        File stage = new File(root, STAGE_FILE);
652        try {
653            FileOutputStream filestream = new FileOutputStream(stage);
654            BufferedOutputStream bufstream = new BufferedOutputStream(filestream);
655            DataOutputStream out = new DataOutputStream(bufstream);
656
657            if (DEBUG_BACKUP) Log.d(TAG, "Writing flattened data version " + FULL_BACKUP_VERSION);
658            out.writeInt(FULL_BACKUP_VERSION);
659
660            if (DEBUG_BACKUP) Log.d(TAG, systemSettingsData.length + " bytes of settings data");
661            out.writeInt(systemSettingsData.length);
662            out.write(systemSettingsData);
663            if (DEBUG_BACKUP) {
664                Log.d(TAG, secureSettingsData.length + " bytes of secure settings data");
665            }
666            out.writeInt(secureSettingsData.length);
667            out.write(secureSettingsData);
668            if (DEBUG_BACKUP) {
669                Log.d(TAG, globalSettingsData.length + " bytes of global settings data");
670            }
671            out.writeInt(globalSettingsData.length);
672            out.write(globalSettingsData);
673            if (DEBUG_BACKUP) Log.d(TAG, locale.length + " bytes of locale data");
674            out.writeInt(locale.length);
675            out.write(locale);
676            if (DEBUG_BACKUP) {
677                Log.d(TAG, wifiSupplicantData.length + " bytes of wifi supplicant data");
678            }
679            out.writeInt(wifiSupplicantData.length);
680            out.write(wifiSupplicantData);
681            if (DEBUG_BACKUP) Log.d(TAG, wifiConfigData.length + " bytes of wifi config data");
682            out.writeInt(wifiConfigData.length);
683            out.write(wifiConfigData);
684            if (DEBUG_BACKUP) Log.d(TAG, lockSettingsData.length + " bytes of lock settings data");
685            out.writeInt(lockSettingsData.length);
686            out.write(lockSettingsData);
687            if (DEBUG_BACKUP) Log.d(TAG, softApConfigData.length + " bytes of softap config data");
688            out.writeInt(softApConfigData.length);
689            out.write(softApConfigData);
690            if (DEBUG_BACKUP) Log.d(TAG, netPoliciesData.length + " bytes of net policies data");
691            out.writeInt(netPoliciesData.length);
692            out.write(netPoliciesData);
693
694            out.flush();    // also flushes downstream
695
696            // now we're set to emit the tar stream
697            fullBackupFile(stage, data);
698        } finally {
699            stage.delete();
700        }
701    }
702
703    @Override
704    public void onRestoreFile(ParcelFileDescriptor data, long size,
705            int type, String domain, String relpath, long mode, long mtime)
706            throws IOException {
707        if (DEBUG_BACKUP) Log.d(TAG, "onRestoreFile() invoked");
708        // Our data is actually a blob of flattened settings data identical to that
709        // produced during incremental backups.  Just unpack and apply it all in
710        // turn.
711        FileInputStream instream = new FileInputStream(data.getFileDescriptor());
712        DataInputStream in = new DataInputStream(instream);
713
714        int version = in.readInt();
715        if (DEBUG_BACKUP) Log.d(TAG, "Flattened data version " + version);
716        if (version <= FULL_BACKUP_VERSION) {
717            // Generate the moved-to-global lookup table
718            HashSet<String> movedToGlobal = new HashSet<String>();
719            Settings.System.getMovedToGlobalSettings(movedToGlobal);
720            Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
721
722            // system settings data first
723            int nBytes = in.readInt();
724            if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of settings data");
725            byte[] buffer = new byte[nBytes];
726            in.readFully(buffer, 0, nBytes);
727            restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal);
728
729            // secure settings
730            nBytes = in.readInt();
731            if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data");
732            if (nBytes > buffer.length) buffer = new byte[nBytes];
733            in.readFully(buffer, 0, nBytes);
734            restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal);
735
736            // Global only if sufficiently new
737            if (version >= FULL_BACKUP_ADDED_GLOBAL) {
738                nBytes = in.readInt();
739                if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of global settings data");
740                if (nBytes > buffer.length) buffer = new byte[nBytes];
741                in.readFully(buffer, 0, nBytes);
742                movedToGlobal.clear();  // no redirection; this *is* the global namespace
743                restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal);
744            }
745
746            // locale
747            nBytes = in.readInt();
748            if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of locale data");
749            if (nBytes > buffer.length) buffer = new byte[nBytes];
750            in.readFully(buffer, 0, nBytes);
751            mSettingsHelper.setLocaleData(buffer, nBytes);
752
753            // wifi supplicant
754            nBytes = in.readInt();
755            if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of wifi supplicant data");
756            if (nBytes > buffer.length) buffer = new byte[nBytes];
757            in.readFully(buffer, 0, nBytes);
758            int retainedWifiState = enableWifi(false);
759            restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, buffer, nBytes);
760            FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
761                    FileUtils.S_IRUSR | FileUtils.S_IWUSR
762                    | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
763                    Process.myUid(), Process.WIFI_UID);
764            // retain the previous WIFI state.
765            enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED
766                    || retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
767
768            // wifi config
769            nBytes = in.readInt();
770            if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of wifi config data");
771            if (nBytes > buffer.length) buffer = new byte[nBytes];
772            in.readFully(buffer, 0, nBytes);
773            restoreFileData(mWifiConfigFile, buffer, nBytes);
774
775            if (version >= FULL_BACKUP_ADDED_LOCK_SETTINGS) {
776                nBytes = in.readInt();
777                if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of lock settings data");
778                if (nBytes > buffer.length) buffer = new byte[nBytes];
779                if (nBytes > 0) {
780                    in.readFully(buffer, 0, nBytes);
781                    restoreLockSettings(buffer, nBytes);
782                }
783            }
784            // softap config
785            if (version >= FULL_BACKUP_ADDED_SOFTAP_CONF) {
786                nBytes = in.readInt();
787                if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of softap config data");
788                if (nBytes > buffer.length) buffer = new byte[nBytes];
789                if (nBytes > 0) {
790                    in.readFully(buffer, 0, nBytes);
791                    restoreSoftApConfiguration(buffer);
792                }
793            }
794            // network policies
795            if (version >= FULL_BACKUP_ADDED_NETWORK_POLICIES) {
796                nBytes = in.readInt();
797                if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of network policies data");
798                if (nBytes > buffer.length) buffer = new byte[nBytes];
799                if (nBytes > 0) {
800                    in.readFully(buffer, 0, nBytes);
801                    restoreNetworkPolicies(buffer);
802                }
803            }
804            if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
805        } else {
806            data.close();
807            throw new IOException("Invalid file schema");
808        }
809    }
810
811    private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException {
812        long[] stateChecksums = new long[STATE_SIZE];
813
814        DataInputStream dataInput = new DataInputStream(
815                new FileInputStream(oldState.getFileDescriptor()));
816
817        try {
818            int stateVersion = dataInput.readInt();
819            if (stateVersion > STATE_VERSION) {
820                // Constrain the maximum state version this backup agent
821                // can handle in case a newer or corrupt backup set existed
822                stateVersion = STATE_VERSION;
823            }
824            for (int i = 0; i < STATE_SIZES[stateVersion]; i++) {
825                stateChecksums[i] = dataInput.readLong();
826            }
827        } catch (EOFException eof) {
828            // With the default 0 checksum we'll wind up forcing a backup of
829            // any unhandled data sets, which is appropriate.
830        }
831        dataInput.close();
832        return stateChecksums;
833    }
834
835    private void writeNewChecksums(long[] checksums, ParcelFileDescriptor newState)
836            throws IOException {
837        DataOutputStream dataOutput = new DataOutputStream(
838                new BufferedOutputStream(new FileOutputStream(newState.getFileDescriptor())));
839
840        dataOutput.writeInt(STATE_VERSION);
841        for (int i = 0; i < STATE_SIZE; i++) {
842            dataOutput.writeLong(checksums[i]);
843        }
844        dataOutput.close();
845    }
846
847    private long writeIfChanged(long oldChecksum, String key, byte[] data,
848            BackupDataOutput output) {
849        CRC32 checkSummer = new CRC32();
850        checkSummer.update(data);
851        long newChecksum = checkSummer.getValue();
852        if (oldChecksum == newChecksum) {
853            return oldChecksum;
854        }
855        try {
856            if (DEBUG_BACKUP) {
857                Log.v(TAG, "Writing entity " + key + " of size " + data.length);
858            }
859            output.writeEntityHeader(key, data.length);
860            output.writeEntityData(data, data.length);
861        } catch (IOException ioe) {
862            // Bail
863        }
864        return newChecksum;
865    }
866
867    private byte[] getSystemSettings() {
868        Cursor cursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION, null,
869                null, null);
870        try {
871            return extractRelevantValues(cursor, Settings.System.SETTINGS_TO_BACKUP);
872        } finally {
873            cursor.close();
874        }
875    }
876
877    private byte[] getSecureSettings() {
878        Cursor cursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION, null,
879                null, null);
880        try {
881            return extractRelevantValues(cursor, Settings.Secure.SETTINGS_TO_BACKUP);
882        } finally {
883            cursor.close();
884        }
885    }
886
887    private byte[] getGlobalSettings() {
888        Cursor cursor = getContentResolver().query(Settings.Global.CONTENT_URI, PROJECTION, null,
889                null, null);
890        try {
891            return extractRelevantValues(cursor, Settings.Global.SETTINGS_TO_BACKUP);
892        } finally {
893            cursor.close();
894        }
895    }
896
897    /**
898     * Serialize the owner info settings
899     */
900    private byte[] getLockSettings() {
901        final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
902        final boolean ownerInfoEnabled = lockPatternUtils.isOwnerInfoEnabled(UserHandle.myUserId());
903        final String ownerInfo = lockPatternUtils.getOwnerInfo(UserHandle.myUserId());
904
905        ByteArrayOutputStream baos = new ByteArrayOutputStream();
906        DataOutputStream out = new DataOutputStream(baos);
907        try {
908            out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED);
909            out.writeUTF(ownerInfoEnabled ? "1" : "0");
910            if (ownerInfo != null) {
911                out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO);
912                out.writeUTF(ownerInfo != null ? ownerInfo : "");
913            }
914            // End marker
915            out.writeUTF("");
916            out.flush();
917        } catch (IOException ioe) {
918        }
919        return baos.toByteArray();
920    }
921
922    private void restoreSettings(BackupDataInput data, Uri contentUri,
923            HashSet<String> movedToGlobal) {
924        byte[] settings = new byte[data.getDataSize()];
925        try {
926            data.readEntityData(settings, 0, settings.length);
927        } catch (IOException ioe) {
928            Log.e(TAG, "Couldn't read entity data");
929            return;
930        }
931        restoreSettings(settings, settings.length, contentUri, movedToGlobal);
932    }
933
934    private void restoreSettings(byte[] settings, int bytes, Uri contentUri,
935            HashSet<String> movedToGlobal) {
936        if (DEBUG) {
937            Log.i(TAG, "restoreSettings: " + contentUri);
938        }
939
940        // Figure out the white list and redirects to the global table.
941        final String[] whitelist;
942        if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
943            whitelist = Settings.Secure.SETTINGS_TO_BACKUP;
944        } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
945            whitelist = Settings.System.SETTINGS_TO_BACKUP;
946        } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
947            whitelist = Settings.Global.SETTINGS_TO_BACKUP;
948        } else {
949            throw new IllegalArgumentException("Unknown URI: " + contentUri);
950        }
951
952        // Restore only the white list data.
953        int pos = 0;
954        Map<String, String> cachedEntries = new HashMap<String, String>();
955        ContentValues contentValues = new ContentValues(2);
956        SettingsHelper settingsHelper = mSettingsHelper;
957        ContentResolver cr = getContentResolver();
958
959        final int whiteListSize = whitelist.length;
960        for (int i = 0; i < whiteListSize; i++) {
961            String key = whitelist[i];
962            String value = cachedEntries.remove(key);
963
964            // If the value not cached, let us look it up.
965            if (value == null) {
966                while (pos < bytes) {
967                    int length = readInt(settings, pos);
968                    pos += INTEGER_BYTE_COUNT;
969                    String dataKey = length > 0 ? new String(settings, pos, length) : null;
970                    pos += length;
971                    length = readInt(settings, pos);
972                    pos += INTEGER_BYTE_COUNT;
973                    String dataValue = length > 0 ? new String(settings, pos, length) : null;
974                    pos += length;
975                    if (key.equals(dataKey)) {
976                        value = dataValue;
977                        break;
978                    }
979                    cachedEntries.put(dataKey, dataValue);
980                }
981            }
982
983            if (value == null) {
984                continue;
985            }
986
987            final Uri destination = (movedToGlobal != null && movedToGlobal.contains(key))
988                    ? Settings.Global.CONTENT_URI
989                    : contentUri;
990            settingsHelper.restoreValue(this, cr, contentValues, destination, key, value);
991
992            if (DEBUG) {
993                Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
994            }
995        }
996    }
997
998    /**
999     * Restores the owner info enabled and owner info settings in LockSettings.
1000     *
1001     * @param buffer
1002     * @param nBytes
1003     */
1004    private void restoreLockSettings(byte[] buffer, int nBytes) {
1005        final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
1006
1007        ByteArrayInputStream bais = new ByteArrayInputStream(buffer, 0, nBytes);
1008        DataInputStream in = new DataInputStream(bais);
1009        try {
1010            String key;
1011            // Read until empty string marker
1012            while ((key = in.readUTF()).length() > 0) {
1013                final String value = in.readUTF();
1014                if (DEBUG_BACKUP) {
1015                    Log.v(TAG, "Restoring lock_settings " + key + " = " + value);
1016                }
1017                switch (key) {
1018                    case KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED:
1019                        lockPatternUtils.setOwnerInfoEnabled("1".equals(value),
1020                                UserHandle.myUserId());
1021                        break;
1022                    case KEY_LOCK_SETTINGS_OWNER_INFO:
1023                        lockPatternUtils.setOwnerInfo(value, UserHandle.myUserId());
1024                        break;
1025                }
1026            }
1027            in.close();
1028        } catch (IOException ioe) {
1029        }
1030    }
1031
1032    private void restoreLockSettings(BackupDataInput data) {
1033        final byte[] settings = new byte[data.getDataSize()];
1034        try {
1035            data.readEntityData(settings, 0, settings.length);
1036        } catch (IOException ioe) {
1037            Log.e(TAG, "Couldn't read entity data");
1038            return;
1039        }
1040        restoreLockSettings(settings, settings.length);
1041    }
1042
1043    /**
1044     * Given a cursor and a set of keys, extract the required keys and
1045     * values and write them to a byte array.
1046     *
1047     * @param cursor A cursor with settings data.
1048     * @param settings The settings to extract.
1049     * @return The byte array of extracted values.
1050     */
1051    private byte[] extractRelevantValues(Cursor cursor, String[] settings) {
1052        final int settingsCount = settings.length;
1053        byte[][] values = new byte[settingsCount * 2][]; // keys and values
1054        if (!cursor.moveToFirst()) {
1055            Log.e(TAG, "Couldn't read from the cursor");
1056            return new byte[0];
1057        }
1058
1059        // Obtain the relevant data in a temporary array.
1060        int totalSize = 0;
1061        int backedUpSettingIndex = 0;
1062        Map<String, String> cachedEntries = new HashMap<String, String>();
1063        for (int i = 0; i < settingsCount; i++) {
1064            String key = settings[i];
1065            String value = cachedEntries.remove(key);
1066
1067            final int nameColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.NAME);
1068            final int valueColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.VALUE);
1069
1070            // If the value not cached, let us look it up.
1071            if (value == null) {
1072                while (!cursor.isAfterLast()) {
1073                    String cursorKey = cursor.getString(nameColumnIndex);
1074                    String cursorValue = cursor.getString(valueColumnIndex);
1075                    cursor.moveToNext();
1076                    if (key.equals(cursorKey)) {
1077                        value = cursorValue;
1078                        break;
1079                    }
1080                    cachedEntries.put(cursorKey, cursorValue);
1081                }
1082            }
1083
1084            // Intercept the keys and see if they need special handling
1085            value = mSettingsHelper.onBackupValue(key, value);
1086
1087            if (value == null) {
1088                continue;
1089            }
1090            // Write the key and value in the intermediary array.
1091            byte[] keyBytes = key.getBytes();
1092            totalSize += INTEGER_BYTE_COUNT + keyBytes.length;
1093            values[backedUpSettingIndex * 2] = keyBytes;
1094
1095            byte[] valueBytes = value.getBytes();
1096            totalSize += INTEGER_BYTE_COUNT + valueBytes.length;
1097            values[backedUpSettingIndex * 2 + 1] = valueBytes;
1098
1099            backedUpSettingIndex++;
1100
1101            if (DEBUG) {
1102                Log.d(TAG, "Backed up setting: " + key + "=" + value);
1103            }
1104        }
1105
1106        // Aggregate the result.
1107        byte[] result = new byte[totalSize];
1108        int pos = 0;
1109        final int keyValuePairCount = backedUpSettingIndex * 2;
1110        for (int i = 0; i < keyValuePairCount; i++) {
1111            pos = writeInt(result, pos, values[i].length);
1112            pos = writeBytes(result, pos, values[i]);
1113        }
1114        return result;
1115    }
1116
1117    private byte[] getFileData(String filename) {
1118        InputStream is = null;
1119        try {
1120            File file = new File(filename);
1121            is = new FileInputStream(file);
1122
1123            //Will truncate read on a very long file,
1124            //should not happen for a config file
1125            byte[] bytes = new byte[(int) file.length()];
1126
1127            int offset = 0;
1128            int numRead = 0;
1129            while (offset < bytes.length
1130                    && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
1131                offset += numRead;
1132            }
1133
1134            //read failure
1135            if (offset < bytes.length) {
1136                Log.w(TAG, "Couldn't backup " + filename);
1137                return EMPTY_DATA;
1138            }
1139            return bytes;
1140        } catch (IOException ioe) {
1141            Log.w(TAG, "Couldn't backup " + filename);
1142            return EMPTY_DATA;
1143        } finally {
1144            if (is != null) {
1145                try {
1146                    is.close();
1147                } catch (IOException e) {
1148                }
1149            }
1150        }
1151    }
1152
1153    private void restoreFileData(String filename, byte[] bytes, int size) {
1154        try {
1155            File file = new File(filename);
1156            if (file.exists()) file.delete();
1157
1158            OutputStream os = new BufferedOutputStream(new FileOutputStream(filename, true));
1159            os.write(bytes, 0, size);
1160            os.close();
1161        } catch (IOException ioe) {
1162            Log.w(TAG, "Couldn't restore " + filename);
1163        }
1164    }
1165
1166
1167    private byte[] getWifiSupplicant(String filename) {
1168        BufferedReader br = null;
1169        try {
1170            File file = new File(filename);
1171            if (!file.exists()) {
1172                return EMPTY_DATA;
1173            }
1174
1175            WifiManager wifi = (WifiManager) getSystemService(WIFI_SERVICE);
1176            List<WifiConfiguration> configs = wifi.getConfiguredNetworks();
1177
1178            WifiNetworkSettings fromFile = new WifiNetworkSettings();
1179            br = new BufferedReader(new FileReader(file));
1180            fromFile.readNetworks(br, configs, false);
1181
1182            // Write the parsed networks into a packed byte array
1183            if (fromFile.mKnownNetworks.size() > 0) {
1184                ByteArrayOutputStream bos = new ByteArrayOutputStream();
1185                OutputStreamWriter out = new OutputStreamWriter(bos);
1186                fromFile.write(out);
1187                out.flush();
1188                return bos.toByteArray();
1189            } else {
1190                return EMPTY_DATA;
1191            }
1192        } catch (IOException ioe) {
1193            Log.w(TAG, "Couldn't backup " + filename);
1194            return EMPTY_DATA;
1195        } finally {
1196            IoUtils.closeQuietly(br);
1197        }
1198    }
1199
1200    private void restoreWifiSupplicant(String filename, byte[] bytes, int size) {
1201        try {
1202            WifiNetworkSettings supplicantImage = new WifiNetworkSettings();
1203
1204            File supplicantFile = new File(FILE_WIFI_SUPPLICANT);
1205            if (supplicantFile.exists()) {
1206                // Retain the existing APs; we'll append the restored ones to them
1207                BufferedReader in = new BufferedReader(new FileReader(FILE_WIFI_SUPPLICANT));
1208                supplicantImage.readNetworks(in, null, true);
1209                in.close();
1210
1211                supplicantFile.delete();
1212            }
1213
1214            // Incorporate the restore AP information
1215            if (size > 0) {
1216                char[] restoredAsBytes = new char[size];
1217                for (int i = 0; i < size; i++) restoredAsBytes[i] = (char) bytes[i];
1218                BufferedReader in = new BufferedReader(new CharArrayReader(restoredAsBytes));
1219                supplicantImage.readNetworks(in, null, false);
1220
1221                if (DEBUG_BACKUP) {
1222                    Log.v(TAG, "Final AP list:");
1223                    supplicantImage.dump();
1224                }
1225            }
1226
1227            // Install the correct default template
1228            BufferedWriter bw = new BufferedWriter(new FileWriter(FILE_WIFI_SUPPLICANT));
1229            copyWifiSupplicantTemplate(bw);
1230
1231            // Write the restored supplicant config and we're done
1232            supplicantImage.write(bw);
1233            bw.close();
1234        } catch (IOException ioe) {
1235            Log.w(TAG, "Couldn't restore " + filename);
1236        }
1237    }
1238
1239    private void copyWifiSupplicantTemplate(BufferedWriter bw) {
1240        try {
1241            BufferedReader br = new BufferedReader(new FileReader(FILE_WIFI_SUPPLICANT_TEMPLATE));
1242            char[] temp = new char[1024];
1243            int size;
1244            while ((size = br.read(temp)) > 0) {
1245                bw.write(temp, 0, size);
1246            }
1247            br.close();
1248        } catch (IOException ioe) {
1249            Log.w(TAG, "Couldn't copy wpa_supplicant file");
1250        }
1251    }
1252
1253    private byte[] getSoftAPConfiguration() {
1254        WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
1255        try {
1256            return wifiManager.getWifiApConfiguration().getBytesForBackup();
1257        } catch (IOException ioe) {
1258            Log.e(TAG, "Failed to marshal SoftAPConfiguration" + ioe.getMessage());
1259            return new byte[0];
1260        }
1261    }
1262
1263    private void restoreSoftApConfiguration(byte[] data) {
1264        WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
1265        try {
1266            WifiConfiguration config = WifiConfiguration
1267                    .getWifiConfigFromBackup(new DataInputStream(new ByteArrayInputStream(data)));
1268            if (DEBUG) Log.d(TAG, "Successfully unMarshaled WifiConfiguration ");
1269            wifiManager.setWifiApConfiguration(config);
1270        } catch (IOException | BackupUtils.BadVersionException e) {
1271            Log.e(TAG, "Failed to unMarshal SoftAPConfiguration " + e.getMessage());
1272        }
1273    }
1274
1275    private byte[] getNetworkPolicies() {
1276        NetworkPolicyManager networkPolicyManager =
1277                (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE);
1278        NetworkPolicy[] policies = networkPolicyManager.getNetworkPolicies();
1279        ByteArrayOutputStream baos = new ByteArrayOutputStream();
1280        if (policies != null && policies.length != 0) {
1281            DataOutputStream out = new DataOutputStream(baos);
1282            try {
1283                out.writeInt(NETWORK_POLICIES_BACKUP_VERSION);
1284                out.writeInt(policies.length);
1285                for (NetworkPolicy policy : policies) {
1286                    if (policy != null) {
1287                        byte[] marshaledPolicy = policy.getBytesForBackup();
1288                        out.writeByte(BackupUtils.NOT_NULL);
1289                        out.writeInt(marshaledPolicy.length);
1290                        out.write(marshaledPolicy);
1291                    } else {
1292                        out.writeByte(BackupUtils.NULL);
1293                    }
1294                }
1295            } catch (IOException ioe) {
1296                Log.e(TAG, "Failed to convert NetworkPolicies to byte array " + ioe.getMessage());
1297                baos.reset();
1298            }
1299        }
1300        return baos.toByteArray();
1301    }
1302
1303    private void restoreNetworkPolicies(byte[] data) {
1304        NetworkPolicyManager networkPolicyManager =
1305                (NetworkPolicyManager) getSystemService(NETWORK_POLICY_SERVICE);
1306        if (data != null && data.length != 0) {
1307            DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
1308            try {
1309                int version = in.readInt();
1310                if (version < 1 || version > NETWORK_POLICIES_BACKUP_VERSION) {
1311                    throw new BackupUtils.BadVersionException(
1312                            "Unknown Backup Serialization Version");
1313                }
1314                int length = in.readInt();
1315                NetworkPolicy[] policies = new NetworkPolicy[length];
1316                for (int i = 0; i < length; i++) {
1317                    byte isNull = in.readByte();
1318                    if (isNull == BackupUtils.NULL) continue;
1319                    int byteLength = in.readInt();
1320                    byte[] policyData = new byte[byteLength];
1321                    in.read(policyData, 0, byteLength);
1322                    policies[i] = NetworkPolicy.getNetworkPolicyFromBackup(
1323                            new DataInputStream(new ByteArrayInputStream(policyData)));
1324                }
1325                // Only set the policies if there was no error in the restore operation
1326                networkPolicyManager.setNetworkPolicies(policies);
1327            } catch (NullPointerException | IOException | BackupUtils.BadVersionException e) {
1328                // NPE can be thrown when trying to instantiate a NetworkPolicy
1329                Log.e(TAG, "Failed to convert byte array to NetworkPolicies " + e.getMessage());
1330            }
1331        }
1332    }
1333
1334    /**
1335     * Write an int in BigEndian into the byte array.
1336     * @param out byte array
1337     * @param pos current pos in array
1338     * @param value integer to write
1339     * @return the index after adding the size of an int (4) in bytes.
1340     */
1341    private int writeInt(byte[] out, int pos, int value) {
1342        out[pos + 0] = (byte) ((value >> 24) & 0xFF);
1343        out[pos + 1] = (byte) ((value >> 16) & 0xFF);
1344        out[pos + 2] = (byte) ((value >>  8) & 0xFF);
1345        out[pos + 3] = (byte) ((value >>  0) & 0xFF);
1346        return pos + INTEGER_BYTE_COUNT;
1347    }
1348
1349    private int writeBytes(byte[] out, int pos, byte[] value) {
1350        System.arraycopy(value, 0, out, pos, value.length);
1351        return pos + value.length;
1352    }
1353
1354    private int readInt(byte[] in, int pos) {
1355        int result = ((in[pos] & 0xFF) << 24)
1356                | ((in[pos + 1] & 0xFF) << 16)
1357                | ((in[pos + 2] & 0xFF) <<  8)
1358                | ((in[pos + 3] & 0xFF) <<  0);
1359        return result;
1360    }
1361
1362    private int enableWifi(boolean enable) {
1363        if (mWfm == null) {
1364            mWfm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
1365        }
1366        if (mWfm != null) {
1367            int state = mWfm.getWifiState();
1368            mWfm.setWifiEnabled(enable);
1369            return state;
1370        } else {
1371            Log.e(TAG, "Failed to fetch WifiManager instance");
1372        }
1373        return WifiManager.WIFI_STATE_UNKNOWN;
1374    }
1375}
1376