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 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 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 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 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 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 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 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 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 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 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 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 */ 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 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 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 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 */ 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 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 */ 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 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 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 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 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 878 private byte[] getNewWifiConfigData() { 879 return mWifiManager.retrieveBackupData(); 880 } 881 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 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 */ 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 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 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