summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSunny Goyal <sunnygoyal@google.com>2014-10-16 09:24:19 -0700
committerSunny Goyal <sunnygoyal@google.com>2014-10-28 09:27:10 -0700
commit33d443897658e6ad8b76bd2e58e3598161fd3ead (patch)
tree2205d9f2b6c9ce04a1fbb47495b45eae04345a11
parent09ae4aef52b4486b97ecc77feeb64307bcbd3993 (diff)
downloadandroid_packages_apps_Trebuchet-33d443897658e6ad8b76bd2e58e3598161fd3ead.tar.gz
android_packages_apps_Trebuchet-33d443897658e6ad8b76bd2e58e3598161fd3ead.tar.bz2
android_packages_apps_Trebuchet-33d443897658e6ad8b76bd2e58e3598161fd3ead.zip
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
-rw-r--r--protos/backup.proto24
-rw-r--r--src/com/android/launcher3/Launcher.java17
-rw-r--r--src/com/android/launcher3/LauncherAppState.java32
-rw-r--r--src/com/android/launcher3/LauncherBackupAgentHelper.java34
-rw-r--r--src/com/android/launcher3/LauncherBackupHelper.java88
-rw-r--r--src/com/android/launcher3/LauncherPreferencesBackupHelper.java44
-rw-r--r--src/com/android/launcher3/LauncherProvider.java10
7 files changed, 147 insertions, 102 deletions
diff --git a/protos/backup.proto b/protos/backup.proto
index 7ba293702..8ae175234 100644
--- a/protos/backup.proto
+++ b/protos/backup.proto
@@ -37,12 +37,36 @@ message CheckedMessage {
required int64 checksum = 2;
}
+message DeviceProfieData {
+ required float desktop_rows = 1;
+ required float desktop_cols = 2;
+ required float hotseat_count = 3;
+ required int32 allapps_rank = 4;
+}
+
message Journal {
required int32 app_version = 1;
+
+ // Time when the backup was created
required int64 t = 2;
+
+ // Total bytes written during the last backup
+ // OBSOLETE: A state may contain entries which are already present in the backup
+ // and were not written in the last backup
optional int64 bytes = 3;
+
+ // Total entries written during the last backup
+ // OBSOLETE: A state may contain entries which are already present in the backup
+ // and were not written in the last backup
optional int32 rows = 4;
+
+ // Valid keys for this state
repeated Key key = 5;
+
+ // Backup format version.
+ optional int32 backup_version = 6 [default = 1];
+
+ optional DeviceProfieData profile = 7;
}
message Favorite {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b357e7a41..bb7265e44 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -53,7 +53,6 @@ import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -71,7 +70,6 @@ import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
@@ -418,22 +416,9 @@ public class Launcher extends Activity
LauncherAppState.setApplicationContext(getApplicationContext());
LauncherAppState app = LauncherAppState.getInstance();
LauncherAppState.getLauncherProvider().setLauncherProviderChangeListener(this);
- // Determine the dynamic grid properties
- Point smallestSize = new Point();
- Point largestSize = new Point();
- Point realSize = new Point();
- Display display = getWindowManager().getDefaultDisplay();
- display.getCurrentSizeRange(smallestSize, largestSize);
- display.getRealSize(realSize);
- DisplayMetrics dm = new DisplayMetrics();
- display.getMetrics(dm);
// Lazy-initialize the dynamic grid
- DeviceProfile grid = app.initDynamicGrid(this,
- Math.min(smallestSize.x, smallestSize.y),
- Math.min(largestSize.x, largestSize.y),
- realSize.x, realSize.y,
- dm.widthPixels, dm.heightPixels);
+ DeviceProfile grid = app.initDynamicGrid(this);
// the LauncherApplication should call this, but in case of Instrumentation it might not be present yet
mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 03ab94bab..48af216bb 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -25,8 +25,12 @@ import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.Point;
import android.os.Handler;
+import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
@@ -190,21 +194,35 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
return LauncherFiles.SHARED_PREFERENCES_KEY;
}
- DeviceProfile initDynamicGrid(Context context, int minWidth, int minHeight,
- int width, int height,
- int availableWidth, int availableHeight) {
+ DeviceProfile initDynamicGrid(Context context) {
+ // Determine the dynamic grid properties
+ WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ Display display = wm.getDefaultDisplay();
+
+ Point realSize = new Point();
+ display.getRealSize(realSize);
+ DisplayMetrics dm = new DisplayMetrics();
+ display.getMetrics(dm);
+
if (mDynamicGrid == null) {
+ Point smallestSize = new Point();
+ Point largestSize = new Point();
+ display.getCurrentSizeRange(smallestSize, largestSize);
+
mDynamicGrid = new DynamicGrid(context,
context.getResources(),
- minWidth, minHeight, width, height,
- availableWidth, availableHeight);
+ Math.min(smallestSize.x, smallestSize.y),
+ Math.min(largestSize.x, largestSize.y),
+ realSize.x, realSize.y,
+ dm.widthPixels, dm.heightPixels);
mDynamicGrid.getDeviceProfile().addCallback(this);
}
// Update the icon size
DeviceProfile grid = mDynamicGrid.getDeviceProfile();
- grid.updateFromConfiguration(context, context.getResources(), width, height,
- availableWidth, availableHeight);
+ grid.updateFromConfiguration(context, context.getResources(),
+ realSize.x, realSize.y,
+ dm.widthPixels, dm.heightPixels);
return grid;
}
public DynamicGrid getDynamicGrid() {
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index 09ad22b47..b03b13ccc 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -22,7 +22,6 @@ import android.app.backup.BackupManager;
import android.content.Context;
import android.database.Cursor;
import android.os.ParcelFileDescriptor;
-import android.provider.Settings;
import android.util.Log;
import java.io.IOException;
@@ -30,13 +29,14 @@ import java.io.IOException;
public class LauncherBackupAgentHelper extends BackupAgentHelper {
private static final String TAG = "LauncherBackupAgentHelper";
+
+ private static final String LAUNCHER_DATA_PREFIX = "L";
+
static final boolean VERBOSE = true;
static final boolean DEBUG = false;
private static BackupManager sBackupManager;
- protected static final String SETTING_RESTORE_ENABLED = "launcher_restore_enabled";
-
/**
* Notify the backup manager that out database is dirty.
*
@@ -51,28 +51,13 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper {
sBackupManager.dataChanged();
}
- @Override
- public void onDestroy() {
- // There is only one process accessing this preference file, but the restore
- // modifies the file outside the normal codepaths, so it looks like another
- // process. This forces a reload of the file, in case this process persists.
- String spKey = LauncherAppState.getSharedPreferencesKey();
- getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
- super.onDestroy();
- }
+ private LauncherBackupHelper mHelper;
@Override
public void onCreate() {
- boolean restoreEnabled = 0 != Settings.Secure.getInt(
- getContentResolver(), SETTING_RESTORE_ENABLED, 1);
- if (VERBOSE) Log.v(TAG, "restore is " + (restoreEnabled ? "enabled" : "disabled"));
-
- addHelper(LauncherBackupHelper.LAUNCHER_PREFS_PREFIX,
- new LauncherPreferencesBackupHelper(this,
- LauncherAppState.getSharedPreferencesKey(),
- restoreEnabled));
- addHelper(LauncherBackupHelper.LAUNCHER_PREFIX,
- new LauncherBackupHelper(this, restoreEnabled));
+ super.onCreate();
+ mHelper = new LauncherBackupHelper(this);
+ addHelper(LAUNCHER_DATA_PREFIX, mHelper);
}
@Override
@@ -92,7 +77,10 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper {
boolean hasData = c.moveToNext();
c.close();
- if (!hasData) {
+ if (hasData && mHelper.restoreSuccessful) {
+ LauncherAppState.getLauncherProvider().clearFlagEmptyDbCreated();
+ LauncherClings.synchonouslyMarkFirstRunClingDismissed(this);
+ } else {
if (VERBOSE) Log.v(TAG, "Nothing was restored, clearing DB");
LauncherAppState.getLauncherProvider().createEmptyDB();
}
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<String>();
mKeys = new ArrayList<Key>();
+ 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);
}
@@ -228,14 +237,26 @@ public class LauncherBackupHelper implements BackupHelper {
}
/**
+ * @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.
- *
- * <P>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;
}
@@ -301,6 +345,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.
*
* @param data output stream for key/value pairs
@@ -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);
diff --git a/src/com/android/launcher3/LauncherPreferencesBackupHelper.java b/src/com/android/launcher3/LauncherPreferencesBackupHelper.java
deleted file mode 100644
index 6f9c05c88..000000000
--- a/src/com/android/launcher3/LauncherPreferencesBackupHelper.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.app.backup.BackupDataInputStream;
-import android.app.backup.SharedPreferencesBackupHelper;
-import android.content.Context;
-import android.util.Log;
-
-public class LauncherPreferencesBackupHelper extends SharedPreferencesBackupHelper {
-
- private static final String TAG = "LauncherPreferencesBackupHelper";
- private static final boolean VERBOSE = LauncherBackupAgentHelper.VERBOSE;
-
- private final boolean mRestoreEnabled;
-
- public LauncherPreferencesBackupHelper(Context context, String sharedPreferencesKey,
- boolean restoreEnabled) {
- super(context, sharedPreferencesKey);
- mRestoreEnabled = restoreEnabled;
- }
-
- @Override
- public void restoreEntity(BackupDataInputStream data) {
- if (mRestoreEnabled) {
- if (VERBOSE) Log.v(TAG, "restoring preferences");
- super.restoreEntity(data);
- }
- }
-}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index fe9bc171f..365d989bf 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -293,6 +293,14 @@ public class LauncherProvider extends ContentProvider {
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
}
+ public void clearFlagEmptyDbCreated() {
+ String spKey = LauncherAppState.getSharedPreferencesKey();
+ getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE)
+ .edit()
+ .remove(EMPTY_DATABASE_CREATED)
+ .commit();
+ }
+
/**
* Loads the default workspace based on the following priority scheme:
* 1) From a package provided by play store
@@ -334,7 +342,7 @@ public class LauncherProvider extends ContentProvider {
mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(),
getDefaultLayoutParser());
}
- sp.edit().remove(EMPTY_DATABASE_CREATED).commit();
+ clearFlagEmptyDbCreated();
}
}