M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions

View File

@ -0,0 +1,75 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.File;
/**
* Like FileBackupHelper, but takes absolute paths for the files instead of
* subpaths of getFilesDir()
*
* @hide
*/
public class AbsoluteFileBackupHelper extends FileBackupHelperBase implements BackupHelper {
private static final String TAG = "AbsoluteFileBackupHelper";
private static final boolean DEBUG = false;
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);
mContext = context;
mFiles = files;
}
/**
* Based on oldState, determine which of the files from the application's data directory
* need to be backed up, write them to the data stream, and fill in newState with the
* state as it exists now.
*/
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) {
// use the file paths as the keys, too
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();
if (isKeyInList(key, mFiles)) {
File f = new File(key);
writeFile(f, data);
}
}
}

View File

@ -0,0 +1,252 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.app.IBackupAgent;
import android.app.backup.IBackupManager;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;
import java.io.IOException;
/**
* Provides 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) onBackup()}
* and {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} methods,
* and provide the name of its backup agent class in its {@code AndroidManifest.xml} file via
* the <code><a
* href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a></code>
* tag's {@code android:backupAgent} attribute.
* <h3>Basic Operation</h3>
* <p>
* 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, schedules a
* backup pass to be performed at an opportune time.
* <p>
* Restore operations are typically performed only 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 being installed, and if so,
* begins an immediate restore pass to deliver the backup data as part of the installation
* process.
* <p>
* When a backup or restore pass is run, the application's process is launched
* (if not already running), the manifest-declared backup agent class (in the {@code
* android:backupAgent} attribute) is instantiated within
* that process, and the agent's {@link #onCreate()} method is 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 is 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";
private static final boolean DEBUG = false;
public BackupAgent() {
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() {
}
/**
* The application is being asked to write any data changed since the last
* time it performed a backup operation. The state data recorded during the
* last backup pass is provided in the <code>oldState</code> file
* descriptor. If <code>oldState</code> is <code>null</code>, no old state
* is available and the application should perform a full backup. In both
* 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
* 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 the <code>data</code>
* output stream.
*/
public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException;
/**
* The application is being restored from backup and should replace any
* existing data with the contents of the backup. The backup data is
* 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>
* 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
* 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 value of the <a
* href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code
* android:versionCode}</a> manifest attribute,
* from 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 the <code>data</code> stream.
*/
public abstract void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState)
throws IOException;
// ----- Core implementation -----
/** @hide */
public final IBinder onBind() {
return mBinder;
}
private final IBinder mBinder = new BackupServiceBinder().asBinder();
/** @hide */
public void attach(Context context) {
attachBaseContext(context);
}
// ----- IBackupService binder interface -----
private class BackupServiceBinder extends IBackupAgent.Stub {
private static final String TAG = "BackupServiceBinder";
public void doBackup(ParcelFileDescriptor oldState,
ParcelFileDescriptor data,
ParcelFileDescriptor newState,
int token, IBackupManager callbackBinder) throws RemoteException {
// Ensure that we're running with the app's normal permission level
long ident = Binder.clearCallingIdentity();
if (DEBUG) Log.v(TAG, "doBackup() invoked");
BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
try {
BackupAgent.this.onBackup(oldState, output, newState);
} 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
}
}
}
public void doRestore(ParcelFileDescriptor data, int appVersionCode,
ParcelFileDescriptor newState,
int token, IBackupManager callbackBinder) throws RemoteException {
// Ensure that we're running with the app's normal permission level
long ident = Binder.clearCallingIdentity();
if (DEBUG) Log.v(TAG, "doRestore() invoked");
BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
try {
BackupAgent.this.onRestore(input, appVersionCode, newState);
} catch (IOException ex) {
Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw new RuntimeException(ex);
} catch (RuntimeException ex) {
Log.d(TAG, "onRestore (" + 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
}
}
}
}
}

View File

@ -0,0 +1,91 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.os.ParcelFileDescriptor;
import java.io.IOException;
/**
* A convenient {@link BackupAgent} wrapper class that automatically manages
* heterogeneous data sets within the backup data, each identified by a unique
* key prefix. When processing a backup or restore operation, the BackupAgentHelper
* dispatches to one or more installed {@link BackupHelper} objects, each
* of which is responsible for a defined subset of the data being processed.
* <p>
* An application will typically extend this class in its own
* backup agent. Then, within the agent's {@link BackupAgent#onCreate() onCreate()}
* method, it will call {@link #addHelper(String, BackupHelper) addHelper()} one or more times to
* install the handlers for each kind of data it wishes to manage within its backups.
* <p>
* The Android framework currently provides two predefined {@link BackupHelper} classes:</p>
* <ul><li>{@link FileBackupHelper} - Manages the backup and restore of entire files
* within an application's data directory hierarchy.</li>
* <li>{@link SharedPreferencesBackupHelper} - Manages the backup and restore of an
* application's {@link android.content.SharedPreferences} data.</li></ul>
* <p>
* An application can also implement its own helper classes to work within the
* {@link BackupAgentHelper} framework. See the {@link BackupHelper} interface
* documentation for details.
*
* @see BackupHelper
* @see FileBackupHelper
* @see SharedPreferencesBackupHelper
*/
public class BackupAgentHelper extends BackupAgent {
static final String TAG = "BackupAgentHelper";
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);
}
}

View File

@ -0,0 +1,194 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import java.io.FileDescriptor;
import java.io.IOException;
/**
* Provides the structured interface through which a {@link BackupAgent} reads
* information from the backup data set, via its
* {@link BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) onRestore()}
* 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(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
* onBackup()} implementation. An entity is composed of a descriptive header plus a
* byte array that holds the raw data saved in the remote backup.
* <p>
* The agent must consume every entity in the data stream, otherwise the
* restored state of the application will be incomplete.
* <h3>Example</h3>
* <p>
* A typical
* {@link BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
* onRestore()} implementation might be structured something like this:
* <pre>
* public void onRestore(BackupDataInput data, int appVersionCode,
* ParcelFileDescriptor newState) {
* 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;
private EntityHeader mHeader = new EntityHeader();
private boolean mHeaderReady;
private static class EntityHeader {
String key;
int dataSize;
}
/** @hide */
public BackupDataInput(FileDescriptor fd) {
if (fd == null) throw new NullPointerException();
mBackupReader = ctor(fd);
if (mBackupReader == 0) {
throw new RuntimeException("Native initialization failed with fd=" + fd);
}
}
/** @hide */
protected void finalize() throws Throwable {
try {
dtor(mBackupReader);
} finally {
super.finalize();
}
}
/**
* 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 <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 {
int result = readNextHeader_native(mBackupReader, mHeader);
if (result == 0) {
// read successfully
mHeaderReady = true;
return true;
} else if (result > 0) {
// done
mHeaderReady = false;
return false;
} else {
// error
mHeaderReady = false;
throw new IOException("failed: 0x" + Integer.toHexString(result));
}
}
/**
* 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("Entity header not read");
}
}
/**
* 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
* @throws IllegalStateException if the next record header has not yet been read
*/
public int getDataSize() {
if (mHeaderReady) {
return mHeader.dataSize;
} else {
throw new IllegalStateException("Entity header not read");
}
}
/**
* 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. 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. 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 {
if (mHeaderReady) {
int result = readEntityData_native(mBackupReader, data, offset, size);
if (result >= 0) {
return result;
} else {
throw new IOException("result=0x" + Integer.toHexString(result));
}
} else {
throw new IllegalStateException("Entity header not read");
}
}
/**
* 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.
*
* @throws IOException if an error occurred when trying to read the restore data stream
*/
public void skipEntityData() throws IOException {
if (mHeaderReady) {
skipEntityData_native(mBackupReader);
} else {
throw new IllegalStateException("Entity header not read");
}
}
private native static int ctor(FileDescriptor fd);
private native static void dtor(int mBackupReader);
private native int readNextHeader_native(int mBackupReader, EntityHeader entity);
private native int readEntityData_native(int mBackupReader, byte[] data, int offset, int size);
private native int skipEntityData_native(int mBackupReader);
}

View File

@ -0,0 +1,118 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import java.io.InputStream;
import java.io.IOException;
/**
* Provides an {@link java.io.InputStream}-like interface for accessing an
* entity's data during a restore operation. Used by {@link BackupHelper} classes within the {@link
* BackupAgentHelper} mechanism.
* <p>
* When {@link BackupHelper#restoreEntity(BackupDataInputStream) BackupHelper.restoreEntity()}
* is called, the current entity's header has already been read from the underlying
* {@link BackupDataInput}. The entity's key string and total data size are available
* through this class's {@link #getKey()} and {@link #size()} methods, respectively.
* <p class="note">
* <strong>Note:</strong> The caller should take care not to seek or close the underlying data
* source, nor read more than {@link #size()} bytes from the stream.</p>
*
* @see BackupAgentHelper
* @see BackupHelper
*/
public class BackupDataInputStream extends InputStream {
String key;
int dataSize;
BackupDataInput mData;
byte[] mOneByte;
/** @hide */
BackupDataInputStream(BackupDataInput data) {
mData = data;
}
/**
* Read one byte of entity data from the stream, returning it as
* an integer value. If more than {@link #size()} bytes of data
* are read from the stream, the output of this method is undefined.
*
* @return The byte read, or undefined if the end of the stream has been reached.
*/
public int read() throws IOException {
byte[] one = mOneByte;
if (mOneByte == null) {
one = mOneByte = new byte[1];
}
mData.readEntityData(one, 0, 1);
return one[0];
}
/**
* Read up to {@code size} bytes of data into a byte array, beginning at position
* {@code offset} within the array.
*
* @param b Byte array into which the data will be read
* @param offset The data will be stored in {@code b} beginning at this index
* within the array.
* @param size The number of bytes to read in this operation. If insufficient
* data exists within the entity to fulfill this request, only as much data
* will be read as is available.
* @return The number of bytes of data read, or zero if all of the entity's
* data has already been read.
*/
public int read(byte[] b, int offset, int size) throws IOException {
return mData.readEntityData(b, offset, size);
}
/**
* Read enough entity data into a byte array to fill the array.
*
* @param b Byte array to fill with data from the stream. If the stream does not
* have sufficient data to fill the array, then the contents of the remainder of
* the array will be undefined.
* @return The number of bytes of data read, or zero if all of the entity's
* data has already been read.
*/
public int read(byte[] b) throws IOException {
return mData.readEntityData(b, 0, b.length);
}
/**
* Report the key string associated with this entity within the backup data set.
*
* @return The key string for this entity, equivalent to calling
* {@link BackupDataInput#getKey()} on the underlying {@link BackupDataInput}.
*/
public String getKey() {
return this.key;
}
/**
* Report the total number of bytes of data available for the current entity.
*
* @return The number of data bytes available, equivalent to calling
* {@link BackupDataInput#getDataSize()} on the underlying {@link BackupDataInput}.
*/
public int size() {
return this.dataSize;
}
}

View File

@ -0,0 +1,131 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.os.ParcelFileDescriptor;
import java.io.FileDescriptor;
import java.io.IOException;
/**
* Provides the structured interface through which a {@link BackupAgent} commits
* information to the backup data set, via its {@link
* BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
* onBackup()} method. 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(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
* onBackup()} 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) writeEntityHeader()}.
* <p>
* Entity key strings are considered to be unique within a given application's backup
* data set. If a backup agent writes a new entity under an existing key string, its value will
* replace any previous value in the transport's remote data store. You can remove a record
* 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 you do so, the agent does not need to call {@link #writeEntityData(byte[], int)}.
* <h3>Example</h3>
* <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;
/** @hide */
public BackupDataOutput(FileDescriptor fd) {
if (fd == null) throw new NullPointerException();
mBackupWriter = ctor(fd);
if (mBackupWriter == 0) {
throw new RuntimeException("Native initialization failed with fd=" + fd);
}
}
/**
* Mark the beginning of one record in the backup data stream. This must be called before
* {@link #writeEntityData}.
* @param key A string key that uniquely identifies the data record within the application
* @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) {
return result;
} else {
throw new IOException("result=0x" + Integer.toHexString(result));
}
}
/**
* 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) {
return result;
} else {
throw new IOException("result=0x" + Integer.toHexString(result));
}
}
/** @hide */
public void setKeyPrefix(String keyPrefix) {
setKeyPrefix_native(mBackupWriter, keyPrefix);
}
/** @hide */
protected void finalize() throws Throwable {
try {
dtor(mBackupWriter);
} finally {
super.finalize();
}
}
private native static int ctor(FileDescriptor fd);
private native static void dtor(int mBackupWriter);
private native static int writeEntityHeader_native(int mBackupWriter, String key, int dataSize);
private native static int writeEntityData_native(int mBackupWriter, byte[] data, int size);
private native static void setKeyPrefix_native(int mBackupWriter, String keyPrefix);
}

View File

@ -0,0 +1,105 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.os.ParcelFileDescriptor;
/**
* Defines the calling interface that {@link BackupAgentHelper} uses
* when dispatching backup and restore operations to the installed helpers.
* Applications can define and install their own helpers as well as using those
* provided as part of the Android framework.
* <p>
* Although multiple helper objects may be installed simultaneously, each helper
* is responsible only for handling its own data, and will not see entities
* created by other components within the backup system. Invocations of multiple
* helpers are performed sequentially by the {@link BackupAgentHelper}, with each
* helper given a chance to access its own saved state from within the state record
* produced during the previous backup operation.
*
* @see BackupAgentHelper
* @see FileBackupHelper
* @see SharedPreferencesBackupHelper
*/
public interface BackupHelper {
/**
* Based on <code>oldState</code>, determine which of the files from the
* application's data directory need to be backed up, write them to
* <code>data</code>, and fill in <code>newState</code> with the state as it
* exists now.
* <p>
* Implementing this method is much like implementing
* {@link BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)
* onBackup()} &mdash; the method parameters are the same. When this method is invoked the
* {@code oldState} descriptor points to the beginning of the state data
* written during this helper's previous backup operation, and the {@code newState}
* descriptor points to the file location at which the helper should write its
* new state after performing the backup operation.
* <p class="note">
* <strong>Note:</strong> The helper should not close or seek either the {@code oldState} or
* the {@code newState} file descriptors.</p>
*
* @param oldState An open, read-only {@link android.os.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 An open, read/write {@link BackupDataOutput}
* 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 {@link android.os.ParcelFileDescriptor} pointing to an
* empty file. The application should record the final backup
* state here after writing the requested data to the <code>data</code>
* output stream.
*/
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState);
/**
* Called by {@link android.app.backup.BackupAgentHelper BackupAgentHelper}
* to restore a single entity from the restore data set. This method will be
* called for each entity in the data set that belongs to this handler.
* <p class="note">
* <strong>Note:</strong> Do not close the <code>data</code> stream. Do not read more than
* {@link android.app.backup.BackupDataInputStream#size() size()} bytes from
* <code>data</code>.</p>
*
* @param data An open {@link BackupDataInputStream} from which the backup data can be read.
*/
public void restoreEntity(BackupDataInputStream data);
/**
* Called by {@link android.app.backup.BackupAgentHelper BackupAgentHelper}
* after a restore operation to write the backup state file corresponding to
* the data as processed by the helper. The data written here will be
* available to the helper during the next call to its
* {@link #performBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)
* performBackup()} method.
* <p>
* This method will be called even if the handler's
* {@link #restoreEntity(BackupDataInputStream) restoreEntity()} method was never invoked during
* the restore operation.
* <p class="note">
* <strong>Note:</strong> The helper should not close or seek the {@code newState}
* file descriptor.</p>
*
* @param newState A {@link android.os.ParcelFileDescriptor} to which the new state will be
* written.
*/
public void writeNewStateDescription(ParcelFileDescriptor newState);
}

View File

@ -0,0 +1,149 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;
/** @hide */
public class BackupHelperDispatcher {
private static final String TAG = "BackupHelperDispatcher";
private static class Header {
int chunkSize; // not including the header
String keyPrefix;
}
TreeMap<String,BackupHelper> mHelpers = new TreeMap<String,BackupHelper>();
public BackupHelperDispatcher() {
}
public void addHelper(String keyPrefix, BackupHelper helper) {
mHelpers.put(keyPrefix, helper);
}
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException {
// First, do the helpers that we've already done, since they're already in the state
// file.
int err;
Header header = new Header();
TreeMap<String,BackupHelper> helpers = (TreeMap<String,BackupHelper>)mHelpers.clone();
FileDescriptor oldStateFD = null;
FileDescriptor newStateFD = newState.getFileDescriptor();
if (oldState != null) {
oldStateFD = oldState.getFileDescriptor();
while ((err = readHeader_native(header, oldStateFD)) >= 0) {
if (err == 0) {
BackupHelper helper = helpers.get(header.keyPrefix);
Log.d(TAG, "handling existing helper '" + header.keyPrefix + "' " + helper);
if (helper != null) {
doOneBackup(oldState, data, newState, header, helper);
helpers.remove(header.keyPrefix);
} else {
skipChunk_native(oldStateFD, header.chunkSize);
}
}
}
}
// Then go through and do the rest that we haven't done.
for (Map.Entry<String,BackupHelper> entry: helpers.entrySet()) {
header.keyPrefix = entry.getKey();
Log.d(TAG, "handling new helper '" + header.keyPrefix + "'");
BackupHelper helper = entry.getValue();
doOneBackup(oldState, data, newState, header, helper);
}
}
private void doOneBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState, Header header, BackupHelper helper)
throws IOException {
int err;
FileDescriptor newStateFD = newState.getFileDescriptor();
// allocate space for the header in the file
int pos = allocateHeader_native(header, newStateFD);
if (pos < 0) {
throw new IOException("allocateHeader_native failed (error " + pos + ")");
}
data.setKeyPrefix(header.keyPrefix);
// do the backup
helper.performBackup(oldState, data, newState);
// fill in the header (seeking back to pos). The file pointer will be returned to
// where it was at the end of performBackup. Header.chunkSize will not be filled in.
err = writeHeader_native(header, newStateFD, pos);
if (err != 0) {
throw new IOException("writeHeader_native failed (error " + err + ")");
}
}
public void performRestore(BackupDataInput input, int appVersionCode,
ParcelFileDescriptor newState)
throws IOException {
boolean alreadyComplained = false;
BackupDataInputStream stream = new BackupDataInputStream(input);
while (input.readNextHeader()) {
String rawKey = input.getKey();
int pos = rawKey.indexOf(':');
if (pos > 0) {
String prefix = rawKey.substring(0, pos);
BackupHelper helper = mHelpers.get(prefix);
if (helper != null) {
stream.dataSize = input.getDataSize();
stream.key = rawKey.substring(pos+1);
helper.restoreEntity(stream);
} else {
if (!alreadyComplained) {
Log.w(TAG, "Couldn't find helper for: '" + rawKey + "'");
alreadyComplained = true;
}
}
} else {
if (!alreadyComplained) {
Log.w(TAG, "Entity with no prefix: '" + rawKey + "'");
alreadyComplained = true;
}
}
input.skipEntityData(); // In case they didn't consume the data.
}
// Write out the state files -- mHelpers is a TreeMap, so the order is well defined.
for (BackupHelper helper: mHelpers.values()) {
helper.writeNewStateDescription(newState);
}
}
private static native int readHeader_native(Header h, FileDescriptor fd);
private static native int skipChunk_native(FileDescriptor fd, int bytesToSkip);
private static native int allocateHeader_native(Header h, FileDescriptor fd);
private static native int writeHeader_native(Header h, FileDescriptor fd, int pos);
}

View File

@ -0,0 +1,175 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.app.backup.RestoreSession;
import android.app.backup.IBackupManager;
import android.app.backup.IRestoreSession;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
/**
* The interface through which an application interacts with the Android backup service to
* request backup and restore operations.
* Applications instantiate it using the constructor and issue calls through that instance.
* <p>
* When an application has made changes to data which should be backed up, a
* call to {@link #dataChanged()} will notify the backup service. The system
* will then schedule a backup operation to occur in the near future. Repeated
* calls to {@link #dataChanged()} have no further effect until the backup
* operation actually occurs.
* <p>
* A backup or restore operation for your application begins when the system launches the
* {@link android.app.backup.BackupAgent} subclass you've declared in your manifest. See the
* documentation for {@link android.app.backup.BackupAgent} for a detailed description
* of how the operation then proceeds.
* <p>
* Several attributes affecting the operation of the backup and restore mechanism
* can be set on the <code><a
* href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a></code>
* tag in your application's AndroidManifest.xml file.
*
* @attr ref android.R.styleable#AndroidManifestApplication_allowBackup
* @attr ref android.R.styleable#AndroidManifestApplication_backupAgent
* @attr ref android.R.styleable#AndroidManifestApplication_killAfterRestore
* @attr ref android.R.styleable#AndroidManifestApplication_restoreAnyVersion
*/
public class BackupManager {
private static final String TAG = "BackupManager";
private Context mContext;
private static IBackupManager sService;
private static void checkServiceBinder() {
if (sService == null) {
sService = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
}
}
/**
* Constructs a BackupManager object through which the application can
* communicate with the Android backup system.
*
* @param context The {@link android.content.Context} that was provided when
* one of your application's {@link android.app.Activity Activities}
* was created.
*/
public BackupManager(Context context) {
mContext = context;
}
/**
* Notifies the Android backup system that your application wishes to back up
* new changes to its data. A backup operation using your application's
* {@link android.app.backup.BackupAgent} subclass will be scheduled when you
* call this method.
*/
public void dataChanged() {
checkServiceBinder();
if (sService != null) {
try {
sService.dataChanged(mContext.getPackageName());
} catch (RemoteException e) {
Log.d(TAG, "dataChanged() couldn't connect");
}
}
}
/**
* Convenience method for callers who need to indicate that some other package
* needs a backup pass. This can be useful in the case of groups of packages
* that share a uid.
* <p>
* This method requires that the application hold the "android.permission.BACKUP"
* permission if the package named in the argument does not run under the same uid
* as the caller.
*
* @param packageName The package name identifying the application to back up.
*/
public static void dataChanged(String packageName) {
checkServiceBinder();
if (sService != null) {
try {
sService.dataChanged(packageName);
} catch (RemoteException e) {
Log.d(TAG, "dataChanged(pkg) couldn't connect");
}
}
}
/**
* Restore the calling application from backup. The data will be restored from the
* current backup dataset if the application has stored data there, or from
* the dataset used during the last full device setup operation if the current
* backup dataset has no matching data. If no backup data exists for this application
* in either source, a nonzero value will be returned.
*
* <p>If this method returns zero (meaning success), the OS will attempt to retrieve
* a backed-up dataset from the remote transport, instantiate the application's
* backup agent, and pass the dataset to the agent's
* {@link android.app.backup.BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) onRestore()}
* method.
*
* @param observer The {@link RestoreObserver} to receive callbacks during the restore
* operation. This must not be null.
*
* @return Zero on success; nonzero on error.
*/
public int requestRestore(RestoreObserver observer) {
int result = -1;
checkServiceBinder();
if (sService != null) {
RestoreSession session = null;
try {
IRestoreSession binder = sService.beginRestoreSession(mContext.getPackageName(),
null);
session = new RestoreSession(mContext, binder);
result = session.restorePackage(mContext.getPackageName(), observer);
} catch (RemoteException e) {
Log.w(TAG, "restoreSelf() unable to contact service");
} finally {
if (session != null) {
session.endRestoreSession();
}
}
}
return result;
}
/**
* Begin the process of restoring data from backup. See the
* {@link android.app.backup.RestoreSession} class for documentation on that process.
* @hide
*/
public RestoreSession beginRestoreSession() {
RestoreSession session = null;
checkServiceBinder();
if (sService != null) {
try {
// All packages, current transport
IRestoreSession binder = sService.beginRestoreSession(null, null);
session = new RestoreSession(mContext, binder);
} catch (RemoteException e) {
Log.w(TAG, "beginRestoreSession() couldn't connect");
}
}
return session;
}
}

View File

@ -0,0 +1,103 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.File;
/**
* A helper class that can be used in conjunction with
* {@link android.app.backup.BackupAgentHelper} to manage the backup of a set of
* files. Whenever backup is performed, all files changed since the last backup
* will be saved in their entirety. When backup first occurs,
* every file in the list provided to {@link #FileBackupHelper} will be backed up.
* <p>
* During restore, if the helper encounters data for a file that was not
* specified when the FileBackupHelper object was constructed, that data
* will be ignored.
* <p class="note"><strong>Note:</strong> This should be
* used only with small configuration files, not large binary files.
*/
public class FileBackupHelper extends FileBackupHelperBase implements BackupHelper {
private static final String TAG = "FileBackupHelper";
private static final boolean DEBUG = false;
Context mContext;
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);
mContext = context;
mFilesDir = context.getFilesDir();
mFiles = files;
}
/**
* Based on <code>oldState</code>, determine which of the files from the
* application's data directory need to be backed up, write them to the data
* stream, and fill in <code>newState</code> with the state as it exists
* now. When <code>oldState</code> is <code>null</code>, all the files will
* be backed up.
* <p>
* This should only be called directly from within the {@link BackupAgentHelper}
* implementation. See
* {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
* for a description of parameter meanings.
*/
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) {
// file names
String[] files = mFiles;
File base = mContext.getFilesDir();
final int N = files.length;
String[] fullPaths = new String[N];
for (int i=0; i<N; i++) {
fullPaths[i] = (new File(base, files[i])).getAbsolutePath();
}
// go
performBackup_checked(oldState, data, newState, fullPaths, files);
}
/**
* Restore one record [representing a single file] from the restore dataset.
* <p>
* This should only be called directly from within the {@link BackupAgentHelper}
* implementation.
*/
public void restoreEntity(BackupDataInputStream data) {
if (DEBUG) Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size());
String key = data.getKey();
if (isKeyInList(key, mFiles)) {
File f = new File(mFilesDir, key);
writeFile(f, data);
}
}
}

View File

@ -0,0 +1,127 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.File;
import java.io.FileDescriptor;
/**
* Base class for the {@link android.app.backup.FileBackupHelper} implementation.
*/
class FileBackupHelperBase {
private static final String TAG = "FileBackupHelperBase";
int mPtr;
Context mContext;
boolean mExceptionLogged;
FileBackupHelperBase(Context context) {
mPtr = ctor();
mContext = context;
}
protected void finalize() throws Throwable {
try {
dtor(mPtr);
} finally {
super.finalize();
}
}
/**
* Check the parameters so the native code doesn't have to throw all the exceptions
* since it's easier to do that from Java.
*/
static void performBackup_checked(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState, String[] files, String[] keys) {
if (files.length == 0) {
return;
}
// files must be all absolute paths
for (String f: files) {
if (f.charAt(0) != '/') {
throw new RuntimeException("files must have all absolute paths: " + f);
}
}
// the length of files and keys must be the same
if (files.length != keys.length) {
throw new RuntimeException("files.length=" + files.length
+ " keys.length=" + keys.length);
}
// oldStateFd can be null
FileDescriptor oldStateFd = oldState != null ? oldState.getFileDescriptor() : null;
FileDescriptor newStateFd = newState.getFileDescriptor();
if (newStateFd == null) {
throw new NullPointerException();
}
int err = performBackup_native(oldStateFd, data.mBackupWriter, newStateFd, files, keys);
if (err != 0) {
// TODO: more here
throw new RuntimeException("Backup failed 0x" + Integer.toHexString(err));
}
}
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(), in.mData.mBackupReader);
if (result != 0) {
// Bail on this entity. Only log one failure per helper object.
if (!mExceptionLogged) {
Log.e(TAG, "Failed restoring file '" + f + "' for app '"
+ mContext.getPackageName() + "\' result=0x"
+ Integer.toHexString(result));
mExceptionLogged = true;
}
}
}
public void writeNewStateDescription(ParcelFileDescriptor fd) {
int result = writeSnapshot_native(mPtr, fd.getFileDescriptor());
// TODO: Do something with the error.
}
boolean isKeyInList(String key, String[] list) {
for (String s: list) {
if (s.equals(key)) {
return true;
}
}
return false;
}
private static native int ctor();
private static native void dtor(int ptr);
native private static int performBackup_native(FileDescriptor oldState,
int data, FileDescriptor newState, String[] files, String[] keys);
private static native int writeFile_native(int ptr, String filename, int backupReader);
private static native int writeSnapshot_native(int ptr, FileDescriptor fd);
}

View File

@ -0,0 +1,176 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.app.backup.IRestoreSession;
/**
* Direct interface to the Backup Manager Service that applications invoke on. The only
* operation currently needed is a simple notification that the app has made changes to
* data it wishes to back up, so the system should run a backup pass.
*
* Apps will use the {@link android.app.backup.BackupManager} class rather than going through
* this Binder interface directly.
*
* {@hide}
*/
interface IBackupManager {
/**
* Tell the system service that the caller has made changes to its
* data, and therefore needs to undergo an incremental backup pass.
*
* Any application can invoke this method for its own package, but
* only callers who hold the android.permission.BACKUP permission
* may invoke it for arbitrary packages.
*/
void dataChanged(String packageName);
/**
* Erase all backed-up data for the given package from the storage
* destination.
*
* Any application can invoke this method for its own package, but
* only callers who hold the android.permission.BACKUP permission
* may invoke it for arbitrary packages.
*/
void clearBackupData(String packageName);
/**
* Notifies the Backup Manager Service that an agent has become available. This
* method is only invoked by the Activity Manager.
*/
void agentConnected(String packageName, IBinder agent);
/**
* Notify the Backup Manager Service that an agent has unexpectedly gone away.
* This method is only invoked by the Activity Manager.
*/
void agentDisconnected(String packageName);
/**
* Notify the Backup Manager Service that an application being installed will
* need a data-restore pass. This method is only invoked by the Package Manager.
*/
void restoreAtInstall(String packageName, int token);
/**
* Enable/disable the backup service entirely. When disabled, no backup
* or restore operations will take place. Data-changed notifications will
* still be observed and collected, however, so that changes made while the
* mechanism was disabled will still be backed up properly if it is enabled
* at some point in the future.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
*/
void setBackupEnabled(boolean isEnabled);
/**
* Enable/disable automatic restore of application data at install time. When
* enabled, installation of any package will involve the Backup Manager. If data
* exists for the newly-installed package, either from the device's current [enabled]
* backup dataset or from the restore set used in the last wholesale restore operation,
* that data will be supplied to the new package's restore agent before the package
* is made generally available for launch.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
*
* @param doAutoRestore When true, enables the automatic app-data restore facility. When
* false, this facility will be disabled.
*/
void setAutoRestore(boolean doAutoRestore);
/**
* Indicate that any necessary one-time provisioning has occurred.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
*/
void setBackupProvisioned(boolean isProvisioned);
/**
* Report whether the backup mechanism is currently enabled.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
*/
boolean isBackupEnabled();
/**
* Schedule an immediate backup attempt for all pending updates. This is
* primarily intended for transports to use when they detect a suitable
* opportunity for doing a backup pass. If there are no pending updates to
* be sent, no action will be taken. Even if some updates are pending, the
* transport will still be asked to confirm via the usual requestBackupTime()
* method.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
*/
void backupNow();
/**
* Identify the currently selected transport. Callers must hold the
* android.permission.BACKUP permission to use this method.
*/
String getCurrentTransport();
/**
* Request a list of all available backup transports' names. Callers must
* hold the android.permission.BACKUP permission to use this method.
*/
String[] listAllTransports();
/**
* Specify the current backup transport. Callers must hold the
* android.permission.BACKUP permission to use this method.
*
* @param transport The name of the transport to select. This should be one
* of {@link BackupManager.TRANSPORT_GOOGLE} or {@link BackupManager.TRANSPORT_ADB}.
* @return The name of the previously selected transport. If the given transport
* name is not one of the currently available transports, no change is made to
* the current transport setting and the method returns null.
*/
String selectBackupTransport(String transport);
/**
* Begin a restore session. Either or both of packageName and transportID
* may be null. If packageName is non-null, then only the given package will be
* considered for restore. If transportID is null, then the restore will use
* the current active transport.
* <p>
* This method requires the android.permission.BACKUP permission <i>except</i>
* when transportID is null and packageName is the name of the caller's own
* package. In that case, the restore session returned is suitable for supporting
* the BackupManager.requestRestore() functionality via RestoreSession.restorePackage()
* without requiring the app to hold any special permission.
*
* @param packageName The name of the single package for which a restore will
* be requested. May be null, in which case all packages in the restore
* set can be restored.
* @param transportID The name of the transport to use for the restore operation.
* May be null, in which case the current active transport is used.
* @return An interface to the restore session, or null on error.
*/
IRestoreSession beginRestoreSession(String packageName, String transportID);
/**
* 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.
* {@hide}
*/
void opComplete(int token);
}

View File

@ -0,0 +1,65 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.app.backup.RestoreSet;
/**
* Callback class for receiving progress reports during a restore operation.
*
* @hide
*/
oneway interface IRestoreObserver {
/**
* Supply a list of the restore datasets available from the current transport. This
* method is invoked as a callback following the application's use of the
* {@link android.app.backup.IRestoreSession.getAvailableRestoreSets} method.
*
* @param result An array of {@link android.app.backup.RestoreSet RestoreSet} objects
* describing all of the available datasets that are candidates for restoring to
* the current device. If no applicable datasets exist, {@code result} will be
* {@code null}.
*/
void restoreSetsAvailable(in RestoreSet[] result);
/**
* The restore operation has begun.
*
* @param numPackages The total number of packages being processed in
* this restore operation.
*/
void restoreStarting(int numPackages);
/**
* An indication of which package is being restored currently, out of the
* total number provided in the {@link #restoreStarting(int numPackages)} callback.
* This method is not guaranteed to be called.
*
* @param nowBeingRestored The index, between 1 and the numPackages parameter
* to the restoreStarting() callback, of the package now being restored.
* @param currentPackage The name of the package now being restored.
*/
void onUpdate(int nowBeingRestored, String curentPackage);
/**
* The restore operation has completed.
*
* @param error Zero on success; a nonzero error code if the restore operation
* as a whole failed.
*/
void restoreFinished(int error);
}

View File

@ -0,0 +1,79 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.app.backup.RestoreSet;
import android.app.backup.IRestoreObserver;
/**
* Binder interface used by clients who wish to manage a restore operation. Every
* method in this interface requires the android.permission.BACKUP permission.
*
* {@hide}
*/
interface IRestoreSession {
/**
* Ask the current transport what the available restore sets are.
*
* @param observer This binder points to an object whose onRestoreSetsAvailable()
* method will be called to supply the results of the transport's lookup.
* @return Zero on success; nonzero on error. The observer will only receive a
* result callback if this method returned zero.
*/
int getAvailableRestoreSets(IRestoreObserver observer);
/**
* Restore the given set onto the device, replacing the current data of any app
* contained in the restore set with the data previously backed up.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
*
* @return Zero on success; nonzero on error. The observer will only receive
* progress callbacks if this method returned zero.
* @param token The token from {@link getAvailableRestoreSets()} corresponding to
* 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.
*/
int restoreAll(long token, IRestoreObserver observer);
/**
* Restore a single application from backup. The data will be restored from the
* current backup dataset if the given package has stored data there, or from
* the dataset used during the last full device setup operation if the current
* backup dataset has no matching data. If no backup data exists for this package
* in either source, a nonzero value will be returned.
*
* @return Zero on success; nonzero on error. The observer will only receive
* progress callbacks if this method returned zero.
* @param packageName The name of the package whose data to restore. If this is
* not the name of the caller's own package, then the android.permission.BACKUP
* permission must be held.
* @param observer If non-null, this binder points to an object that will receive
* progress callbacks during the restore operation.
*/
int restorePackage(in String packageName, IRestoreObserver observer);
/**
* End this restore session. After this method is called, the IRestoreSession binder
* is no longer valid.
*
* <p><b>Note:</b> The caller <i>must</i> invoke this method to end the restore session,
* even if {@link getAvailableRestoreSets} or {@link performRestore} failed.
*/
void endRestoreSession();
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2010 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import java.lang.String;
import android.app.backup.RestoreSet;
/**
* Callback class for receiving progress reports during a restore operation. These
* methods will all be called on your application's main thread.
*/
public abstract class RestoreObserver {
/**
* Supply a list of the restore datasets available from the current transport. This
* method is invoked as a callback following the application's use of the
* {@link android.app.backup.IRestoreSession.getAvailableRestoreSets} method.
*
* @param result An array of {@link android.app.backup.RestoreSet RestoreSet} objects
* describing all of the available datasets that are candidates for restoring to
* the current device. If no applicable datasets exist, {@code result} will be
* {@code null}.
*
* @hide
*/
public void restoreSetsAvailable(RestoreSet[] result) {
}
/**
* The restore operation has begun.
*
* @param numPackages The total number of packages being processed in
* this restore operation.
*/
public void restoreStarting(int numPackages) {
}
/**
* An indication of which package is being restored currently, out of the
* total number provided in the {@link #restoreStarting(int)} callback. This method
* is not guaranteed to be called: if the transport is unable to obtain
* data for one or more of the requested packages, no onUpdate() call will
* occur for those packages.
*
* @param nowBeingRestored The index, between 1 and the numPackages parameter
* to the {@link #restoreStarting(int)} callback, of the package now being
* restored. This may be non-monotonic; it is intended purely as a rough
* indication of the backup manager's progress through the overall restore process.
* @param currentPackage The name of the package now being restored.
*/
public void onUpdate(int nowBeingRestored, String currentPackage) {
}
/**
* The restore process has completed. This method will always be called,
* even if no individual package restore operations were attempted.
*
* @param error Zero on success; a nonzero error code if the restore operation
* as a whole failed.
*/
public void restoreFinished(int error) {
}
}

View File

@ -0,0 +1,203 @@
/*
* Copyright (C) 2010 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.app.backup.RestoreObserver;
import android.app.backup.RestoreSet;
import android.app.backup.IRestoreObserver;
import android.app.backup.IRestoreSession;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
/**
* Interface for managing a restore session.
* @hide
*/
public class RestoreSession {
static final String TAG = "RestoreSession";
final Context mContext;
IRestoreSession mBinder;
RestoreObserverWrapper mObserver = null;
/**
* Ask the current transport what the available restore sets are.
*
* @param observer a RestoreObserver object whose restoreSetsAvailable() method will
* be called on the application's main thread in order to supply the results of
* the restore set lookup by the backup transport. This parameter must not be
* null.
* @return Zero on success, nonzero on error. The observer's restoreSetsAvailable()
* method will only be called if this method returned zero.
*/
public int getAvailableRestoreSets(RestoreObserver observer) {
int err = -1;
RestoreObserverWrapper obsWrapper = new RestoreObserverWrapper(mContext, observer);
try {
err = mBinder.getAvailableRestoreSets(obsWrapper);
} catch (RemoteException e) {
Log.d(TAG, "Can't contact server to get available sets");
}
return err;
}
/**
* Restore the given set onto the device, replacing the current data of any app
* contained in the restore set with the data previously backed up.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
*
* @return Zero on success; nonzero on error. The observer will only receive
* progress callbacks if this method returned zero.
* @param token The token from {@link #getAvailableRestoreSets()} corresponding to
* 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.
*/
public int restoreAll(long token, RestoreObserver observer) {
int err = -1;
if (mObserver != null) {
Log.d(TAG, "restoreAll() called during active restore");
return -1;
}
mObserver = new RestoreObserverWrapper(mContext, observer);
try {
err = mBinder.restoreAll(token, mObserver);
} catch (RemoteException e) {
Log.d(TAG, "Can't contact server to restore");
}
return err;
}
/**
* Restore a single application from backup. The data will be restored from the
* current backup dataset if the given package has stored data there, or from
* the dataset used during the last full device setup operation if the current
* backup dataset has no matching data. If no backup data exists for this package
* in either source, a nonzero value will be returned.
*
* @return Zero on success; nonzero on error. The observer will only receive
* progress callbacks if this method returned zero.
* @param packageName The name of the package whose data to restore. If this is
* not the name of the caller's own package, then the android.permission.BACKUP
* permission must be held.
* @param observer If non-null, this binder points to an object that will receive
* progress callbacks during the restore operation.
*/
public int restorePackage(String packageName, RestoreObserver observer) {
int err = -1;
if (mObserver != null) {
Log.d(TAG, "restorePackage() called during active restore");
return -1;
}
mObserver = new RestoreObserverWrapper(mContext, observer);
try {
err = mBinder.restorePackage(packageName, mObserver);
} catch (RemoteException e) {
Log.d(TAG, "Can't contact server to restore package");
}
return err;
}
/**
* End this restore session. After this method is called, the RestoreSession
* object is no longer valid.
*
* <p><b>Note:</b> The caller <i>must</i> invoke this method to end the restore session,
* even if {@link #restorePackage(String, RestoreObserver)} failed.
*/
public void endRestoreSession() {
try {
mBinder.endRestoreSession();
} catch (RemoteException e) {
Log.d(TAG, "Can't contact server to get available sets");
} finally {
mBinder = null;
}
}
/*
* Nonpublic implementation here
*/
RestoreSession(Context context, IRestoreSession binder) {
mContext = context;
mBinder = binder;
}
/*
* We wrap incoming binder calls with a private class implementation that
* redirects them into main-thread actions. This serializes the restore
* progress callbacks nicely within the usual main-thread lifecycle pattern.
*/
private class RestoreObserverWrapper extends IRestoreObserver.Stub {
final Handler mHandler;
final RestoreObserver mAppObserver;
static final int MSG_RESTORE_STARTING = 1;
static final int MSG_UPDATE = 2;
static final int MSG_RESTORE_FINISHED = 3;
static final int MSG_RESTORE_SETS_AVAILABLE = 4;
RestoreObserverWrapper(Context context, RestoreObserver appObserver) {
mHandler = new Handler(context.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RESTORE_STARTING:
mAppObserver.restoreStarting(msg.arg1);
break;
case MSG_UPDATE:
mAppObserver.onUpdate(msg.arg1, (String)msg.obj);
break;
case MSG_RESTORE_FINISHED:
mAppObserver.restoreFinished(msg.arg1);
break;
case MSG_RESTORE_SETS_AVAILABLE:
mAppObserver.restoreSetsAvailable((RestoreSet[])msg.obj);
break;
}
}
};
mAppObserver = appObserver;
}
// Binder calls into this object just enqueue on the main-thread handler
public void restoreSetsAvailable(RestoreSet[] result) {
mHandler.sendMessage(
mHandler.obtainMessage(MSG_RESTORE_SETS_AVAILABLE, result));
}
public void restoreStarting(int numPackages) {
mHandler.sendMessage(
mHandler.obtainMessage(MSG_RESTORE_STARTING, numPackages, 0));
}
public void onUpdate(int nowBeingRestored, String currentPackage) {
mHandler.sendMessage(
mHandler.obtainMessage(MSG_UPDATE, nowBeingRestored, 0, currentPackage));
}
public void restoreFinished(int error) {
mHandler.sendMessage(
mHandler.obtainMessage(MSG_RESTORE_FINISHED, error, 0));
}
}
}

View File

@ -0,0 +1,19 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
parcelable RestoreSet;

View File

@ -0,0 +1,87 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Descriptive information about a set of backed-up app data available for restore.
* Used by IRestoreSession clients.
*
* @hide
*/
public class RestoreSet implements Parcelable {
/**
* Name of this restore set. May be user generated, may simply be the name
* of the handset model, e.g. "T-Mobile G1".
*/
public String name;
/**
* Identifier of the device whose data this is. This will be as unique as
* is practically possible; for example, it might be an IMEI.
*/
public String device;
/**
* Token that identifies this backup set unambiguously to the backup/restore
* transport. This is guaranteed to be valid for the duration of a restore
* session, but is meaningless once the session has ended.
*/
public long token;
public RestoreSet() {
// Leave everything zero / null
}
public RestoreSet(String _name, String _dev, long _token) {
name = _name;
device = _dev;
token = _token;
}
// Parcelable implementation
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeString(name);
out.writeString(device);
out.writeLong(token);
}
public static final Parcelable.Creator<RestoreSet> CREATOR
= new Parcelable.Creator<RestoreSet>() {
public RestoreSet createFromParcel(Parcel in) {
return new RestoreSet(in);
}
public RestoreSet[] newArray(int size) {
return new RestoreSet[size];
}
};
private RestoreSet(Parcel in) {
name = in.readString();
device = in.readString();
token = in.readLong();
}
}

View File

@ -0,0 +1,130 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import android.app.QueuedWork;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.File;
/**
* A helper class that can be used in conjunction with
* {@link android.app.backup.BackupAgentHelper} to manage the backup of
* {@link android.content.SharedPreferences}. Whenever a backup is performed, it
* will back up all named shared preferences that have changed since the last
* backup operation.
* <p>
* To use this class, the application's backup agent class should extend
* {@link android.app.backup.BackupAgentHelper}. Then, in the agent's
* {@link BackupAgent#onCreate()} method, an instance of this class should be
* allocated and installed as a backup/restore handler within the BackupAgentHelper
* framework. For example, an agent supporting backup and restore for
* an application with two groups of {@link android.content.SharedPreferences}
* data might look something like this:
* <pre>
* import android.app.backup.BackupAgentHelper;
* import android.app.backup.SharedPreferencesBackupHelper;
*
* public class MyBackupAgent extends BackupAgentHelper {
* // The names of the SharedPreferences groups that the application maintains. These
* // are the same strings that are passed to {@link Context#getSharedPreferences(String, int)}.
* static final String PREFS_DISPLAY = "displayprefs";
* static final String PREFS_SCORES = "highscores";
*
* // An arbitrary string used within the BackupAgentHelper implementation to
* // identify the SharedPreferenceBackupHelper's data.
* static final String MY_PREFS_BACKUP_KEY = "myprefs";
*
* // Simply allocate a helper and install it
* void onCreate() {
* SharedPreferencesBackupHelper helper =
* new SharedPreferencesBackupHelper(this, PREFS_DISPLAY, PREFS_SCORES);
* addHelper(MY_PREFS_BACKUP_KEY, helper);
* }
* }</pre>
* <p>
* No further implementation is needed; the {@link BackupAgentHelper} mechanism automatically
* dispatches the
* {@link BackupAgent#onBackup(android.os.ParcelFileDescriptor, BackupDataOutput, android.os.ParcelFileDescriptor) BackupAgent.onBackup()}
* and
* {@link BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) BackupAgent.onRestore()}
* callbacks to the SharedPreferencesBackupHelper as appropriate.
*/
public class SharedPreferencesBackupHelper extends FileBackupHelperBase implements BackupHelper {
private static final String TAG = "SharedPreferencesBackupHelper";
private static final boolean DEBUG = false;
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 The application {@link android.content.Context}
* @param prefGroups The names of each {@link android.content.SharedPreferences} file to
* back up
*/
public SharedPreferencesBackupHelper(Context context, String... prefGroups) {
super(context);
mContext = context;
mPrefGroups = prefGroups;
}
/**
* Backs up the configured {@link android.content.SharedPreferences} groups.
*/
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) {
Context context = mContext;
// If a SharedPreference has an outstanding write in flight,
// wait for it to finish flushing to disk.
QueuedWork.waitToFinish();
// make filenames for the prefGroups
String[] prefGroups = mPrefGroups;
final int N = prefGroups.length;
String[] files = new String[N];
for (int i=0; i<N; i++) {
files[i] = context.getSharedPrefsFile(prefGroups[i]).getAbsolutePath();
}
// go
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;
String key = data.getKey();
if (DEBUG) Log.d(TAG, "got entity '" + key + "' size=" + data.size());
if (isKeyInList(key, mPrefGroups)) {
File f = context.getSharedPrefsFile(key).getAbsoluteFile();
writeFile(f, data);
}
}
}

View File

@ -0,0 +1,29 @@
<HTML>
<BODY>
<p>Contains the backup and restore functionality available to
applications. If a user wipes the data on their device or upgrades to a new Android-powered
device, all applications that have enabled backup will restore the user's previous data.</p>
<p>For a detailed guide to using the backup APIs, see the <a
href="{@docRoot}guide/topics/data/backup.html">Data Backup</a> developer guide.</p>
{@more}
<p>All backup and restore operations are controlled by the {@link
android.app.backup.BackupManager}. Each application that would
like to enable backup and preserve its data on remote strage must implement a
backup agent. A backup agent can be built by extending either {@link android.app.backup.BackupAgent}
or {@link android.app.backup.BackupAgentHelper}. The {@link
android.app.backup.BackupAgentHelper} class provides a wrapper around {@link
android.app.backup.BackupAgent} that simplifies the procedures to implement a backup agent by
employing backup helpers such as {@link android.app.backup.SharedPreferencesBackupHelper} and
{@link android.app.backup.FileBackupHelper}.</p>
<p>The backup APIs let applications:</p>
<ul>
<li>Perform backup of arbitrary data to remote storage</li>
<li>Easily perform backup of {@link android.content.SharedPreferences} and files</li>
<li>Restore the data saved to remote storage</li>
</ul>
</BODY>
</HTML>