diff options
| author | 2009-06-12 15:20:04 -0700 | |
|---|---|---|
| committer | 2009-06-14 19:39:29 -0700 | |
| commit | 2fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2 (patch) | |
| tree | 985ee68d951d7c141e296aa4fec60d41f667538a | |
| parent | 31390c752828dce0a33d0adeacdf10a49ff847ce (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.java | 86 | ||||
| -rw-r--r-- | core/jni/android_backup_BackupDataInput.cpp | 81 | ||||
| -rw-r--r-- | core/jni/android_backup_BackupDataOutput.cpp | 2 |
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; |