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