summaryrefslogtreecommitdiffstats
path: root/services/backup
diff options
context:
space:
mode:
authorChristopher Tate <ctate@google.com>2015-01-15 17:26:22 -0800
committerChristopher Tate <ctate@google.com>2015-01-15 17:37:06 -0800
commitcf962601186a05a8818c02b690c1e7b5c15144f1 (patch)
tree1de13ba946caf9f3ba2a68ba81a4058570e4ae26 /services/backup
parentfaf92d725649337a29312603dc8c10a3f0ee103a (diff)
downloadframeworks_base-cf962601186a05a8818c02b690c1e7b5c15144f1.zip
frameworks_base-cf962601186a05a8818c02b690c1e7b5c15144f1.tar.gz
frameworks_base-cf962601186a05a8818c02b690c1e7b5c15144f1.tar.bz2
Don't write widget metadata to backup unless it's new/changed
Redundant backup traffic is bad. Don't commit the widget metadata payload (or the deletion operation for it) unless the widget state of the app has actually changed since the last backup. Bug 19003911 Change-Id: I93819173c0e2357b030d9e2b3d2ee57f2410bb57
Diffstat (limited to 'services/backup')
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java91
1 files changed, 80 insertions, 11 deletions
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 7d085a3..289152b 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -130,6 +130,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
@@ -2695,6 +2696,84 @@ public class BackupManagerService {
}
}
+ // SHA-1 a byte array and return the result in hex
+ private String SHA1Checksum(byte[] input) {
+ final byte[] checksum;
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ checksum = md.digest(input);
+ } catch (NoSuchAlgorithmException e) {
+ Slog.e(TAG, "Unable to use SHA-1!");
+ return "00";
+ }
+
+ StringBuffer sb = new StringBuffer(checksum.length * 2);
+ for (int i = 0; i < checksum.length; i++) {
+ sb.append(Integer.toHexString(checksum[i]));
+ }
+ return sb.toString();
+ }
+
+ private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
+ throws IOException {
+ byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
+ UserHandle.USER_OWNER);
+ // has the widget state changed since last time?
+ final File widgetFile = new File(mStateDir, pkgName + "_widget");
+ final boolean priorStateExists = widgetFile.exists();
+
+ if (MORE_DEBUG) {
+ if (priorStateExists || widgetState != null) {
+ Slog.i(TAG, "Checking widget update: state=" + (widgetState != null)
+ + " prior=" + priorStateExists);
+ }
+ }
+
+ if (!priorStateExists && widgetState == null) {
+ // no prior state, no new state => nothing to do
+ return;
+ }
+
+ // if the new state is not null, we might need to compare checksums to
+ // determine whether to update the widget blob in the archive. If the
+ // widget state *is* null, we know a priori at this point that we simply
+ // need to commit a deletion for it.
+ String newChecksum = null;
+ if (widgetState != null) {
+ newChecksum = SHA1Checksum(widgetState);
+ if (priorStateExists) {
+ final String priorChecksum;
+ try (
+ FileInputStream fin = new FileInputStream(widgetFile);
+ DataInputStream in = new DataInputStream(fin)
+ ) {
+ priorChecksum = in.readUTF();
+ }
+ if (Objects.equals(newChecksum, priorChecksum)) {
+ // Same checksum => no state change => don't rewrite the widget data
+ return;
+ }
+ }
+ } // else widget state *became* empty, so we need to commit a deletion
+
+ BackupDataOutput out = new BackupDataOutput(fd);
+ if (widgetState != null) {
+ try (
+ FileOutputStream fout = new FileOutputStream(widgetFile);
+ DataOutputStream stateOut = new DataOutputStream(fout)
+ ) {
+ stateOut.writeUTF(newChecksum);
+ }
+
+ out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
+ out.writeEntityData(widgetState, widgetState.length);
+ } else {
+ // Widget state for this app has been removed; commit a deletion
+ out.writeEntityHeader(KEY_WIDGET_STATE, -1);
+ widgetFile.delete();
+ }
+ }
+
@Override
public void operationComplete() {
// Okay, the agent successfully reported back to us!
@@ -2733,17 +2812,7 @@ public class BackupManagerService {
}
// Piggyback the widget state payload, if any
- BackupDataOutput out = new BackupDataOutput(fd);
- byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
- UserHandle.USER_OWNER);
- if (widgetState != null) {
- out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
- out.writeEntityData(widgetState, widgetState.length);
- } else {
- // No widget state for this app, but push a 'delete' operation for it
- // in case they're trying to play games with the payload.
- out.writeEntityHeader(KEY_WIDGET_STATE, -1);
- }
+ writeWidgetPayloadIfAppropriate(fd, pkgName);
} catch (IOException e) {
// Hard disk error; recovery/failure policy TBD. For now roll back,
// but we may want to consider this a transport-level failure (i.e.