From 33d443897658e6ad8b76bd2e58e3598161fd3ead Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 16 Oct 2014 09:24:19 -0700 Subject: Updating backup restore logic > Adding DeviceProfile information in the backup > Removing SharedPreference backup > Adding helper methods to abort backup in the middle > Comparing keys against the backup journal during restore to avoid restoring corrupt/lost entries > Old backups are still compatible, but lost keys verification will be ignored in that case. Bug: 17937935 Bug: 17951775 Bug: 17260941 Change-Id: Iad48646cfdd69abaff5c163b2055f3b8a9b39b19 --- .../android/launcher3/LauncherBackupHelper.java | 88 +++++++++++++++++++--- 1 file changed, 77 insertions(+), 11 deletions(-) (limited to 'src/com/android/launcher3/LauncherBackupHelper.java') diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index b2ac577f4..8b9c5d96a 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -40,6 +40,7 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.LauncherSettings.WorkspaceScreens; import com.android.launcher3.backup.BackupProtos; import com.android.launcher3.backup.BackupProtos.CheckedMessage; +import com.android.launcher3.backup.BackupProtos.DeviceProfieData; import com.android.launcher3.backup.BackupProtos.Favorite; import com.android.launcher3.backup.BackupProtos.Journal; import com.android.launcher3.backup.BackupProtos.Key; @@ -66,24 +67,24 @@ import java.util.zip.CRC32; * Persist the launcher home state across calamities. */ public class LauncherBackupHelper implements BackupHelper { - private static final String TAG = "LauncherBackupHelper"; private static final boolean VERBOSE = LauncherBackupAgentHelper.VERBOSE; private static final boolean DEBUG = LauncherBackupAgentHelper.DEBUG; + private static final int BACKUP_VERSION = 2; private static final int MAX_JOURNAL_SIZE = 1000000; + // Journal key is such that it is always smaller than any dynamically generated + // key (any Base64 encoded string). + private static final String JOURNAL_KEY = "#"; + /** icons are large, dribble them out */ private static final int MAX_ICONS_PER_PASS = 10; /** widgets contain previews, which are very large, dribble them out */ private static final int MAX_WIDGETS_PER_PASS = 5; - public static final int IMAGE_COMPRESSION_QUALITY = 75; - - public static final String LAUNCHER_PREFIX = "L"; - - public static final String LAUNCHER_PREFS_PREFIX = "LP"; + private static final int IMAGE_COMPRESSION_QUALITY = 75; private static final Bitmap.CompressFormat IMAGE_FORMAT = android.graphics.Bitmap.CompressFormat.PNG; @@ -145,10 +146,13 @@ public class LauncherBackupHelper implements BackupHelper { private byte[] mBuffer = new byte[512]; private long mLastBackupRestoreTime; - public LauncherBackupHelper(Context context, boolean restoreEnabled) { + boolean restoreSuccessful; + + public LauncherBackupHelper(Context context) { mContext = context; mExistingKeys = new HashSet(); mKeys = new ArrayList(); + restoreSuccessful = true; } private void dataChanged() { @@ -178,7 +182,6 @@ public class LauncherBackupHelper implements BackupHelper { * @param oldState notes from the last backup * @param data incremental key/value pairs to persist off-device * @param newState notes for the next backup - * @throws IOException */ @Override public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, @@ -220,6 +223,12 @@ public class LauncherBackupHelper implements BackupHelper { mExistingKeys.clear(); mLastBackupRestoreTime = newBackupTime; + + // We store the journal at two places. + // 1) Storing it in newState allows us to do partial backups by comparing old state + // 2) Storing it in backup data allows us to validate keys during restore + Journal state = getCurrentStateJournal(); + writeRowToBackup(JOURNAL_KEY, state, data); } catch (IOException e) { Log.e(TAG, "launcher backup has failed", e); } @@ -227,15 +236,27 @@ public class LauncherBackupHelper implements BackupHelper { writeNewStateDescription(newState); } + /** + * @return true if the backup corresponding to oldstate can be successfully applied + * to this device. + */ + private boolean isBackupCompatible(Journal oldState) { + return true; + } + /** * Restore launcher configuration from the restored data stream. - * - *

Keys may arrive in any order. + * It assumes that the keys will arrive in lexical order. So if the journal was present in the + * backup, it should arrive first. * * @param data the key/value pair from the server */ @Override public void restoreEntity(BackupDataInputStream data) { + if (!restoreSuccessful) { + return; + } + int dataSize = data.size(); if (mBuffer.length < dataSize) { mBuffer = new byte[dataSize]; @@ -244,6 +265,27 @@ public class LauncherBackupHelper implements BackupHelper { int bytesRead = data.read(mBuffer, 0, dataSize); if (DEBUG) Log.d(TAG, "read " + bytesRead + " of " + dataSize + " available"); String backupKey = data.getKey(); + + if (JOURNAL_KEY.equals(backupKey)) { + if (VERBOSE) Log.v(TAG, "Journal entry restored"); + if (!mKeys.isEmpty()) { + // We received the journal key after a restore key. + Log.wtf(TAG, keyToBackupKey(mKeys.get(0)) + " received after " + JOURNAL_KEY); + restoreSuccessful = false; + return; + } + + Journal journal = new Journal(); + MessageNano.mergeFrom(journal, readCheckedBytes(mBuffer, dataSize)); + applyJournal(journal); + restoreSuccessful = isBackupCompatible(journal); + return; + } + + if (!mExistingKeys.isEmpty() && !mExistingKeys.contains(backupKey)) { + if (DEBUG) Log.e(TAG, "Ignoring key not present in the backup state " + backupKey); + return; + } Key key = backupKeyToKey(backupKey); mKeys.add(key); switch (key.type) { @@ -288,6 +330,8 @@ public class LauncherBackupHelper implements BackupHelper { journal.t = mLastBackupRestoreTime; journal.key = mKeys.toArray(new BackupProtos.Key[mKeys.size()]); journal.appVersion = getAppVersion(); + journal.backupVersion = BACKUP_VERSION; + journal.profile = getDeviceProfieData(); return journal; } @@ -300,6 +344,29 @@ public class LauncherBackupHelper implements BackupHelper { } } + /** + * @return the current device profile information. + */ + private DeviceProfieData getDeviceProfieData() { + LauncherAppState.setApplicationContext(mContext.getApplicationContext()); + LauncherAppState app = LauncherAppState.getInstance(); + + DeviceProfile profile; + if (app.getDynamicGrid() == null) { + // Initialize the grid + profile = app.initDynamicGrid(mContext); + } else { + profile = app.getDynamicGrid().getDeviceProfile(); + } + + DeviceProfieData data = new DeviceProfieData(); + data.desktopRows = profile.numRows; + data.desktopCols = profile.numColumns; + data.hotseatCount = profile.numHotseatIcons; + data.allappsRank = profile.hotseatAllAppsRank; + return data; + } + /** * Write all modified favorites to the data stream. * @@ -902,7 +969,6 @@ public class LauncherBackupHelper implements BackupHelper { return journal; } - private void writeRowToBackup(Key key, MessageNano proto, BackupDataOutput data) throws IOException { writeRowToBackup(keyToBackupKey(key), proto, data); -- cgit v1.2.3