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