summaryrefslogtreecommitdiffstats
path: root/core/java/android/app/backup
diff options
context:
space:
mode:
authorChristopher Tate <ctate@google.com>2015-03-24 18:48:10 -0700
committerChristopher Tate <ctate@google.com>2015-03-26 18:57:36 -0700
commit11ae768cf1b8348e761ad9c09e98788da1e591b1 (patch)
treefa4a4e9fffc83e61af98476d41df4252e3cd1323 /core/java/android/app/backup
parente7f931c4505a6bd62e01bef5193dd724571a672b (diff)
downloadframeworks_base-11ae768cf1b8348e761ad9c09e98788da1e591b1.zip
frameworks_base-11ae768cf1b8348e761ad9c09e98788da1e591b1.tar.gz
frameworks_base-11ae768cf1b8348e761ad9c09e98788da1e591b1.tar.bz2
Add payload-size preflight stage to full transport backup
We now peform a total-size preflight pass before committing data to the wire. This is to eliminate the large superfluous network traffic that would otherwise happen if the transport enforces internal quotas: we now instead ask the transport up front whether it's prepared to accept a given payload size for the package. From the app's perspective this preflight operation is indistinguishable from a full-data backup pass. If the app has provided its own full-data handling in a subclassed backup agent, their usual file-providing code path will be executed. However, the files named for backup during this pass are not opened and read; just measured for their total size. As far as component lifecycles, this measurement pass is simply another call to the agent, immediately after it is bound, with identical timeout semantics to the existing full-data backup invocation. Once the app's file set has been measured the preflight operation invokes a new method on BackupTransport, called checkFullBackupSize(). This method is called after performFullBackup() (which applies any overall whitelist/blacklist policy) but before any data is delivered to the transport via sendBackupData(). The return code from checkFullBackupSize() is similar to the other transport methods: TRANSPORT_OK to permit the full backup to proceed; or TRANSPORT_REJECT_PACKAGE to indicate that the requested payload is unacceptable; or TRANSPORT_ERROR to report a more serious overall transport-level problem that prevents a full-data backup operation from occurring right now. The estimated payload currently does not include the size of the source-package metadata (technically, the manifest entry in its archive payload) or the size of any widget metadata associated with the package's install. In practice this means the preflighted size underestimates by 3 to 5 KB. In addition, the preflight API currently cannot distinguish between payload sizes larger than 2 gigabytes; any payload estimate larger than that is passed as Integer.MAX_VALUE to the checkFullBackupSize() query. Bug 19846750 Change-Id: I44498201e2d4b07482dcb3ca8fa6935dddc467ca
Diffstat (limited to 'core/java/android/app/backup')
-rw-r--r--core/java/android/app/backup/BackupAgent.java55
-rw-r--r--core/java/android/app/backup/BackupTransport.java25
-rw-r--r--core/java/android/app/backup/FullBackup.java2
-rw-r--r--core/java/android/app/backup/FullBackupDataOutput.java19
-rw-r--r--core/java/android/app/backup/IBackupManager.aidl9
5 files changed, 92 insertions, 18 deletions
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 7f89100..2bf267a 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -424,10 +424,12 @@ public abstract class BackupAgent extends ContextWrapper {
}
// And now that we know where it lives, semantically, back it up appropriately
- Log.i(TAG, "backupFile() of " + filePath + " => domain=" + domain
+ // In the measurement case, backupToTar() updates the size in output and returns
+ // without transmitting any file data.
+ if (DEBUG) Log.i(TAG, "backupFile() of " + filePath + " => domain=" + domain
+ " rootpath=" + rootpath);
- FullBackup.backupToTar(getPackageName(), domain, null, rootpath, filePath,
- output.getData());
+
+ FullBackup.backupToTar(getPackageName(), domain, null, rootpath, filePath, output);
}
/**
@@ -477,9 +479,8 @@ public abstract class BackupAgent extends ContextWrapper {
continue;
}
- // Finally, back this file up before proceeding
- FullBackup.backupToTar(packageName, domain, null, rootPath, filePath,
- output.getData());
+ // Finally, back this file up (or measure it) before proceeding
+ FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, output);
}
}
}
@@ -640,7 +641,7 @@ public abstract class BackupAgent extends ContextWrapper {
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
@@ -670,7 +671,7 @@ public abstract class BackupAgent extends ContextWrapper {
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
@@ -692,10 +693,10 @@ public abstract class BackupAgent extends ContextWrapper {
try {
BackupAgent.this.onFullBackup(new FullBackupDataOutput(data));
} catch (IOException ex) {
- Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ Log.d(TAG, "onFullBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw new RuntimeException(ex);
} catch (RuntimeException ex) {
- Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ Log.d(TAG, "onFullBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw ex;
} finally {
// ... and then again after, as in the doBackup() case
@@ -713,13 +714,37 @@ public abstract class BackupAgent extends ContextWrapper {
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
}
}
+ public void doMeasureFullBackup(int token, IBackupManager callbackBinder) {
+ // Ensure that we're running with the app's normal permission level
+ final long ident = Binder.clearCallingIdentity();
+ FullBackupDataOutput measureOutput = new FullBackupDataOutput();
+
+ waitForSharedPrefs();
+ try {
+ BackupAgent.this.onFullBackup(measureOutput);
+ } catch (IOException ex) {
+ Log.d(TAG, "onFullBackup[M] (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw new RuntimeException(ex);
+ } catch (RuntimeException ex) {
+ Log.d(TAG, "onFullBackup[M] (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw ex;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ try {
+ callbackBinder.opComplete(token, measureOutput.getSize());
+ } catch (RemoteException e) {
+ // timeout, so we're safe
+ }
+ }
+ }
+
@Override
public void doRestoreFile(ParcelFileDescriptor data, long size,
int type, String domain, String path, long mode, long mtime,
@@ -728,6 +753,7 @@ public abstract class BackupAgent extends ContextWrapper {
try {
BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime);
} catch (IOException e) {
+ Log.d(TAG, "onRestoreFile (" + BackupAgent.this.getClass().getName() + ") threw", e);
throw new RuntimeException(e);
} finally {
// Ensure that any side-effect SharedPreferences writes have landed
@@ -735,7 +761,7 @@ public abstract class BackupAgent extends ContextWrapper {
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
@@ -747,13 +773,16 @@ public abstract class BackupAgent extends ContextWrapper {
long ident = Binder.clearCallingIdentity();
try {
BackupAgent.this.onRestoreFinished();
+ } catch (Exception e) {
+ Log.d(TAG, "onRestoreFinished (" + BackupAgent.this.getClass().getName() + ") threw", e);
+ throw e;
} finally {
// Ensure that any side-effect SharedPreferences writes have landed
waitForSharedPrefs();
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index e853540..ca6dc69 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -393,6 +393,26 @@ public class BackupTransport {
}
/**
+ * Called after {@link #performFullBackup} to make sure that the transport is willing to
+ * handle a full-data backup operation of the specified size on the current package.
+ * If the transport returns anything other than TRANSPORT_OK, the package's backup
+ * operation will be skipped (and {@link #finishBackup() invoked} with no data for that
+ * package being passed to {@link #sendBackupData}.
+ *
+ * Added in MNC (API 23).
+ *
+ * @param size The estimated size of the full-data payload for this app. This includes
+ * manifest and archive format overhead, but is not guaranteed to be precise.
+ * @return TRANSPORT_OK if the platform is to proceed with the full-data backup,
+ * TRANSPORT_PACKAGE_REJECTED if the proposed payload size is too large for
+ * the transport to handle, or TRANSPORT_ERROR to indicate a fatal error
+ * condition that means the platform cannot perform a backup at this time.
+ */
+ public int checkFullBackupSize(long size) {
+ return BackupTransport.TRANSPORT_OK;
+ }
+
+ /**
* Tells the transport to read {@code numBytes} bytes of data from the socket file
* descriptor provided in the {@link #performFullBackup(PackageInfo, ParcelFileDescriptor)}
* call, and deliver those bytes to the datastore.
@@ -588,6 +608,11 @@ public class BackupTransport {
}
@Override
+ public int checkFullBackupSize(long size) {
+ return BackupTransport.this.checkFullBackupSize(size);
+ }
+
+ @Override
public int sendBackupData(int numBytes) throws RemoteException {
return BackupTransport.this.sendBackupData(numBytes);
}
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index e5b47c6..259884e 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -58,7 +58,7 @@ public class FullBackup {
* @hide
*/
static public native int backupToTar(String packageName, String domain,
- String linkdomain, String rootpath, String path, BackupDataOutput output);
+ String linkdomain, String rootpath, String path, FullBackupDataOutput output);
/**
* Copy data from a socket to the given File location on permanent storage. The
diff --git a/core/java/android/app/backup/FullBackupDataOutput.java b/core/java/android/app/backup/FullBackupDataOutput.java
index 99dab1f..94704b9 100644
--- a/core/java/android/app/backup/FullBackupDataOutput.java
+++ b/core/java/android/app/backup/FullBackupDataOutput.java
@@ -9,7 +9,14 @@ import android.os.ParcelFileDescriptor;
*/
public class FullBackupDataOutput {
// Currently a name-scoping shim around BackupDataOutput
- private BackupDataOutput mData;
+ private final BackupDataOutput mData;
+ private long mSize;
+
+ /** @hide - used only in measure operation */
+ public FullBackupDataOutput() {
+ mData = null;
+ mSize = 0;
+ }
/** @hide */
public FullBackupDataOutput(ParcelFileDescriptor fd) {
@@ -18,4 +25,14 @@ public class FullBackupDataOutput {
/** @hide */
public BackupDataOutput getData() { return mData; }
+
+ /** @hide - used for measurement pass */
+ public void addSize(long size) {
+ if (size > 0) {
+ mSize += size;
+ }
+ }
+
+ /** @hide - used for measurement pass */
+ public long getSize() { return mSize; }
}
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 41ad936..8f36dc4 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -286,11 +286,14 @@ interface IBackupManager {
* Notify the backup manager that a BackupAgent has completed the operation
* corresponding to the given token.
*
- * @param token The transaction token passed to a BackupAgent's doBackup() or
- * doRestore() method.
+ * @param token The transaction token passed to the BackupAgent method being
+ * invoked.
+ * @param result In the case of a full backup measure operation, the estimated
+ * total file size that would result from the operation. Unused in all other
+ * cases.
* {@hide}
*/
- void opComplete(int token);
+ void opComplete(int token, long result);
/**
* Make the device's backup and restore machinery (in)active. When it is inactive,