1616
1717package com .android .providers .settings ;
1818
19+ import android .app .backup .BackupAgentHelper ;
20+ import android .app .backup .BackupDataInput ;
21+ import android .app .backup .BackupDataOutput ;
22+ import android .app .backup .FullBackupDataOutput ;
23+ import android .content .ContentValues ;
24+ import android .content .Context ;
25+ import android .database .Cursor ;
26+ import android .net .Uri ;
27+ import android .net .wifi .WifiManager ;
28+ import android .os .FileUtils ;
29+ import android .os .ParcelFileDescriptor ;
30+ import android .os .Process ;
31+ import android .provider .Settings ;
32+ import android .util .Log ;
33+
1934import java .io .BufferedOutputStream ;
2035import java .io .BufferedReader ;
2136import java .io .BufferedWriter ;
2742import java .io .FileOutputStream ;
2843import java .io .FileReader ;
2944import java .io .FileWriter ;
30- import java .io .InputStream ;
3145import java .io .IOException ;
46+ import java .io .InputStream ;
3247import java .io .OutputStream ;
33- import java .util .Arrays ;
48+ import java .util .HashMap ;
49+ import java .util .Map ;
3450import java .util .zip .CRC32 ;
3551
36- import android .app .backup .BackupDataInput ;
37- import android .app .backup .BackupDataOutput ;
38- import android .app .backup .BackupAgentHelper ;
39- import android .app .backup .FullBackupDataOutput ;
40- import android .content .ContentValues ;
41- import android .content .Context ;
42- import android .database .Cursor ;
43- import android .net .Uri ;
44- import android .net .wifi .WifiManager ;
45- import android .os .FileUtils ;
46- import android .os .ParcelFileDescriptor ;
47- import android .os .Process ;
48- import android .provider .Settings ;
49- import android .text .TextUtils ;
50- import android .util .Log ;
51-
5252/**
5353 * Performs backup and restore of the System and Secure settings.
5454 * List of settings that are backed up are stored in the Settings.java file
@@ -79,8 +79,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
7979 // Versioning of the 'full backup' format
8080 private static final int FULL_BACKUP_VERSION = 1 ;
8181
82- private static String [] sortedSystemKeys = null ;
83- private static String [] sortedSecureKeys = null ;
82+ private static final int INTEGER_BYTE_COUNT = Integer .SIZE / Byte .SIZE ;
8483
8584 private static final byte [] EMPTY_DATA = new byte [0 ];
8685
@@ -112,6 +111,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
112111 private WifiManager mWfm ;
113112 private static String mWifiConfigFile ;
114113
114+ @ Override
115115 public void onCreate () {
116116 if (DEBUG_BACKUP ) Log .d (TAG , "onCreate() invoked" );
117117
@@ -348,26 +348,17 @@ private long writeIfChanged(long oldChecksum, String key, byte[] data,
348348 }
349349
350350 private byte [] getSystemSettings () {
351- Cursor sortedCursor = getContentResolver ().query (Settings .System .CONTENT_URI , PROJECTION ,
352- null , null , Settings .NameValueTable .NAME );
353- // Copy and sort the array
354- if (sortedSystemKeys == null ) {
355- sortedSystemKeys = copyAndSort (Settings .System .SETTINGS_TO_BACKUP );
356- }
357- byte [] result = extractRelevantValues (sortedCursor , sortedSystemKeys );
358- sortedCursor .close ();
351+ Cursor cursor = getContentResolver ().query (Settings .System .CONTENT_URI , PROJECTION , null ,
352+ null , null );
353+ byte [] result = extractRelevantValues (cursor , Settings .System .SETTINGS_TO_BACKUP );
354+ cursor .close ();
359355 return result ;
360356 }
361357
362358 private byte [] getSecureSettings () {
363- Cursor sortedCursor = getContentResolver ().query (Settings .Secure .CONTENT_URI , PROJECTION ,
364- null , null , Settings .NameValueTable .NAME );
365- // Copy and sort the array
366- if (sortedSecureKeys == null ) {
367- sortedSecureKeys = copyAndSort (Settings .Secure .SETTINGS_TO_BACKUP );
368- }
369- byte [] result = extractRelevantValues (sortedCursor , sortedSecureKeys );
370- sortedCursor .close ();
359+ Cursor cursor = getContentResolver ().query (Settings .Secure .CONTENT_URI , PROJECTION , null ,
360+ null , null );
361+ byte [] result = extractRelevantValues (cursor , Settings .Secure .SETTINGS_TO_BACKUP );
371362 return result ;
372363 }
373364
@@ -383,119 +374,132 @@ private void restoreSettings(BackupDataInput data, Uri contentUri) {
383374 }
384375
385376 private void restoreSettings (byte [] settings , int bytes , Uri contentUri ) {
386- if (DEBUG ) Log .i (TAG , "restoreSettings: " + contentUri );
377+ if (DEBUG ) {
378+ Log .i (TAG , "restoreSettings: " + contentUri );
379+ }
380+
381+ // Figure out the white list.
387382 String [] whitelist = null ;
388383 if (contentUri .equals (Settings .Secure .CONTENT_URI )) {
389384 whitelist = Settings .Secure .SETTINGS_TO_BACKUP ;
390385 } else if (contentUri .equals (Settings .System .CONTENT_URI )) {
391386 whitelist = Settings .System .SETTINGS_TO_BACKUP ;
387+ } else {
388+ throw new IllegalArgumentException ("Unknown URI: " + contentUri );
392389 }
393390
394- ContentValues cv = new ContentValues ( 2 );
391+ // Restore only the white list data.
395392 int pos = 0 ;
396- while (pos < bytes ) {
397- int length = readInt (settings , pos );
398- pos += 4 ;
399- String settingName = length > 0 ? new String (settings , pos , length ) : null ;
400- pos += length ;
401- length = readInt (settings , pos );
402- pos += 4 ;
403- String settingValue = length > 0 ? new String (settings , pos , length ) : null ;
404- pos += length ;
405- if (!TextUtils .isEmpty (settingName ) && !TextUtils .isEmpty (settingValue )) {
406- //Log.i(TAG, "Restore " + settingName + " = " + settingValue);
407-
408- // Only restore settings in our list of known-acceptable data
409- if (invalidSavedSetting (whitelist , settingName )) {
410- continue ;
393+ Map <String , String > cachedEntries = new HashMap <String , String >();
394+ ContentValues contentValues = new ContentValues (2 );
395+ SettingsHelper settingsHelper = mSettingsHelper ;
396+
397+ final int whiteListSize = whitelist .length ;
398+ for (int i = 0 ; i < whiteListSize ; i ++) {
399+ String key = whitelist [i ];
400+ String value = cachedEntries .remove (key );
401+
402+ // If the value not cached, let us look it up.
403+ if (value == null ) {
404+ while (pos < bytes ) {
405+ int length = readInt (settings , pos );
406+ pos += INTEGER_BYTE_COUNT ;
407+ String dataKey = length > 0 ? new String (settings , pos , length ) : null ;
408+ pos += length ;
409+ length = readInt (settings , pos );
410+ pos += INTEGER_BYTE_COUNT ;
411+ String dataValue = length > 0 ? new String (settings , pos , length ) : null ;
412+ pos += length ;
413+ if (key .equals (dataKey )) {
414+ value = dataValue ;
415+ break ;
416+ }
417+ cachedEntries .put (dataKey , dataValue );
411418 }
419+ }
412420
413- if (mSettingsHelper .restoreValue (settingName , settingValue )) {
414- cv .clear ();
415- cv .put (Settings .NameValueTable .NAME , settingName );
416- cv .put (Settings .NameValueTable .VALUE , settingValue );
417- getContentResolver ().insert (contentUri , cv );
418- }
421+ if (value == null ) {
422+ continue ;
419423 }
420- }
421- }
422424
423- // Returns 'true' if the given setting is one that we refuse to restore
424- private boolean invalidSavedSetting ( String [] knownNames , String candidate ) {
425- // no filter? allow everything
426- if ( knownNames == null ) {
427- return false ;
428- }
425+ if ( settingsHelper . restoreValue ( key , value )) {
426+ contentValues . clear ();
427+ contentValues . put ( Settings . NameValueTable . NAME , key );
428+ contentValues . put ( Settings . NameValueTable . VALUE , value );
429+ getContentResolver (). insert ( contentUri , contentValues ) ;
430+ }
429431
430- // whitelisted setting? allow it
431- for (String name : knownNames ) {
432- if (name .equals (candidate )) {
433- return false ;
432+ if (DEBUG ) {
433+ Log .d (TAG , "Restored setting: " + key + "=" + value );
434434 }
435435 }
436-
437- // refuse everything else
438- if (DEBUG ) Log .v (TAG , "Ignoring restore datum: " + candidate );
439- return true ;
440- }
441-
442- private String [] copyAndSort (String [] keys ) {
443- String [] sortedKeys = new String [keys .length ];
444- System .arraycopy (keys , 0 , sortedKeys , 0 , keys .length );
445- Arrays .sort (sortedKeys );
446- return sortedKeys ;
447436 }
448437
449438 /**
450- * Given a cursor sorted by key name and a set of keys sorted by name,
451- * extract the required keys and values and write them to a byte array.
452- * @param sortedCursor
453- * @param sortedKeys
454- * @return
439+ * Given a cursor and a set of keys, extract the required keys and
440+ * values and write them to a byte array.
441+ *
442+ * @param cursor A cursor with settings data.
443+ * @param settings The settings to extract.
444+ * @return The byte array of extracted values.
455445 */
456- byte [] extractRelevantValues (Cursor sortedCursor , String [] sortedKeys ) {
457- byte [][] values = new byte [sortedKeys .length * 2 ][]; // keys and values
458- if (!sortedCursor .moveToFirst ()) {
446+ private byte [] extractRelevantValues (Cursor cursor , String [] settings ) {
447+ final int settingsCount = settings .length ;
448+ byte [][] values = new byte [settingsCount * 2 ][]; // keys and values
449+ if (!cursor .moveToFirst ()) {
459450 Log .e (TAG , "Couldn't read from the cursor" );
460451 return new byte [0 ];
461452 }
462- int keyIndex = 0 ;
453+
454+ // Obtain the relevant data in a temporary array.
463455 int totalSize = 0 ;
464- while (!sortedCursor .isAfterLast ()) {
465- String name = sortedCursor .getString (COLUMN_NAME );
466- while (sortedKeys [keyIndex ].compareTo (name .toString ()) < 0 ) {
467- keyIndex ++;
468- if (keyIndex == sortedKeys .length ) break ;
469- }
470- if (keyIndex < sortedKeys .length && name .equals (sortedKeys [keyIndex ])) {
471- String value = sortedCursor .getString (COLUMN_VALUE );
472- byte [] nameBytes = name .toString ().getBytes ();
473- totalSize += 4 + nameBytes .length ;
474- values [keyIndex * 2 ] = nameBytes ;
475- byte [] valueBytes ;
476- if (TextUtils .isEmpty (value )) {
477- valueBytes = null ;
478- totalSize += 4 ;
479- } else {
480- valueBytes = value .toString ().getBytes ();
481- totalSize += 4 + valueBytes .length ;
482- //Log.i(TAG, "Backing up " + name + " = " + value);
456+ int backedUpSettingIndex = 0 ;
457+ Map <String , String > cachedEntries = new HashMap <String , String >();
458+ for (int i = 0 ; i < settingsCount ; i ++) {
459+ String key = settings [i ];
460+ String value = cachedEntries .remove (key );
461+
462+ // If the value not cached, let us look it up.
463+ if (value == null ) {
464+ while (!cursor .isAfterLast ()) {
465+ String cursorKey = cursor .getString (COLUMN_NAME );
466+ String cursorValue = cursor .getString (COLUMN_VALUE );
467+ cursor .moveToNext ();
468+ if (key .equals (cursorKey )) {
469+ value = cursorValue ;
470+ break ;
471+ }
472+ cachedEntries .put (cursorKey , cursorValue );
483473 }
484- values [keyIndex * 2 + 1 ] = valueBytes ;
485- keyIndex ++;
486474 }
487- if (keyIndex == sortedKeys .length || !sortedCursor .moveToNext ()) {
488- break ;
475+
476+ if (value == null ) {
477+ continue ;
478+ }
479+
480+ // Write the key and value in the intermediary array.
481+ byte [] keyBytes = key .getBytes ();
482+ totalSize += INTEGER_BYTE_COUNT + keyBytes .length ;
483+ values [backedUpSettingIndex * 2 ] = keyBytes ;
484+
485+ byte [] valueBytes = value .getBytes ();
486+ totalSize += INTEGER_BYTE_COUNT + valueBytes .length ;
487+ values [backedUpSettingIndex * 2 + 1 ] = valueBytes ;
488+
489+ backedUpSettingIndex ++;
490+
491+ if (DEBUG ) {
492+ Log .d (TAG , "Backed up setting: " + key + "=" + value );
489493 }
490494 }
491495
496+ // Aggregate the result.
492497 byte [] result = new byte [totalSize ];
493498 int pos = 0 ;
494- for (int i = 0 ; i < sortedKeys .length * 2 ; i ++) {
495- if (values [i ] != null ) {
496- pos = writeInt (result , pos , values [i ].length );
497- pos = writeBytes (result , pos , values [i ]);
498- }
499+ final int keyValuePairCount = backedUpSettingIndex * 2 ;
500+ for (int i = 0 ; i < keyValuePairCount ; i ++) {
501+ pos = writeInt (result , pos , values [i ].length );
502+ pos = writeBytes (result , pos , values [i ]);
499503 }
500504 return result ;
501505 }
@@ -647,14 +651,14 @@ private void copyWifiSupplicantTemplate() {
647651 * @param out byte array
648652 * @param pos current pos in array
649653 * @param value integer to write
650- * @return the index after adding the size of an int (4)
654+ * @return the index after adding the size of an int (4) in bytes.
651655 */
652656 private int writeInt (byte [] out , int pos , int value ) {
653657 out [pos + 0 ] = (byte ) ((value >> 24 ) & 0xFF );
654658 out [pos + 1 ] = (byte ) ((value >> 16 ) & 0xFF );
655659 out [pos + 2 ] = (byte ) ((value >> 8 ) & 0xFF );
656660 out [pos + 3 ] = (byte ) ((value >> 0 ) & 0xFF );
657- return pos + 4 ;
661+ return pos + INTEGER_BYTE_COUNT ;
658662 }
659663
660664 private int writeBytes (byte [] out , int pos , byte [] value ) {
0 commit comments