Merge "BackupAgent#getBackupQuota() API"
diff --git a/api/current.txt b/api/current.txt
index e0d895e..65f80ee 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6604,6 +6604,7 @@
public abstract class BackupAgent extends android.content.ContextWrapper {
ctor public BackupAgent();
method public final void fullBackupFile(java.io.File, android.app.backup.FullBackupDataOutput);
+ method public long getBackupQuota();
method public abstract void onBackup(android.os.ParcelFileDescriptor, android.app.backup.BackupDataOutput, android.os.ParcelFileDescriptor) throws java.io.IOException;
method public void onCreate();
method public void onDestroy();
diff --git a/api/system-current.txt b/api/system-current.txt
index a5f3081..edf778b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6841,6 +6841,7 @@
public abstract class BackupAgent extends android.content.ContextWrapper {
ctor public BackupAgent();
method public final void fullBackupFile(java.io.File, android.app.backup.FullBackupDataOutput);
+ method public long getBackupQuota();
method public abstract void onBackup(android.os.ParcelFileDescriptor, android.app.backup.BackupDataOutput, android.os.ParcelFileDescriptor) throws java.io.IOException;
method public void onCreate();
method public void onDestroy();
diff --git a/api/test-current.txt b/api/test-current.txt
index a983cf5..6c699bb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6627,6 +6627,7 @@
public abstract class BackupAgent extends android.content.ContextWrapper {
ctor public BackupAgent();
method public final void fullBackupFile(java.io.File, android.app.backup.FullBackupDataOutput);
+ method public long getBackupQuota();
method public abstract void onBackup(android.os.ParcelFileDescriptor, android.app.backup.BackupDataOutput, android.os.ParcelFileDescriptor) throws java.io.IOException;
method public void onCreate();
method public void onDestroy();
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index eda9603..a07374b 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -41,6 +41,8 @@
* @param newState Read-write file, empty when onBackup() is called,
* where the new state blob is to be recorded.
*
+ * @param quota Quota reported by the transport for this backup operation (in bytes).
+ *
* @param token Opaque token identifying this transaction. This must
* be echoed back to the backup service binder once the new
* data has been written to the data and newState files.
@@ -51,7 +53,7 @@
void doBackup(in ParcelFileDescriptor oldState,
in ParcelFileDescriptor data,
in ParcelFileDescriptor newState,
- int token, IBackupManager callbackBinder);
+ long quotaBytes, int token, IBackupManager callbackBinder);
/**
* Restore an entire data snapshot to the application.
@@ -89,6 +91,8 @@
* The data must be formatted correctly for the resulting archive to be
* legitimate, so that will be tightly controlled by the available API.
*
+ * @param quota Quota reported by the transport for this backup operation (in bytes).
+ *
* @param token Opaque token identifying this transaction. This must
* be echoed back to the backup service binder once the agent is
* finished restoring the application based on the restore data
@@ -97,12 +101,12 @@
* @param callbackBinder Binder on which to indicate operation completion,
* passed here as a convenience to the agent.
*/
- void doFullBackup(in ParcelFileDescriptor data, int token, IBackupManager callbackBinder);
+ void doFullBackup(in ParcelFileDescriptor data, long quotaBytes, int token, IBackupManager callbackBinder);
/**
* Estimate how much data a full backup will deliver
*/
- void doMeasureFullBackup(int token, IBackupManager callbackBinder);
+ void doMeasureFullBackup(long quotaBytes, int token, IBackupManager callbackBinder);
/**
* Tells the application agent that the backup data size exceeded current transport quota.
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 45d9fb7..11636a5 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -133,6 +133,8 @@
Handler mHandler = null;
+ private long mBackupQuotaBytes = -1;
+
Handler getHandler() {
if (mHandler == null) {
mHandler = new Handler(Looper.getMainLooper());
@@ -184,6 +186,21 @@
}
/**
+ * Returns the quota in bytes for the currently requested backup operation. The value can
+ * vary for each operation depending on the type of backup being done.
+ *
+ * <p>Can be called only from {@link BackupAgent#onFullBackup(FullBackupDataOutput)} or
+ * {@link BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}.
+ */
+ public long getBackupQuota() {
+ if (mBackupQuotaBytes < 0) {
+ throw new IllegalStateException(
+ "Backup quota is available only during backup operations.");
+ }
+ return mBackupQuotaBytes;
+ }
+
+ /**
* The application is being asked to write any data changed since the last
* time it performed a backup operation. The state data recorded during the
* last backup pass is provided in the <code>oldState</code> file
@@ -897,10 +914,12 @@
public void doBackup(ParcelFileDescriptor oldState,
ParcelFileDescriptor data,
ParcelFileDescriptor newState,
- int token, IBackupManager callbackBinder) throws RemoteException {
+ long quotaBytes, int token, IBackupManager callbackBinder) throws RemoteException {
// Ensure that we're running with the app's normal permission level
long ident = Binder.clearCallingIdentity();
+ mBackupQuotaBytes = quotaBytes;
+
if (DEBUG) Log.v(TAG, "doBackup() invoked");
BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
@@ -918,6 +937,9 @@
// guarantee themselves).
waitForSharedPrefs();
+ // Unset quota after onBackup is done.
+ mBackupQuotaBytes = -1;
+
Binder.restoreCallingIdentity(ident);
try {
callbackBinder.opComplete(token, 0);
@@ -971,10 +993,12 @@
@Override
public void doFullBackup(ParcelFileDescriptor data,
- int token, IBackupManager callbackBinder) {
+ long quotaBytes, int token, IBackupManager callbackBinder) {
// Ensure that we're running with the app's normal permission level
long ident = Binder.clearCallingIdentity();
+ mBackupQuotaBytes = quotaBytes;
+
if (DEBUG) Log.v(TAG, "doFullBackup() invoked");
// Ensure that any SharedPreferences writes have landed *before*
@@ -993,6 +1017,9 @@
// ... and then again after, as in the doBackup() case
waitForSharedPrefs();
+ // Unset quota after onFullBackup is done.
+ mBackupQuotaBytes = -1;
+
// Send the EOD marker indicating that there is no more data
// forthcoming from this agent.
try {
@@ -1016,11 +1043,13 @@
}
}
- public void doMeasureFullBackup(int token, IBackupManager callbackBinder) {
+ public void doMeasureFullBackup(long quotaBytes, 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();
+ mBackupQuotaBytes = quotaBytes;
+
waitForSharedPrefs();
try {
BackupAgent.this.onFullBackup(measureOutput);
@@ -1031,6 +1060,8 @@
Log.d(TAG, "onFullBackup[M] (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw ex;
} finally {
+ // Unset quota after onFullBackup is done.
+ mBackupQuotaBytes = -1;
Binder.restoreCallingIdentity(ident);
try {
callbackBinder.opComplete(token, measureOutput.getSize());
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 5e8f4a2..f76b702 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -73,6 +73,8 @@
// Full backup size quota is set to reasonable value.
private static final long FULL_BACKUP_SIZE_QUOTA = 25 * 1024 * 1024;
+ private static final long KEY_VALUE_BACKUP_SIZE_QUOTA = 5 * 1024 * 1024;
+
private Context mContext;
private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN));
@@ -712,6 +714,6 @@
@Override
public long getBackupQuota(String packageName, boolean isFullBackup) {
- return isFullBackup ? FULL_BACKUP_SIZE_QUOTA : Long.MAX_VALUE;
+ return isFullBackup ? FULL_BACKUP_SIZE_QUOTA : KEY_VALUE_BACKUP_SIZE_QUOTA;
}
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index c4e2a53..6bf0e8d 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -2906,6 +2906,7 @@
mNewState = null;
final int token = generateToken();
+ boolean callingAgent = false;
try {
// Look up the package info & signatures. This is first so that if it
// throws an exception, there's no file setup yet that would need to
@@ -2939,18 +2940,24 @@
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE);
+ final long quota = mTransport.getBackupQuota(packageName, false /* isFullBackup */);
+ callingAgent = true;
+
// Initiate the target's backup pass
addBackupTrace("setting timeout");
prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, this);
addBackupTrace("calling agent doBackup()");
- agent.doBackup(mSavedState, mBackupData, mNewState, token, mBackupManagerBinder);
+
+ agent.doBackup(mSavedState, mBackupData, mNewState, quota, token,
+ mBackupManagerBinder);
} catch (Exception e) {
- Slog.e(TAG, "Error invoking for backup on " + packageName);
+ Slog.e(TAG, "Error invoking for backup on " + packageName + ". " + e);
addBackupTrace("exception: " + e);
EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
e.toString());
- agentErrorCleanup();
- return BackupTransport.AGENT_ERROR;
+ errorCleanup();
+ return callingAgent ? BackupTransport.AGENT_ERROR
+ : BackupTransport.TRANSPORT_ERROR;
} finally {
if (mNonIncremental) {
blankStateName.delete();
@@ -3093,8 +3100,8 @@
mBackupHandler.removeMessages(MSG_TIMEOUT);
sendBackupOnPackageResult(mObserver, pkgName,
BackupManager.ERROR_AGENT_FAILURE);
- agentErrorCleanup();
- // agentErrorCleanup() implicitly executes next state properly
+ errorCleanup();
+ // errorCleanup() implicitly executes next state properly
return;
}
in.skipEntityData();
@@ -3240,7 +3247,7 @@
BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_TIMEOUT,
mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT);
addBackupTrace("timeout of " + mCurrentPackage.packageName);
- agentErrorCleanup();
+ errorCleanup();
dataChangedImpl(mCurrentPackage.packageName);
}
@@ -3265,7 +3272,7 @@
}
- void agentErrorCleanup() {
+ void errorCleanup() {
mBackupDataName.delete();
mNewStateName.delete();
clearAgentState();
@@ -3473,6 +3480,7 @@
File mMetadataFile;
boolean mIncludeApks;
PackageInfo mPkg;
+ private final long mQuota;
class FullBackupRunner implements Runnable {
PackageInfo mPackage;
@@ -3528,7 +3536,7 @@
if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL,
mTimeoutMonitor /* in parent class */);
- mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder);
+ mAgent.doFullBackup(mPipe, mQuota, mToken, mBackupManagerBinder);
} catch (IOException e) {
Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
} catch (RemoteException e) {
@@ -3543,7 +3551,7 @@
}
FullBackupEngine(OutputStream output, FullBackupPreflight preflightHook, PackageInfo pkg,
- boolean alsoApks, BackupRestoreTask timeoutMonitor) {
+ boolean alsoApks, BackupRestoreTask timeoutMonitor, long quota) {
mOutput = output;
mPreflightHook = preflightHook;
mPkg = pkg;
@@ -3552,6 +3560,7 @@
mFilesDir = new File("/data/system");
mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
+ mQuota = quota;
}
public int preflightCheck() throws RemoteException {
@@ -4147,7 +4156,8 @@
final boolean isSharedStorage =
pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
- mBackupEngine = new FullBackupEngine(out, null, pkg, mIncludeApks, this);
+ mBackupEngine = new FullBackupEngine(out, null, pkg, mIncludeApks,
+ this /* BackupRestoreTask */, Long.MAX_VALUE /* quota */);
sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
// Don't need to check preflight result as there is no preflight hook.
@@ -4340,6 +4350,9 @@
int backupPackageStatus = transport.performFullBackup(currentPackage,
transportPipes[0], flags);
if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
+ final long quota = transport.getBackupQuota(currentPackage.packageName,
+ true /* isFullBackup */);
+
// The transport has its own copy of the read end of the pipe,
// so close ours now
transportPipes[0].close();
@@ -4347,9 +4360,10 @@
// Now set up the backup engine / data source end of things
enginePipes = ParcelFileDescriptor.createPipe();
+
SinglePackageBackupRunner backupRunner =
new SinglePackageBackupRunner(enginePipes[1], currentPackage,
- transport);
+ transport, quota);
// The runner dup'd the pipe half, so we close it here
enginePipes[1].close();
enginePipes[1] = null;
@@ -4402,7 +4416,6 @@
// Despite preflight succeeded, package still can hit quota on flight.
if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
- long quota = transport.getBackupQuota(packageName, true);
Slog.w(TAG, "Package hit quota limit in-flight " + packageName
+ ": " + totalRead + " of " + quota);
backupRunner.sendQuotaExceeded(totalRead, quota);
@@ -4587,9 +4600,11 @@
final AtomicLong mResult = new AtomicLong(BackupTransport.AGENT_ERROR);
final CountDownLatch mLatch = new CountDownLatch(1);
final IBackupTransport mTransport;
+ final long mQuota;
- public SinglePackageBackupPreflight(IBackupTransport transport) {
+ public SinglePackageBackupPreflight(IBackupTransport transport, long quota) {
mTransport = transport;
+ mQuota = quota;
}
@Override
@@ -4602,7 +4617,7 @@
if (MORE_DEBUG) {
Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
}
- agent.doMeasureFullBackup(token, mBackupManagerBinder);
+ agent.doMeasureFullBackup(mQuota, token, mBackupManagerBinder);
// Now wait to get our result back. If this backstop timeout is reached without
// the latch being thrown, flow will continue as though a result or "normal"
@@ -4622,12 +4637,11 @@
result = mTransport.checkFullBackupSize(totalSize);
if (result == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
- final long quota = mTransport.getBackupQuota(pkg.packageName, true);
if (MORE_DEBUG) {
Slog.d(TAG, "Package hit quota limit on preflight " +
- pkg.packageName + ": " + totalSize + " of " + quota);
+ pkg.packageName + ": " + totalSize + " of " + mQuota);
}
- agent.doQuotaExceeded(totalSize, quota);
+ agent.doQuotaExceeded(totalSize, mQuota);
}
} catch (Exception e) {
Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage());
@@ -4680,22 +4694,24 @@
private FullBackupEngine mEngine;
private volatile int mPreflightResult;
private volatile int mBackupResult;
+ private final long mQuota;
SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
- IBackupTransport transport) throws IOException {
+ IBackupTransport transport, long quota) throws IOException {
mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
mTarget = target;
- mPreflight = new SinglePackageBackupPreflight(transport);
+ mPreflight = new SinglePackageBackupPreflight(transport, quota);
mPreflightLatch = new CountDownLatch(1);
mBackupLatch = new CountDownLatch(1);
mPreflightResult = BackupTransport.AGENT_ERROR;
mBackupResult = BackupTransport.AGENT_ERROR;
+ mQuota = quota;
}
@Override
public void run() {
FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
- mEngine = new FullBackupEngine(out, mPreflight, mTarget, false, this);
+ mEngine = new FullBackupEngine(out, mPreflight, mTarget, false, this, mQuota);
try {
try {
mPreflightResult = mEngine.preflightCheck();