Merge change 4708 into donut

* changes:
  Make RestoreHelper and friends also write out the snapshot state.
This commit is contained in:
Android (Google) Code Review
2009-06-18 19:02:30 -07:00
10 changed files with 278 additions and 70 deletions

View File

@ -16,6 +16,8 @@
package android.backup;
import android.os.ParcelFileDescriptor;
import java.io.InputStream;
/** @hide */
@ -27,5 +29,6 @@ public interface RestoreHelper {
* <code>dataSize</code> bytes from <code>data</code>.
*/
public void restoreEntity(BackupDataInputStream data);
public void writeSnapshot(ParcelFileDescriptor fd);
}

View File

@ -17,69 +17,66 @@
package android.backup;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.InputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
class RestoreHelperBase {
private static final String TAG = "RestoreHelperBase";
private static final int BUF_SIZE = 8 * 1024;
int mPtr;
Context mContext;
byte[] mBuf = new byte[BUF_SIZE];
boolean mExceptionLogged;
RestoreHelperBase(Context context) {
mPtr = ctor();
mContext = context;
}
void writeFile(File f, InputStream in) {
boolean success = false;
FileOutputStream out = null;
protected void finalize() throws Throwable {
try {
// Create the enclosing directory.
File parent = f.getParentFile();
parent.mkdirs();
dtor(mPtr);
} finally {
super.finalize();
}
}
// Copy the file.
int sum = 0;
out = new FileOutputStream(f);
byte[] buf = mBuf;
int amt;
while ((amt = in.read(buf)) > 0) {
out.write(buf, 0, amt);
sum += amt;
}
void writeFile(File f, InputStream in) {
if (!(in instanceof BackupDataInputStream)) {
throw new IllegalStateException("input stream must be a BackupDataInputStream");
}
int result = -1;
// TODO: Set the permissions of the file.
// Create the enclosing directory.
File parent = f.getParentFile();
parent.mkdirs();
// We're done
success = true;
out = null;
} catch (IOException ex) {
// Bail on this entity. Only log one exception per helper object.
result = writeFile_native(mPtr, f.getAbsolutePath(),
((BackupDataInputStream)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() + '\'', ex);
+ mContext.getPackageName() + "\' result=0x"
+ Integer.toHexString(result));
mExceptionLogged = true;
}
}
finally {
if (out != null) {
try {
out.close();
} catch (IOException ex) {
}
}
if (!success) {
// Something didn't work out, delete the file
f.delete();
}
}
}
public void writeSnapshot(ParcelFileDescriptor fd) {
int result = writeSnapshot_native(mPtr, fd.getFileDescriptor());
// TODO: Do something with the error.
}
private static native int ctor();
private static native void dtor(int ptr);
private static native int writeFile_native(int ptr, String filename, int backupReader);
private static native int writeSnapshot_native(int ptr, FileDescriptor fd);
}

View File

@ -16,10 +16,12 @@
package android.backup;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/** @hide */
public class RestoreHelperDispatcher {
@ -31,7 +33,7 @@ public class RestoreHelperDispatcher {
mHelpers.put(keyPrefix, helper);
}
public void dispatch(BackupDataInput input) throws IOException {
public void dispatch(BackupDataInput input, ParcelFileDescriptor newState) throws IOException {
boolean alreadyComplained = false;
BackupDataInputStream stream = new BackupDataInputStream(input);
@ -60,5 +62,17 @@ public class RestoreHelperDispatcher {
}
input.skipEntityData(); // In case they didn't consume the data.
}
if (mHelpers.size() > 1) {
throw new RuntimeException("RestoreHelperDispatcher won't get your your"
+ " data in the right order yet.");
}
// Write out the state files
for (RestoreHelper helper: mHelpers.values()) {
// TODO: Write a header for the state
helper.writeSnapshot(newState);
}
}
}

View File

@ -119,7 +119,8 @@ LOCAL_SRC_FILES:= \
com_android_internal_graphics_NativeUtils.cpp \
android_backup_BackupDataInput.cpp \
android_backup_BackupDataOutput.cpp \
android_backup_FileBackupHelper.cpp
android_backup_FileBackupHelper.cpp \
android_backup_RestoreHelperBase.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \

View File

@ -158,6 +158,7 @@ extern int register_android_location_GpsLocationProvider(JNIEnv* env);
extern int register_android_backup_BackupDataInput(JNIEnv *env);
extern int register_android_backup_BackupDataOutput(JNIEnv *env);
extern int register_android_backup_FileBackupHelper(JNIEnv *env);
extern int register_android_backup_RestoreHelperBase(JNIEnv *env);
static AndroidRuntime* gCurRuntime = NULL;
@ -1131,6 +1132,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_backup_BackupDataInput),
REG_JNI(register_android_backup_BackupDataOutput),
REG_JNI(register_android_backup_FileBackupHelper),
REG_JNI(register_android_backup_RestoreHelperBase),
};
/*

View File

@ -0,0 +1,94 @@
/*
* 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.
*/
#define LOG_TAG "FileBackupHelper_native"
#include <utils/Log.h>
#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/BackupHelpers.h>
namespace android
{
// java.io.FileDescriptor
static jfieldID s_descriptorField = 0;
static int
ctor(JNIEnv* env, jobject clazz)
{
return (int)new RestoreHelperBase();
}
static void
dtor(JNIEnv* env, jobject clazz, jint ptr)
{
delete (RestoreHelperBase*)ptr;
}
static int
writeFile_native(JNIEnv* env, jobject clazz, jint ptr, jstring filenameObj, int backupReaderPtr)
{
int err;
RestoreHelperBase* restore = (RestoreHelperBase*)ptr;
BackupDataReader* reader = (BackupDataReader*)backupReaderPtr;
char const* filename;
filename = env->GetStringUTFChars(filenameObj, NULL);
err = restore->WriteFile(String8(filename), reader);
env->ReleaseStringUTFChars(filenameObj, filename);
return err;
}
static int
writeSnapshot_native(JNIEnv* env, jobject clazz, jint ptr, jobject fileDescriptor)
{
int err;
RestoreHelperBase* restore = (RestoreHelperBase*)ptr;
int fd = env->GetIntField(fileDescriptor, s_descriptorField);
err = restore->WriteSnapshot(fd);
return err;
}
static const JNINativeMethod g_methods[] = {
{ "ctor", "()I", (void*)ctor },
{ "dtor", "(I)V", (void*)dtor },
{ "writeFile_native", "(ILjava/lang/String;I)I", (void*)writeFile_native },
{ "writeSnapshot_native", "(ILjava/io/FileDescriptor;)I", (void*)writeSnapshot_native },
};
int register_android_backup_RestoreHelperBase(JNIEnv* env)
{
jclass clazz;
clazz = env->FindClass("java/io/FileDescriptor");
LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
s_descriptorField = env->GetFieldID(clazz, "descriptor", "I");
LOG_FATAL_IF(s_descriptorField == NULL,
"Unable to find descriptor field in java.io.FileDescriptor");
return AndroidRuntime::registerNativeMethods(env, "android/backup/RestoreHelperBase",
g_methods, NELEM(g_methods));
}
}

View File

@ -19,6 +19,7 @@
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/KeyedVector.h>
namespace android {
@ -32,6 +33,27 @@ typedef struct {
int dataSize; // size of the data, not including the padding, -1 means delete
} entity_header_v1;
struct SnapshotHeader {
int magic0;
int fileCount;
int magic1;
int totalSize;
};
struct FileState {
int modTime_sec;
int modTime_nsec;
int size;
int crc32;
int nameLen;
};
struct FileRec {
String8 file;
bool deleted;
FileState s;
};
/**
* Writes the data.
@ -99,6 +121,19 @@ private:
int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD,
char const* const* files, char const* const *keys, int fileCount);
class RestoreHelperBase
{
public:
RestoreHelperBase();
~RestoreHelperBase();
status_t WriteFile(const String8& filename, BackupDataReader* in);
status_t WriteSnapshot(int fd);
private:
void* m_buf;
KeyedVector<String8,FileRec> m_files;
};
#define TEST_BACKUP_HELPERS 1

View File

@ -47,27 +47,6 @@ namespace android {
#define LOGP(x...) LOGD(x)
#endif
struct SnapshotHeader {
int magic0;
int fileCount;
int magic1;
int totalSize;
};
struct FileState {
int modTime_sec;
int modTime_nsec;
int size;
int crc32;
int nameLen;
};
struct FileRec {
char const* file; // this object does not own this string
bool deleted;
FileState s;
};
const static int ROUND_UP[4] = { 0, 3, 2, 1 };
static inline int
@ -310,7 +289,8 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD
for (int i=0; i<fileCount; i++) {
String8 key(keys[i]);
FileRec r;
char const* file = r.file = files[i];
char const* file = files[i];
r.file = file;
struct stat st;
err = stat(file, &st);
@ -351,20 +331,20 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD
}
else if (cmp > 0) {
// file added
LOGP("file added: %s", g.file);
write_update_file(dataStream, q, g.file);
LOGP("file added: %s", g.file.string());
write_update_file(dataStream, q, g.file.string());
m++;
}
else {
// both files exist, check them
const FileState& f = oldSnapshot.valueAt(n);
int fd = open(g.file, O_RDONLY);
int fd = open(g.file.string(), O_RDONLY);
if (fd < 0) {
// We can't open the file. Don't report it as a delete either. Let the
// server keep the old version. Maybe they'll be able to deal with it
// on restore.
LOGP("Unable to open file %s - skipping", g.file);
LOGP("Unable to open file %s - skipping", g.file.string());
} else {
g.s.crc32 = compute_crc32(fd);
@ -375,7 +355,7 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD
g.s.modTime_sec, g.s.modTime_nsec, g.s.size, g.s.crc32);
if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec
|| f.size != g.s.size || f.crc32 != g.s.crc32) {
write_update_file(dataStream, fd, p, g.file);
write_update_file(dataStream, fd, p, g.file.string());
}
close(fd);
@ -395,7 +375,7 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD
while (m<fileCount) {
const String8& q = newSnapshot.keyAt(m);
FileRec& g = newSnapshot.editValueAt(m);
write_update_file(dataStream, q, g.file);
write_update_file(dataStream, q, g.file.string());
m++;
}
@ -404,6 +384,84 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD
return 0;
}
#define RESTORE_BUF_SIZE (8*1024)
RestoreHelperBase::RestoreHelperBase()
{
m_buf = malloc(RESTORE_BUF_SIZE);
}
RestoreHelperBase::~RestoreHelperBase()
{
free(m_buf);
}
status_t
RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in)
{
ssize_t err;
size_t dataSize;
String8 key;
int fd;
void* buf = m_buf;
ssize_t amt;
int mode;
int crc;
struct stat st;
FileRec r;
err = in->ReadEntityHeader(&key, &dataSize);
if (err != NO_ERROR) {
return err;
}
// TODO: World readable/writable for now.
mode = 0666;
// Write the file and compute the crc
crc = crc32(0L, Z_NULL, 0);
fd = open(filename.string(), O_CREAT|O_RDWR, mode);
if (fd != -1) {
return errno;
}
while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) {
err = write(fd, buf, amt);
if (err != amt) {
close(fd);
return errno;
}
crc = crc32(crc, (Bytef*)buf, amt);
}
close(fd);
// Record for the snapshot
err = stat(filename.string(), &st);
if (err != 0) {
LOGW("Error stating file that we just created %s", filename.string());
return errno;
}
r.file = filename;
r.deleted = false;
r.s.modTime_sec = st.st_mtime;
r.s.modTime_nsec = 0; // workaround sim breakage
//r.s.modTime_nsec = st.st_mtime_nsec;
r.s.size = st.st_size;
r.s.crc32 = crc;
m_files.add(key, r);
return NO_ERROR;
}
status_t
RestoreHelperBase::WriteSnapshot(int fd)
{
return write_snapshot_file(fd, m_files);;
}
#if TEST_BACKUP_HELPERS
#define SCRATCH_DIR "/data/backup_helper_test/"
@ -560,7 +618,6 @@ backup_helper_test_four()
FileState states[4];
FileRec r;
r.deleted = false;
r.file = NULL;
states[0].modTime_sec = 0xfedcba98;
states[0].modTime_nsec = 0xdeadbeef;

View File

@ -161,8 +161,13 @@ public class BackupTestActivity extends ListActivity
new FileRestoreHelper(BackupTestActivity.this));
FileInputStream dataFile = openFileInput("backup_test");
BackupDataInput data = new BackupDataInput(dataFile.getFD());
dispatch.dispatch(data);
ParcelFileDescriptor state = ParcelFileDescriptor.open(
new File(getFilesDir(), "restore_state"),
ParcelFileDescriptor.MODE_READ_WRITE|ParcelFileDescriptor.MODE_CREATE|
ParcelFileDescriptor.MODE_TRUNCATE);
dispatch.dispatch(data, state);
dataFile.close();
state.close();
} catch (IOException ex) {
throw new RuntimeException(ex);
}

View File

@ -55,7 +55,7 @@ public class BackupTestAgent extends BackupAgent
// dispatch.addHelper(SHARED_PREFS, new SharedPrefsRestoreHelper(this));
dispatch.addHelper(DATA_FILES, new FileRestoreHelper(this));
dispatch.dispatch(data);
dispatch.dispatch(data, newState);
}
}