diff options
-rw-r--r-- | core/java/android/backup/RestoreHelper.java | 3 | ||||
-rw-r--r-- | core/java/android/backup/RestoreHelperBase.java | 71 | ||||
-rw-r--r-- | core/java/android/backup/RestoreHelperDispatcher.java | 16 | ||||
-rw-r--r-- | core/jni/Android.mk | 3 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 2 | ||||
-rw-r--r-- | core/jni/android_backup_RestoreHelperBase.cpp | 94 | ||||
-rw-r--r-- | include/utils/BackupHelpers.h | 35 | ||||
-rw-r--r-- | libs/utils/BackupHelpers.cpp | 115 | ||||
-rw-r--r-- | tests/backup/src/com/android/backuptest/BackupTestActivity.java | 7 | ||||
-rw-r--r-- | tests/backup/src/com/android/backuptest/BackupTestAgent.java | 2 |
10 files changed, 278 insertions, 70 deletions
diff --git a/core/java/android/backup/RestoreHelper.java b/core/java/android/backup/RestoreHelper.java index ac7d8ee..e47869c 100644 --- a/core/java/android/backup/RestoreHelper.java +++ b/core/java/android/backup/RestoreHelper.java @@ -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); } diff --git a/core/java/android/backup/RestoreHelperBase.java b/core/java/android/backup/RestoreHelperBase.java index 894c9af..93a8fef 100644 --- a/core/java/android/backup/RestoreHelperBase.java +++ b/core/java/android/backup/RestoreHelperBase.java @@ -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); } diff --git a/core/java/android/backup/RestoreHelperDispatcher.java b/core/java/android/backup/RestoreHelperDispatcher.java index 5928914..4861775 100644 --- a/core/java/android/backup/RestoreHelperDispatcher.java +++ b/core/java/android/backup/RestoreHelperDispatcher.java @@ -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); + } } } + diff --git a/core/jni/Android.mk b/core/jni/Android.mk index aca6670..a42eef1 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -117,7 +117,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) \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 4512fef..3d1ce8c 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -156,6 +156,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; @@ -1172,6 +1173,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), }; /* diff --git a/core/jni/android_backup_RestoreHelperBase.cpp b/core/jni/android_backup_RestoreHelperBase.cpp new file mode 100644 index 0000000..3173420 --- /dev/null +++ b/core/jni/android_backup_RestoreHelperBase.cpp @@ -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)); +} + +} diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index fa7f8d5..fc701fd 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -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 diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index c1d5404..99687bc 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -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; diff --git a/tests/backup/src/com/android/backuptest/BackupTestActivity.java b/tests/backup/src/com/android/backuptest/BackupTestActivity.java index d87e85c..f0c3f93 100644 --- a/tests/backup/src/com/android/backuptest/BackupTestActivity.java +++ b/tests/backup/src/com/android/backuptest/BackupTestActivity.java @@ -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); } diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java index e3566ec..0da4151 100644 --- a/tests/backup/src/com/android/backuptest/BackupTestAgent.java +++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java @@ -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); } } |