Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.providers.settings;
     18 
     19 import android.app.backup.BackupAgentHelper;
     20 import android.app.backup.BackupDataInput;
     21 import android.app.backup.BackupDataOutput;
     22 import android.app.backup.FullBackupDataOutput;
     23 import android.content.ContentValues;
     24 import android.content.Context;
     25 import android.database.Cursor;
     26 import android.net.Uri;
     27 import android.net.wifi.WifiManager;
     28 import android.os.FileUtils;
     29 import android.os.ParcelFileDescriptor;
     30 import android.os.Process;
     31 import android.provider.Settings;
     32 import android.util.Log;
     33 
     34 import java.io.BufferedOutputStream;
     35 import java.io.BufferedReader;
     36 import java.io.BufferedWriter;
     37 import java.io.CharArrayReader;
     38 import java.io.DataInputStream;
     39 import java.io.DataOutputStream;
     40 import java.io.EOFException;
     41 import java.io.File;
     42 import java.io.FileInputStream;
     43 import java.io.FileOutputStream;
     44 import java.io.FileReader;
     45 import java.io.FileWriter;
     46 import java.io.IOException;
     47 import java.io.InputStream;
     48 import java.io.OutputStream;
     49 import java.io.Reader;
     50 import java.io.Writer;
     51 import java.util.ArrayList;
     52 import java.util.HashMap;
     53 import java.util.HashSet;
     54 import java.util.Map;
     55 import java.util.zip.CRC32;
     56 
     57 /**
     58  * Performs backup and restore of the System and Secure settings.
     59  * List of settings that are backed up are stored in the Settings.java file
     60  */
     61 public class SettingsBackupAgent extends BackupAgentHelper {
     62     private static final boolean DEBUG = false;
     63     private static final boolean DEBUG_BACKUP = DEBUG || false;
     64 
     65     private static final String KEY_SYSTEM = "system";
     66     private static final String KEY_SECURE = "secure";
     67     private static final String KEY_LOCALE = "locale";
     68 
     69     //Version 2 adds STATE_WIFI_CONFIG
     70     private static final int STATE_VERSION_1       = 1;
     71     private static final int STATE_VERSION_1_SIZE  = 4;
     72 
     73     // Versioning of the state file.  Increment this version
     74     // number any time the set of state items is altered.
     75     private static final int STATE_VERSION = 2;
     76 
     77     private static final int STATE_SYSTEM          = 0;
     78     private static final int STATE_SECURE          = 1;
     79     private static final int STATE_LOCALE          = 2;
     80     private static final int STATE_WIFI_SUPPLICANT = 3;
     81     private static final int STATE_WIFI_CONFIG     = 4;
     82     private static final int STATE_SIZE            = 5; // The number of state items
     83 
     84     // Versioning of the 'full backup' format
     85     private static final int FULL_BACKUP_VERSION = 1;
     86 
     87     private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
     88 
     89     private static final byte[] EMPTY_DATA = new byte[0];
     90 
     91     private static final String TAG = "SettingsBackupAgent";
     92 
     93     private static final int COLUMN_NAME = 1;
     94     private static final int COLUMN_VALUE = 2;
     95 
     96     private static final String[] PROJECTION = {
     97         Settings.NameValueTable._ID,
     98         Settings.NameValueTable.NAME,
     99         Settings.NameValueTable.VALUE
    100     };
    101 
    102     private static final String FILE_WIFI_SUPPLICANT = "/data/misc/wifi/wpa_supplicant.conf";
    103     private static final String FILE_WIFI_SUPPLICANT_TEMPLATE =
    104             "/system/etc/wifi/wpa_supplicant.conf";
    105 
    106     // the key to store the WIFI data under, should be sorted as last, so restore happens last.
    107     // use very late unicode character to quasi-guarantee last sort position.
    108     private static final String KEY_WIFI_SUPPLICANT = "\uffedWIFI";
    109     private static final String KEY_WIFI_CONFIG = "\uffedCONFIG_WIFI";
    110 
    111     // Name of the temporary file we use during full backup/restore.  This is
    112     // stored in the full-backup tarfile as well, so should not be changed.
    113     private static final String STAGE_FILE = "flattened-data";
    114 
    115     private SettingsHelper mSettingsHelper;
    116     private WifiManager mWfm;
    117     private static String mWifiConfigFile;
    118 
    119     // Class for capturing a network definition from the wifi supplicant config file
    120     static class Network {
    121         String ssid = "";  // equals() and hashCode() need these to be non-null
    122         String key_mgmt = "";
    123         final ArrayList<String> rawLines = new ArrayList<String>();
    124 
    125         public static Network readFromStream(BufferedReader in) {
    126             final Network n = new Network();
    127             String line;
    128             try {
    129                 while (in.ready()) {
    130                     line = in.readLine();
    131                     if (line == null || line.startsWith("}")) {
    132                         break;
    133                     }
    134                     n.rememberLine(line);
    135                 }
    136             } catch (IOException e) {
    137                 return null;
    138             }
    139             return n;
    140         }
    141 
    142         void rememberLine(String line) {
    143             // can't rely on particular whitespace patterns so strip leading/trailing
    144             line = line.trim();
    145             if (line.isEmpty()) return; // only whitespace; drop the line
    146             rawLines.add(line);
    147 
    148             // remember the ssid and key_mgmt lines for duplicate culling
    149             if (line.startsWith("ssid")) {
    150                 ssid = line;
    151             } else if (line.startsWith("key_mgmt")) {
    152                 key_mgmt = line;
    153             }
    154         }
    155 
    156         public void write(Writer w) throws IOException {
    157             w.write("\nnetwork={\n");
    158             for (String line : rawLines) {
    159                 w.write("\t" + line + "\n");
    160             }
    161             w.write("}\n");
    162         }
    163 
    164         public void dump() {
    165             Log.v(TAG, "network={");
    166             for (String line : rawLines) {
    167                 Log.v(TAG, "   " + line);
    168             }
    169             Log.v(TAG, "}");
    170         }
    171 
    172         // Same approach as Pair.equals() and Pair.hashCode()
    173         @Override
    174         public boolean equals(Object o) {
    175             if (o == this) return true;
    176             if (!(o instanceof Network)) return false;
    177             final Network other;
    178             try {
    179                 other = (Network) o;
    180             } catch (ClassCastException e) {
    181                 return false;
    182             }
    183             return ssid.equals(other.ssid) && key_mgmt.equals(other.key_mgmt);
    184         }
    185 
    186         @Override
    187         public int hashCode() {
    188             int result = 17;
    189             result = 31 * result + ssid.hashCode();
    190             result = 31 * result + key_mgmt.hashCode();
    191             return result;
    192         }
    193     }
    194 
    195     // Ingest multiple wifi config file fragments, looking for network={} blocks
    196     // and eliminating duplicates
    197     class WifiNetworkSettings {
    198         // One for fast lookup, one for maintaining ordering
    199         final HashSet<Network> mKnownNetworks = new HashSet<Network>();
    200         final ArrayList<Network> mNetworks = new ArrayList<Network>(8);
    201 
    202         public void readNetworks(BufferedReader in) {
    203             try {
    204                 String line;
    205                 while (in.ready()) {
    206                     line = in.readLine();
    207                     if (line != null) {
    208                         // Parse out 'network=' decls so we can ignore duplicates
    209                         if (line.startsWith("network")) {
    210                             Network net = Network.readFromStream(in);
    211                             if (! mKnownNetworks.contains(net)) {
    212                                 if (DEBUG_BACKUP) {
    213                                     Log.v(TAG, "Adding " + net.ssid + " / " + net.key_mgmt);
    214                                 }
    215                                 mKnownNetworks.add(net);
    216                                 mNetworks.add(net);
    217                             } else {
    218                                 if (DEBUG_BACKUP) {
    219                                     Log.v(TAG, "Dupe; skipped " + net.ssid + " / " + net.key_mgmt);
    220                                 }
    221                             }
    222                         }
    223                     }
    224                 }
    225             } catch (IOException e) {
    226                 // whatever happened, we're done now
    227             }
    228         }
    229 
    230         public void write(Writer w) throws IOException {
    231             for (Network net : mNetworks) {
    232                 net.write(w);
    233             }
    234         }
    235 
    236         public void dump() {
    237             for (Network net : mNetworks) {
    238                 net.dump();
    239             }
    240         }
    241     }
    242 
    243     @Override
    244     public void onCreate() {
    245         if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
    246 
    247         mSettingsHelper = new SettingsHelper(this);
    248         super.onCreate();
    249 
    250         WifiManager mWfm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    251         if (mWfm != null) mWifiConfigFile = mWfm.getConfigFile();
    252     }
    253 
    254     @Override
    255     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
    256             ParcelFileDescriptor newState) throws IOException {
    257 
    258         byte[] systemSettingsData = getSystemSettings();
    259         byte[] secureSettingsData = getSecureSettings();
    260         byte[] locale = mSettingsHelper.getLocaleData();
    261         byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
    262         byte[] wifiConfigData = getFileData(mWifiConfigFile);
    263 
    264         long[] stateChecksums = readOldChecksums(oldState);
    265 
    266         stateChecksums[STATE_SYSTEM] =
    267             writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
    268         stateChecksums[STATE_SECURE] =
    269             writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
    270         stateChecksums[STATE_LOCALE] =
    271             writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
    272         stateChecksums[STATE_WIFI_SUPPLICANT] =
    273             writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT,
    274                     wifiSupplicantData, data);
    275         stateChecksums[STATE_WIFI_CONFIG] =
    276             writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData,
    277                     data);
    278 
    279         writeNewChecksums(stateChecksums, newState);
    280     }
    281 
    282     @Override
    283     public void onRestore(BackupDataInput data, int appVersionCode,
    284             ParcelFileDescriptor newState) throws IOException {
    285 
    286         while (data.readNextHeader()) {
    287             final String key = data.getKey();
    288             final int size = data.getDataSize();
    289             if (KEY_SYSTEM.equals(key)) {
    290                 restoreSettings(data, Settings.System.CONTENT_URI);
    291                 mSettingsHelper.applyAudioSettings();
    292             } else if (KEY_SECURE.equals(key)) {
    293                 restoreSettings(data, Settings.Secure.CONTENT_URI);
    294             } else if (KEY_WIFI_SUPPLICANT.equals(key)) {
    295                 int retainedWifiState = enableWifi(false);
    296                 restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, data);
    297                 FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
    298                         FileUtils.S_IRUSR | FileUtils.S_IWUSR |
    299                         FileUtils.S_IRGRP | FileUtils.S_IWGRP,
    300                         Process.myUid(), Process.WIFI_UID);
    301                 // retain the previous WIFI state.
    302                 enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
    303                         retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
    304             } else if (KEY_LOCALE.equals(key)) {
    305                 byte[] localeData = new byte[size];
    306                 data.readEntityData(localeData, 0, size);
    307                 mSettingsHelper.setLocaleData(localeData, size);
    308             } else if (KEY_WIFI_CONFIG.equals(key)) {
    309                 restoreFileData(mWifiConfigFile, data);
    310              } else {
    311                 data.skipEntityData();
    312             }
    313         }
    314     }
    315 
    316     @Override
    317     public void onFullBackup(FullBackupDataOutput data)  throws IOException {
    318         byte[] systemSettingsData = getSystemSettings();
    319         byte[] secureSettingsData = getSecureSettings();
    320         byte[] locale = mSettingsHelper.getLocaleData();
    321         byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
    322         byte[] wifiConfigData = getFileData(mWifiConfigFile);
    323 
    324         // Write the data to the staging file, then emit that as our tarfile
    325         // representation of the backed-up settings.
    326         String root = getFilesDir().getAbsolutePath();
    327         File stage = new File(root, STAGE_FILE);
    328         try {
    329             FileOutputStream filestream = new FileOutputStream(stage);
    330             BufferedOutputStream bufstream = new BufferedOutputStream(filestream);
    331             DataOutputStream out = new DataOutputStream(bufstream);
    332 
    333             if (DEBUG_BACKUP) Log.d(TAG, "Writing flattened data version " + FULL_BACKUP_VERSION);
    334             out.writeInt(FULL_BACKUP_VERSION);
    335 
    336             if (DEBUG_BACKUP) Log.d(TAG, systemSettingsData.length + " bytes of settings data");
    337             out.writeInt(systemSettingsData.length);
    338             out.write(systemSettingsData);
    339             if (DEBUG_BACKUP) Log.d(TAG, secureSettingsData.length + " bytes of secure settings data");
    340             out.writeInt(secureSettingsData.length);
    341             out.write(secureSettingsData);
    342             if (DEBUG_BACKUP) Log.d(TAG, locale.length + " bytes of locale data");
    343             out.writeInt(locale.length);
    344             out.write(locale);
    345             if (DEBUG_BACKUP) Log.d(TAG, wifiSupplicantData.length + " bytes of wifi supplicant data");
    346             out.writeInt(wifiSupplicantData.length);
    347             out.write(wifiSupplicantData);
    348             if (DEBUG_BACKUP) Log.d(TAG, wifiConfigData.length + " bytes of wifi config data");
    349             out.writeInt(wifiConfigData.length);
    350             out.write(wifiConfigData);
    351 
    352             out.flush();    // also flushes downstream
    353 
    354             // now we're set to emit the tar stream
    355             fullBackupFile(stage, data);
    356         } finally {
    357             stage.delete();
    358         }
    359     }
    360 
    361     @Override
    362     public void onRestoreFile(ParcelFileDescriptor data, long size,
    363             int type, String domain, String relpath, long mode, long mtime)
    364             throws IOException {
    365         if (DEBUG_BACKUP) Log.d(TAG, "onRestoreFile() invoked");
    366         // Our data is actually a blob of flattened settings data identical to that
    367         // produced during incremental backups.  Just unpack and apply it all in
    368         // turn.
    369         FileInputStream instream = new FileInputStream(data.getFileDescriptor());
    370         DataInputStream in = new DataInputStream(instream);
    371 
    372         int version = in.readInt();
    373         if (DEBUG_BACKUP) Log.d(TAG, "Flattened data version " + version);
    374         if (version == FULL_BACKUP_VERSION) {
    375             // system settings data first
    376             int nBytes = in.readInt();
    377             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of settings data");
    378             byte[] buffer = new byte[nBytes];
    379             in.readFully(buffer, 0, nBytes);
    380             restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI);
    381 
    382             // secure settings
    383             nBytes = in.readInt();
    384             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data");
    385             if (nBytes > buffer.length) buffer = new byte[nBytes];
    386             in.readFully(buffer, 0, nBytes);
    387             restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI);
    388 
    389             // locale
    390             nBytes = in.readInt();
    391             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of locale data");
    392             if (nBytes > buffer.length) buffer = new byte[nBytes];
    393             in.readFully(buffer, 0, nBytes);
    394             mSettingsHelper.setLocaleData(buffer, nBytes);
    395 
    396             // wifi supplicant
    397             nBytes = in.readInt();
    398             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of wifi supplicant data");
    399             if (nBytes > buffer.length) buffer = new byte[nBytes];
    400             in.readFully(buffer, 0, nBytes);
    401             int retainedWifiState = enableWifi(false);
    402             restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, buffer, nBytes);
    403             FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
    404                     FileUtils.S_IRUSR | FileUtils.S_IWUSR |
    405                     FileUtils.S_IRGRP | FileUtils.S_IWGRP,
    406                     Process.myUid(), Process.WIFI_UID);
    407             // retain the previous WIFI state.
    408             enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
    409                     retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
    410 
    411             // wifi config
    412             nBytes = in.readInt();
    413             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of wifi config data");
    414             if (nBytes > buffer.length) buffer = new byte[nBytes];
    415             in.readFully(buffer, 0, nBytes);
    416             restoreFileData(mWifiConfigFile, buffer, nBytes);
    417 
    418             if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
    419         } else {
    420             data.close();
    421             throw new IOException("Invalid file schema");
    422         }
    423     }
    424 
    425     private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException {
    426         long[] stateChecksums = new long[STATE_SIZE];
    427 
    428         DataInputStream dataInput = new DataInputStream(
    429                 new FileInputStream(oldState.getFileDescriptor()));
    430 
    431         try {
    432             int stateVersion = dataInput.readInt();
    433             if (stateVersion == STATE_VERSION_1) {
    434                 for (int i = 0; i < STATE_VERSION_1_SIZE; i++) {
    435                     stateChecksums[i] = dataInput.readLong();
    436                 }
    437             } else if (stateVersion == STATE_VERSION) {
    438                 for (int i = 0; i < STATE_SIZE; i++) {
    439                     stateChecksums[i] = dataInput.readLong();
    440                 }
    441             }
    442         } catch (EOFException eof) {
    443             // With the default 0 checksum we'll wind up forcing a backup of
    444             // any unhandled data sets, which is appropriate.
    445         }
    446         dataInput.close();
    447         return stateChecksums;
    448     }
    449 
    450     private void writeNewChecksums(long[] checksums, ParcelFileDescriptor newState)
    451             throws IOException {
    452         DataOutputStream dataOutput = new DataOutputStream(
    453                 new FileOutputStream(newState.getFileDescriptor()));
    454 
    455         dataOutput.writeInt(STATE_VERSION);
    456         for (int i = 0; i < STATE_SIZE; i++) {
    457             dataOutput.writeLong(checksums[i]);
    458         }
    459         dataOutput.close();
    460     }
    461 
    462     private long writeIfChanged(long oldChecksum, String key, byte[] data,
    463             BackupDataOutput output) {
    464         CRC32 checkSummer = new CRC32();
    465         checkSummer.update(data);
    466         long newChecksum = checkSummer.getValue();
    467         if (oldChecksum == newChecksum) {
    468             return oldChecksum;
    469         }
    470         try {
    471             output.writeEntityHeader(key, data.length);
    472             output.writeEntityData(data, data.length);
    473         } catch (IOException ioe) {
    474             // Bail
    475         }
    476         return newChecksum;
    477     }
    478 
    479     private byte[] getSystemSettings() {
    480         Cursor cursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION, null,
    481                 null, null);
    482         try {
    483             return extractRelevantValues(cursor, Settings.System.SETTINGS_TO_BACKUP);
    484         } finally {
    485             cursor.close();
    486         }
    487     }
    488 
    489     private byte[] getSecureSettings() {
    490         Cursor cursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION, null,
    491                 null, null);
    492         try {
    493             return extractRelevantValues(cursor, Settings.Secure.SETTINGS_TO_BACKUP);
    494         } finally {
    495             cursor.close();
    496         }
    497     }
    498 
    499     private void restoreSettings(BackupDataInput data, Uri contentUri) {
    500         byte[] settings = new byte[data.getDataSize()];
    501         try {
    502             data.readEntityData(settings, 0, settings.length);
    503         } catch (IOException ioe) {
    504             Log.e(TAG, "Couldn't read entity data");
    505             return;
    506         }
    507         restoreSettings(settings, settings.length, contentUri);
    508     }
    509 
    510     private void restoreSettings(byte[] settings, int bytes, Uri contentUri) {
    511         if (DEBUG) {
    512             Log.i(TAG, "restoreSettings: " + contentUri);
    513         }
    514 
    515         // Figure out the white list.
    516         String[] whitelist = null;
    517         if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
    518             whitelist = Settings.Secure.SETTINGS_TO_BACKUP;
    519         } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
    520             whitelist = Settings.System.SETTINGS_TO_BACKUP;
    521         } else {
    522             throw new IllegalArgumentException("Unknown URI: " + contentUri);
    523         }
    524 
    525         // Restore only the white list data.
    526         int pos = 0;
    527         Map<String, String> cachedEntries = new HashMap<String, String>();
    528         ContentValues contentValues = new ContentValues(2);
    529         SettingsHelper settingsHelper = mSettingsHelper;
    530 
    531         final int whiteListSize = whitelist.length;
    532         for (int i = 0; i < whiteListSize; i++) {
    533             String key = whitelist[i];
    534             String value = cachedEntries.remove(key);
    535 
    536             // If the value not cached, let us look it up.
    537             if (value == null) {
    538                 while (pos < bytes) {
    539                     int length = readInt(settings, pos);
    540                     pos += INTEGER_BYTE_COUNT;
    541                     String dataKey = length > 0 ? new String(settings, pos, length) : null;
    542                     pos += length;
    543                     length = readInt(settings, pos);
    544                     pos += INTEGER_BYTE_COUNT;
    545                     String dataValue = length > 0 ? new String(settings, pos, length) : null;
    546                     pos += length;
    547                     if (key.equals(dataKey)) {
    548                         value = dataValue;
    549                         break;
    550                     }
    551                     cachedEntries.put(dataKey, dataValue);
    552                 }
    553             }
    554 
    555             if (value == null) {
    556                 continue;
    557             }
    558 
    559             if (settingsHelper.restoreValue(key, value)) {
    560                 contentValues.clear();
    561                 contentValues.put(Settings.NameValueTable.NAME, key);
    562                 contentValues.put(Settings.NameValueTable.VALUE, value);
    563                 getContentResolver().insert(contentUri, contentValues);
    564             }
    565 
    566             if (DEBUG) {
    567                 Log.d(TAG, "Restored setting: " + key + "=" + value);
    568             }
    569         }
    570     }
    571 
    572     /**
    573      * Given a cursor and a set of keys, extract the required keys and
    574      * values and write them to a byte array.
    575      *
    576      * @param cursor A cursor with settings data.
    577      * @param settings The settings to extract.
    578      * @return The byte array of extracted values.
    579      */
    580     private byte[] extractRelevantValues(Cursor cursor, String[] settings) {
    581         final int settingsCount = settings.length;
    582         byte[][] values = new byte[settingsCount * 2][]; // keys and values
    583         if (!cursor.moveToFirst()) {
    584             Log.e(TAG, "Couldn't read from the cursor");
    585             return new byte[0];
    586         }
    587 
    588         // Obtain the relevant data in a temporary array.
    589         int totalSize = 0;
    590         int backedUpSettingIndex = 0;
    591         Map<String, String> cachedEntries = new HashMap<String, String>();
    592         for (int i = 0; i < settingsCount; i++) {
    593             String key = settings[i];
    594             String value = cachedEntries.remove(key);
    595 
    596             // If the value not cached, let us look it up.
    597             if (value == null) {
    598                 while (!cursor.isAfterLast()) {
    599                     String cursorKey = cursor.getString(COLUMN_NAME);
    600                     String cursorValue = cursor.getString(COLUMN_VALUE);
    601                     cursor.moveToNext();
    602                     if (key.equals(cursorKey)) {
    603                         value = cursorValue;
    604                         break;
    605                     }
    606                     cachedEntries.put(cursorKey, cursorValue);
    607                 }
    608             }
    609 
    610             if (value == null) {
    611                 continue;
    612             }
    613 
    614             // Write the key and value in the intermediary array.
    615             byte[] keyBytes = key.getBytes();
    616             totalSize += INTEGER_BYTE_COUNT + keyBytes.length;
    617             values[backedUpSettingIndex * 2] = keyBytes;
    618 
    619             byte[] valueBytes = value.getBytes();
    620             totalSize += INTEGER_BYTE_COUNT + valueBytes.length;
    621             values[backedUpSettingIndex * 2 + 1] = valueBytes;
    622 
    623             backedUpSettingIndex++;
    624 
    625             if (DEBUG) {
    626                 Log.d(TAG, "Backed up setting: " + key + "=" + value);
    627             }
    628         }
    629 
    630         // Aggregate the result.
    631         byte[] result = new byte[totalSize];
    632         int pos = 0;
    633         final int keyValuePairCount = backedUpSettingIndex * 2;
    634         for (int i = 0; i < keyValuePairCount; i++) {
    635             pos = writeInt(result, pos, values[i].length);
    636             pos = writeBytes(result, pos, values[i]);
    637         }
    638         return result;
    639     }
    640 
    641     private byte[] getFileData(String filename) {
    642         InputStream is = null;
    643         try {
    644             File file = new File(filename);
    645             is = new FileInputStream(file);
    646 
    647             //Will truncate read on a very long file,
    648             //should not happen for a config file
    649             byte[] bytes = new byte[(int)file.length()];
    650 
    651             int offset = 0;
    652             int numRead = 0;
    653             while (offset < bytes.length
    654                     && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
    655                 offset += numRead;
    656             }
    657 
    658             //read failure
    659             if (offset < bytes.length) {
    660                 Log.w(TAG, "Couldn't backup " + filename);
    661                 return EMPTY_DATA;
    662             }
    663             return bytes;
    664         } catch (IOException ioe) {
    665             Log.w(TAG, "Couldn't backup " + filename);
    666             return EMPTY_DATA;
    667         } finally {
    668             if (is != null) {
    669                 try {
    670                     is.close();
    671                 } catch (IOException e) {
    672                 }
    673             }
    674         }
    675 
    676     }
    677 
    678     private void restoreFileData(String filename, BackupDataInput data) {
    679         byte[] bytes = new byte[data.getDataSize()];
    680         if (bytes.length <= 0) return;
    681         try {
    682             data.readEntityData(bytes, 0, data.getDataSize());
    683             restoreFileData(filename, bytes, bytes.length);
    684         } catch (IOException e) {
    685             Log.w(TAG, "Unable to read file data for " + filename);
    686         }
    687     }
    688 
    689     private void restoreFileData(String filename, byte[] bytes, int size) {
    690         try {
    691             File file = new File(filename);
    692             if (file.exists()) file.delete();
    693 
    694             OutputStream os = new BufferedOutputStream(new FileOutputStream(filename, true));
    695             os.write(bytes, 0, size);
    696             os.close();
    697         } catch (IOException ioe) {
    698             Log.w(TAG, "Couldn't restore " + filename);
    699         }
    700     }
    701 
    702 
    703     private byte[] getWifiSupplicant(String filename) {
    704         BufferedReader br = null;
    705         try {
    706             File file = new File(filename);
    707             if (file.exists()) {
    708                 br = new BufferedReader(new FileReader(file));
    709                 StringBuffer relevantLines = new StringBuffer();
    710                 boolean started = false;
    711                 String line;
    712                 while ((line = br.readLine()) != null) {
    713                     if (!started && line.startsWith("network")) {
    714                         started = true;
    715                     }
    716                     if (started) {
    717                         relevantLines.append(line).append("\n");
    718                     }
    719                 }
    720                 if (relevantLines.length() > 0) {
    721                     return relevantLines.toString().getBytes();
    722                 } else {
    723                     return EMPTY_DATA;
    724                 }
    725             } else {
    726                 return EMPTY_DATA;
    727             }
    728         } catch (IOException ioe) {
    729             Log.w(TAG, "Couldn't backup " + filename);
    730             return EMPTY_DATA;
    731         } finally {
    732             if (br != null) {
    733                 try {
    734                     br.close();
    735                 } catch (IOException e) {
    736                 }
    737             }
    738         }
    739     }
    740 
    741     private void restoreWifiSupplicant(String filename, BackupDataInput data) {
    742         byte[] bytes = new byte[data.getDataSize()];
    743         if (bytes.length <= 0) return;
    744         try {
    745             data.readEntityData(bytes, 0, data.getDataSize());
    746             restoreWifiSupplicant(filename, bytes, bytes.length);
    747         } catch (IOException e) {
    748             Log.w(TAG, "Unable to read supplicant data");
    749         }
    750     }
    751 
    752     private void restoreWifiSupplicant(String filename, byte[] bytes, int size) {
    753         try {
    754             WifiNetworkSettings supplicantImage = new WifiNetworkSettings();
    755 
    756             File supplicantFile = new File(FILE_WIFI_SUPPLICANT);
    757             if (supplicantFile.exists()) {
    758                 // Retain the existing APs; we'll append the restored ones to them
    759                 BufferedReader in = new BufferedReader(new FileReader(FILE_WIFI_SUPPLICANT));
    760                 supplicantImage.readNetworks(in);
    761                 in.close();
    762 
    763                 supplicantFile.delete();
    764             }
    765 
    766             // Incorporate the restore AP information
    767             if (size > 0) {
    768                 char[] restoredAsBytes = new char[size];
    769                 for (int i = 0; i < size; i++) restoredAsBytes[i] = (char) bytes[i];
    770                 BufferedReader in = new BufferedReader(new CharArrayReader(restoredAsBytes));
    771                 supplicantImage.readNetworks(in);
    772 
    773                 if (DEBUG_BACKUP) {
    774                     Log.v(TAG, "Final AP list:");
    775                     supplicantImage.dump();
    776                 }
    777             }
    778 
    779             // Install the correct default template
    780             BufferedWriter bw = new BufferedWriter(new FileWriter(FILE_WIFI_SUPPLICANT));
    781             copyWifiSupplicantTemplate(bw);
    782 
    783             // Write the restored supplicant config and we're done
    784             supplicantImage.write(bw);
    785             bw.close();
    786         } catch (IOException ioe) {
    787             Log.w(TAG, "Couldn't restore " + filename);
    788         }
    789     }
    790 
    791     private void copyWifiSupplicantTemplate(BufferedWriter bw) {
    792         try {
    793             BufferedReader br = new BufferedReader(new FileReader(FILE_WIFI_SUPPLICANT_TEMPLATE));
    794             char[] temp = new char[1024];
    795             int size;
    796             while ((size = br.read(temp)) > 0) {
    797                 bw.write(temp, 0, size);
    798             }
    799             br.close();
    800         } catch (IOException ioe) {
    801             Log.w(TAG, "Couldn't copy wpa_supplicant file");
    802         }
    803     }
    804 
    805     /**
    806      * Write an int in BigEndian into the byte array.
    807      * @param out byte array
    808      * @param pos current pos in array
    809      * @param value integer to write
    810      * @return the index after adding the size of an int (4) in bytes.
    811      */
    812     private int writeInt(byte[] out, int pos, int value) {
    813         out[pos + 0] = (byte) ((value >> 24) & 0xFF);
    814         out[pos + 1] = (byte) ((value >> 16) & 0xFF);
    815         out[pos + 2] = (byte) ((value >>  8) & 0xFF);
    816         out[pos + 3] = (byte) ((value >>  0) & 0xFF);
    817         return pos + INTEGER_BYTE_COUNT;
    818     }
    819 
    820     private int writeBytes(byte[] out, int pos, byte[] value) {
    821         System.arraycopy(value, 0, out, pos, value.length);
    822         return pos + value.length;
    823     }
    824 
    825     private int readInt(byte[] in, int pos) {
    826         int result =
    827                 ((in[pos    ] & 0xFF) << 24) |
    828                 ((in[pos + 1] & 0xFF) << 16) |
    829                 ((in[pos + 2] & 0xFF) <<  8) |
    830                 ((in[pos + 3] & 0xFF) <<  0);
    831         return result;
    832     }
    833 
    834     private int enableWifi(boolean enable) {
    835         if (mWfm == null) {
    836             mWfm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    837         }
    838         if (mWfm != null) {
    839             int state = mWfm.getWifiState();
    840             mWfm.setWifiEnabled(enable);
    841             return state;
    842         } else {
    843             Log.e(TAG, "Failed to fetch WifiManager instance");
    844         }
    845         return WifiManager.WIFI_STATE_UNKNOWN;
    846     }
    847 }
    848