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