diff options
-rw-r--r-- | src/com/android/launcher3/LauncherBackupAgentHelper.java | 46 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherBackupHelper.java (renamed from src/com/android/launcher3/LauncherBackupAgent.java) | 254 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherProvider.java | 2 | ||||
-rw-r--r-- | util/com/android/launcher3/DecoderRing.java | 20 |
4 files changed, 199 insertions, 123 deletions
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java new file mode 100644 index 000000000..2b5059b72 --- /dev/null +++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java @@ -0,0 +1,46 @@ +/* + * 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.BackupManager; +import android.content.Context; + +public class LauncherBackupAgentHelper extends BackupAgentHelper { + + private static BackupManager sBackupManager; + + /** + * Notify the backup manager that out database is dirty. + * + * <P>This does not force an immediate backup. + * + * @param context application context + */ + public static void dataChanged(Context context) { + if (sBackupManager == null) { + sBackupManager = new BackupManager(context); + } + sBackupManager.dataChanged(); + } + + + @Override + public void onCreate() { + addHelper(LauncherBackupHelper.LAUNCHER_PREFIX, new LauncherBackupHelper(this)); + } +} diff --git a/src/com/android/launcher3/LauncherBackupAgent.java b/src/com/android/launcher3/LauncherBackupHelper.java index f40238cec..9b901eea1 100644 --- a/src/com/android/launcher3/LauncherBackupAgent.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -30,7 +30,8 @@ import com.android.launcher3.backup.BackupProtos.Resource; import com.android.launcher3.backup.BackupProtos.Screen; import com.android.launcher3.backup.BackupProtos.Widget; -import android.app.backup.BackupAgent; +import android.app.backup.BackupDataInputStream; +import android.app.backup.BackupHelper; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.BackupManager; @@ -64,10 +65,11 @@ import java.util.zip.CRC32; /** * Persist the launcher home state across calamities. */ -public class LauncherBackupAgent extends BackupAgent { +public class LauncherBackupHelper implements BackupHelper { - private static final String TAG = "LauncherBackupAgent"; + private static final String TAG = "LauncherBackupHelper"; private static final boolean DEBUG = false; + private static final boolean DEBUG_PAYLOAD = false; private static final int MAX_JOURNAL_SIZE = 1000000; @@ -79,6 +81,8 @@ public class LauncherBackupAgent extends BackupAgent { public static final int IMAGE_COMPRESSION_QUALITY = 75; + public static final String LAUNCHER_PREFIX = "L"; + private static final Bitmap.CompressFormat IMAGE_FORMAT = android.graphics.Bitmap.CompressFormat.PNG; @@ -130,26 +134,19 @@ public class LauncherBackupAgent extends BackupAgent { private static final int SCREEN_RANK_INDEX = 2; - - private static final String[] ICON_PROJECTION = { - Favorites._ID, // 0 - Favorites.MODIFIED, // 1 - Favorites.INTENT // 2 - }; + private final Context mContext; private HashMap<ComponentName, AppWidgetProviderInfo> mWidgetMap; + private ArrayList<Key> mKeys; - /** - * Notify the backup manager that out database is dirty. - * - * <P>This does not force an immediate backup. - * - * @param context application context - */ - public static void dataChanged(Context context) { + public LauncherBackupHelper(Context context) { + mContext = context; + } + + private void dataChanged() { if (sBackupManager == null) { - sBackupManager = new BackupManager(context); + sBackupManager = new BackupManager(mContext); } sBackupManager.dataChanged(); } @@ -167,9 +164,8 @@ public class LauncherBackupAgent extends BackupAgent { * @throws IOException */ @Override - public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState) - throws IOException { + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { Log.v(TAG, "onBackup"); Journal in = readJournal(oldState); @@ -183,10 +179,14 @@ public class LauncherBackupAgent extends BackupAgent { Log.v(TAG, "lastBackupTime=" + lastBackupTime); ArrayList<Key> keys = new ArrayList<Key>(); - backupFavorites(in, data, out, keys); - backupScreens(in, data, out, keys); - backupIcons(in, data, out, keys); - backupWidgets(in, data, out, keys); + try { + backupFavorites(in, data, out, keys); + backupScreens(in, data, out, keys); + backupIcons(in, data, out, keys); + backupWidgets(in, data, out, keys); + } catch (IOException e) { + Log.e(TAG, "launcher backup has failed", e); + } out.key = keys.toArray(BackupProtos.Key.EMPTY_ARRAY); writeJournal(newState, out); @@ -194,70 +194,76 @@ public class LauncherBackupAgent extends BackupAgent { } /** - * Restore home screen from the restored data stream. + * Restore launcher configuration from the restored data stream. * * <P>Keys may arrive in any order. * - * @param data the key/value pairs from the server - * @param versionCode the version of the app that generated the data - * @param newState notes for the next backup - * @throws IOException + * @param data the key/value pair from the server */ @Override - public void onRestore(BackupDataInput data, int versionCode, ParcelFileDescriptor newState) - throws IOException { - Log.v(TAG, "onRestore"); - int numRows = 0; - Journal out = new Journal(); - - ArrayList<Key> keys = new ArrayList<Key>(); + public void restoreEntity(BackupDataInputStream data) { + Log.v(TAG, "restoreEntity"); + if (mKeys == null) { + mKeys = new ArrayList<Key>(); + } byte[] buffer = new byte[512]; - while (data.readNextHeader()) { - numRows++; String backupKey = data.getKey(); - int dataSize = data.getDataSize(); + int dataSize = data.size(); if (buffer.length < dataSize) { buffer = new byte[dataSize]; } Key key = null; - int bytesRead = data.readEntityData(buffer, 0, dataSize); - if (DEBUG) { - Log.d(TAG, "read " + bytesRead + " of " + dataSize + " available"); - } - try { - key = backupKeyToKey(backupKey); - switch (key.type) { - case Key.FAVORITE: - restoreFavorite(key, buffer, dataSize, keys); - break; - - case Key.SCREEN: - restoreScreen(key, buffer, dataSize, keys); - break; - - case Key.ICON: - restoreIcon(key, buffer, dataSize, keys); - break; - - case Key.WIDGET: - restoreWidget(key, buffer, dataSize, keys); - break; - - default: - Log.w(TAG, "unknown restore entity type: " + key.type); - break; - } - } catch (KeyParsingException e) { - Log.w(TAG, "ignoring unparsable backup key: " + backupKey); + int bytesRead = 0; + try { + bytesRead = data.read(buffer, 0, dataSize); + if (DEBUG) Log.d(TAG, "read " + bytesRead + " of " + dataSize + " available"); + } catch (IOException e) { + Log.d(TAG, "failed to read entity from restore data", e); + } + try { + key = backupKeyToKey(backupKey); + switch (key.type) { + case Key.FAVORITE: + restoreFavorite(key, buffer, dataSize, mKeys); + break; + + case Key.SCREEN: + restoreScreen(key, buffer, dataSize, mKeys); + break; + + case Key.ICON: + restoreIcon(key, buffer, dataSize, mKeys); + break; + + case Key.WIDGET: + restoreWidget(key, buffer, dataSize, mKeys); + break; + + default: + Log.w(TAG, "unknown restore entity type: " + key.type); + break; } + } catch (KeyParsingException e) { + Log.w(TAG, "ignoring unparsable backup key: " + backupKey); } + } + + /** + * Record the restore state for the next backup. + * + * @param newState notes about the backup state after restore. + */ + @Override + public void writeNewStateDescription(ParcelFileDescriptor newState) { // clear the output journal time, to force a full backup to // will catch any changes the restore process might have made + Journal out = new Journal(); out.t = 0; - out.key = keys.toArray(BackupProtos.Key.EMPTY_ARRAY); + out.key = mKeys.toArray(BackupProtos.Key.EMPTY_ARRAY); writeJournal(newState, out); - Log.v(TAG, "onRestore: read " + numRows + " rows"); + Log.v(TAG, "onRestore: read " + mKeys.size() + " rows"); + mKeys.clear(); } /** @@ -278,7 +284,7 @@ public class LauncherBackupAgent extends BackupAgent { if (DEBUG) Log.d(TAG, "favorite savedIds.size()=" + savedIds.size()); // persist things that have changed since the last backup - ContentResolver cr = getContentResolver(); + ContentResolver cr = mContext.getContentResolver(); Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION, null, null, null); Set<String> currentIds = new HashSet<String>(cursor.getCount()); @@ -346,7 +352,7 @@ public class LauncherBackupAgent extends BackupAgent { if (DEBUG) Log.d(TAG, "screen savedIds.size()=" + savedIds.size()); // persist things that have changed since the last backup - ContentResolver cr = getContentResolver(); + ContentResolver cr = mContext.getContentResolver(); Cursor cursor = cr.query(WorkspaceScreens.CONTENT_URI, SCREEN_PROJECTION, null, null, null); Set<String> currentIds = new HashSet<String>(cursor.getCount()); @@ -408,15 +414,15 @@ public class LauncherBackupAgent extends BackupAgent { private void backupIcons(Journal in, BackupDataOutput data, Journal out, ArrayList<Key> keys) throws IOException { // persist icons that haven't been persisted yet - final LauncherAppState app = LauncherAppState.getInstanceNoCreate(); - if (app == null) { - dataChanged(this); // try again later + final LauncherAppState appState = LauncherAppState.getInstanceNoCreate(); + if (appState == null) { + dataChanged(); // try again later if (DEBUG) Log.d(TAG, "Launcher is not initialized, delaying icon backup"); return; } - final ContentResolver cr = getContentResolver(); - final IconCache iconCache = app.getIconCache(); - final int dpi = getResources().getDisplayMetrics().densityDpi; + final ContentResolver cr = mContext.getContentResolver(); + final IconCache iconCache = appState.getIconCache(); + final int dpi = mContext.getResources().getDisplayMetrics().densityDpi; // read the old ID set Set<String> savedIds = getSavedIdsByType(Key.ICON, in); @@ -463,7 +469,7 @@ public class LauncherBackupAgent extends BackupAgent { } else { if (DEBUG) Log.d(TAG, "scheduling another run for icon " + backupKey); // too many icons for this pass, request another. - dataChanged(this); + dataChanged(); } } } catch (URISyntaxException e) { @@ -527,15 +533,15 @@ public class LauncherBackupAgent extends BackupAgent { // persist static widget info that hasn't been persisted yet final LauncherAppState appState = LauncherAppState.getInstanceNoCreate(); if (appState == null) { - dataChanged(this); // try again later + dataChanged(); // try again later if (DEBUG) Log.d(TAG, "Launcher is not initialized, delaying widget backup"); return; } - final ContentResolver cr = getContentResolver(); - final WidgetPreviewLoader previewLoader = new WidgetPreviewLoader(this); - final PagedViewCellLayout widgetSpacingLayout = new PagedViewCellLayout(this); + final ContentResolver cr = mContext.getContentResolver(); + final WidgetPreviewLoader previewLoader = new WidgetPreviewLoader(mContext); + final PagedViewCellLayout widgetSpacingLayout = new PagedViewCellLayout(mContext); final IconCache iconCache = appState.getIconCache(); - final int dpi = getResources().getDisplayMetrics().densityDpi; + final int dpi = mContext.getResources().getDisplayMetrics().densityDpi; final DeviceProfile profile = appState.getDynamicGrid().getDeviceProfile(); if (DEBUG) Log.d(TAG, "cellWidthPx: " + profile.cellWidthPx); @@ -584,7 +590,7 @@ public class LauncherBackupAgent extends BackupAgent { } else { if (DEBUG) Log.d(TAG, "scheduling another run for widget " + backupKey); // too many widgets for this pass, request another. - dataChanged(this); + dataChanged(); } } } @@ -812,7 +818,7 @@ public class LauncherBackupAgent extends BackupAgent { if (info.icon != 0) { widget.icon = new Resource(); Drawable fullResIcon = iconCache.getFullResIcon(provider.getPackageName(), info.icon); - Bitmap icon = Utilities.createIconBitmap(fullResIcon, this); + Bitmap icon = Utilities.createIconBitmap(fullResIcon, mContext); ByteArrayOutputStream os = new ByteArrayOutputStream(); if (icon.compress(IMAGE_FORMAT, IMAGE_COMPRESSION_QUALITY, os)) { widget.icon.data = os.toByteArray(); @@ -849,43 +855,49 @@ public class LauncherBackupAgent extends BackupAgent { * @return a Journal protocol bugffer */ private Journal readJournal(ParcelFileDescriptor oldState) { - int fileSize = (int) oldState.getStatSize(); - int remaining = fileSize; - byte[] buffer = null; Journal journal = new Journal(); - if (remaining < MAX_JOURNAL_SIZE) { - FileInputStream inStream = new FileInputStream(oldState.getFileDescriptor()); - int offset = 0; - - buffer = new byte[remaining]; - while (remaining > 0) { + if (oldState == null) { + return journal; + } + FileInputStream inStream = new FileInputStream(oldState.getFileDescriptor()); + try { + int remaining = inStream.available(); + if (DEBUG) Log.d(TAG, "available " + remaining); + if (remaining < MAX_JOURNAL_SIZE) { + byte[] buffer = new byte[remaining]; int bytesRead = 0; - try { - bytesRead = inStream.read(buffer, offset, remaining); - } catch (IOException e) { - Log.w(TAG, "failed to read the journal", e); - buffer = null; - remaining = 0; - } - if (bytesRead > 0) { - remaining -= bytesRead; - } else { - // act like there is not journal - Log.w(TAG, "failed to read the journal"); - buffer = null; - remaining = 0; + while (remaining > 0) { + try { + int result = inStream.read(buffer, bytesRead, remaining); + if (result > 0) { + if (DEBUG) Log.d(TAG, "read some bytes: " + result); + remaining -= result; + bytesRead += result; + } else { + // stop reading ands see what there is to parse + Log.w(TAG, "read error: " + result); + remaining = 0; + } + } catch (IOException e) { + Log.w(TAG, "failed to read the journal", e); + buffer = null; + remaining = 0; + } } - } - - if (buffer != null) { - try { - MessageNano.mergeFrom(journal, readCheckedBytes(buffer, 0, fileSize)); - } catch (InvalidProtocolBufferNanoException e) { - Log.d(TAG, "failed to read the journal", e); - journal.clear(); + if (DEBUG) Log.d(TAG, "journal bytes read: " + bytesRead); + + if (buffer != null) { + try { + MessageNano.mergeFrom(journal, readCheckedBytes(buffer, 0, bytesRead)); + } catch (InvalidProtocolBufferNanoException e) { + Log.d(TAG, "failed to read the journal", e); + journal.clear(); + } } } - + } catch (IOException e) { + Log.d(TAG, "failed to close the journal", e); + } finally { try { inStream.close(); } catch (IOException e) { @@ -904,7 +916,7 @@ public class LauncherBackupAgent extends BackupAgent { out.bytes += blob.length; Log.v(TAG, "saving " + geKeyType(key) + " " + backupKey + ": " + getKeyName(key) + "/" + blob.length); - if(DEBUG) { + if(DEBUG_PAYLOAD) { String encoded = Base64.encodeToString(blob, 0, blob.length, Base64.NO_WRAP); final int chunkSize = 1024; for (int offset = 0; offset < encoded.length(); offset += chunkSize) { @@ -983,7 +995,7 @@ public class LauncherBackupAgent extends BackupAgent { private AppWidgetProviderInfo findAppWidgetProviderInfo(ComponentName component) { if (mWidgetMap == null) { List<AppWidgetProviderInfo> widgets = - AppWidgetManager.getInstance(this).getInstalledProviders(); + AppWidgetManager.getInstance(mContext).getInstalledProviders(); mWidgetMap = new HashMap<ComponentName, AppWidgetProviderInfo>(widgets.size()); for (AppWidgetProviderInfo info : widgets) { mWidgetMap.put(info.provider, info); diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 5a8f630bc..39afe1042 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -210,7 +210,7 @@ public class LauncherProvider extends ContentProvider { } // always notify the backup agent - LauncherBackupAgent.dataChanged(getContext()); + LauncherBackupAgentHelper.dataChanged(getContext()); } private void addModifiedTime(ContentValues values) { diff --git a/util/com/android/launcher3/DecoderRing.java b/util/com/android/launcher3/DecoderRing.java index b7349fef7..1d9e0defb 100644 --- a/util/com/android/launcher3/DecoderRing.java +++ b/util/com/android/launcher3/DecoderRing.java @@ -18,6 +18,7 @@ package com.android.launcher3; import com.android.launcher3.backup.BackupProtos.CheckedMessage; import com.android.launcher3.backup.BackupProtos.Favorite; import com.android.launcher3.backup.BackupProtos.Key; +import com.android.launcher3.backup.BackupProtos.Journal; import com.android.launcher3.backup.BackupProtos.Resource; import com.android.launcher3.backup.BackupProtos.Screen; import com.android.launcher3.backup.BackupProtos.Widget; @@ -49,17 +50,27 @@ class DecoderRing { throws Exception { File source = null; Class type = Key.class; + int skip = 0; + for (int i = 0; i < args.length; i++) { if ("-k".equals(args[i])) { type = Key.class; } else if ("-f".equals(args[i])) { type = Favorite.class; + } else if ("-j".equals(args[i])) { + type = Journal.class; } else if ("-i".equals(args[i])) { type = Resource.class; } else if ("-s".equals(args[i])) { type = Screen.class; } else if ("-w".equals(args[i])) { type = Widget.class; + } else if ("-S".equals(args[i])) { + if ((i + 1) < args.length) { + skip = Integer.valueOf(args[++i]); + } else { + usage(args); + } } else if (args[i] != null && !args[i].startsWith("-")) { source = new File(args[i]); } else { @@ -86,8 +97,14 @@ class DecoderRing { try { while (input.available() > 0) { int n = input.read(buffer); + int offset = 0; + if (skip > 0) { + offset = Math.min(skip, n); + n -= offset; + skip -= offset; + } if (n > 0) { - byteStream.write(buffer, 0, n); + byteStream.write(buffer, offset, n); } } } catch (IOException e) { @@ -190,6 +207,7 @@ class DecoderRing { System.err.println("\t-i\tdecode a icon"); System.err.println("\t-s\tdecode a screen"); System.err.println("\t-w\tdecode a widget"); + System.err.println("\t-s b\tskip b bytes"); System.err.println("\tfilename\tread from filename, not stdin"); System.exit(1); } |