summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorChristopher Tate <ctate@google.com>2011-06-24 14:58:49 -0700
committerChristopher Tate <ctate@google.com>2011-07-06 14:40:32 -0700
commit79ec80db70d788f35aa13346e4684ecbd401bd84 (patch)
treefd18f64033def7461692f9542bf9e5f01afe2fe0 /core
parentbe87cc945b5b094060cbc77b77383aefc60265e4 (diff)
downloadframeworks_base-79ec80db70d788f35aa13346e4684ecbd401bd84.zip
frameworks_base-79ec80db70d788f35aa13346e4684ecbd401bd84.tar.gz
frameworks_base-79ec80db70d788f35aa13346e4684ecbd401bd84.tar.bz2
Make full backup API available to apps
New methods for full backup/restore have been added to BackupAgent (still hidden): onFullBackup() and onRestoreFile(). The former is the entry point for a full app backup to adb/socket/etc: the app then writes all of its files, entire, to the output. During restore, the latter new callback is invoked, once for each file being restored. The full backup/restore interface does not use the previously-defined BackupDataInput / BackupDataOutput classes, because those classes provide an API designed for incremental key/value data structuring. Instead, a new FullBackupDataOutput class has been introduced, through which we restrict apps' abilities to write data during a full backup operation to *only* writing entire on-disk files via a new BackupAgent method called fullBackupFile(). "FullBackupAgent" exists now solely as a concrete shell class that can be instantiated in the case of apps that do not have their own BackupAgent implementations. Along with the API change, responsibility for backing up the .apk file and OBB container has been moved into the framework rather than have the application side of the transaction do it. Change-Id: I12849b06b1a6e4c44d080587c1e9828a52b70dae
Diffstat (limited to 'core')
-rw-r--r--core/java/android/app/ActivityThread.java11
-rw-r--r--core/java/android/app/IBackupAgent.aidl20
-rw-r--r--core/java/android/app/backup/BackupAgent.java289
-rw-r--r--core/java/android/app/backup/FullBackup.java49
-rw-r--r--core/java/android/app/backup/FullBackupAgent.java192
-rw-r--r--core/java/android/app/backup/FullBackupDataOutput.java21
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java11
-rw-r--r--core/java/android/content/pm/PackageParser.java11
-rw-r--r--core/res/AndroidManifest.xml1
-rw-r--r--core/res/res/values/attrs_manifest.xml8
-rw-r--r--core/res/res/values/public.xml2
11 files changed, 365 insertions, 250 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1ec7a96..eee14fb 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2013,15 +2013,10 @@ public final class ActivityThread {
BackupAgent agent = null;
String classname = data.appInfo.backupAgentName;
- if (data.backupMode == IApplicationThread.BACKUP_MODE_FULL
- || data.backupMode == IApplicationThread.BACKUP_MODE_RESTORE_FULL) {
+ // full backup operation but no app-supplied agent? use the default implementation
+ if (classname == null && (data.backupMode == IApplicationThread.BACKUP_MODE_FULL
+ || data.backupMode == IApplicationThread.BACKUP_MODE_RESTORE_FULL)) {
classname = "android.app.backup.FullBackupAgent";
- if ((data.appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- // system packages can supply their own full-backup agent
- if (data.appInfo.fullBackupAgentName != null) {
- classname = data.appInfo.fullBackupAgentName;
- }
- }
}
try {
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 8af78fa..087f83c 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -51,7 +51,6 @@ oneway interface IBackupAgent {
void doBackup(in ParcelFileDescriptor oldState,
in ParcelFileDescriptor data,
in ParcelFileDescriptor newState,
- boolean storeApk,
int token, IBackupManager callbackBinder);
/**
@@ -81,6 +80,25 @@ oneway interface IBackupAgent {
in ParcelFileDescriptor newState, int token, IBackupManager callbackBinder);
/**
+ * Perform a "full" backup to the given file descriptor. The output file is presumed
+ * to be a socket or other non-seekable, write-only data sink. When this method is
+ * called, the app should write all of its files to the output.
+ *
+ * @param data Write-only file to receive the backed-up file content stream.
+ * The data must be formatted correctly for the resulting archive to be
+ * legitimate, so that will be tightly controlled by the available API.
+ *
+ * @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
+ * contents.
+ *
+ * @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);
+
+ /**
* 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 63f3258..65c73f9 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -20,13 +20,22 @@ import android.app.IBackupAgent;
import android.app.backup.IBackupManager;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.pm.ApplicationInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;
+import java.io.File;
import java.io.IOException;
+import java.util.HashSet;
+import java.util.LinkedList;
+
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.io.OsConstants;
+import libcore.io.StructStat;
/**
* Provides the central interface between an
@@ -87,6 +96,24 @@ public abstract class BackupAgent extends ContextWrapper {
private static final String TAG = "BackupAgent";
private static final boolean DEBUG = true;
+ /** @hide */
+ public static final int TYPE_EOF = 0;
+
+ /**
+ * During a full restore, indicates that the file system object being restored
+ * is an ordinary file.
+ */
+ public static final int TYPE_FILE = 1;
+
+ /**
+ * During a full restore, indicates that the file system object being restored
+ * is a directory.
+ */
+ public static final int TYPE_DIRECTORY = 2;
+
+ /** @hide */
+ public static final int TYPE_SYMLINK = 3;
+
public BackupAgent() {
super(null);
}
@@ -179,18 +206,240 @@ public abstract class BackupAgent extends ContextWrapper {
throws IOException;
/**
+ * The default implementation backs up the entirety of the application's "owned"
+ * file system trees to the output.
+ */
+ public void onFullBackup(FullBackupDataOutput data) throws IOException {
+ ApplicationInfo appInfo = getApplicationInfo();
+
+ String rootDir = new File(appInfo.dataDir).getAbsolutePath();
+ String filesDir = getFilesDir().getAbsolutePath();
+ String databaseDir = getDatabasePath("foo").getParentFile().getAbsolutePath();
+ String sharedPrefsDir = getSharedPrefsFile("foo").getParentFile().getAbsolutePath();
+ String cacheDir = getCacheDir().getAbsolutePath();
+ String libDir = (appInfo.nativeLibraryDir != null)
+ ? new File(appInfo.nativeLibraryDir).getAbsolutePath()
+ : null;
+
+ // Filters, the scan queue, and the set of resulting entities
+ HashSet<String> filterSet = new HashSet<String>();
+ String packageName = getPackageName();
+
+ // Okay, start with the app's root tree, but exclude all of the canonical subdirs
+ if (libDir != null) {
+ filterSet.add(libDir);
+ }
+ filterSet.add(cacheDir);
+ filterSet.add(databaseDir);
+ filterSet.add(sharedPrefsDir);
+ filterSet.add(filesDir);
+ fullBackupFileTree(packageName, FullBackup.ROOT_TREE_TOKEN, rootDir, filterSet, data);
+
+ // Now do the same for the files dir, db dir, and shared prefs dir
+ filterSet.add(rootDir);
+ filterSet.remove(filesDir);
+ fullBackupFileTree(packageName, FullBackup.DATA_TREE_TOKEN, filesDir, filterSet, data);
+
+ filterSet.add(filesDir);
+ filterSet.remove(databaseDir);
+ fullBackupFileTree(packageName, FullBackup.DATABASE_TREE_TOKEN, databaseDir, filterSet, data);
+
+ filterSet.add(databaseDir);
+ filterSet.remove(sharedPrefsDir);
+ fullBackupFileTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, sharedPrefsDir, filterSet, data);
+ }
+
+ /**
+ * Write an entire file as part of a full-backup operation. The file's contents
+ * will be delivered to the backup destination along with the metadata necessary
+ * to place it with the proper location and permissions on the device where the
+ * data is restored.
* @hide
+ *
+ * @param context The BackupAgent that is calling this method. It is an error to
+ * call it from something other than a running BackupAgent instance.
+ * @param file The file to be backed up. The file must exist and be readable by
+ * the caller.
+ * @param output The destination to which the backed-up file data will be sent.
+ */
+ public final void fullBackupFile(File file, FullBackupDataOutput output) {
+ // Look up where all of our various well-defined dir trees live on this device
+ String mainDir;
+ String filesDir;
+ String dbDir;
+ String spDir;
+ String cacheDir;
+ String libDir;
+
+ ApplicationInfo appInfo = getApplicationInfo();
+
+ mainDir = new File(appInfo.dataDir).getAbsolutePath();
+ filesDir = getFilesDir().getAbsolutePath();
+ dbDir = getDatabasePath("foo").getParentFile().getAbsolutePath();
+ spDir = getSharedPrefsFile("foo").getParentFile().getAbsolutePath();
+ cacheDir = getCacheDir().getAbsolutePath();
+ libDir = (appInfo.nativeLibraryDir == null) ? null
+ : new File(appInfo.nativeLibraryDir).getAbsolutePath();
+
+ // Now figure out which well-defined tree the file is placed in, working from
+ // most to least specific. We also specifically exclude the lib and cache dirs.
+ String filePath = file.getAbsolutePath();
+
+ if (filePath.startsWith(cacheDir) || filePath.startsWith(libDir)) {
+ Log.w(TAG, "lib and cache files are not backed up");
+ return;
+ }
+
+ final String domain;
+ String rootpath = null;
+ if (filePath.startsWith(dbDir)) {
+ domain = FullBackup.DATABASE_TREE_TOKEN;
+ rootpath = dbDir;
+ } else if (filePath.startsWith(spDir)) {
+ domain = FullBackup.SHAREDPREFS_TREE_TOKEN;
+ rootpath = spDir;
+ } else if (filePath.startsWith(filesDir)) {
+ domain = FullBackup.DATA_TREE_TOKEN;
+ rootpath = filesDir;
+ } else if (filePath.startsWith(mainDir)) {
+ domain = FullBackup.ROOT_TREE_TOKEN;
+ rootpath = mainDir;
+ } else {
+ Log.w(TAG, "File " + filePath + " is in an unsupported location; skipping");
+ return;
+ }
+
+ // And now that we know where it lives, semantically, back it up appropriately
+ Log.i(TAG, "backupFile() of " + filePath + " => domain=" + domain
+ + " rootpath=" + rootpath);
+ FullBackup.backupToTar(getPackageName(), domain, null, rootpath, filePath,
+ output.getData());
+ }
+
+ /**
+ * Scan the dir tree (if it actually exists) and process each entry we find. If the
+ * 'excludes' parameter is non-null, it is consulted each time a new file system entity
+ * is visited to see whether that entity (and its subtree, if appropriate) should be
+ * omitted from the backup process.
+ *
+ * @hide
+ */
+ protected final void fullBackupFileTree(String packageName, String domain, String rootPath,
+ HashSet<String> excludes, FullBackupDataOutput output) {
+ File rootFile = new File(rootPath);
+ if (rootFile.exists()) {
+ LinkedList<File> scanQueue = new LinkedList<File>();
+ scanQueue.add(rootFile);
+
+ while (scanQueue.size() > 0) {
+ File file = scanQueue.remove(0);
+ String filePath = file.getAbsolutePath();
+
+ // prune this subtree?
+ if (excludes != null && excludes.contains(filePath)) {
+ continue;
+ }
+
+ // If it's a directory, enqueue its contents for scanning.
+ try {
+ StructStat stat = Libcore.os.lstat(filePath);
+ if (OsConstants.S_ISLNK(stat.st_mode)) {
+ if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file);
+ continue;
+ } else if (OsConstants.S_ISDIR(stat.st_mode)) {
+ File[] contents = file.listFiles();
+ if (contents != null) {
+ for (File entry : contents) {
+ scanQueue.add(0, entry);
+ }
+ }
+ }
+ } catch (ErrnoException e) {
+ if (DEBUG) Log.w(TAG, "Error scanning file " + file + " : " + e);
+ continue;
+ }
+
+ // Finally, back this file up before proceeding
+ FullBackup.backupToTar(packageName, domain, null, rootPath, filePath,
+ output.getData());
+ }
+ }
+ }
+
+ /**
+ * Handle the data delivered via the given file descriptor during a full restore
+ * operation. The agent is given the path to the file's original location as well
+ * as its size and metadata.
+ * <p>
+ * The file descriptor can only be read for {@code size} bytes; attempting to read
+ * more data has undefined behavior.
+ * <p>
+ * The default implementation creates the destination file/directory and populates it
+ * with the data from the file descriptor, then sets the file's access mode and
+ * modification time to match the restore arguments.
+ *
+ * @param data A read-only file descriptor from which the agent can read {@code size}
+ * bytes of file data.
+ * @param size The number of bytes of file content to be restored to the given
+ * destination. If the file system object being restored is a directory, {@code size}
+ * will be zero.
+ * @param destination The File on disk to be restored with the given data.
+ * @param type The kind of file system object being restored. This will be either
+ * {@link BackupAgent#TYPE_FILE} or {@link BackupAgent#TYPE_DIRECTORY}.
+ * @param mode The access mode to be assigned to the destination after its data is
+ * written. This is in the standard format used by {@code chmod()}.
+ * @param mtime The modification time of the file when it was backed up, suitable to
+ * be assigned to the file after its data is written.
+ * @throws IOException
*/
public void onRestoreFile(ParcelFileDescriptor data, long size,
- int type, String domain, String path, long mode, long mtime)
+ File destination, int type, long mode, long mtime)
throws IOException {
- // empty stub implementation
+ FullBackup.restoreFile(data, size, type, mode, mtime, destination);
}
/**
- * Package-private, used only for dispatching an extra step during full backup
+ * Only specialized platform agents should overload this entry point to support
+ * restores to crazy non-app locations.
+ * @hide
*/
- void onSaveApk(BackupDataOutput data) {
+ protected void onRestoreFile(ParcelFileDescriptor data, long size,
+ int type, String domain, String path, long mode, long mtime)
+ throws IOException {
+ String basePath = null;
+
+ if (DEBUG) Log.d(TAG, "onRestoreFile() size=" + size + " type=" + type
+ + " domain=" + domain + " relpath=" + path + " mode=" + mode
+ + " mtime=" + mtime);
+
+ // Parse out the semantic domains into the correct physical location
+ if (domain.equals(FullBackup.DATA_TREE_TOKEN)) {
+ basePath = getFilesDir().getAbsolutePath();
+ } else if (domain.equals(FullBackup.DATABASE_TREE_TOKEN)) {
+ basePath = getDatabasePath("foo").getParentFile().getAbsolutePath();
+ } else if (domain.equals(FullBackup.ROOT_TREE_TOKEN)) {
+ basePath = new File(getApplicationInfo().dataDir).getAbsolutePath();
+ } else if (domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)) {
+ basePath = getSharedPrefsFile("foo").getParentFile().getAbsolutePath();
+ } else if (domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
+ basePath = getCacheDir().getAbsolutePath();
+ } else {
+ // Not a supported location
+ Log.i(TAG, "Data restored from non-app domain " + domain + ", ignoring");
+ }
+
+ // Now that we've figured out where the data goes, send it on its way
+ if (basePath != null) {
+ File outFile = new File(basePath, path);
+ if (DEBUG) Log.i(TAG, "[" + domain + " : " + path + "] mapped to " + outFile.getPath());
+ onRestoreFile(data, size, outFile, type, mode, mtime);
+ } else {
+ // Not a supported output location? We need to consume the data
+ // anyway, so just use the default "copy the data out" implementation
+ // with a null destination.
+ if (DEBUG) Log.i(TAG, "[ skipping data from unsupported domain " + domain + "]");
+ FullBackup.restoreFile(data, size, type, mode, mtime, null);
+ }
}
// ----- Core implementation -----
@@ -215,7 +464,6 @@ public abstract class BackupAgent extends ContextWrapper {
public void doBackup(ParcelFileDescriptor oldState,
ParcelFileDescriptor data,
ParcelFileDescriptor newState,
- boolean storeApk,
int token, IBackupManager callbackBinder) throws RemoteException {
// Ensure that we're running with the app's normal permission level
long ident = Binder.clearCallingIdentity();
@@ -223,10 +471,6 @@ public abstract class BackupAgent extends ContextWrapper {
if (DEBUG) Log.v(TAG, "doBackup() invoked");
BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
- if (storeApk) {
- onSaveApk(output);
- }
-
try {
BackupAgent.this.onBackup(oldState, output, newState);
} catch (IOException ex) {
@@ -273,6 +517,33 @@ public abstract class BackupAgent extends ContextWrapper {
}
@Override
+ public void doFullBackup(ParcelFileDescriptor data,
+ int token, IBackupManager callbackBinder) {
+ // Ensure that we're running with the app's normal permission level
+ long ident = Binder.clearCallingIdentity();
+
+ if (DEBUG) Log.v(TAG, "doFullBackup() invoked");
+ BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
+
+ try {
+ BackupAgent.this.onFullBackup(new FullBackupDataOutput(data));
+ } catch (IOException ex) {
+ Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw new RuntimeException(ex);
+ } catch (RuntimeException ex) {
+ Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw ex;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ try {
+ callbackBinder.opComplete(token);
+ } catch (RemoteException e) {
+ // we'll time out anyway, so we're safe
+ }
+ }
+ }
+
+ @Override
public void doRestoreFile(ParcelFileDescriptor data, long size,
int type, String domain, String path, long mode, long mtime,
int token, IBackupManager callbackBinder) throws RemoteException {
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 3b70e19..d7f1c9f 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -16,6 +16,9 @@
package android.app.backup;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -29,7 +32,8 @@ import libcore.io.Libcore;
/**
* Global constant definitions et cetera related to the full-backup-to-fd
- * binary format.
+ * binary format. Nothing in this namespace is part of any API; it's all
+ * hidden details of the current implementation gathered into one location.
*
* @hide
*/
@@ -52,18 +56,41 @@ public class FullBackup {
public static final String FULL_RESTORE_INTENT_ACTION = "fullrest";
public static final String CONF_TOKEN_INTENT_EXTRA = "conftoken";
- public static final int TYPE_EOF = 0;
- public static final int TYPE_FILE = 1;
- public static final int TYPE_DIRECTORY = 2;
- public static final int TYPE_SYMLINK = 3;
-
+ /**
+ * @hide
+ */
static public native int backupToTar(String packageName, String domain,
String linkdomain, String rootpath, String path, BackupDataOutput output);
- static public void restoreToFile(ParcelFileDescriptor data,
- long size, int type, long mode, long mtime, File outFile,
- boolean doChmod) throws IOException {
- if (type == FullBackup.TYPE_DIRECTORY) {
+ /**
+ * Copy data from a socket to the given File location on permanent storage. The
+ * modification time and access mode of the resulting file will be set if desired.
+ * If the {@code type} parameter indicates that the result should be a directory,
+ * the socket parameter may be {@code null}; even if it is valid, no data will be
+ * read from it in this case.
+ * <p>
+ * If the {@code mode} argument is negative, then the resulting output file will not
+ * have its access mode or last modification time reset as part of this operation.
+ *
+ * @param data Socket supplying the data to be copied to the output file. If the
+ * output is a directory, this may be {@code null}.
+ * @param size Number of bytes of data to copy from the socket to the file. At least
+ * this much data must be available through the {@code data} parameter.
+ * @param type Must be either {@link BackupAgent#TYPE_FILE} for ordinary file data
+ * or {@link BackupAgent#TYPE_DIRECTORY} for a directory.
+ * @param mode Unix-style file mode (as used by the chmod(2) syscall) to be set on
+ * the output file or directory. If this parameter is negative then neither
+ * the mode nor the mtime parameters will be used.
+ * @param mtime A timestamp in the standard Unix epoch that will be imposed as the
+ * last modification time of the output file. if the {@code mode} parameter is
+ * negative then this parameter will be ignored.
+ * @param outFile Location within the filesystem to place the data. This must point
+ * to a location that is writeable by the caller, prefereably using an absolute path.
+ * @throws IOException
+ */
+ static public void restoreFile(ParcelFileDescriptor data,
+ long size, int type, long mode, long mtime, File outFile) throws IOException {
+ if (type == BackupAgent.TYPE_DIRECTORY) {
// Canonically a directory has no associated content, so we don't need to read
// anything from the pipe in this case. Just create the directory here and
// drop down to the final metadata adjustment.
@@ -117,7 +144,7 @@ public class FullBackup {
}
// Now twiddle the state to match the backup, assuming all went well
- if (doChmod && outFile != null) {
+ if (mode >= 0 && outFile != null) {
try {
Libcore.os.chmod(outFile.getPath(), (int)mode);
} catch (ErrnoException e) {
diff --git a/core/java/android/app/backup/FullBackupAgent.java b/core/java/android/app/backup/FullBackupAgent.java
index df1c363..faea76a 100644
--- a/core/java/android/app/backup/FullBackupAgent.java
+++ b/core/java/android/app/backup/FullBackupAgent.java
@@ -16,210 +16,26 @@
package android.app.backup;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import libcore.io.Libcore;
-import libcore.io.ErrnoException;
-import libcore.io.OsConstants;
-import libcore.io.StructStat;
-
-import java.io.File;
import java.io.IOException;
-import java.util.HashSet;
-import java.util.LinkedList;
/**
- * Backs up an application's entire /data/data/&lt;package&gt;/... file system. This
- * class is used by the desktop full backup mechanism and is not intended for direct
- * use by applications.
+ * Simple concrete class that merely provides the default BackupAgent full backup/restore
+ * implementations for applications that do not supply their own.
*
* {@hide}
*/
public class FullBackupAgent extends BackupAgent {
- // !!! TODO: turn off debugging
- private static final String TAG = "FullBackupAgent";
- private static final boolean DEBUG = true;
-
- PackageManager mPm;
-
- private String mMainDir;
- private String mFilesDir;
- private String mDatabaseDir;
- private String mSharedPrefsDir;
- private String mCacheDir;
- private String mLibDir;
-
- private File NULL_FILE;
-
- @Override
- public void onCreate() {
- NULL_FILE = new File("/dev/null");
-
- mPm = getPackageManager();
- try {
- ApplicationInfo appInfo = mPm.getApplicationInfo(getPackageName(), 0);
- mMainDir = new File(appInfo.dataDir).getAbsolutePath();
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Unable to find package " + getPackageName());
- throw new RuntimeException(e);
- }
-
- mFilesDir = getFilesDir().getAbsolutePath();
- mDatabaseDir = getDatabasePath("foo").getParentFile().getAbsolutePath();
- mSharedPrefsDir = getSharedPrefsFile("foo").getParentFile().getAbsolutePath();
- mCacheDir = getCacheDir().getAbsolutePath();
-
- ApplicationInfo app = getApplicationInfo();
- mLibDir = (app.nativeLibraryDir != null)
- ? new File(app.nativeLibraryDir).getAbsolutePath()
- : null;
- }
-
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException {
- // Filters, the scan queue, and the set of resulting entities
- HashSet<String> filterSet = new HashSet<String>();
- String packageName = getPackageName();
-
- // Okay, start with the app's root tree, but exclude all of the canonical subdirs
- if (mLibDir != null) {
- filterSet.add(mLibDir);
- }
- filterSet.add(mCacheDir);
- filterSet.add(mDatabaseDir);
- filterSet.add(mSharedPrefsDir);
- filterSet.add(mFilesDir);
- processTree(packageName, FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data);
-
- // Now do the same for the files dir, db dir, and shared prefs dir
- filterSet.add(mMainDir);
- filterSet.remove(mFilesDir);
- processTree(packageName, FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data);
-
- filterSet.add(mFilesDir);
- filterSet.remove(mDatabaseDir);
- processTree(packageName, FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data);
-
- filterSet.add(mDatabaseDir);
- filterSet.remove(mSharedPrefsDir);
- processTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data);
+ // Doesn't do incremental backup/restore
}
- // Scan the dir tree (if it actually exists) and process each entry we find. If the
- // 'excludes' parameter is non-null, it is consulted each time a new file system entity
- // is visited to see whether that entity (and its subtree, if appropriate) should be
- // omitted from the backup process.
- protected void processTree(String packageName, String domain, String rootPath,
- HashSet<String> excludes, BackupDataOutput data) {
- File rootFile = new File(rootPath);
- if (rootFile.exists()) {
- LinkedList<File> scanQueue = new LinkedList<File>();
- scanQueue.add(rootFile);
-
- while (scanQueue.size() > 0) {
- File file = scanQueue.remove(0);
- String filePath = file.getAbsolutePath();
-
- // prune this subtree?
- if (excludes != null && excludes.contains(filePath)) {
- continue;
- }
-
- // If it's a directory, enqueue its contents for scanning.
- try {
- StructStat stat = Libcore.os.lstat(filePath);
- if (OsConstants.S_ISLNK(stat.st_mode)) {
- if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file);
- continue;
- } else if (OsConstants.S_ISDIR(stat.st_mode)) {
- File[] contents = file.listFiles();
- if (contents != null) {
- for (File entry : contents) {
- scanQueue.add(0, entry);
- }
- }
- }
- } catch (ErrnoException e) {
- if (DEBUG) Log.w(TAG, "Error scanning file " + file + " : " + e);
- continue;
- }
-
- // Finally, back this file up before proceeding
- FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, data);
- }
- }
- }
-
- @Override
- void onSaveApk(BackupDataOutput data) {
- ApplicationInfo app = getApplicationInfo();
- if (DEBUG) Log.i(TAG, "APK flags: system=" + ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0)
- + " updated=" + ((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)
- + " locked=" + ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) );
- if (DEBUG) Log.i(TAG, "codepath: " + getPackageCodePath());
-
- // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
- final String pkgName = getPackageName();
- final String apkDir = new File(getPackageCodePath()).getParent();
- FullBackup.backupToTar(pkgName, FullBackup.APK_TREE_TOKEN, null,
- apkDir, getPackageCodePath(), data);
-
- // Save associated .obb content if it exists and we did save the apk
- // check for .obb and save those too
- final File obbDir = Environment.getExternalStorageAppObbDirectory(pkgName);
- if (obbDir != null) {
- if (DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
- File[] obbFiles = obbDir.listFiles();
- if (obbFiles != null) {
- final String obbDirName = obbDir.getAbsolutePath();
- for (File obb : obbFiles) {
- FullBackup.backupToTar(pkgName, FullBackup.OBB_TREE_TOKEN, null,
- obbDirName, obb.getAbsolutePath(), data);
- }
- }
- }
- }
-
- /**
- * Dummy -- We're never used for restore of an incremental dataset
- */
@Override
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
throws IOException {
- }
-
- /**
- * Restore the described file from the given pipe.
- */
- @Override
- public void onRestoreFile(ParcelFileDescriptor data, long size,
- int type, String domain, String relpath, long mode, long mtime)
- throws IOException {
- String basePath = null;
- File outFile = null;
-
- if (DEBUG) Log.d(TAG, "onRestoreFile() size=" + size + " type=" + type
- + " domain=" + domain + " relpath=" + relpath + " mode=" + mode
- + " mtime=" + mtime);
-
- // Parse out the semantic domains into the correct physical location
- if (domain.equals(FullBackup.DATA_TREE_TOKEN)) basePath = mFilesDir;
- else if (domain.equals(FullBackup.DATABASE_TREE_TOKEN)) basePath = mDatabaseDir;
- else if (domain.equals(FullBackup.ROOT_TREE_TOKEN)) basePath = mMainDir;
- else if (domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)) basePath = mSharedPrefsDir;
-
- // Not a supported output location? We need to consume the data
- // anyway, so send it to /dev/null
- outFile = (basePath != null) ? new File(basePath, relpath) : null;
- if (DEBUG) Log.i(TAG, "[" + domain + " : " + relpath + "] mapped to " + outFile.getPath());
-
- // Now that we've figured out where the data goes, send it on its way
- FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true);
+ // Doesn't do incremental backup/restore
}
}
diff --git a/core/java/android/app/backup/FullBackupDataOutput.java b/core/java/android/app/backup/FullBackupDataOutput.java
new file mode 100644
index 0000000..99dab1f
--- /dev/null
+++ b/core/java/android/app/backup/FullBackupDataOutput.java
@@ -0,0 +1,21 @@
+package android.app.backup;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Provides the interface through which a {@link BackupAgent} writes entire files
+ * to a full backup data set, via its {@link BackupAgent#onFullBackup(FullBackupDataOutput)}
+ * method.
+ */
+public class FullBackupDataOutput {
+ // Currently a name-scoping shim around BackupDataOutput
+ private BackupDataOutput mData;
+
+ /** @hide */
+ public FullBackupDataOutput(ParcelFileDescriptor fd) {
+ mData = new BackupDataOutput(fd.getFileDescriptor());
+ }
+
+ /** @hide */
+ public BackupDataOutput getData() { return mData; }
+}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 454cb31..ddb6ef0 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -91,15 +91,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public String backupAgentName;
/**
- * Class implementing the package's *full* backup functionality. This
- * is not usable except by system-installed packages. It can be the same
- * as the backupAgent.
- *
- * @hide
- */
- public String fullBackupAgentName;
-
- /**
* Value for {@link #flags}: if set, this application is installed in the
* device's system image.
*/
@@ -555,7 +546,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeInt(installLocation);
dest.writeString(manageSpaceActivityName);
dest.writeString(backupAgentName);
- dest.writeString(fullBackupAgentName);
dest.writeInt(descriptionRes);
}
@@ -593,7 +583,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
installLocation = source.readInt();
manageSpaceActivityName = source.readString();
backupAgentName = source.readString();
- fullBackupAgentName = source.readString();
descriptionRes = source.readInt();
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 208869b..53d6bb1 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1517,17 +1517,6 @@ public class PackageParser {
}
}
- // fullBackupAgent is explicitly handled even if allowBackup is false
- name = sa.getNonConfigurationString(
- com.android.internal.R.styleable.AndroidManifestApplication_fullBackupAgent, 0);
- if (name != null) {
- ai.fullBackupAgentName = buildClassName(pkgName, name, outError);
- if (false) {
- Log.v(TAG, "android:fullBackupAgent=" + ai.fullBackupAgentName
- + " from " + pkgName + "+" + name);
- }
- }
-
TypedValue v = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestApplication_label);
if (v != null && (ai.labelRes=v.resourceId) == 0) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1a32060..49eaf19 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1418,7 +1418,6 @@
android:label="@string/android_system_label"
android:allowClearUserData="false"
android:backupAgent="com.android.server.SystemBackupAgent"
- android:fullBackupAgent="com.android.server.SystemBackupAgent"
android:killAfterRestore="false"
android:icon="@drawable/ic_launcher_android">
<activity android:name="com.android.internal.app.ChooserActivity"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 03b332e..dd16bd0 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -696,13 +696,6 @@
<p>The default value of this attribute is <code>false</code>. -->
<attr name="restoreAnyVersion" format="boolean" />
- <!-- The agent to use for a *full* backup of the package. Only system applications
- can use this to override the ordinary FullBackupAgent with a custom implementation.
- It's needed strictly for packages with strongly device-specific data, such as the
- Settings provider.
- -->
- <attr name="fullBackupAgent" format="string" />
-
<!-- The default install location defined by an application. -->
<attr name="installLocation">
<!-- Let the system decide ideal install location -->
@@ -800,7 +793,6 @@
<attr name="killAfterRestore" />
<attr name="restoreNeedsApplication" />
<attr name="restoreAnyVersion" />
- <attr name="fullBackupAgent" />
<attr name="neverEncrypt" />
<!-- Request that your application's processes be created with
a large Dalvik heap. This applies to <em>all</em> processes
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 54e484e..20f5e7c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1725,7 +1725,6 @@
<public type="attr" name="layoutDirection" />
- <public type="attr" name="fullBackupAgent" />
<public type="attr" name="suggestionsEnabled" />
<public type="attr" name="rowCount" />
@@ -1737,7 +1736,6 @@
<public type="attr" name="layout_row" />
<public type="attr" name="layout_rowSpan" />
-
<public type="attr" name="layout_columnSpan" />
<public type="attr" name="layout_widthSpec" />