summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/system-current.txt3
-rw-r--r--core/java/android/app/IBackupAgent.aidl5
-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
-rw-r--r--core/java/com/android/internal/backup/IBackupTransport.aidl1
-rw-r--r--core/java/com/android/server/backup/SystemBackupAgent.java4
-rw-r--r--core/jni/android_app_backup_FullBackup.cpp42
-rw-r--r--include/androidfw/BackupHelpers.h5
-rw-r--r--libs/androidfw/BackupHelpers.cpp21
-rw-r--r--packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java8
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java225
-rw-r--r--services/backup/java/com/android/server/backup/Trampoline.java4
15 files changed, 316 insertions, 112 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index 71474a3..d7c5562 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5908,6 +5908,7 @@ package android.app.backup {
ctor public BackupTransport();
method public int abortFullRestore();
method public void cancelFullBackup();
+ method public int checkFullBackupSize(long);
method public int clearBackupData(android.content.pm.PackageInfo);
method public android.content.Intent configurationIntent();
method public java.lang.String currentDestinationString();
@@ -32771,6 +32772,7 @@ package android.test.mock {
method public android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
+ method public void grantPermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public boolean hasSystemFeature(java.lang.String);
method public boolean isSafeMode();
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -32786,6 +32788,7 @@ package android.test.mock {
method public android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
+ method public void revokePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public void setApplicationEnabledSetting(java.lang.String, int, int);
method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
method public void setInstallerPackageName(java.lang.String, java.lang.String);
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 451af99..fe8e228 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -100,6 +100,11 @@ oneway interface IBackupAgent {
void doFullBackup(in ParcelFileDescriptor data, int token, IBackupManager callbackBinder);
/**
+ * Estimate how much data a full backup will deliver
+ */
+ void doMeasureFullBackup(int token, IBackupManager callbackBinder);
+
+ /**
* Restore a single "file" to the application. The file was typically obtained from
* a full-backup dataset. The agent reads 'size' bytes of file content
* from the provided file descriptor.
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,
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 6158a7b..083d6c7 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -238,6 +238,7 @@ interface IBackupTransport {
long requestFullBackupTime();
int performFullBackup(in PackageInfo targetPackage, in ParcelFileDescriptor socket);
+ int checkFullBackupSize(long size);
int sendBackupData(int numBytes);
void cancelFullBackup();
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index b5f2f37..037fd66 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -104,9 +104,9 @@ public class SystemBackupAgent extends BackupAgentHelper {
// steps during restore; the restore will happen properly when the individual
// files are restored piecemeal.
FullBackup.backupToTar(getPackageName(), FullBackup.ROOT_TREE_TOKEN, null,
- WALLPAPER_INFO_DIR, WALLPAPER_INFO, output.getData());
+ WALLPAPER_INFO_DIR, WALLPAPER_INFO, output);
FullBackup.backupToTar(getPackageName(), FullBackup.ROOT_TREE_TOKEN, null,
- WALLPAPER_IMAGE_DIR, WALLPAPER_IMAGE, output.getData());
+ WALLPAPER_IMAGE_DIR, WALLPAPER_IMAGE, output);
}
@Override
diff --git a/core/jni/android_app_backup_FullBackup.cpp b/core/jni/android_app_backup_FullBackup.cpp
index 2c02b37..63b2e2a 100644
--- a/core/jni/android_app_backup_FullBackup.cpp
+++ b/core/jni/android_app_backup_FullBackup.cpp
@@ -15,6 +15,8 @@
*/
#define LOG_TAG "FullBackup_native"
+#include <sys/stat.h>
+
#include <utils/Log.h>
#include <utils/String8.h>
@@ -30,6 +32,12 @@
namespace android
{
+// android.app.backup.FullBackupDataOutput
+static struct {
+ jfieldID mData; // type android.app.backup.BackupDataOutput
+ jmethodID addSize;
+} sFullBackupDataOutput;
+
// android.app.backup.BackupDataOutput
static struct {
// This is actually a native pointer to the underlying BackupDataWriter instance
@@ -70,7 +78,7 @@ static struct {
* linkdomain: where a symlink points for purposes of rewriting; current unused
* rootpath: prefix to be snipped from full path when encoding in tar
* path: absolute path to the file to be saved
- * dataOutput: the BackupDataOutput object that we're saving into
+ * dataOutput: the FullBackupDataOutput object that we're saving into
*/
static jint backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
jstring domainObj, jstring linkdomain,
@@ -91,15 +99,11 @@ static jint backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
if (rootchars) env->ReleaseStringUTFChars(rootpathObj, rootchars);
if (packagenamechars) env->ReleaseStringUTFChars(packageNameObj, packagenamechars);
- // Extract the data output fd
- BackupDataWriter* writer = (BackupDataWriter*) env->GetLongField(dataOutputObj,
- sBackupDataOutput.mBackupWriter);
-
- // Validate
- if (!writer) {
- ALOGE("No output stream provided [%s]", path.string());
- return (jint) -1;
- }
+ // Extract the data output fd. 'writer' ends up NULL in the measure-only case.
+ jobject bdo = env->GetObjectField(dataOutputObj, sFullBackupDataOutput.mData);
+ BackupDataWriter* writer = (bdo != NULL)
+ ? (BackupDataWriter*) env->GetLongField(bdo, sBackupDataOutput.mBackupWriter)
+ : NULL;
if (path.length() < rootpath.length()) {
ALOGE("file path [%s] shorter than root path [%s]",
@@ -107,20 +111,30 @@ static jint backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
return (jint) -1;
}
- return (jint) write_tarfile(packageName, domain, rootpath, path, writer);
+ off_t tarSize = 0;
+ jint err = write_tarfile(packageName, domain, rootpath, path, &tarSize, writer);
+ if (!err) {
+ //ALOGI("measured [%s] at %lld", path.string(), (long long) tarSize);
+ env->CallVoidMethod(dataOutputObj, sFullBackupDataOutput.addSize, (jlong) tarSize);
+ }
+
+ return err;
}
static const JNINativeMethod g_methods[] = {
{ "backupToTar",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/BackupDataOutput;)I",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/FullBackupDataOutput;)I",
(void*)backupToTar },
};
int register_android_app_backup_FullBackup(JNIEnv* env)
{
- jclass clazz = FindClassOrDie(env, "android/app/backup/BackupDataOutput");
+ jclass fbdoClazz = FindClassOrDie(env, "android/app/backup/FullBackupDataOutput");
+ sFullBackupDataOutput.mData = GetFieldIDOrDie(env, fbdoClazz, "mData", "Landroid/app/backup/BackupDataOutput;");
+ sFullBackupDataOutput.addSize = GetMethodIDOrDie(env, fbdoClazz, "addSize", "(J)V");
- sBackupDataOutput.mBackupWriter = GetFieldIDOrDie(env, clazz, "mBackupWriter", "J");
+ jclass bdoClazz = FindClassOrDie(env, "android/app/backup/BackupDataOutput");
+ sBackupDataOutput.mBackupWriter = GetFieldIDOrDie(env, bdoClazz, "mBackupWriter", "J");
return RegisterMethodsOrDie(env, "android/app/backup/FullBackup", g_methods, NELEM(g_methods));
}
diff --git a/include/androidfw/BackupHelpers.h b/include/androidfw/BackupHelpers.h
index 0841af6..fc1ad47 100644
--- a/include/androidfw/BackupHelpers.h
+++ b/include/androidfw/BackupHelpers.h
@@ -17,6 +17,8 @@
#ifndef _UTILS_BACKUP_HELPERS_H
#define _UTILS_BACKUP_HELPERS_H
+#include <sys/stat.h>
+
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/KeyedVector.h>
@@ -135,7 +137,8 @@ int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapsh
char const* const* files, char const* const *keys, int fileCount);
int write_tarfile(const String8& packageName, const String8& domain,
- const String8& rootPath, const String8& filePath, BackupDataWriter* outputStream);
+ const String8& rootPath, const String8& filePath, off_t* outSize,
+ BackupDataWriter* outputStream);
class RestoreHelperBase
{
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index 227de3b..9300794 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -478,7 +478,8 @@ void send_tarfile_chunk(BackupDataWriter* writer, const char* buffer, size_t siz
}
int write_tarfile(const String8& packageName, const String8& domain,
- const String8& rootpath, const String8& filepath, BackupDataWriter* writer)
+ const String8& rootpath, const String8& filepath, off_t* outSize,
+ BackupDataWriter* writer)
{
// In the output stream everything is stored relative to the root
const char* relstart = filepath.string() + rootpath.length();
@@ -488,6 +489,7 @@ int write_tarfile(const String8& packageName, const String8& domain,
// If relpath is empty, it means this is the top of one of the standard named
// domain directories, so we should just skip it
if (relpath.length() == 0) {
+ *outSize = 0;
return 0;
}
@@ -517,12 +519,25 @@ int write_tarfile(const String8& packageName, const String8& domain,
return err;
}
+ // very large files need a pax extended size header
+ if (s.st_size > 077777777777LL) {
+ needExtended = true;
+ }
+
String8 fullname; // for pax later on
String8 prefix;
const int isdir = S_ISDIR(s.st_mode);
if (isdir) s.st_size = 0; // directories get no actual data in the tar stream
+ // Report the size, including a rough tar overhead estimation: 512 bytes for the
+ // overall tar file-block header, plus 2 blocks if using the pax extended format,
+ // plus the raw content size rounded up to a multiple of 512.
+ *outSize = 512 + (needExtended ? 1024 : 0) + 512*((s.st_size + 511)/512);
+
+ // Measure case: we've returned the size; now return without moving data
+ if (!writer) return 0;
+
// !!! TODO: use mmap when possible to avoid churning the buffer cache
// !!! TODO: this will break with symlinks; need to use readlink(2)
int fd = open(filepath.string(), O_RDONLY);
@@ -560,10 +575,6 @@ int write_tarfile(const String8& packageName, const String8& domain,
snprintf(buf + 116, 8, "0%lo", (unsigned long)s.st_gid);
// [ 124 : 12 ] file size in bytes
- if (s.st_size > 077777777777LL) {
- // very large files need a pax extended size header
- needExtended = true;
- }
snprintf(buf + 124, 12, "%011llo", (isdir) ? 0LL : s.st_size);
// [ 136 : 12 ] last mod time as a UTC time_t
diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java
index 0485334..0f8ccd7 100644
--- a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java
+++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java
@@ -17,8 +17,8 @@
package com.android.sharedstoragebackup;
import android.app.Service;
-import android.app.backup.BackupDataOutput;
import android.app.backup.FullBackup;
+import android.app.backup.FullBackupDataOutput;
import android.app.backup.IBackupManager;
import android.content.Intent;
import android.os.Environment;
@@ -67,7 +67,7 @@ public class ObbBackupService extends Service {
Log.i(TAG, obbList.size() + " files to back up");
}
final String rootPath = obbDir.getCanonicalPath();
- final BackupDataOutput out = new BackupDataOutput(outFd);
+ final FullBackupDataOutput out = new FullBackupDataOutput(data);
for (File f : obbList) {
final String filePath = f.getCanonicalPath();
if (DEBUG) {
@@ -92,7 +92,7 @@ public class ObbBackupService extends Service {
}
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
}
}
@@ -119,7 +119,7 @@ public class ObbBackupService extends Service {
Log.i(TAG, "Exception restoring OBB " + path, e);
} finally {
try {
- callbackBinder.opComplete(token);
+ callbackBinder.opComplete(token, 0);
} catch (RemoteException e) {
}
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 31f9e22..1a71fad 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -29,6 +29,7 @@ import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupTransport;
import android.app.backup.FullBackup;
+import android.app.backup.FullBackupDataOutput;
import android.app.backup.RestoreDescription;
import android.app.backup.RestoreSet;
import android.app.backup.IBackupManager;
@@ -134,6 +135,7 @@ import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.Deflater;
@@ -726,7 +728,7 @@ public class BackupManagerService {
{
try {
BackupRestoreTask task = (BackupRestoreTask) msg.obj;
- task.operationComplete();
+ task.operationComplete(msg.arg1);
} catch (ClassCastException e) {
Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
}
@@ -2224,7 +2226,7 @@ public class BackupManagerService {
void execute();
// An operation that wanted a callback has completed
- void operationComplete();
+ void operationComplete(int result);
// An operation that wanted a callback has timed out
void handleTimeout();
@@ -2792,7 +2794,7 @@ public class BackupManagerService {
}
@Override
- public void operationComplete() {
+ public void operationComplete(int unusedResult) {
// The agent reported back to us!
if (mBackupData == null) {
@@ -3128,8 +3130,23 @@ public class BackupManagerService {
// Core logic for performing one package's full backup, gathering the tarball from the
// application and emitting it to the designated OutputStream.
+
+ // Callout from the engine to an interested participant that might need to communicate
+ // with the agent prior to asking it to move data
+ interface FullBackupPreflight {
+ /**
+ * Perform the preflight operation necessary for the given package.
+ * @param pkg The name of the package being proposed for full-data backup
+ * @param agent Live BackupAgent binding to the target app's agent
+ * @return BackupTransport.TRANSPORT_OK to proceed with the backup operation,
+ * or one of the other BackupTransport.* error codes as appropriate
+ */
+ int preflightFullBackup(PackageInfo pkg, IBackupAgent agent);
+ };
+
class FullBackupEngine {
OutputStream mOutput;
+ FullBackupPreflight mPreflightHook;
IFullBackupRestoreObserver mObserver;
File mFilesDir;
File mManifestFile;
@@ -3160,8 +3177,7 @@ public class BackupManagerService {
@Override
public void run() {
try {
- BackupDataOutput output = new BackupDataOutput(
- mPipe.getFileDescriptor());
+ FullBackupDataOutput output = new FullBackupDataOutput(mPipe);
if (mWriteManifest) {
final boolean writeWidgetData = mWidgetData != null;
@@ -3204,15 +3220,16 @@ public class BackupManagerService {
}
}
- FullBackupEngine(OutputStream output, String packageName, boolean alsoApks) {
+ FullBackupEngine(OutputStream output, String packageName, FullBackupPreflight preflightHook,
+ boolean alsoApks) {
mOutput = output;
+ mPreflightHook = preflightHook;
mIncludeApks = alsoApks;
mFilesDir = new File("/data/system");
mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
}
-
public int backupOnePackage(PackageInfo pkg) throws RemoteException {
int result = BackupTransport.TRANSPORT_OK;
Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
@@ -3222,42 +3239,52 @@ public class BackupManagerService {
if (agent != null) {
ParcelFileDescriptor[] pipes = null;
try {
- pipes = ParcelFileDescriptor.createPipe();
-
- ApplicationInfo app = pkg.applicationInfo;
- final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
- final boolean sendApk = mIncludeApks
- && !isSharedStorage
- && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0)
- && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
+ // Call the preflight hook, if any
+ if (mPreflightHook != null) {
+ result = mPreflightHook.preflightFullBackup(pkg, agent);
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "preflight returned " + result);
+ }
+ }
+
+ // If we're still good to go after preflighting, start moving data
+ if (result == BackupTransport.TRANSPORT_OK) {
+ pipes = ParcelFileDescriptor.createPipe();
+
+ ApplicationInfo app = pkg.applicationInfo;
+ final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
+ final boolean sendApk = mIncludeApks
+ && !isSharedStorage
+ && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0)
+ && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
(app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
- byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
- UserHandle.USER_OWNER);
+ byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
+ UserHandle.USER_OWNER);
- final int token = generateToken();
- FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
- token, sendApk, !isSharedStorage, widgetBlob);
- pipes[1].close(); // the runner has dup'd it
- pipes[1] = null;
- Thread t = new Thread(runner, "app-data-runner");
- t.start();
+ final int token = generateToken();
+ FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
+ token, sendApk, !isSharedStorage, widgetBlob);
+ pipes[1].close(); // the runner has dup'd it
+ pipes[1] = null;
+ Thread t = new Thread(runner, "app-data-runner");
+ t.start();
- // Now pull data from the app and stuff it into the output
- try {
- routeSocketDataToOutput(pipes[0], mOutput);
- } catch (IOException e) {
- Slog.i(TAG, "Caught exception reading from agent", e);
- result = BackupTransport.AGENT_ERROR;
- }
+ // Now pull data from the app and stuff it into the output
+ try {
+ routeSocketDataToOutput(pipes[0], mOutput);
+ } catch (IOException e) {
+ Slog.i(TAG, "Caught exception reading from agent", e);
+ result = BackupTransport.AGENT_ERROR;
+ }
- if (!waitUntilOperationComplete(token)) {
- Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
- result = BackupTransport.AGENT_ERROR;
- } else {
- if (DEBUG) Slog.d(TAG, "Full package backup success: " + pkg.packageName);
+ if (!waitUntilOperationComplete(token)) {
+ Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
+ result = BackupTransport.AGENT_ERROR;
+ } else {
+ if (DEBUG) Slog.d(TAG, "Full package backup success: " + pkg.packageName);
+ }
}
-
} catch (IOException e) {
Slog.e(TAG, "Error backing up " + pkg.packageName, e);
result = BackupTransport.AGENT_ERROR;
@@ -3282,7 +3309,7 @@ public class BackupManagerService {
return result;
}
- private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) {
+ private void writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output) {
// Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
// TODO: handle backing up split APKs
final String appSourceDir = pkg.applicationInfo.getBaseCodePath();
@@ -3781,7 +3808,7 @@ public class BackupManagerService {
final boolean isSharedStorage =
pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
- mBackupEngine = new FullBackupEngine(out, pkg.packageName, mIncludeApks);
+ mBackupEngine = new FullBackupEngine(out, pkg.packageName, null, mIncludeApks);
sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
mBackupEngine.backupOnePackage(pkg);
@@ -3828,13 +3855,13 @@ public class BackupManagerService {
static final String TAG = "PFTBT";
ArrayList<PackageInfo> mPackages;
boolean mUpdateSchedule;
- AtomicBoolean mLatch;
+ CountDownLatch mLatch;
AtomicBoolean mKeepRunning; // signal from job scheduler
FullBackupJob mJob; // if a scheduled job needs to be finished afterwards
PerformFullTransportBackupTask(IFullBackupRestoreObserver observer,
String[] whichPackages, boolean updateSchedule,
- FullBackupJob runningJob, AtomicBoolean latch) {
+ FullBackupJob runningJob, CountDownLatch latch) {
super(observer);
mUpdateSchedule = updateSchedule;
mLatch = latch;
@@ -3944,10 +3971,10 @@ public class BackupManagerService {
// Now set up the backup engine / data source end of things
enginePipes = ParcelFileDescriptor.createPipe();
- AtomicBoolean runnerLatch = new AtomicBoolean(false);
+ CountDownLatch runnerLatch = new CountDownLatch(1);
SinglePackageBackupRunner backupRunner =
new SinglePackageBackupRunner(enginePipes[1], currentPackage,
- runnerLatch);
+ transport, runnerLatch);
// The runner dup'd the pipe half, so we close it here
enginePipes[1].close();
enginePipes[1] = null;
@@ -3972,6 +3999,9 @@ public class BackupManagerService {
break;
}
nRead = in.read(buffer);
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "in.read(buffer) from app: " + nRead);
+ }
if (nRead > 0) {
out.write(buffer, 0, nRead);
result = transport.sendBackupData(nRead);
@@ -4055,10 +4085,7 @@ public class BackupManagerService {
mRunningFullBackupTask = null;
}
- synchronized (mLatch) {
- mLatch.set(true);
- mLatch.notifyAll();
- }
+ mLatch.countDown();
// Now that we're actually done with schedule-driven work, reschedule
// the next pass based on the new queue state.
@@ -4094,15 +4121,79 @@ public class BackupManagerService {
// Run the backup and pipe it back to the given socket -- expects to run on
// a standalone thread. The runner owns this half of the pipe, and closes
// it to indicate EOD to the other end.
+ class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
+ final AtomicInteger mResult = new AtomicInteger();
+ final CountDownLatch mLatch = new CountDownLatch(1);
+ final IBackupTransport mTransport;
+
+ public SinglePackageBackupPreflight(IBackupTransport transport) {
+ mTransport = transport;
+ }
+
+ @Override
+ public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
+ int result;
+ try {
+ final int token = generateToken();
+ prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, this);
+ addBackupTrace("preflighting");
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
+ }
+ agent.doMeasureFullBackup(token, mBackupManagerBinder);
+
+ // now wait to get our result back
+ mLatch.await();
+ int totalSize = mResult.get();
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "Got preflight response; size=" + totalSize);
+ }
+
+ result = mTransport.checkFullBackupSize(totalSize);
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage());
+ result = BackupTransport.AGENT_ERROR;
+ }
+ return result;
+ }
+
+ @Override
+ public void execute() {
+ // Unused in this case
+ }
+
+ @Override
+ public void operationComplete(int result) {
+ // got the callback, and our preflightFullBackup() method is waiting for the result
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Preflight op complete, result=" + result);
+ }
+ mResult.set(result);
+ mLatch.countDown();
+ }
+
+ @Override
+ public void handleTimeout() {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Preflight timeout; failing");
+ }
+ mResult.set(BackupTransport.AGENT_ERROR);
+ mLatch.countDown();
+ }
+
+ }
+
class SinglePackageBackupRunner implements Runnable {
final ParcelFileDescriptor mOutput;
final PackageInfo mTarget;
- final AtomicBoolean mLatch;
+ final FullBackupPreflight mPreflight;
+ final CountDownLatch mLatch;
SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
- AtomicBoolean latch) throws IOException {
+ IBackupTransport transport, CountDownLatch latch) throws IOException {
mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
mTarget = target;
+ mPreflight = new SinglePackageBackupPreflight(transport);
mLatch = latch;
}
@@ -4110,15 +4201,13 @@ public class BackupManagerService {
public void run() {
try {
FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
- FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName, false);
+ FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName,
+ mPreflight, false);
engine.backupOnePackage(mTarget);
} catch (Exception e) {
Slog.e(TAG, "Exception during full package backup of " + mTarget);
} finally {
- synchronized (mLatch) {
- mLatch.set(true);
- mLatch.notifyAll();
- }
+ mLatch.countDown();
try {
mOutput.close();
} catch (IOException e) {
@@ -4126,7 +4215,6 @@ public class BackupManagerService {
}
}
}
-
}
}
@@ -4258,7 +4346,7 @@ public class BackupManagerService {
// Okay, the top thing is runnable now. Pop it off and get going.
mFullBackupQueue.remove(0);
- AtomicBoolean latch = new AtomicBoolean(false);
+ CountDownLatch latch = new CountDownLatch(1);
String[] pkg = new String[] {entry.packageName};
mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true,
scheduledJob, latch);
@@ -7895,7 +7983,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
}
@Override
- public void operationComplete() {
+ public void operationComplete(int unusedResult) {
if (MORE_DEBUG) {
Slog.i(TAG, "operationComplete() during restore: target="
+ mCurrentPackage.packageName
@@ -8395,17 +8483,18 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
Slog.d(TAG, "fullTransportBackup()");
}
- AtomicBoolean latch = new AtomicBoolean(false);
+ CountDownLatch latch = new CountDownLatch(1);
PerformFullTransportBackupTask task =
new PerformFullTransportBackupTask(null, pkgNames, false, null, latch);
(new Thread(task, "full-transport-master")).start();
- synchronized (latch) {
+ do {
try {
- while (latch.get() == false) {
- latch.wait();
- }
- } catch (InterruptedException e) {}
- }
+ latch.await();
+ break;
+ } catch (InterruptedException e) {
+ // Just go back to waiting for the latch to indicate completion
+ }
+ } while (true);
if (DEBUG) {
Slog.d(TAG, "Done with full transport backup.");
}
@@ -8948,8 +9037,10 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
// Note that a currently-active backup agent has notified us that it has
// completed the given outstanding asynchronous backup/restore operation.
- public void opComplete(int token) {
- if (MORE_DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
+ public void opComplete(int token, long result) {
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
+ }
Operation op = null;
synchronized (mCurrentOpLock) {
op = mCurrentOperations.get(token);
@@ -8962,6 +9053,8 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
// The completion callback, if any, is invoked on the handler
if (op != null && op.callback != null) {
Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
+ // NB: this cannot distinguish between results > 2 gig
+ msg.arg1 = (result > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) result;
mBackupHandler.sendMessage(msg);
}
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 2e84fbe..99bbdae 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -309,10 +309,10 @@ public class Trampoline extends IBackupManager.Stub {
}
@Override
- public void opComplete(int token) throws RemoteException {
+ public void opComplete(int token, long result) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.opComplete(token);
+ svc.opComplete(token, result);
}
}