From eb77aaea8990ede3ba774c7b92d48d098bda0f24 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 24 May 2016 13:19:01 -0700 Subject: [PATCH] Removing Key-Value pair backup logic as Launcher now uses full-data backup Change-Id: I3bbaec2b33afe8c1da3ca1468c7c733cc3194604 --- protos/backup.proto | 131 -- .../launcher3/LauncherBackupAgentHelper.java | 137 -- .../launcher3/LauncherBackupHelper.java | 1257 ----------------- .../android/launcher3/LauncherProvider.java | 10 - .../android/launcher3/LauncherSettings.java | 3 - .../model/GridSizeMigrationTask.java | 30 +- .../provider/LossyScreenMigrationTask.java | 5 +- .../model/GridSizeMigrationTaskTest.java | 8 +- 8 files changed, 9 insertions(+), 1572 deletions(-) delete mode 100644 protos/backup.proto delete mode 100644 src/com/android/launcher3/LauncherBackupAgentHelper.java delete mode 100644 src/com/android/launcher3/LauncherBackupHelper.java diff --git a/protos/backup.proto b/protos/backup.proto deleted file mode 100644 index 62f935c43f..0000000000 --- a/protos/backup.proto +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -syntax = "proto2"; - -package launcher_backup; - -option java_package = "com.android.launcher3.backup.nano"; -option java_outer_classname = "BackupProtos"; - -message Key { - enum Type { - FAVORITE = 1; - SCREEN = 2; - ICON = 3; - WIDGET = 4; - } - required Type type = 1; - optional string name = 2; // keep this short - optional int64 id = 3; - optional int64 checksum = 4; -} - -message CheckedMessage { - required bytes payload = 1; - 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 { - // Type of the app, this target represents - enum TargetType { - TARGET_NONE = 0; - TARGET_PHONE = 1; - TARGET_MESSENGER = 2; - TARGET_EMAIL = 3; - TARGET_BROWSER = 4; - TARGET_GALLERY = 5; - TARGET_CAMERA = 6; - } - - required int64 id = 1; - required int32 itemType = 2; - optional string title = 3; - optional int32 container = 4; - optional int32 screen = 5; - optional int32 cellX = 6; - optional int32 cellY = 7; - optional int32 spanX = 8; - optional int32 spanY = 9; - optional int32 displayMode = 10; - optional int32 appWidgetId = 11; - optional string appWidgetProvider = 12; - optional string intent = 13; - optional string uri = 14; - optional int32 iconType = 15 [deprecated = true]; - optional string iconPackage = 16; - optional string iconResource = 17; - optional bytes icon = 18; - - // Added in backup version 4 - optional TargetType targetType = 19 [default = TARGET_NONE]; - optional int32 rank = 20; -} - -message Screen { - required int64 id = 1; - optional int32 rank = 2; -} - -message Resource { - required int32 dpi = 1; - required bytes data = 2; -} - -message Widget { - required string provider = 1; - optional string label = 2; - optional bool configure = 3; - optional Resource icon = 4; - optional Resource preview = 5; - - // Added in backup version 3 - // Assume that a widget is resizable upto 2x2 if no data is available - optional int32 minSpanX = 6 [default = 2]; - optional int32 minSpanY = 7 [default = 2]; -} diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java deleted file mode 100644 index c2ab20a629..0000000000 --- a/src/com/android/launcher3/LauncherBackupAgentHelper.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2013 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.BackupAgentHelper; -import android.app.backup.BackupDataInput; -import android.app.backup.BackupManager; -import android.content.Context; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.os.ParcelFileDescriptor; -import android.util.Log; - -import com.android.launcher3.model.GridSizeMigrationTask; - -import java.io.IOException; - -public class LauncherBackupAgentHelper extends BackupAgentHelper { - - private static final String TAG = "LauncherBAHelper"; - - private static final String KEY_LAST_NOTIFIED_TIME = "backup_manager_last_notified"; - - private static final String LAUNCHER_DATA_PREFIX = "L"; - - static final boolean VERBOSE = false; - static final boolean DEBUG = false; - - /** - * Notify the backup manager that out database is dirty. - * - *

This does not force an immediate backup. - * - * @param context application context - */ - public static void dataChanged(Context context) { - dataChanged(context, 0); - } - - /** - * Notify the backup manager that out database is dirty. - * - *

This does not force an immediate backup. - * - * @param context application context - * @param throttleMs duration in ms for which two consecutive calls to backup manager should - * not be made. - */ - public static void dataChanged(Context context, long throttleMs) { - SharedPreferences prefs = Utilities.getPrefs(context); - long now = System.currentTimeMillis(); - long lastTime = prefs.getLong(KEY_LAST_NOTIFIED_TIME, 0); - - // User can manually change the system time, which could lead to now < lastTime. - // Re-backup in that case, as the backup will have a wrong lastModifiedTime. - if (now < lastTime || now >= (lastTime + throttleMs)) { - BackupManager.dataChanged(context.getPackageName()); - prefs.edit().putLong(KEY_LAST_NOTIFIED_TIME, now).apply(); - } - } - - private LauncherBackupHelper mHelper; - - @Override - public void onCreate() { - super.onCreate(); - mHelper = new LauncherBackupHelper(this); - addHelper(LAUNCHER_DATA_PREFIX, mHelper); - } - - @Override - public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) - throws IOException { - if (!Utilities.ATLEAST_LOLLIPOP) { - // No restore for old devices. - Log.i(TAG, "You shall not pass!!!"); - Log.d(TAG, "Restore is only supported on devices running Lollipop and above."); - return; - } - - // Clear dB before restore - LauncherSettings.Settings.call(getContentResolver(), - LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); - - boolean hasData; - try { - super.onRestore(data, appVersionCode, newState); - // If no favorite was migrated, clear the data and start fresh. - final Cursor c = getContentResolver().query( - LauncherSettings.Favorites.CONTENT_URI, null, null, null, null); - hasData = c.moveToNext(); - c.close(); - } catch (Exception e) { - // If the restore fails, we should do a fresh start. - Log.e(TAG, "Restore failed", e); - hasData = false; - } - - if (hasData && mHelper.restoreSuccessful) { - LauncherSettings.Settings.call(getContentResolver(), - LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); - LauncherClings.markFirstRunClingDismissed(this); - - // Rank was added in v4. - if (mHelper.restoredBackupVersion <= 3) { - LauncherSettings.Settings.call(getContentResolver(), - LauncherSettings.Settings.METHOD_UPDATE_FOLDER_ITEMS_RANK); - } - - if (GridSizeMigrationTask.ENABLED && mHelper.shouldAttemptWorkspaceMigration()) { - GridSizeMigrationTask.markForMigration(getApplicationContext(), - mHelper.widgetSizes, mHelper.migrationCompatibleProfileData); - } - - LauncherSettings.Settings.call(getContentResolver(), - LauncherSettings.Settings.METHOD_CONVERT_SHORTCUTS_TO_ACTIVITIES); - } else { - if (VERBOSE) Log.v(TAG, "Nothing was restored, clearing DB"); - LauncherSettings.Settings.call(getContentResolver(), - LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); - } - } -} diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java deleted file mode 100644 index e987a9bdfa..0000000000 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ /dev/null @@ -1,1257 +0,0 @@ -/* - * Copyright (C) 2013 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.BackupDataOutput; -import android.app.backup.BackupHelper; -import android.app.backup.BackupManager; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; -import android.content.res.XmlResourceParser; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Point; -import android.graphics.drawable.Drawable; -import android.os.ParcelFileDescriptor; -import android.text.TextUtils; -import android.util.Base64; -import android.util.Log; - -import com.android.launcher3.LauncherSettings.Favorites; -import com.android.launcher3.LauncherSettings.WorkspaceScreens; -import com.android.launcher3.backup.nano.BackupProtos; -import com.android.launcher3.backup.nano.BackupProtos.CheckedMessage; -import com.android.launcher3.backup.nano.BackupProtos.DeviceProfieData; -import com.android.launcher3.backup.nano.BackupProtos.Favorite; -import com.android.launcher3.backup.nano.BackupProtos.Journal; -import com.android.launcher3.backup.nano.BackupProtos.Key; -import com.android.launcher3.backup.nano.BackupProtos.Resource; -import com.android.launcher3.backup.nano.BackupProtos.Screen; -import com.android.launcher3.backup.nano.BackupProtos.Widget; -import com.android.launcher3.compat.AppWidgetManagerCompat; -import com.android.launcher3.compat.UserHandleCompat; -import com.android.launcher3.compat.UserManagerCompat; -import com.android.launcher3.model.GridSizeMigrationTask; -import com.android.launcher3.util.Thunk; -import com.google.protobuf.nano.InvalidProtocolBufferNanoException; -import com.google.protobuf.nano.MessageNano; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -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 = 4; - 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; - - private static final String[] FAVORITE_PROJECTION = { - Favorites._ID, // 0 - Favorites.MODIFIED, // 1 - Favorites.INTENT, // 2 - Favorites.APPWIDGET_PROVIDER, // 3 - Favorites.APPWIDGET_ID, // 4 - Favorites.CELLX, // 5 - Favorites.CELLY, // 6 - Favorites.CONTAINER, // 7 - Favorites.ICON, // 8 - Favorites.ICON_PACKAGE, // 9 - Favorites.ICON_RESOURCE, // 10 - Favorites.ITEM_TYPE, // 11 - Favorites.SCREEN, // 12 - Favorites.SPANX, // 13 - Favorites.SPANY, // 14 - Favorites.TITLE, // 15 - Favorites.PROFILE_ID, // 16 - Favorites.RANK, // 17 - }; - - private static final int ID_INDEX = 0; - private static final int ID_MODIFIED = 1; - private static final int INTENT_INDEX = 2; - private static final int APPWIDGET_PROVIDER_INDEX = 3; - private static final int APPWIDGET_ID_INDEX = 4; - private static final int CELLX_INDEX = 5; - private static final int CELLY_INDEX = 6; - private static final int CONTAINER_INDEX = 7; - private static final int ICON_INDEX = 8; - private static final int ICON_PACKAGE_INDEX = 9; - private static final int ICON_RESOURCE_INDEX = 10; - private static final int ITEM_TYPE_INDEX = 11; - private static final int SCREEN_INDEX = 12; - private static final int SPANX_INDEX = 13; - private static final int SPANY_INDEX = 14; - private static final int TITLE_INDEX = 15; - private static final int RANK_INDEX = 17; - - private static final String[] SCREEN_PROJECTION = { - WorkspaceScreens._ID, // 0 - WorkspaceScreens.MODIFIED, // 1 - WorkspaceScreens.SCREEN_RANK // 2 - }; - - private static final int SCREEN_RANK_INDEX = 2; - - @Thunk final Context mContext; - private final HashSet mExistingKeys; - private final ArrayList mKeys; - private final ItemTypeMatcher[] mItemTypeMatchers; - private final long mUserSerial; - - private BackupManager mBackupManager; - private byte[] mBuffer = new byte[512]; - private long mLastBackupRestoreTime; - private boolean mBackupDataWasUpdated; - - private IconCache mIconCache; - private DeviceProfieData mDeviceProfileData; - private InvariantDeviceProfile mIdp; - - DeviceProfieData migrationCompatibleProfileData; - HashSet widgetSizes = new HashSet<>(); - - boolean restoreSuccessful; - int restoredBackupVersion = 1; - - // When migrating from a device which different hotseat configuration, the icons are shifted - // to center along the new all-apps icon. - private int mHotseatShift = 0; - - public LauncherBackupHelper(Context context) { - mContext = context; - mExistingKeys = new HashSet(); - mKeys = new ArrayList(); - restoreSuccessful = true; - mItemTypeMatchers = new ItemTypeMatcher[CommonAppTypeParser.SUPPORTED_TYPE_COUNT]; - - UserManagerCompat userManager = UserManagerCompat.getInstance(mContext); - mUserSerial = userManager.getSerialNumberForUser(UserHandleCompat.myUserHandle()); - } - - private void dataChanged() { - if (mBackupManager == null) { - mBackupManager = new BackupManager(mContext); - } - mBackupManager.dataChanged(); - } - - private void applyJournal(Journal journal) { - mLastBackupRestoreTime = journal.t; - mExistingKeys.clear(); - if (journal.key != null) { - for (Key key : journal.key) { - mExistingKeys.add(keyToBackupKey(key)); - } - } - restoredBackupVersion = journal.backupVersion; - } - - /** - * Back up launcher data so we can restore the user's state on a new device. - * - *

The journal is a timestamp and a list of keys that were saved as of that time. - * - *

Keys may come back in any order, so each key/value is one complete row of the database. - * - * @param oldState notes from the last backup - * @param data incremental key/value pairs to persist off-device - * @param newState notes for the next backup - */ - @Override - public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState) { - if (VERBOSE) Log.v(TAG, "onBackup"); - - Journal in = readJournal(oldState); - if (!launcherIsReady()) { - dataChanged(); - // Perform backup later. - writeJournal(newState, in); - return; - } - - if (mDeviceProfileData == null) { - LauncherAppState app = LauncherAppState.getInstance(); - mIdp = app.getInvariantDeviceProfile(); - mDeviceProfileData = initDeviceProfileData(mIdp); - mIconCache = app.getIconCache(); - } - - Log.v(TAG, "lastBackupTime = " + in.t); - mKeys.clear(); - applyJournal(in); - - // Record the time before performing backup so that entries edited while the backup - // was going on, do not get missed in next backup. - long newBackupTime = System.currentTimeMillis(); - mBackupDataWasUpdated = false; - try { - backupFavorites(data); - backupScreens(data); - backupIcons(data); - backupWidgets(data); - - // Delete any key which still exist in the old backup, but is not valid anymore. - HashSet validKeys = new HashSet(); - for (Key key : mKeys) { - validKeys.add(keyToBackupKey(key)); - } - mExistingKeys.removeAll(validKeys); - - // Delete anything left in the existing keys. - for (String deleted: mExistingKeys) { - if (VERBOSE) Log.v(TAG, "dropping deleted item " + deleted); - data.writeEntityHeader(deleted, -1); - mBackupDataWasUpdated = true; - } - - mExistingKeys.clear(); - if (!mBackupDataWasUpdated) { - // Check if any metadata has changed - mBackupDataWasUpdated = (in.profile == null) - || !Arrays.equals(DeviceProfieData.toByteArray(in.profile), - DeviceProfieData.toByteArray(mDeviceProfileData)) - || (in.backupVersion != BACKUP_VERSION) - || (in.appVersion != getAppVersion()); - } - - if (mBackupDataWasUpdated) { - 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); - } else { - if (DEBUG) Log.d(TAG, "Nothing was written during backup"); - } - } catch (IOException e) { - Log.e(TAG, "launcher backup has failed", e); - } - - writeNewStateDescription(newState); - } - - /** - * @return true if the backup corresponding to oldstate can be successfully applied - * to this device. - */ - private boolean isBackupCompatible(Journal oldState) { - DeviceProfieData currentProfile = mDeviceProfileData; - DeviceProfieData oldProfile = oldState.profile; - - if (oldProfile == null || oldProfile.desktopCols == 0) { - // Profile info is not valid, ignore the check. - return true; - } - - boolean isHotseatCompatible = false; - if (currentProfile.allappsRank >= oldProfile.hotseatCount) { - isHotseatCompatible = true; - mHotseatShift = 0; - } - - if ((currentProfile.allappsRank >= oldProfile.allappsRank) - && ((currentProfile.hotseatCount - currentProfile.allappsRank) >= - (oldProfile.hotseatCount - oldProfile.allappsRank))) { - // There is enough space on both sides of the hotseat. - isHotseatCompatible = true; - mHotseatShift = currentProfile.allappsRank - oldProfile.allappsRank; - } - - if (!isHotseatCompatible) { - return false; - } - if ((currentProfile.desktopCols >= oldProfile.desktopCols) - && (currentProfile.desktopRows >= oldProfile.desktopRows)) { - return true; - } - - if (GridSizeMigrationTask.ENABLED) { - // One time migrate the workspace when launcher starts. - migrationCompatibleProfileData = initDeviceProfileData(mIdp); - migrationCompatibleProfileData.desktopCols = oldProfile.desktopCols; - migrationCompatibleProfileData.desktopRows = oldProfile.desktopRows; - migrationCompatibleProfileData.hotseatCount = oldProfile.hotseatCount; - migrationCompatibleProfileData.allappsRank = oldProfile.allappsRank; - return true; - } - return false; - } - - /** - * Restore launcher configuration from the restored data stream. - * 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; - } - - if (mDeviceProfileData == null) { - // This call does not happen on a looper thread. So LauncherAppState - // can't be created . Instead initialize required dependencies directly. - mIdp = new InvariantDeviceProfile(mContext); - mDeviceProfileData = initDeviceProfileData(mIdp); - mIconCache = new IconCache(mContext, mIdp); - } - - int dataSize = data.size(); - if (mBuffer.length < dataSize) { - mBuffer = new byte[dataSize]; - } - try { - 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) { - case Key.FAVORITE: - restoreFavorite(key, mBuffer, dataSize); - break; - - case Key.SCREEN: - restoreScreen(key, mBuffer, dataSize); - break; - - case Key.ICON: - restoreIcon(key, mBuffer, dataSize); - break; - - case Key.WIDGET: - restoreWidget(key, mBuffer, dataSize); - break; - - default: - Log.w(TAG, "unknown restore entity type: " + key.type); - mKeys.remove(key); - break; - } - } catch (IOException e) { - Log.w(TAG, "ignoring unparsable backup entry", e); - } - } - - /** - * Record the restore state for the next backup. - * - * @param newState notes about the backup state after restore. - */ - @Override - public void writeNewStateDescription(ParcelFileDescriptor newState) { - writeJournal(newState, getCurrentStateJournal()); - } - - private Journal getCurrentStateJournal() { - Journal journal = new Journal(); - journal.t = mLastBackupRestoreTime; - journal.key = mKeys.toArray(new BackupProtos.Key[mKeys.size()]); - journal.appVersion = getAppVersion(); - journal.backupVersion = BACKUP_VERSION; - journal.profile = mDeviceProfileData; - return journal; - } - - private int getAppVersion() { - try { - return mContext.getPackageManager() - .getPackageInfo(mContext.getPackageName(), 0).versionCode; - } catch (NameNotFoundException e) { - return 0; - } - } - - private DeviceProfieData initDeviceProfileData(InvariantDeviceProfile profile) { - DeviceProfieData data = new DeviceProfieData(); - data.desktopRows = profile.numRows; - data.desktopCols = profile.numColumns; - data.hotseatCount = profile.numHotseatIcons; - return data; - } - - /** - * Write all modified favorites to the data stream. - * - * @param data output stream for key/value pairs - * @throws IOException - */ - private void backupFavorites(BackupDataOutput data) throws IOException { - // persist things that have changed since the last backup - ContentResolver cr = mContext.getContentResolver(); - // Don't backup apps in other profiles for now. - Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION, - getUserSelectionArg(), null, null); - try { - cursor.moveToPosition(-1); - while(cursor.moveToNext()) { - final long id = cursor.getLong(ID_INDEX); - final long updateTime = cursor.getLong(ID_MODIFIED); - Key key = getKey(Key.FAVORITE, id); - mKeys.add(key); - final String backupKey = keyToBackupKey(key); - - // Favorite proto changed in v4. Backup again if the version is old. - if (!mExistingKeys.contains(backupKey) || updateTime >= mLastBackupRestoreTime - || restoredBackupVersion < 4) { - writeRowToBackup(key, packFavorite(cursor), data); - } else { - if (DEBUG) Log.d(TAG, "favorite already backup up: " + id); - } - } - } finally { - cursor.close(); - } - } - - /** - * Read a favorite from the stream. - * - *

Keys arrive in any order, so screens and containers may not exist yet. - * - * @param key identifier for the row - * @param buffer the serialized proto from the stream, may be larger than dataSize - * @param dataSize the size of the proto from the stream - */ - private void restoreFavorite(Key key, byte[] buffer, int dataSize) throws IOException { - if (VERBOSE) Log.v(TAG, "unpacking favorite " + key.id); - if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " + - Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP)); - - ContentResolver cr = mContext.getContentResolver(); - ContentValues values = unpackFavorite(buffer, dataSize); - cr.insert(Favorites.CONTENT_URI, values); - } - - /** - * Write all modified screens to the data stream. - * - * @param data output stream for key/value pairs - * @throws IOException - */ - private void backupScreens(BackupDataOutput data) throws IOException { - // persist things that have changed since the last backup - ContentResolver cr = mContext.getContentResolver(); - Cursor cursor = cr.query(WorkspaceScreens.CONTENT_URI, SCREEN_PROJECTION, - null, null, null); - try { - cursor.moveToPosition(-1); - if (DEBUG) Log.d(TAG, "dumping screens after: " + mLastBackupRestoreTime); - while(cursor.moveToNext()) { - final long id = cursor.getLong(ID_INDEX); - final long updateTime = cursor.getLong(ID_MODIFIED); - Key key = getKey(Key.SCREEN, id); - mKeys.add(key); - final String backupKey = keyToBackupKey(key); - if (!mExistingKeys.contains(backupKey) || updateTime >= mLastBackupRestoreTime) { - writeRowToBackup(key, packScreen(cursor), data); - } else { - if (VERBOSE) Log.v(TAG, "screen already backup up " + id); - } - } - } finally { - cursor.close(); - } - } - - /** - * Read a screen from the stream. - * - *

Keys arrive in any order, so children of this screen may already exist. - * - * @param key identifier for the row - * @param buffer the serialized proto from the stream, may be larger than dataSize - * @param dataSize the size of the proto from the stream - */ - private void restoreScreen(Key key, byte[] buffer, int dataSize) throws IOException { - if (VERBOSE) Log.v(TAG, "unpacking screen " + key.id); - if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " + - Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP)); - - ContentResolver cr = mContext.getContentResolver(); - ContentValues values = unpackScreen(buffer, dataSize); - cr.insert(WorkspaceScreens.CONTENT_URI, values); - } - - /** - * Write all the static icon resources we need to render placeholders - * for a package that is not installed. - * - * @param data output stream for key/value pairs - */ - private void backupIcons(BackupDataOutput data) throws IOException { - // persist icons that haven't been persisted yet - final ContentResolver cr = mContext.getContentResolver(); - final int dpi = mContext.getResources().getDisplayMetrics().densityDpi; - final UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle(); - int backupUpIconCount = 0; - - // Don't backup apps in other profiles for now. - String where = "(" + Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION + " OR " + - Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT + " OR " + - Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_DEEP_SHORTCUT + ") AND " + - getUserSelectionArg(); - Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION, - where, null, null); - try { - cursor.moveToPosition(-1); - while(cursor.moveToNext()) { - final long id = cursor.getLong(ID_INDEX); - final String intentDescription = cursor.getString(INTENT_INDEX); - try { - Intent intent = Intent.parseUri(intentDescription, 0); - ComponentName cn = intent.getComponent(); - Key key = null; - String backupKey = null; - if (cn != null) { - key = getKey(Key.ICON, cn.flattenToShortString()); - backupKey = keyToBackupKey(key); - } else { - Log.w(TAG, "empty intent on application favorite: " + id); - } - if (mExistingKeys.contains(backupKey)) { - if (DEBUG) Log.d(TAG, "already saved icon " + backupKey); - - // remember that we already backed this up previously - mKeys.add(key); - } else if (backupKey != null) { - if (DEBUG) Log.d(TAG, "I can count this high: " + backupUpIconCount); - if (backupUpIconCount < MAX_ICONS_PER_PASS) { - if (DEBUG) Log.d(TAG, "saving icon " + backupKey); - Bitmap icon = mIconCache.getIcon(intent, myUserHandle); - if (icon != null && !mIconCache.isDefaultIcon(icon, myUserHandle)) { - writeRowToBackup(key, packIcon(dpi, icon), data); - mKeys.add(key); - backupUpIconCount ++; - } - } else { - if (VERBOSE) Log.v(TAG, "deferring icon backup " + backupKey); - // too many icons for this pass, request another. - dataChanged(); - } - } - } catch (URISyntaxException e) { - Log.e(TAG, "invalid URI on application favorite: " + id); - } catch (IOException e) { - Log.e(TAG, "unable to save application icon for favorite: " + id); - } - - } - } finally { - cursor.close(); - } - } - - /** - * Read an icon from the stream. - * - *

Keys arrive in any order, so shortcuts that use this icon may already exist. - * - * @param key identifier for the row - * @param buffer the serialized proto from the stream, may be larger than dataSize - * @param dataSize the size of the proto from the stream - */ - private void restoreIcon(Key key, byte[] buffer, int dataSize) throws IOException { - if (VERBOSE) Log.v(TAG, "unpacking icon " + key.id); - if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " + - Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP)); - - Resource res = unpackProto(new Resource(), buffer, dataSize); - if (DEBUG) { - Log.d(TAG, "unpacked " + res.dpi + " dpi icon"); - } - Bitmap icon = BitmapFactory.decodeByteArray(res.data, 0, res.data.length); - if (icon == null) { - Log.w(TAG, "failed to unpack icon for " + key.name); - } else { - if (VERBOSE) Log.v(TAG, "saving restored icon as: " + key.name); - mIconCache.preloadIcon(ComponentName.unflattenFromString(key.name), icon, res.dpi, - "" /* label */, mUserSerial, mIdp); - } - } - - /** - * Write all the static widget resources we need to render placeholders - * for a package that is not installed. - * - * @param data output stream for key/value pairs - * @throws IOException - */ - private void backupWidgets(BackupDataOutput data) throws IOException { - // persist static widget info that hasn't been persisted yet - final ContentResolver cr = mContext.getContentResolver(); - final int dpi = mContext.getResources().getDisplayMetrics().densityDpi; - int backupWidgetCount = 0; - - String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPWIDGET + " AND " - + getUserSelectionArg(); - Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION, - where, null, null); - AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(mContext); - try { - cursor.moveToPosition(-1); - while(cursor.moveToNext()) { - final long id = cursor.getLong(ID_INDEX); - final String providerName = cursor.getString(APPWIDGET_PROVIDER_INDEX); - final ComponentName provider = ComponentName.unflattenFromString(providerName); - - Key key = null; - String backupKey = null; - if (provider != null) { - key = getKey(Key.WIDGET, providerName); - backupKey = keyToBackupKey(key); - } else { - Log.w(TAG, "empty intent on appwidget: " + id); - } - - // Widget backup proto changed in v3. So add it again if the original backup is old. - if (mExistingKeys.contains(backupKey) && restoredBackupVersion >= 3) { - if (DEBUG) Log.d(TAG, "already saved widget " + backupKey); - - // remember that we already backed this up previously - mKeys.add(key); - } else if (backupKey != null) { - if (DEBUG) Log.d(TAG, "I can count this high: " + backupWidgetCount); - if (backupWidgetCount < MAX_WIDGETS_PER_PASS) { - LauncherAppWidgetProviderInfo widgetInfo = widgetManager - .getLauncherAppWidgetInfo(cursor.getInt(APPWIDGET_ID_INDEX)); - if (widgetInfo != null) { - if (DEBUG) Log.d(TAG, "saving widget " + backupKey); - writeRowToBackup(key, packWidget(dpi, widgetInfo), data); - mKeys.add(key); - backupWidgetCount ++; - } - } else { - if (VERBOSE) Log.v(TAG, "deferring widget backup " + backupKey); - // too many widgets for this pass, request another. - dataChanged(); - } - } - } - } finally { - cursor.close(); - } - } - - /** - * Read a widget from the stream. - * - *

Keys arrive in any order, so widgets that use this data may already exist. - * - * @param key identifier for the row - * @param buffer the serialized proto from the stream, may be larger than dataSize - * @param dataSize the size of the proto from the stream - */ - private void restoreWidget(Key key, byte[] buffer, int dataSize) throws IOException { - if (VERBOSE) Log.v(TAG, "unpacking widget " + key.id); - if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " + - Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP)); - Widget widget = unpackProto(new Widget(), buffer, dataSize); - if (DEBUG) Log.d(TAG, "unpacked " + widget.provider); - if (widget.icon.data != null) { - Bitmap icon = BitmapFactory - .decodeByteArray(widget.icon.data, 0, widget.icon.data.length); - if (icon == null) { - Log.w(TAG, "failed to unpack widget icon for " + key.name); - } else { - mIconCache.preloadIcon(ComponentName.unflattenFromString(widget.provider), - icon, widget.icon.dpi, widget.label, mUserSerial, mIdp); - } - } - - // Cache widget min sizes incase migration is required. - widgetSizes.add(widget.provider + "#" + widget.minSpanX + "," + widget.minSpanY); - } - - /** create a new key, with an integer ID. - * - *

Keys contain their own checksum instead of using - * the heavy-weight CheckedMessage wrapper. - */ - private Key getKey(int type, long id) { - Key key = new Key(); - key.type = type; - key.id = id; - key.checksum = checkKey(key); - return key; - } - - /** create a new key for a named object. - * - *

Keys contain their own checksum instead of using - * the heavy-weight CheckedMessage wrapper. - */ - private Key getKey(int type, String name) { - Key key = new Key(); - key.type = type; - key.name = name; - key.checksum = checkKey(key); - return key; - } - - /** keys need to be strings, serialize and encode. */ - private String keyToBackupKey(Key key) { - return Base64.encodeToString(Key.toByteArray(key), Base64.NO_WRAP); - } - - /** keys need to be strings, decode and parse. */ - private Key backupKeyToKey(String backupKey) throws InvalidBackupException { - try { - Key key = Key.parseFrom(Base64.decode(backupKey, Base64.DEFAULT)); - if (key.checksum != checkKey(key)) { - throw new InvalidBackupException("invalid key read from stream" + backupKey); - } - return key; - } catch (InvalidProtocolBufferNanoException | IllegalArgumentException e) { - throw new InvalidBackupException(e); - } - } - - /** Compute the checksum over the important bits of a key. */ - private long checkKey(Key key) { - CRC32 checksum = new CRC32(); - checksum.update(key.type); - checksum.update((int) (key.id & 0xffff)); - checksum.update((int) ((key.id >> 32) & 0xffff)); - if (!TextUtils.isEmpty(key.name)) { - checksum.update(key.name.getBytes()); - } - return checksum.getValue(); - } - - /** - * @return true if its an hotseat item, that can be replaced during restore. - * TODO: Extend check for folders in hotseat. - */ - private boolean isReplaceableHotseatItem(Favorite favorite) { - return favorite.container == Favorites.CONTAINER_HOTSEAT - && favorite.intent != null - && (favorite.itemType == Favorites.ITEM_TYPE_APPLICATION - || favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT - || favorite.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT); - } - - /** Serialize a Favorite for persistence, including a checksum wrapper. */ - private Favorite packFavorite(Cursor c) { - Favorite favorite = new Favorite(); - favorite.id = c.getLong(ID_INDEX); - favorite.screen = c.getInt(SCREEN_INDEX); - favorite.container = c.getInt(CONTAINER_INDEX); - favorite.cellX = c.getInt(CELLX_INDEX); - favorite.cellY = c.getInt(CELLY_INDEX); - favorite.spanX = c.getInt(SPANX_INDEX); - favorite.spanY = c.getInt(SPANY_INDEX); - favorite.rank = c.getInt(RANK_INDEX); - - String title = c.getString(TITLE_INDEX); - if (!TextUtils.isEmpty(title)) { - favorite.title = title; - } - String intentDescription = c.getString(INTENT_INDEX); - Intent intent = null; - if (!TextUtils.isEmpty(intentDescription)) { - try { - intent = Intent.parseUri(intentDescription, 0); - intent.removeExtra(ItemInfo.EXTRA_PROFILE); - favorite.intent = intent.toUri(0); - } catch (URISyntaxException e) { - Log.e(TAG, "Invalid intent", e); - } - } - favorite.itemType = c.getInt(ITEM_TYPE_INDEX); - if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) { - favorite.appWidgetId = c.getInt(APPWIDGET_ID_INDEX); - String appWidgetProvider = c.getString(APPWIDGET_PROVIDER_INDEX); - if (!TextUtils.isEmpty(appWidgetProvider)) { - favorite.appWidgetProvider = appWidgetProvider; - } - } else if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT - || favorite.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - String iconPackage = c.getString(ICON_PACKAGE_INDEX); - String iconResource = c.getString(ICON_RESOURCE_INDEX); - if (!TextUtils.isEmpty(iconPackage) && !TextUtils.isEmpty(iconResource)) { - favorite.iconResource = iconResource; - favorite.iconPackage = iconPackage; - } - - byte[] blob = c.getBlob(ICON_INDEX); - if (blob != null && blob.length > 0) { - favorite.icon = blob; - } - } - - if (isReplaceableHotseatItem(favorite)) { - if (intent != null && intent.getComponent() != null) { - PackageManager pm = mContext.getPackageManager(); - ActivityInfo activity = null;; - try { - activity = pm.getActivityInfo(intent.getComponent(), 0); - } catch (NameNotFoundException e) { - Log.e(TAG, "Target not found", e); - } - if (activity == null) { - return favorite; - } - for (int i = 0; i < mItemTypeMatchers.length; i++) { - if (mItemTypeMatchers[i] == null) { - mItemTypeMatchers[i] = new ItemTypeMatcher( - CommonAppTypeParser.getResourceForItemType(i)); - } - if (mItemTypeMatchers[i].matches(activity, pm)) { - favorite.targetType = i; - break; - } - } - } - } - - return favorite; - } - - /** Deserialize a Favorite from persistence, after verifying checksum wrapper. */ - private ContentValues unpackFavorite(byte[] buffer, int dataSize) - throws IOException { - Favorite favorite = unpackProto(new Favorite(), buffer, dataSize); - - // If it is a hotseat item, move it accordingly. - if (favorite.container == Favorites.CONTAINER_HOTSEAT) { - favorite.screen += mHotseatShift; - } - - ContentValues values = new ContentValues(); - values.put(Favorites._ID, favorite.id); - values.put(Favorites.SCREEN, favorite.screen); - values.put(Favorites.CONTAINER, favorite.container); - values.put(Favorites.CELLX, favorite.cellX); - values.put(Favorites.CELLY, favorite.cellY); - values.put(Favorites.SPANX, favorite.spanX); - values.put(Favorites.SPANY, favorite.spanY); - values.put(Favorites.RANK, favorite.rank); - - if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT - || favorite.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - values.put(Favorites.ICON_PACKAGE, favorite.iconPackage); - values.put(Favorites.ICON_RESOURCE, favorite.iconResource); - values.put(Favorites.ICON, favorite.icon); - } - - if (!TextUtils.isEmpty(favorite.title)) { - values.put(Favorites.TITLE, favorite.title); - } else { - values.put(Favorites.TITLE, ""); - } - if (!TextUtils.isEmpty(favorite.intent)) { - values.put(Favorites.INTENT, favorite.intent); - } - values.put(Favorites.ITEM_TYPE, favorite.itemType); - - UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle(); - long userSerialNumber = - UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle); - values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber); - - // If we will attempt grid resize, use the original profile to validate grid size, as - // anything which fits in the original grid should fit in the current grid after - // grid migration. - DeviceProfieData currentProfile = migrationCompatibleProfileData == null - ? mDeviceProfileData : migrationCompatibleProfileData; - - if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) { - if (!TextUtils.isEmpty(favorite.appWidgetProvider)) { - values.put(Favorites.APPWIDGET_PROVIDER, favorite.appWidgetProvider); - } - values.put(Favorites.APPWIDGET_ID, favorite.appWidgetId); - values.put(LauncherSettings.Favorites.RESTORED, - LauncherAppWidgetInfo.FLAG_ID_NOT_VALID | - LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY | - LauncherAppWidgetInfo.FLAG_UI_NOT_READY); - - // Verify placement - if (((favorite.cellX + favorite.spanX) > currentProfile.desktopCols) - || ((favorite.cellY + favorite.spanY) > currentProfile.desktopRows)) { - restoreSuccessful = false; - throw new InvalidBackupException("Widget not in screen bounds, aborting restore"); - } - } else { - // Check if it is an hotseat item, that can be replaced. - if (isReplaceableHotseatItem(favorite) - && favorite.targetType != Favorite.TARGET_NONE - && favorite.targetType < CommonAppTypeParser.SUPPORTED_TYPE_COUNT) { - Log.e(TAG, "Added item type flag"); - values.put(LauncherSettings.Favorites.RESTORED, - 1 | CommonAppTypeParser.encodeItemTypeToFlag(favorite.targetType)); - } else { - // Let LauncherModel know we've been here. - values.put(LauncherSettings.Favorites.RESTORED, 1); - } - - // Verify placement - if (favorite.container == Favorites.CONTAINER_HOTSEAT) { - if ((favorite.screen >= currentProfile.hotseatCount) - || (favorite.screen == currentProfile.allappsRank)) { - restoreSuccessful = false; - throw new InvalidBackupException("Item not in hotseat bounds, aborting restore"); - } - } else { - if ((favorite.cellX >= currentProfile.desktopCols) - || (favorite.cellY >= currentProfile.desktopRows)) { - restoreSuccessful = false; - throw new InvalidBackupException("Item not in desktop bounds, aborting restore"); - } - } - } - - return values; - } - - /** Serialize a Screen for persistence, including a checksum wrapper. */ - private Screen packScreen(Cursor c) { - Screen screen = new Screen(); - screen.id = c.getLong(ID_INDEX); - screen.rank = c.getInt(SCREEN_RANK_INDEX); - return screen; - } - - /** Deserialize a Screen from persistence, after verifying checksum wrapper. */ - private ContentValues unpackScreen(byte[] buffer, int dataSize) - throws InvalidProtocolBufferNanoException { - Screen screen = unpackProto(new Screen(), buffer, dataSize); - ContentValues values = new ContentValues(); - values.put(WorkspaceScreens._ID, screen.id); - values.put(WorkspaceScreens.SCREEN_RANK, screen.rank); - return values; - } - - /** Serialize an icon Resource for persistence, including a checksum wrapper. */ - private Resource packIcon(int dpi, Bitmap icon) { - Resource res = new Resource(); - res.dpi = dpi; - res.data = Utilities.flattenBitmap(icon); - return res; - } - - /** Serialize a widget for persistence, including a checksum wrapper. */ - private Widget packWidget(int dpi, LauncherAppWidgetProviderInfo info) { - Widget widget = new Widget(); - widget.provider = info.provider.flattenToShortString(); - widget.label = info.label; - widget.configure = info.configure != null; - if (info.icon != 0) { - widget.icon = new Resource(); - Drawable fullResIcon = mIconCache.getFullResIcon(info.provider.getPackageName(), info.icon); - Bitmap icon = Utilities.createIconBitmap(fullResIcon, mContext); - widget.icon.data = Utilities.flattenBitmap(icon); - widget.icon.dpi = dpi; - } - - Point spans = info.getMinSpans(mIdp, mContext); - widget.minSpanX = spans.x; - widget.minSpanY = spans.y; - return widget; - } - - /** - * Deserialize a proto after verifying checksum wrapper. - */ - private T unpackProto(T proto, byte[] buffer, int dataSize) - throws InvalidProtocolBufferNanoException { - MessageNano.mergeFrom(proto, readCheckedBytes(buffer, dataSize)); - if (DEBUG) Log.d(TAG, "unpacked proto " + proto); - return proto; - } - - /** - * Read the old journal from the input file. - * - * In the event of any error, just pretend we didn't have a journal, - * in that case, do a full backup. - * - * @param oldState the read-0only file descriptor pointing to the old journal - * @return a Journal protocol buffer - */ - private Journal readJournal(ParcelFileDescriptor oldState) { - Journal journal = new Journal(); - if (oldState == null) { - return journal; - } - FileInputStream inStream = new FileInputStream(oldState.getFileDescriptor()); - try { - int availableBytes = inStream.available(); - if (DEBUG) Log.d(TAG, "available " + availableBytes); - if (availableBytes < MAX_JOURNAL_SIZE) { - byte[] buffer = new byte[availableBytes]; - int bytesRead = 0; - boolean valid = false; - InvalidProtocolBufferNanoException lastProtoException = null; - while (availableBytes > 0) { - try { - // OMG what are you doing? This is crazy inefficient! - // If we read a byte that is not ours, we will cause trouble: b/12491813 - // However, we don't know how many bytes to expect (oops). - // So we have to step through *slowly*, watching for the end. - int result = inStream.read(buffer, bytesRead, 1); - if (result > 0) { - availableBytes -= result; - bytesRead += result; - } else { - Log.w(TAG, "unexpected end of file while reading journal."); - // stop reading and see what there is to parse - availableBytes = 0; - } - } catch (IOException e) { - buffer = null; - availableBytes = 0; - } - - // check the buffer to see if we have a valid journal - try { - MessageNano.mergeFrom(journal, readCheckedBytes(buffer, bytesRead)); - // if we are here, then we have read a valid, checksum-verified journal - valid = true; - availableBytes = 0; - if (VERBOSE) Log.v(TAG, "read " + bytesRead + " bytes of journal"); - } catch (InvalidProtocolBufferNanoException e) { - // if we don't have the whole journal yet, mergeFrom will throw. keep going. - lastProtoException = e; - journal.clear(); - } - } - if (DEBUG) Log.d(TAG, "journal bytes read: " + bytesRead); - if (!valid) { - Log.w(TAG, "could not find a valid journal", lastProtoException); - } - } - } catch (IOException e) { - Log.w(TAG, "failed to close the journal", e); - } finally { - try { - inStream.close(); - } catch (IOException e) { - Log.w(TAG, "failed to close the journal", e); - } - } - return journal; - } - - private void writeRowToBackup(Key key, MessageNano proto, BackupDataOutput data) - throws IOException { - writeRowToBackup(keyToBackupKey(key), proto, data); - } - - private void writeRowToBackup(String backupKey, MessageNano proto, - BackupDataOutput data) throws IOException { - byte[] blob = writeCheckedBytes(proto); - data.writeEntityHeader(backupKey, blob.length); - data.writeEntityData(blob, blob.length); - mBackupDataWasUpdated = true; - if (VERBOSE) Log.v(TAG, "Writing New entry " + backupKey); - } - - /** - * Write the new journal to the output file. - * - * In the event of any error, just pretend we didn't have a journal, - * in that case, do a full backup. - - * @param newState the write-only file descriptor pointing to the new journal - * @param journal a Journal protocol buffer - */ - private void writeJournal(ParcelFileDescriptor newState, Journal journal) { - try { - FileOutputStream outStream = new FileOutputStream(newState.getFileDescriptor()); - final byte[] journalBytes = writeCheckedBytes(journal); - outStream.write(journalBytes); - outStream.close(); - if (VERBOSE) Log.v(TAG, "wrote " + journalBytes.length + " bytes of journal"); - } catch (IOException e) { - Log.w(TAG, "failed to write backup journal", e); - } - } - - /** Wrap a proto in a CheckedMessage and compute the checksum. */ - private byte[] writeCheckedBytes(MessageNano proto) { - CheckedMessage wrapper = new CheckedMessage(); - wrapper.payload = MessageNano.toByteArray(proto); - CRC32 checksum = new CRC32(); - checksum.update(wrapper.payload); - wrapper.checksum = checksum.getValue(); - return MessageNano.toByteArray(wrapper); - } - - /** Unwrap a proto message from a CheckedMessage, verifying the checksum. */ - private static byte[] readCheckedBytes(byte[] buffer, int dataSize) - throws InvalidProtocolBufferNanoException { - CheckedMessage wrapper = new CheckedMessage(); - MessageNano.mergeFrom(wrapper, buffer, 0, dataSize); - CRC32 checksum = new CRC32(); - checksum.update(wrapper.payload); - if (wrapper.checksum != checksum.getValue()) { - throw new InvalidProtocolBufferNanoException("checksum does not match"); - } - return wrapper.payload; - } - - /** - * @return true if the launcher is in a state to support backup - */ - private boolean launcherIsReady() { - ContentResolver cr = mContext.getContentResolver(); - Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION, null, null, null); - if (cursor == null) { - // launcher data has been wiped, do nothing - return false; - } - cursor.close(); - - if (LauncherAppState.getInstanceNoCreate() == null) { - // launcher services are unavailable, try again later - return false; - } - - return true; - } - - private String getUserSelectionArg() { - return Favorites.PROFILE_ID + '=' + UserManagerCompat.getInstance(mContext) - .getSerialNumberForUser(UserHandleCompat.myUserHandle()); - } - - @Thunk class InvalidBackupException extends IOException { - - private static final long serialVersionUID = 8931456637211665082L; - - @Thunk InvalidBackupException(Throwable cause) { - super(cause); - } - - @Thunk InvalidBackupException(String reason) { - super(reason); - } - } - - public boolean shouldAttemptWorkspaceMigration() { - return migrationCompatibleProfileData != null; - } - - /** - * A class to check if an activity can handle one of the intents from a list of - * predefined intents. - */ - private class ItemTypeMatcher { - - private final ArrayList mIntents; - - ItemTypeMatcher(int xml_res) { - mIntents = xml_res == 0 ? new ArrayList() : parseIntents(xml_res); - } - - private ArrayList parseIntents(int xml_res) { - ArrayList intents = new ArrayList(); - XmlResourceParser parser = mContext.getResources().getXml(xml_res); - try { - DefaultLayoutParser.beginDocument(parser, DefaultLayoutParser.TAG_RESOLVE); - final int depth = parser.getDepth(); - int type; - while (((type = parser.next()) != XmlPullParser.END_TAG || - parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { - if (type != XmlPullParser.START_TAG) { - continue; - } else if (DefaultLayoutParser.TAG_FAVORITE.equals(parser.getName())) { - final String uri = DefaultLayoutParser.getAttributeValue( - parser, DefaultLayoutParser.ATTR_URI); - intents.add(Intent.parseUri(uri, 0)); - } - } - } catch (URISyntaxException | XmlPullParserException | IOException e) { - Log.e(TAG, "Unable to parse " + xml_res, e); - } finally { - parser.close(); - } - return intents; - } - - public boolean matches(ActivityInfo activity, PackageManager pm) { - for (Intent intent : mIntents) { - intent.setPackage(activity.packageName); - ResolveInfo info = pm.resolveActivity(intent, 0); - if (info != null && (info.activityInfo.name.equals(activity.name) - || info.activityInfo.name.equals(activity.targetActivity))) { - return true; - } - } - return false; - } - } -} diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index f5b32edda0..a79df0daf3 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -382,14 +382,6 @@ public class LauncherProvider extends ContentProvider { loadDefaultFavoritesIfNecessary(); return null; } - case LauncherSettings.Settings.METHOD_UPDATE_FOLDER_ITEMS_RANK: { - mOpenHelper.updateFolderItemsRank(mOpenHelper.getWritableDatabase(), false); - return null; - } - case LauncherSettings.Settings.METHOD_CONVERT_SHORTCUTS_TO_ACTIVITIES: { - mOpenHelper.convertShortcutsToLauncherActivities(mOpenHelper.getWritableDatabase()); - return null; - } case LauncherSettings.Settings.METHOD_DELETE_DB: { // Are you sure? (y/n) mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase()); @@ -439,8 +431,6 @@ public class LauncherProvider extends ContentProvider { * Overridden in tests */ protected void notifyListeners() { - // always notify the backup agent - LauncherBackupAgentHelper.dataChanged(getContext()); mListenerHandler.sendEmptyMessage(ChangeListenerWrapper.MSG_LAUNCHER_PROVIDER_CHANGED); } diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index c213c8d77a..8157eb3288 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -285,9 +285,6 @@ public class LauncherSettings { public static final String METHOD_CLEAR_EMPTY_DB_FLAG = "clear_empty_db_flag"; public static final String METHOD_DELETE_EMPTY_FOLDERS = "delete_empty_folders"; - public static final String METHOD_UPDATE_FOLDER_ITEMS_RANK = "update_folder_items_rank"; - public static final String METHOD_CONVERT_SHORTCUTS_TO_ACTIVITIES = - "convert_shortcuts_to_launcher_activities"; public static final String METHOD_NEW_ITEM_ID = "generate_new_item_id"; public static final String METHOD_NEW_SCREEN_ID = "generate_new_screen_id"; diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java index cb1acaac2e..5941af888b 100644 --- a/src/com/android/launcher3/model/GridSizeMigrationTask.java +++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java @@ -51,10 +51,6 @@ public class GridSizeMigrationTask { private static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size"; private static final String KEY_MIGRATION_SRC_HOTSEAT_COUNT = "migration_src_hotseat_count"; - // Set of entries indicating minimum size a widget can be resized to. This is used during - // restore in case the widget has not been installed yet. - private static final String KEY_MIGRATION_WIDGET_MINSIZE = "migration_widget_min_size"; - // These are carefully selected weights for various item types (Math.random?), to allow for // the least absurd migration experience. private static final float WT_SHORTCUT = 1; @@ -81,11 +77,9 @@ public class GridSizeMigrationTask { private final int mDestHotseatSize; protected GridSizeMigrationTask(Context context, InvariantDeviceProfile idp, - HashSet validPackages, HashMap widgetMinSize, - Point sourceSize, Point targetSize) { + HashSet validPackages, Point sourceSize, Point targetSize) { mContext = context; mValidPackages = validPackages; - mWidgetMinSize.putAll(widgetMinSize); mIdp = idp; mSrcX = sourceSize.x; @@ -886,16 +880,6 @@ public class GridSizeMigrationTask { return String.format(Locale.ENGLISH, "%d,%d", x, y); } - public static void markForMigration( - Context context, HashSet widgets, BackupProtos.DeviceProfieData srcProfile) { - Utilities.getPrefs(context).edit() - .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, - getPointString((int) srcProfile.desktopCols, (int) srcProfile.desktopRows)) - .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, (int) srcProfile.hotseatCount) - .putStringSet(KEY_MIGRATION_WIDGET_MINSIZE, widgets) - .apply(); - } - /** * Migrates the workspace and hotseat in case their sizes changed. * @return false if the migration failed. @@ -957,14 +941,6 @@ public class GridSizeMigrationTask { + " to " + targetSize); } - // Min widget sizes - HashMap widgetMinSize = new HashMap<>(); - for (String s : Utilities.getPrefs(context).getStringSet(KEY_MIGRATION_WIDGET_MINSIZE, - Collections.emptySet())) { - String[] parts = s.split("#"); - widgetMinSize.put(parts[0], parsePoint(parts[1])); - } - // Migrate the workspace grid, step by step. while (targetSizeIndex < sourceSizeIndex ) { // We only need to migrate the grid if source size is greater @@ -974,8 +950,7 @@ public class GridSizeMigrationTask { if (new GridSizeMigrationTask(context, LauncherAppState.getInstance().getInvariantDeviceProfile(), - validPackages, widgetMinSize, - stepSourceSize, stepTargetSize).migrateWorkspace()) { + validPackages, stepSourceSize, stepTargetSize).migrateWorkspace()) { dbChanged = true; } sourceSizeIndex--; @@ -1006,7 +981,6 @@ public class GridSizeMigrationTask { prefs.edit() .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString) .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons) - .remove(KEY_MIGRATION_WIDGET_MINSIZE) .apply(); } } diff --git a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java index bb6ed0de98..4addbfa239 100644 --- a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java +++ b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java @@ -47,8 +47,9 @@ public class LossyScreenMigrationTask extends GridSizeMigrationTask { protected LossyScreenMigrationTask( Context context, InvariantDeviceProfile idp, SQLiteDatabase db) { // Decrease the rows count by 1 - super(context, idp, getValidPackages(context), new HashMap(), - new Point(idp.numColumns, idp.numRows + 1), new Point(idp.numColumns, idp.numRows)); + super(context, idp, getValidPackages(context), + new Point(idp.numColumns, idp.numRows + 1), + new Point(idp.numColumns, idp.numRows)); mDb = db; mOriginalItems = new LongArrayMap<>(); diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java index c250cb2918..3cef9e0f6f 100644 --- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java +++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java @@ -130,7 +130,7 @@ public class GridSizeMigrationTaskTest extends ProviderTestCase2(), + new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new Point(4, 4), new Point(3, 3)).migrateWorkspace(); // Column 2 and row 2 got removed. @@ -149,7 +149,7 @@ public class GridSizeMigrationTaskTest extends ProviderTestCase2(), + new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new Point(4, 4), new Point(3, 3)).migrateWorkspace(); // Items in the second column get moved to new screen @@ -173,7 +173,7 @@ public class GridSizeMigrationTaskTest extends ProviderTestCase2(), + new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new Point(4, 4), new Point(3, 3)).migrateWorkspace(); // Items in the second column of the first screen should get placed on the 3rd @@ -204,7 +204,7 @@ public class GridSizeMigrationTaskTest extends ProviderTestCase2(), + new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new Point(4, 4), new Point(3, 3)).migrateWorkspace(); // Items in the second column of the first screen should get placed on a new screen.