Merge change 4708 into donut
* changes: Make RestoreHelper and friends also write out the snapshot state.
This commit is contained in:
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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),
|
||||
};
|
||||
|
||||
/*
|
||||
|
94
core/jni/android_backup_RestoreHelperBase.cpp
Normal file
94
core/jni/android_backup_RestoreHelperBase.cpp
Normal 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));
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user