diff --git a/api/current.xml b/api/current.xml index 9c663fed93a57..b5469e71537e7 100644 --- a/api/current.xml +++ b/api/current.xml @@ -19850,6 +19850,83 @@ > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + STOPSHIP write more documentation about the backup process here. */ public abstract class BackupAgent extends ContextWrapper { private static final String TAG = "BackupAgent"; @@ -62,9 +62,9 @@ public abstract class BackupAgent extends ContextWrapper { * state provided by the application. May be null, in which * case no prior state is being provided and the application should * perform a full backup. - * @param data An open, read/write ParcelFileDescriptor pointing to the backup data - * destination. Typically the application will use backup helper - * classes to write to this file. + * @param data A structured wrapper around an open, read/write ParcelFileDescriptor + * pointing to the backup data destination. Typically the application will use + * backup helper classes to write to this file. * @param newState An open, read/write ParcelFileDescriptor pointing to an empty * file. The application should record the final backup state * here after writing the requested data to dataFd. @@ -77,10 +77,18 @@ public abstract class BackupAgent extends ContextWrapper { * existing data with the contents of the backup. The backup data is * provided in the file pointed to by the dataFd file descriptor. Once * the restore is finished, the application should write a representation - * of the final state to the newStateFd file descriptor, + * of the final state to the newStateFd file descriptor, * - * @param data An open, read-only ParcelFileDescriptor pointing to a full snapshot - * of the application's data. + *

The application is responsible for properly erasing its old data and + * replacing it with the data supplied to this method. No "clear user data" + * operation will be performed automatically by the operating system. The + * exception to this is in the case of a failed restore attempt: if onRestore() + * throws an exception, the OS will assume that the application's data may now + * be in an incoherent state, and will clear it before proceeding. + * + * @param data A structured wrapper around an open, read-only ParcelFileDescriptor + * pointing to a full snapshot of the application's data. Typically the + * application will use helper classes to read this data. * @param appVersionCode The android:versionCode value of the application that backed * up this particular data set. This makes it easier for an application's * agent to distinguish among several possible older data versions when diff --git a/core/java/android/backup/AbsoluteFileBackupHelper.java b/core/java/android/backup/AbsoluteFileBackupHelper.java index 1dbccc9c47af8..6bf848f555dd3 100644 --- a/core/java/android/backup/AbsoluteFileBackupHelper.java +++ b/core/java/android/backup/AbsoluteFileBackupHelper.java @@ -27,7 +27,7 @@ import java.io.FileDescriptor; * Like FileBackupHelper, but takes absolute paths for the files instead of * subpaths of getFilesDir() * - * @hide + * STOPSHIP: document! */ public class AbsoluteFileBackupHelper extends FileBackupHelperBase implements BackupHelper { private static final String TAG = "AbsoluteFileBackupHelper"; @@ -36,6 +36,13 @@ public class AbsoluteFileBackupHelper extends FileBackupHelperBase implements Ba Context mContext; String[] mFiles; + /** + * Construct a helper for backing up / restoring the files at the given absolute locations + * within the file system. + * + * @param context + * @param files + */ public AbsoluteFileBackupHelper(Context context, String... files) { super(context); @@ -54,6 +61,9 @@ public class AbsoluteFileBackupHelper extends FileBackupHelperBase implements Ba performBackup_checked(oldState, data, newState, mFiles, mFiles); } + /** + * Restore one absolute file entity from the restore stream + */ public void restoreEntity(BackupDataInputStream data) { if (DEBUG) Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size()); String key = data.getKey(); diff --git a/core/java/android/backup/BackupDataInput.java b/core/java/android/backup/BackupDataInput.java index e67b0bee76773..67d51eaee6845 100644 --- a/core/java/android/backup/BackupDataInput.java +++ b/core/java/android/backup/BackupDataInput.java @@ -21,7 +21,9 @@ import android.content.Context; import java.io.FileDescriptor; import java.io.IOException; -/** @hide */ +/** + * STOPSHIP: document! + */ public class BackupDataInput { int mBackupReader; @@ -33,6 +35,7 @@ public class BackupDataInput { int dataSize; } + /** @hide */ public BackupDataInput(FileDescriptor fd) { if (fd == null) throw new NullPointerException(); mBackupReader = ctor(fd); @@ -41,6 +44,7 @@ public class BackupDataInput { } } + /** @hide */ protected void finalize() throws Throwable { try { dtor(mBackupReader); @@ -49,6 +53,13 @@ public class BackupDataInput { } } + /** + * Consumes the next header from the restore stream. + * + * @return true when there is an entity ready for consumption from the restore stream, + * false if the restore stream has been fully consumed. + * @throws IOException if an error occurred while reading the restore stream + */ public boolean readNextHeader() throws IOException { int result = readNextHeader_native(mBackupReader, mHeader); if (result == 0) { @@ -66,6 +77,11 @@ public class BackupDataInput { } } + /** + * Report the key associated with the current record in the restore stream + * @return the current record's key string + * @throws IllegalStateException if the next record header has not yet been read + */ public String getKey() { if (mHeaderReady) { return mHeader.key; @@ -74,6 +90,13 @@ public class BackupDataInput { } } + /** + * Report the size in bytes of the data associated with the current record in the + * restore stream. + * + * @return The size of the record's raw data, in bytes + * @throws IllegalStateException if the next record header has not yet been read + */ public int getDataSize() { if (mHeaderReady) { return mHeader.dataSize; @@ -82,6 +105,19 @@ public class BackupDataInput { } } + /** + * Read a record's raw data from the restore stream. The record's header must first + * have been processed by the {@link #readNextHeader()} method. Multiple calls to + * this method may be made in order to process the data in chunks; not all of it + * must be read in a single call. + * + * @param data An allocated byte array of at least 'size' bytes + * @param offset Offset within the 'data' array at which the data will be placed + * when read from the stream. + * @param size The number of bytes to read in this pass. + * @return The number of bytes of data read + * @throws IOException if an error occurred when trying to read the restore data stream + */ public int readEntityData(byte[] data, int offset, int size) throws IOException { if (mHeaderReady) { int result = readEntityData_native(mBackupReader, data, offset, size); @@ -95,6 +131,14 @@ public class BackupDataInput { } } + /** + * Consume the current record's data without actually reading it into a buffer + * for further processing. This allows a {@link android.backup.BackupAgent} to + * efficiently discard obsolete or otherwise uninteresting records during the + * restore operation. + * + * @throws IOException if an error occurred when trying to read the restore data stream + */ public void skipEntityData() throws IOException { if (mHeaderReady) { skipEntityData_native(mBackupReader); diff --git a/core/java/android/backup/BackupDataInputStream.java b/core/java/android/backup/BackupDataInputStream.java index b705c4c360223..503b3c1ba6463 100644 --- a/core/java/android/backup/BackupDataInputStream.java +++ b/core/java/android/backup/BackupDataInputStream.java @@ -16,12 +16,11 @@ package android.backup; -import android.util.Log; - import java.io.InputStream; import java.io.IOException; -/** @hide */ +/** + * STOPSHIP: document */ public class BackupDataInputStream extends InputStream { String key; @@ -30,6 +29,7 @@ public class BackupDataInputStream extends InputStream { BackupDataInput mData; byte[] mOneByte; + /** @hide */ BackupDataInputStream(BackupDataInput data) { mData = data; } diff --git a/core/java/android/backup/BackupDataOutput.java b/core/java/android/backup/BackupDataOutput.java index d29c5ba02f140..672d01f895cd3 100644 --- a/core/java/android/backup/BackupDataOutput.java +++ b/core/java/android/backup/BackupDataOutput.java @@ -21,13 +21,16 @@ import android.content.Context; import java.io.FileDescriptor; import java.io.IOException; -/** @hide */ +/** + * STOPSHIP: document + */ public class BackupDataOutput { int mBackupWriter; public static final int OP_UPDATE = 1; public static final int OP_DELETE = 2; + /** @hide */ public BackupDataOutput(FileDescriptor fd) { if (fd == null) throw new NullPointerException(); mBackupWriter = ctor(fd); @@ -36,7 +39,15 @@ public class BackupDataOutput { } } - // A dataSize of -1 indicates that the record under this key should be deleted + /** + * Mark the beginning of one record in the backup data stream. + * + * @param key + * @param dataSize The size in bytes of this record's data. Passing a dataSize + * of -1 indicates that the record under this key should be deleted. + * @return The number of bytes written to the backup stream + * @throws IOException if the write failed + */ public int writeEntityHeader(String key, int dataSize) throws IOException { int result = writeEntityHeader_native(mBackupWriter, key, dataSize); if (result >= 0) { @@ -46,6 +57,13 @@ public class BackupDataOutput { } } + /** + * Write a chunk of data under the current entity to the backup transport. + * @param data A raw data buffer to send + * @param size The number of bytes to be sent in this chunk + * @return the number of bytes written + * @throws IOException if the write failed + */ public int writeEntityData(byte[] data, int size) throws IOException { int result = writeEntityData_native(mBackupWriter, data, size); if (result >= 0) { @@ -59,6 +77,7 @@ public class BackupDataOutput { setKeyPrefix_native(mBackupWriter, keyPrefix); } + /** @hide */ protected void finalize() throws Throwable { try { dtor(mBackupWriter); diff --git a/core/java/android/backup/BackupHelper.java b/core/java/android/backup/BackupHelper.java index 3983e28cce399..fc48cf25721b0 100644 --- a/core/java/android/backup/BackupHelper.java +++ b/core/java/android/backup/BackupHelper.java @@ -20,7 +20,9 @@ import android.os.ParcelFileDescriptor; import java.io.InputStream; -/** @hide */ +/** + * STOPSHIP: document! + */ public interface BackupHelper { /** * Based on oldState, determine which of the files from the application's data directory @@ -31,16 +33,18 @@ public interface BackupHelper { ParcelFileDescriptor newState); /** - * Called by BackupHelperDispatcher to dispatch one entity of data. + * Called by BackupHelperAgent to restore one entity from the restore dataset. *

* Do not close the data stream. Do not read more than - * dataSize bytes from data. + * data.size() bytes from data. */ public void restoreEntity(BackupDataInputStream data); /** - * + * Called by BackupHelperAgent to write the new backup state file corresponding to + * the current state of the app's data at the time the backup operation was + * performed. */ - public void writeRestoreSnapshot(ParcelFileDescriptor fd); + public void writeNewStateDescription(ParcelFileDescriptor fd); } diff --git a/core/java/android/backup/BackupHelperAgent.java b/core/java/android/backup/BackupHelperAgent.java index 5d0c4a2514db3..dc17154fac62a 100644 --- a/core/java/android/backup/BackupHelperAgent.java +++ b/core/java/android/backup/BackupHelperAgent.java @@ -26,28 +26,54 @@ import android.util.Log; import java.io.IOException; -/** @hide */ +/** + * A convenient BackupAgent wrapper class that automatically manages heterogeneous + * data sets within the backup data, each identified by a unique key prefix. An + * application will typically extend this class in their own backup agent. Then, + * within the agent's onBackup() and onRestore() methods, it will call + * {@link #addHelper(String, BackupHelper)} one or more times to specify the data + * sets, then invoke super.onBackup() or super.onRestore() to have the BackupHelperAgent + * implementation process the data. + * + * STOPSHIP: document! + */ public class BackupHelperAgent extends BackupAgent { static final String TAG = "BackupHelperAgent"; BackupHelperDispatcher mDispatcher = new BackupHelperDispatcher(); + /** + * Run the backup process on each of the configured handlers. + */ @Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { mDispatcher.performBackup(oldState, data, newState); } + /** + * Run the restore process on each of the configured handlers. + */ @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { mDispatcher.performRestore(data, appVersionCode, newState); } + /** @hide */ public BackupHelperDispatcher getDispatcher() { return mDispatcher; } + /** + * Add a helper for a given data subset to the agent's configuration. Each helper + * must have a prefix string that is unique within this backup agent's set of + * helpers. + * + * @param keyPrefix A string used to disambiguate the various helpers within this agent + * @param helper A backup/restore helper object to be invoked during backup and restore + * operations. + */ public void addHelper(String keyPrefix, BackupHelper helper) { mDispatcher.addHelper(keyPrefix, helper); } diff --git a/core/java/android/backup/BackupHelperDispatcher.java b/core/java/android/backup/BackupHelperDispatcher.java index 6ccb83effd454..bf2c44d482af6 100644 --- a/core/java/android/backup/BackupHelperDispatcher.java +++ b/core/java/android/backup/BackupHelperDispatcher.java @@ -138,7 +138,7 @@ public class BackupHelperDispatcher { // Write out the state files -- mHelpers is a TreeMap, so the order is well defined. for (BackupHelper helper: mHelpers.values()) { - helper.writeRestoreSnapshot(newState); + helper.writeNewStateDescription(newState); } } diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java index 0b27117d3f450..4bf59ebe3b8a6 100644 --- a/core/java/android/backup/BackupManager.java +++ b/core/java/android/backup/BackupManager.java @@ -38,7 +38,11 @@ import android.util.Log; * documentation for {@link android.app.BackupAgent} for a detailed description * of how the backup then proceeds. * - * @hide pending API solidification + *

STOPSHIP more documentation here! Include the attributes: + * android:backupAgent + * android:allowBackup + * android:restoreNeedsApplication + * android:killAfterRestore */ public class BackupManager { private static final String TAG = "BackupManager"; @@ -110,11 +114,8 @@ public class BackupManager { } /** - * Begin the process of restoring system data from backup. This method requires - * that the application hold the "android.permission.BACKUP" permission, and is - * not public. - * - * {@hide} + * Begin the process of restoring data from backup. See the + * {@link android.backup.RestoreSession} class for documentation on that process. */ public RestoreSession beginRestoreSession() { if (!EVEN_THINK_ABOUT_DOING_RESTORE) { @@ -128,7 +129,7 @@ public class BackupManager { IRestoreSession binder = sService.beginRestoreSession(transport); session = new RestoreSession(mContext, binder); } catch (RemoteException e) { - Log.d(TAG, "beginRestoreSession() couldn't connect"); + Log.w(TAG, "beginRestoreSession() couldn't connect"); } } return session; diff --git a/core/java/android/backup/FileBackupHelper.java b/core/java/android/backup/FileBackupHelper.java index dacfc8f557dc9..68b4d4219f6ca 100644 --- a/core/java/android/backup/FileBackupHelper.java +++ b/core/java/android/backup/FileBackupHelper.java @@ -23,7 +23,9 @@ import android.util.Log; import java.io.File; import java.io.FileDescriptor; -/** @hide */ +/** + * STOPSHIP: document! [manages backup of a set of files; restore is totally opaque] + */ public class FileBackupHelper extends FileBackupHelperBase implements BackupHelper { private static final String TAG = "FileBackupHelper"; private static final boolean DEBUG = false; @@ -32,6 +34,13 @@ public class FileBackupHelper extends FileBackupHelperBase implements BackupHelp File mFilesDir; String[] mFiles; + /** + * Construct a helper to manage backup/restore of entire files within the + * application's data directory hierarchy. + * + * @param context The backup agent's Context object + * @param files A list of the files to be backed up or restored. + */ public FileBackupHelper(Context context, String... files) { super(context); @@ -60,6 +69,9 @@ public class FileBackupHelper extends FileBackupHelperBase implements BackupHelp performBackup_checked(oldState, data, newState, fullPaths, files); } + /** + * Restore one record [representing a single file] from the restore dataset. + */ public void restoreEntity(BackupDataInputStream data) { if (DEBUG) Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size()); String key = data.getKey(); diff --git a/core/java/android/backup/FileBackupHelperBase.java b/core/java/android/backup/FileBackupHelperBase.java index 03ae4763fbd0c..a0ff38b641269 100644 --- a/core/java/android/backup/FileBackupHelperBase.java +++ b/core/java/android/backup/FileBackupHelperBase.java @@ -20,7 +20,6 @@ import android.content.Context; import android.os.ParcelFileDescriptor; import android.util.Log; -import java.io.InputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileOutputStream; @@ -80,18 +79,14 @@ class FileBackupHelperBase { } } - void writeFile(File f, InputStream in) { - if (!(in instanceof BackupDataInputStream)) { - throw new IllegalStateException("input stream must be a BackupDataInputStream"); - } + void writeFile(File f, BackupDataInputStream in) { int result = -1; // Create the enclosing directory. File parent = f.getParentFile(); parent.mkdirs(); - result = writeFile_native(mPtr, f.getAbsolutePath(), - ((BackupDataInputStream)in).mData.mBackupReader); + result = writeFile_native(mPtr, f.getAbsolutePath(), in.mData.mBackupReader); if (result != 0) { // Bail on this entity. Only log one failure per helper object. if (!mExceptionLogged) { @@ -103,7 +98,7 @@ class FileBackupHelperBase { } } - public void writeRestoreSnapshot(ParcelFileDescriptor fd) { + public void writeNewStateDescription(ParcelFileDescriptor fd) { int result = writeSnapshot_native(mPtr, fd.getFileDescriptor()); // TODO: Do something with the error. } diff --git a/core/java/android/backup/RestoreObserver.java b/core/java/android/backup/RestoreObserver.java index 3be8c086ab50f..e41827503f445 100644 --- a/core/java/android/backup/RestoreObserver.java +++ b/core/java/android/backup/RestoreObserver.java @@ -19,7 +19,6 @@ package android.backup; /** * Callback class for receiving progress reports during a restore operation. These * methods will all be called on your application's main thread. - * @hide */ public abstract class RestoreObserver { /** diff --git a/core/java/android/backup/RestoreSession.java b/core/java/android/backup/RestoreSession.java index d10831e24a155..fc53854a7faa7 100644 --- a/core/java/android/backup/RestoreSession.java +++ b/core/java/android/backup/RestoreSession.java @@ -27,7 +27,6 @@ import android.util.Log; /** * Interface for applications to use when managing a restore session. - * @hide */ public class RestoreSession { static final String TAG = "RestoreSession"; @@ -44,6 +43,8 @@ public class RestoreSession { * and a String array under the key "names" whose entries are the user-meaningful * text corresponding to the backup sets at each index in the tokens array. * On error, returns null. + * + * {@hide} */ public RestoreSet[] getAvailableRestoreSets() { try { @@ -66,6 +67,8 @@ public class RestoreSession { * the restore set that should be used. * @param observer If non-null, this binder points to an object that will receive * progress callbacks during the restore operation. + * + * {@hide} */ public int restoreAll(long token, RestoreObserver observer) { int err = -1; diff --git a/core/java/android/backup/SharedPreferencesBackupHelper.java b/core/java/android/backup/SharedPreferencesBackupHelper.java index 6a0bc96534092..f9c97a3beb10a 100644 --- a/core/java/android/backup/SharedPreferencesBackupHelper.java +++ b/core/java/android/backup/SharedPreferencesBackupHelper.java @@ -23,7 +23,9 @@ import android.util.Log; import java.io.File; import java.io.FileDescriptor; -/** @hide */ +/** + * STOPSHIP: document! + */ public class SharedPreferencesBackupHelper extends FileBackupHelperBase implements BackupHelper { private static final String TAG = "SharedPreferencesBackupHelper"; private static final boolean DEBUG = false; @@ -31,6 +33,13 @@ public class SharedPreferencesBackupHelper extends FileBackupHelperBase implemen private Context mContext; private String[] mPrefGroups; + /** + * Construct a helper for backing up and restoring the + * {@link android.content.SharedPreferences} under the given names. + * + * @param context + * @param prefGroups + */ public SharedPreferencesBackupHelper(Context context, String... prefGroups) { super(context); @@ -38,6 +47,9 @@ public class SharedPreferencesBackupHelper extends FileBackupHelperBase implemen mPrefGroups = prefGroups; } + /** + * Backs up the configured SharedPreferences groups + */ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) { Context context = mContext; @@ -54,6 +66,10 @@ public class SharedPreferencesBackupHelper extends FileBackupHelperBase implemen performBackup_checked(oldState, data, newState, files, prefGroups); } + /** + * Restores one entity from the restore data stream to its proper shared + * preferences file store. + */ public void restoreEntity(BackupDataInputStream data) { Context context = mContext; diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index a7a6df515faac..d43039d97effd 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -1083,15 +1083,11 @@ class BackupManagerService extends IBackupManager.Stub { synchronized(mClearDataLock) { mClearingData = true; - /* This is causing some critical processes to be killed during setup. - Temporarily revert this change until we find a better solution. try { mActivityManager.clearApplicationUserData(packageName, observer); } catch (RemoteException e) { // can't happen because the activity manager is in this process } - */ - mPackageManager.clearApplicationUserData(packageName, observer); // only wait 10 seconds for the clear data to happen long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; @@ -1660,10 +1656,6 @@ class BackupManagerService extends IBackupManager.Stub { + "] is compatible with installed version [" + packageInfo.versionCode + "]"); - // Now perform the actual restore: first clear the app's data - // if appropriate - clearApplicationDataSynchronous(packageName); - // Then set up and bind the agent (with a restricted Application object // unless the application says otherwise) boolean useRealApp = (packageInfo.applicationInfo.flags