summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Christopher Tate <ctate@google.com> 2009-06-12 15:20:04 -0700
committer Christopher Tate <ctate@google.com> 2009-06-14 19:39:29 -0700
commit2fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2 (patch)
tree985ee68d951d7c141e296aa4fec60d41f667538a
parent31390c752828dce0a33d0adeacdf10a49ff847ce (diff)
Fix some backup reader/writer issues; make local transport do backup
As of this change, LocalTransport is successfully propagating data changes from the backup data format into a repository stored in /cache/backup/[packagename]. Each backup key gets a separate file there for ease of manipulation and testing. The general semantics of BackupDataReader have been tweaked, too; it now just returns simple "we're done with the data" when it hits the end, even if no footer has been found, because on the writing side the footer isn't being written. Also, reading an entity now merely requires a "big enough" buffer, not an exactly-sized one. This is all a work in progress, but this is at least working now for purposes of this local transport. Still to do: proper change vs deletion detection, as well as expanding the data format itself to include necessary metadata etc.
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java86
-rw-r--r--core/jni/android_backup_BackupDataInput.cpp81
-rw-r--r--core/jni/android_backup_BackupDataOutput.cpp2
3 files changed, 114 insertions, 55 deletions
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 62fba4ac5295..83182f23b0c6 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -1,5 +1,6 @@
package com.android.internal.backup;
+import android.backup.BackupDataInput;
import android.backup.RestoreSet;
import android.content.Context;
import android.content.pm.PackageInfo;
@@ -24,7 +25,7 @@ import java.util.ArrayList;
public class LocalTransport extends IBackupTransport.Stub {
private static final String TAG = "LocalTransport";
- private static final String DATA_FILE_NAME = "data";
+ private static final boolean DEBUG = true;
private Context mContext;
private PackageManager mPackageManager;
@@ -37,6 +38,7 @@ public class LocalTransport extends IBackupTransport.Stub {
public LocalTransport(Context context) {
+ if (DEBUG) Log.v(TAG, "Transport constructed");
mContext = context;
mPackageManager = context.getPackageManager();
}
@@ -47,29 +49,63 @@ public class LocalTransport extends IBackupTransport.Stub {
}
public int startSession() throws RemoteException {
+ if (DEBUG) Log.v(TAG, "session started");
+ mDataDir.mkdirs();
return 0;
}
public int endSession() throws RemoteException {
+ if (DEBUG) Log.v(TAG, "session ended");
return 0;
}
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
throws RemoteException {
- File packageDir = new File(mDataDir, packageInfo.packageName);
- File imageFileName = new File(packageDir, DATA_FILE_NAME);
-
- //!!! TODO: process the (partial) update into the persistent restore set:
-
- // Parse out the existing image file into the key/value map
+ if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
+ int err = 0;
- // Parse out the backup data into the key/value updates
-
- // Apply the backup key/value updates to the image
+ File packageDir = new File(mDataDir, packageInfo.packageName);
+ packageDir.mkdirs();
- // Write out the image in the canonical format
+ // Each 'record' in the restore set is kept in its own file, named by
+ // the record key. Wind through the data file, extracting individual
+ // record operations and building a set of all the updates to apply
+ // in this update.
+ BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor());
+ try {
+ int bufSize = 512;
+ byte[] buf = new byte[bufSize];
+ while (changeSet.readNextHeader()) {
+ String key = changeSet.getKey();
+ int dataSize = changeSet.getDataSize();
+ if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize);
+ if (dataSize > bufSize) {
+ bufSize = dataSize;
+ buf = new byte[bufSize];
+ }
+ changeSet.readEntityData(buf, dataSize);
+ if (DEBUG) Log.v(TAG, " + data size " + dataSize);
+
+ File entityFile = new File(packageDir, key);
+ FileOutputStream entity = new FileOutputStream(entityFile);
+ try {
+ entity.write(buf, 0, dataSize);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to update key file "
+ + entityFile.getAbsolutePath());
+ err = -1;
+ } finally {
+ entity.close();
+ }
+ }
+ } catch (IOException e) {
+ // oops, something went wrong. abort the operation and return error.
+ Log.v(TAG, "Exception reading backup input:");
+ e.printStackTrace();
+ err = -1;
+ }
- return -1;
+ return err;
}
// Restore handling
@@ -83,6 +119,7 @@ public class LocalTransport extends IBackupTransport.Stub {
}
public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
+ if (DEBUG) Log.v(TAG, "getting app set " + token);
// the available packages are the extant subdirs of mDatadir
File[] packageDirs = mDataDir.listFiles(mDirFileFilter);
ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>();
@@ -99,9 +136,11 @@ public class LocalTransport extends IBackupTransport.Stub {
}
}
- Log.v(TAG, "Built app set of " + packages.size() + " entries:");
- for (PackageInfo p : packages) {
- Log.v(TAG, " + " + p.packageName);
+ if (DEBUG) {
+ Log.v(TAG, "Built app set of " + packages.size() + " entries:");
+ for (PackageInfo p : packages) {
+ Log.v(TAG, " + " + p.packageName);
+ }
}
PackageInfo[] result = new PackageInfo[packages.size()];
@@ -110,16 +149,25 @@ public class LocalTransport extends IBackupTransport.Stub {
public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor output)
throws android.os.RemoteException {
+ if (DEBUG) Log.v(TAG, "getting restore data " + token + " : " + packageInfo.packageName);
// we only support one hardcoded restore set
if (token != 0) return -1;
// the data for a given package is at a known location
File packageDir = new File(mDataDir, packageInfo.packageName);
- File imageFile = new File(packageDir, DATA_FILE_NAME);
- // restore is relatively easy: we already maintain the full data set in
- // the canonical form understandable to the BackupAgent
- return copyFileToFD(imageFile, output);
+ // The restore set is the concatenation of the individual record blobs,
+ // each of which is a file in the package's directory
+ File[] blobs = packageDir.listFiles();
+ int err = 0;
+ if (blobs != null && blobs.length > 0) {
+ for (File f : blobs) {
+ err = copyFileToFD(f, output);
+ if (err != 0) break;
+ }
+ }
+
+ return err;
}
private int copyFileToFD(File source, ParcelFileDescriptor dest) {
diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp
index d8c2535f411c..5b2fb73babc3 100644
--- a/core/jni/android_backup_BackupDataInput.cpp
+++ b/core/jni/android_backup_BackupDataInput.cpp
@@ -62,43 +62,54 @@ readNextHeader_native(JNIEnv* env, jobject clazz, int r, jobject entity)
return err < 0 ? err : -1;
}
- while (reader->HasEntities()) {
- int type;
+ int type = 0;
- err = reader->ReadNextHeader(&type);
+ err = reader->ReadNextHeader(&type);
+ if (err == EIO) {
+ // Clean EOF with no footer block; just claim we're done
+ return 1;
+ }
+
+ if (err != 0) {
+ return err < 0 ? err : -1;
+ }
+
+ switch (type) {
+ case BACKUP_HEADER_APP_V1:
+ {
+ String8 packageName;
+ int cookie;
+ err = reader->ReadAppHeader(&packageName, &cookie);
if (err != 0) {
+ LOGD("ReadAppHeader() returned %d; aborting", err);
return err < 0 ? err : -1;
}
-
- switch (type) {
- case BACKUP_HEADER_APP_V1:
- {
- String8 packageName;
- int cookie;
- err = reader->ReadAppHeader(&packageName, &cookie);
- if (err != 0) {
- return err < 0 ? err : -1;
- }
- break;
- }
- case BACKUP_HEADER_ENTITY_V1:
- {
- String8 key;
- size_t dataSize;
- err = reader->ReadEntityHeader(&key, &dataSize);
- if (err != 0) {
- return err < 0 ? err : -1;
- }
- // TODO: Set the fields in the entity object
- return 0;
- }
- case BACKUP_FOOTER_APP_V1:
- break;
- default:
- LOGD("Unknown header type: 0x%08x\n", type);
- return -1;
+ break;
+ }
+ case BACKUP_HEADER_ENTITY_V1:
+ {
+ String8 key;
+ size_t dataSize;
+ err = reader->ReadEntityHeader(&key, &dataSize);
+ if (err != 0) {
+ LOGD("ReadEntityHeader(); aborting", err);
+ return err < 0 ? err : -1;
}
+ // TODO: Set the fields in the entity object
+ jstring keyStr = env->NewStringUTF(key.string());
+ env->SetObjectField(entity, s_keyField, keyStr);
+ env->SetIntField(entity, s_dataSizeField, dataSize);
+ return 0;
+ }
+ case BACKUP_FOOTER_APP_V1:
+ {
+ break;
}
+ default:
+ LOGD("Unknown header type: 0x%08x\n", type);
+ return -1;
+ }
+
// done
return 1;
}
@@ -109,17 +120,17 @@ readEntityData_native(JNIEnv* env, jobject clazz, int r, jbyteArray data, int si
int err;
BackupDataReader* reader = (BackupDataReader*)r;
- if (env->GetArrayLength(data) > size) {
+ if (env->GetArrayLength(data) < size) {
// size mismatch
return -1;
}
jbyte* dataBytes = env->GetByteArrayElements(data, NULL);
if (dataBytes == NULL) {
- return -1;
+ return -2;
}
- err = reader->ReadEntityData(dataBytes, size);
+ err = reader->ReadEntityData(dataBytes, size);
env->ReleaseByteArrayElements(data, dataBytes, 0);
@@ -136,7 +147,7 @@ static const JNINativeMethod g_methods[] = {
int register_android_backup_BackupDataInput(JNIEnv* env)
{
- LOGD("register_android_backup_BackupDataInput");
+ //LOGD("register_android_backup_BackupDataInput");
jclass clazz;
diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp
index 96603261bc99..6362439c000b 100644
--- a/core/jni/android_backup_BackupDataOutput.cpp
+++ b/core/jni/android_backup_BackupDataOutput.cpp
@@ -96,7 +96,7 @@ static const JNINativeMethod g_methods[] = {
int register_android_backup_BackupDataOutput(JNIEnv* env)
{
- LOGD("register_android_backup_BackupDataOutput");
+ //LOGD("register_android_backup_BackupDataOutput");
jclass clazz;