Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4e14a829 authored by Christopher Tate's avatar Christopher Tate
Browse files

SDK: more backup/restore documentation work

Still not complete, but here's a lot more of the necessary documentation.
Submitting a checkpoint seems prudent.

Bug #2465360

Change-Id: Ifff60d57e4b24cee21f3a34f5f50e290d377c386
parent b9e3e4ce
Loading
Loading
Loading
Loading
+80 −20
Original line number Diff line number Diff line
@@ -29,18 +29,57 @@ import android.util.Log;
import java.io.IOException;

/**
 * This is the central interface between an application and Android's settings
 * backup mechanism. Any implementation of a backup agent should perform backup
 * and restore actions in
 * {@link android.app.backup.BackupAgent} is the central interface between an
 * application and Android's data backup infrastructure.  An application that wishes
 * to participate in the backup and restore mechanism will declare a subclass of
 * {@link android.app.backup.BackupAgent}, implement the
 * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
 * and {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor)}
 * respectively.
 * and {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor)} methods,
 * and provide the name of its agent class in the AndroidManifest.xml file via
 * the <application> tag's android:backupAgent attribute.
 * <p>
 * A backup agent based on convenient helper classes is available in
 * {@link android.app.backup.BackupAgentHelper} for less complex implementation
 * requirements.
 * <b>Basic Operation</b>
 * <p>
 * STOPSHIP write more documentation about the backup process here.
 * When the application makes changes to data that it wishes to keep backed up,
 * it should call the
 * {@link android.app.backup.BackupManager#dataChanged() BackupManager.dataChanged()} method.
 * This notifies the Android backup manager that the application needs an opportunity
 * to update its backup image.  The backup manager, in turn, will then schedule a
 * backup pass to be performed at an opportune time.
 * <p>
 * Restore operations are typically only performed when applications are first
 * installed on a device.  At that time, the operating system checks to see whether
 * there is a previously-saved data set available for the application, and if so,
 * begins an immediate restore pass to deliver that data as part of the installation
 * process.
 * <p>
 * When a backup or restore pass is run, the application's process will be launched
 * (if not already running), the manifest-declared agent class instantiated within
 * that process, and the agent's {@link #onCreate()} method invoked.  This prepares the
 * agent instance to run the actual backup or restore logic.  At this point the
 * agent's
 * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()} or
 * {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} method will be
 * invoked as appropriate for the operation being performed.
 * <p>
 * A backup data set consists of one or more "entities," flattened binary data records
 * that are each identified with a key string unique within the data set.  Adding a
 * record to the active data set, or updating an existing record, are done by simply
 * writing new entity data under the desired key.  Deleting an entity from the data set
 * is done by writing an entity under that key with header specifying a negative data
 * size, and no actual entity data.
 * <p>
 * <b>Helper Classes</b>
 * <p>
 * An extensible agent based on convenient helper classes is available in
 * {@link android.app.backup.BackupAgentHelper}.  That class is particularly
 * suited to handling of simple file or {@link android.content.SharedPreferences}
 * backup and restore.
 *
 * @see android.app.backup.BackupManager
 * @see android.app.backup.BackupAgentHelper
 * @see android.app.backup.BackupDataInput
 * @see android.app.backup.BackupDataOutput
 */
public abstract class BackupAgent extends ContextWrapper {
    private static final String TAG = "BackupAgent";
@@ -50,9 +89,22 @@ public abstract class BackupAgent extends ContextWrapper {
        super(null);
    }

    /**
     * Provided as a convenience for agent implementations that need an opportunity
     * to do one-time initialization before the actual backup or restore operation
     * is begun.
     * <p>
     * Agents do not need to override this method.
     */
    public void onCreate() {
    }

    /**
     * Provided as a convenience for agent implementations that need to do some
     * sort of shutdown process after backup or restore is completed.
     * <p>
     * Agents do not need to override this method.
     */
    public void onDestroy() {
    }

@@ -65,18 +117,26 @@ public abstract class BackupAgent extends ContextWrapper {
     * cases, a representation of the final backup state after this pass should
     * be written to the file pointed to by the file descriptor wrapped in
     * <code>newState</code>.
     * <p>
     * Each entity written to the {@link android.app.backup.BackupDataOutput}
     * <code>data</code> stream will be transmitted
     * over the current backup transport and stored in the remote data set under
     * the key supplied as part of the entity.  Writing an entity with a negative
     * data size instructs the transport to delete whatever entity currently exists
     * under that key from the remote data set.
     * 
     * @param oldState An open, read-only ParcelFileDescriptor pointing to the
     *            last backup state provided by the application. May be
     *            <code>null</code>, in which case no prior state is being
     *            provided and the application should perform a full backup.
     * @param data A structured wrapper around an open, read/write
     *            ParcelFileDescriptor pointing to the backup data destination.
     *            file descriptor 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.
     *            state here after writing the requested data to the <code>data</code>
     *            output stream.
     */
    public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
             ParcelFileDescriptor newState) throws IOException;
@@ -84,8 +144,7 @@ public abstract class BackupAgent extends ContextWrapper {
    /**
     * The application is being restored from backup and should replace any
     * existing data with the contents of the backup. The backup data is
     * provided in the file descriptor pointed to by the
     * {@link android.app.backup.BackupDataInput} instance <code>data</code>. Once
     * provided through the <code>data</code> parameter. Once
     * the restore is finished, the application should write a representation of
     * the final state to the <code>newState</code> file descriptor.
     * <p>
@@ -98,17 +157,18 @@ public abstract class BackupAgent extends ContextWrapper {
     * 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
     *            file descriptor pointing to a full snapshot of the
     *            application's data.  The application should consume every
     *            entity represented in this data stream.
     * @param appVersionCode The
     *            {@link android.R.styleable#AndroidManifest_versionCode android:versionCode}
     *            value of the application that backed up this particular data set. This
     *            makes it possible for an application's agent to distinguish among any
     *            possible older data versions when asked to perform the restore
     *            operation.
     * @param newState An open, read/write ParcelFileDescriptor pointing to an
     *            empty file. The application should record the final backup
     *            state here after restoring its data from dataFd.
     *            state here after restoring its data from the <code>data</code> stream.
     */
    public abstract void onRestore(BackupDataInput data, int appVersionCode,
            ParcelFileDescriptor newState)
+4 −1
Original line number Diff line number Diff line
/*
 * Copyright (C) 2007 The Android Open Source Project
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -30,6 +30,9 @@ import java.io.IOException;
 * have the BackupAgentHelper implementation process the data.
 * <p>
 * STOPSHIP: document!
 *
 * @see FileBackupHelper
 * @see SharedPreferencesBackupHelper
 */
public class BackupAgentHelper extends BackupAgent {
    static final String TAG = "BackupAgentHelper";
+55 −17
Original line number Diff line number Diff line
@@ -20,7 +20,41 @@ import java.io.FileDescriptor;
import java.io.IOException;

/**
 * STOPSHIP: document!
 * BackupDataInput is the structured interface used for passing the contents of
 * a backup data set to an application's {@link BackupAgent} class in its
 * {@link BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)}
 * method.  The data is presented as a set of "entities," each
 * representing one named record as previously stored by the agent's
 * {@link BackupAgent#onBackup(android.os.ParcelFileDescriptor, BackupDataOutput, android.os.ParcelFileDescriptor)}
 * implementation.  An entity is composed of a descriptive header plus a
 * byte array that holds its raw data.
 * <p>
 * The agent must consume every entity in the data stream, otherwise the
 * restored state of the application will be incomplete.
 * <p>
 * <b>Example</b>
 * <p>
 * A typical
 * {@link BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) BackupAgent.onRestore(data, appVersionCode, newState)}
 * implementation might be structured something like this:
 * <pre>
 * while (data.readNextHeader()) {
 *     String key = data.getKey();
 *     int dataSize = data.getDataSize();
 *
 *     if (key.equals(MY_BACKUP_KEY_ONE)) {
 *         // process this kind of record here
 *         byte[] buffer = new byte[dataSize];
 *         data.readEntityData(buffer, 0, dataSize); // reads the entire entity at once
 *
 *         // now 'buffer' holds the raw data and can be processed however
 *         // the agent wishes
 *         processBackupKeyOne(buffer);
 *     } else if (key.equals(MY_BACKUP_KEY_TO_IGNORE) {
 *         // a key we recognize but wish to discard
 *         data.skipEntityData();
 *     } // ... etc.
 * }</pre>
 */
public class BackupDataInput {
    int mBackupReader;
@@ -52,10 +86,12 @@ public class BackupDataInput {
    }

    /**
     * Consumes the next header from the restore stream.
     * Extract the next entity header from the restore stream.  After this method
     * return success, the {@link #getKey()} and {@link #getDataSize()} methods can
     * be used to inspect the entity that is now available for processing.
     *
     * @return true when there is an entity ready for consumption from the restore stream,
     *    false if the restore stream has been fully consumed.
     * @return <code>true</code> when there is an entity ready for consumption from the
     *    restore stream, <code>false</code> if the restore stream has been fully consumed.
     * @throws IOException if an error occurred while reading the restore stream
     */
    public boolean readNextHeader() throws IOException {
@@ -71,25 +107,25 @@ public class BackupDataInput {
        } else {
            // error
            mHeaderReady = false;
            throw new IOException("result=0x" + Integer.toHexString(result));
            throw new IOException("failed: 0x" + Integer.toHexString(result));
        }
    }

    /**
     * Report the key associated with the current record in the restore stream
     * @return the current record's key string
     * Report the key associated with the current entity in the restore stream
     * @return the current entity's key string
     * @throws IllegalStateException if the next record header has not yet been read
     */
    public String getKey() {
        if (mHeaderReady) {
            return mHeader.key;
        } else {
            throw new IllegalStateException("mHeaderReady=false");
            throw new IllegalStateException("Entity header not read");
        }
    }

    /**
     * Report the size in bytes of the data associated with the current record in the
     * Report the size in bytes of the data associated with the current entity in the
     * restore stream.
     *
     * @return The size of the record's raw data, in bytes
@@ -99,7 +135,7 @@ public class BackupDataInput {
        if (mHeaderReady) {
            return mHeader.dataSize;
        } else {
            throw new IllegalStateException("mHeaderReady=false");
            throw new IllegalStateException("Entity header not read");
        }
    }

@@ -107,13 +143,15 @@ 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.
     * must be read in a single call.  Once all of the raw data for the current entity
     * has been read, further calls to this method will simply return zero.
     *
     * @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
     *    when read from the stream
     * @param size The number of bytes to read in this pass
     * @return The number of bytes of data read.  Once all of the data for this entity
     *    has been read, further calls to this method will return zero.
     * @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 {
@@ -125,12 +163,12 @@ public class BackupDataInput {
                throw new IOException("result=0x" + Integer.toHexString(result));
            }
        } else {
            throw new IllegalStateException("mHeaderReady=false");
            throw new IllegalStateException("Entity header not read");
        }
    }

    /**
     * Consume the current record's data without actually reading it into a buffer
     * Consume the current entity's data without extracting it into a buffer
     * for further processing.  This allows a {@link android.app.backup.BackupAgent} to
     * efficiently discard obsolete or otherwise uninteresting records during the
     * restore operation.
@@ -141,7 +179,7 @@ public class BackupDataInput {
        if (mHeaderReady) {
            skipEntityData_native(mBackupReader);
        } else {
            throw new IllegalStateException("mHeaderReady=false");
            throw new IllegalStateException("Entity header not read");
        }
    }

+39 −1
Original line number Diff line number Diff line
@@ -16,11 +16,49 @@

package android.app.backup;

import android.os.ParcelFileDescriptor;

import java.io.FileDescriptor;
import java.io.IOException;

/**
 * STOPSHIP: document 
 * This class is the structured conduit through which a {@link BackupAgent} commits
 * information to the current backup data set.  Data written for backup is presented
 * as a set of "entities," key/value pairs in which each binary data record "value" is
 * named with a string "key."
 * <p>
 * To commit a data record to the backup transport, the agent's
 * {@link BackupAgent#onBackup(android.os.ParcelFileDescriptor, BackupDataOutput, android.os.ParcelFileDescriptor)}
 * method first writes an "entity header" that supplies the key string for the record
 * and the total size of the binary value for the record.  After the header has been
 * written the agent then writes the binary entity value itself.  The entity value can
 * be written in multiple chunks if desired, as long as the total count of bytes written
 * matches what was supplied to {@link #writeEntityHeader(String, int)}.
 * <p>
 * Entity key strings are considered to be unique within a given application's backup
 * data set.  If a new entity is written under an existing key string, its value will
 * replace any previous value in the transport's remote data store.  A record can be
 * removed entirely from the remote data set by writing a new entity header using the
 * existing record's key, but supplying a negative <code>dataSize</code> parameter.
 * When doing this the agent does not need to call {@link #writeEntityData(byte[], int)}.
 * <p>
 * <b>Example</b>
 * <p>
 * Here is an example illustrating a way to back up the value of a String variable
 * called <code>mStringToBackUp</code>:
 * <pre>
 * static final String MY_STRING_KEY = "storedstring";
 *
 * public void {@link BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)}
 *         throws IOException {
 *     ...
 *     byte[] stringBytes = mStringToBackUp.getBytes();
 *     data.writeEntityHeader(MY_STRING_KEY, stringBytes.length);
 *     data.writeEntityData(stringBytes, stringBytes.length);
 *     ...
 * }</pre>
 *
 * @see BackupAgent
 */
public class BackupDataOutput {
    int mBackupWriter;
+4 −5
Original line number Diff line number Diff line
@@ -20,10 +20,10 @@ import android.os.ParcelFileDescriptor;

/**
 * A convenient interface to be used with the
 * {@link android.app.backup.BackupAgentHelper} to implement backup and restore of
 * {@link android.app.backup.BackupAgentHelper} class to implement backup and restore of
 * arbitrary data types.
 * <p>
 * STOPSHOP: document!
 * STOPSHIP: document!
 */
public interface BackupHelper {
    /**
@@ -46,9 +46,8 @@ public interface BackupHelper {

    /**
     * Called by {@link android.app.backup.BackupAgentHelper BackupAgentHelper}
     * 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.
     * after a restore operation to write the backup state file corresponding to
     * the data as processed by the helper.
     */
    public void writeNewStateDescription(ParcelFileDescriptor fd);
}
Loading