diff options
-rw-r--r-- | core/java/android/app/FullBackupAgent.java | 5 | ||||
-rw-r--r-- | core/java/android/backup/BackupDataInput.java | 103 | ||||
-rw-r--r-- | core/java/android/backup/BackupDataOutput.java | 22 | ||||
-rw-r--r-- | core/java/android/backup/FileBackupHelper.java | 43 | ||||
-rw-r--r-- | core/java/android/backup/RestoreHelper.java | 23 | ||||
-rw-r--r-- | core/java/android/backup/RestoreHelperDistributor.java | 28 | ||||
-rw-r--r-- | core/java/android/backup/SharedPreferencesBackupHelper.java | 23 | ||||
-rw-r--r-- | core/jni/Android.mk | 1 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 2 | ||||
-rw-r--r-- | core/jni/android_backup_BackupDataInput.cpp | 162 | ||||
-rw-r--r-- | core/jni/android_backup_BackupDataOutput.cpp | 49 | ||||
-rw-r--r-- | core/jni/android_backup_FileBackupHelper.cpp | 4 | ||||
-rw-r--r-- | libs/utils/BackupData.cpp | 2 | ||||
-rw-r--r-- | tests/backup/src/com/android/backuptest/BackupTestAgent.java | 3 |
14 files changed, 452 insertions, 18 deletions
diff --git a/core/java/android/app/FullBackupAgent.java b/core/java/android/app/FullBackupAgent.java index 18d62e3..bf5cb5d 100644 --- a/core/java/android/app/FullBackupAgent.java +++ b/core/java/android/app/FullBackupAgent.java @@ -47,12 +47,11 @@ public class FullBackupAgent extends BackupAgent { } // That's the file set; now back it all up - FileBackupHelper.performBackup(this, oldState, data, newState, - (String[]) allFiles.toArray()); + FileBackupHelper helper = new FileBackupHelper(this); + helper.performBackup(oldState, data, newState, (String[])allFiles.toArray()); } @Override public void onRestore(ParcelFileDescriptor data, ParcelFileDescriptor newState) { } - } diff --git a/core/java/android/backup/BackupDataInput.java b/core/java/android/backup/BackupDataInput.java new file mode 100644 index 0000000..609dd90 --- /dev/null +++ b/core/java/android/backup/BackupDataInput.java @@ -0,0 +1,103 @@ +/* + * 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. + */ + +package android.backup; + +import android.content.Context; + +import java.io.FileDescriptor; +import java.io.IOException; + +/** @hide */ +public class BackupDataInput { + int mBackupReader; + + private EntityHeader mHeader = new EntityHeader(); + private boolean mHeaderReady; + + private static class EntityHeader { + String key; + int dataSize; + } + + public BackupDataInput(FileDescriptor fd) { + if (fd == null) throw new NullPointerException(); + mBackupReader = ctor(fd); + if (mBackupReader == 0) { + throw new RuntimeException("Native initialization failed with fd=" + fd); + } + } + + protected void finalize() throws Throwable { + try { + dtor(mBackupReader); + } finally { + super.finalize(); + } + } + + public boolean readNextHeader() throws IOException { + int result = readNextHeader_native(mBackupReader, mHeader); + if (result == 0) { + // read successfully + mHeaderReady = true; + return true; + } else if (result > 0) { + // done + mHeaderReady = false; + return false; + } else { + // error + mHeaderReady = false; + throw new IOException("result=0x" + Integer.toHexString(result)); + } + } + + public String getKey() { + if (mHeaderReady) { + return mHeader.key; + } else { + throw new IllegalStateException("mHeaderReady=false"); + } + } + + public int getDataSize() { + if (mHeaderReady) { + return mHeader.dataSize; + } else { + throw new IllegalStateException("mHeaderReady=false"); + } + } + + public int readEntityData(byte[] data, int size) throws IOException { + if (mHeaderReady) { + int result = readEntityData_native(mBackupReader, data, size); + if (result >= 0) { + return result; + } else { + throw new IOException("result=0x" + Integer.toHexString(result)); + } + } else { + throw new IllegalStateException("mHeaderReady=false"); + } + } + + private native static int ctor(FileDescriptor fd); + private native static void dtor(int mBackupReader); + + private native int readNextHeader_native(int mBackupReader, EntityHeader entity); + private native int readEntityData_native(int mBackupReader, byte[] data, int size); +} diff --git a/core/java/android/backup/BackupDataOutput.java b/core/java/android/backup/BackupDataOutput.java index 25ae15b..1348d81 100644 --- a/core/java/android/backup/BackupDataOutput.java +++ b/core/java/android/backup/BackupDataOutput.java @@ -19,6 +19,7 @@ package android.backup; import android.content.Context; import java.io.FileDescriptor; +import java.io.IOException; /** @hide */ public class BackupDataOutput { @@ -37,6 +38,24 @@ public class BackupDataOutput { } } + public int writeEntityHeader(String key, int dataSize) throws IOException { + int result = writeEntityHeader_native(mBackupWriter, key, dataSize); + if (result >= 0) { + return result; + } else { + throw new IOException("result=0x" + Integer.toHexString(result)); + } + } + + public int writeEntityData(byte[] data, int size) throws IOException { + int result = writeEntityData_native(mBackupWriter, data, size); + if (result >= 0) { + return result; + } else { + throw new IOException("result=0x" + Integer.toHexString(result)); + } + } + protected void finalize() throws Throwable { try { dtor(mBackupWriter); @@ -47,5 +66,8 @@ public class BackupDataOutput { private native static int ctor(FileDescriptor fd); private native static void dtor(int mBackupWriter); + + private native static int writeEntityHeader_native(int mBackupWriter, String key, int dataSize); + private native static int writeEntityData_native(int mBackupWriter, byte[] data, int size); } diff --git a/core/java/android/backup/FileBackupHelper.java b/core/java/android/backup/FileBackupHelper.java index 99051bf..ed840bb 100644 --- a/core/java/android/backup/FileBackupHelper.java +++ b/core/java/android/backup/FileBackupHelper.java @@ -27,21 +27,56 @@ import java.io.FileDescriptor; public class FileBackupHelper { private static final String TAG = "FileBackupHelper"; + Context mContext; + String mKeyPrefix; + + public FileBackupHelper(Context context) { + mContext = context; + } + + public FileBackupHelper(Context context, String keyPrefix) { + mContext = context; + mKeyPrefix = keyPrefix; + } + /** * Based on oldState, determine which of the files from the application's data directory * need to be backed up, write them to the data stream, and fill in newState with the * state as it exists now. */ - public static void performBackup(Context context, - ParcelFileDescriptor oldState, BackupDataOutput data, + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState, String[] files) { - File base = context.getFilesDir(); + // file names + File base = mContext.getFilesDir(); final int N = files.length; String[] fullPaths = new String[N]; for (int i=0; i<N; i++) { fullPaths[i] = (new File(base, files[i])).getAbsolutePath(); } - performBackup_checked(oldState, data, newState, fullPaths, files); + + // keys + String[] keys = makeKeys(mKeyPrefix, files); + + // go + performBackup_checked(oldState, data, newState, fullPaths, keys); + } + + /** + * If keyPrefix is not null, prepend it to each of the strings in <code>original</code>; + * otherwise, return original. + */ + static String[] makeKeys(String keyPrefix, String[] original) { + if (keyPrefix != null) { + String[] keys; + final int N = original.length; + keys = new String[N]; + for (int i=0; i<N; i++) { + keys[i] = keyPrefix + ':' + original[i]; + } + return keys; + } else { + return original; + } } /** diff --git a/core/java/android/backup/RestoreHelper.java b/core/java/android/backup/RestoreHelper.java new file mode 100644 index 0000000..ebd9906 --- /dev/null +++ b/core/java/android/backup/RestoreHelper.java @@ -0,0 +1,23 @@ +/* + * 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. + */ + +package android.backup; + +/** @hide */ +public interface RestoreHelper { + public void performRestore(); +} + diff --git a/core/java/android/backup/RestoreHelperDistributor.java b/core/java/android/backup/RestoreHelperDistributor.java new file mode 100644 index 0000000..555ca79 --- /dev/null +++ b/core/java/android/backup/RestoreHelperDistributor.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +package android.backup; + +import java.util.HashMap; + +/** @hide */ +public class RestoreHelperDistributor { + HashMap<String,RestoreHelper> mHelpers; + + public void addHelper(String keyPrefix, RestoreHelper helper) { + mHelpers.put(keyPrefix, helper); + } +} diff --git a/core/java/android/backup/SharedPreferencesBackupHelper.java b/core/java/android/backup/SharedPreferencesBackupHelper.java index 923dc1b..cad79df 100644 --- a/core/java/android/backup/SharedPreferencesBackupHelper.java +++ b/core/java/android/backup/SharedPreferencesBackupHelper.java @@ -23,16 +23,33 @@ import java.io.FileDescriptor; /** @hide */ public class SharedPreferencesBackupHelper { - public static void performBackup(Context context, - ParcelFileDescriptor oldSnapshot, ParcelFileDescriptor newSnapshot, + private Context mContext; + private String mKeyPrefix; + + public SharedPreferencesBackupHelper(Context context) { + mContext = context; + } + + public SharedPreferencesBackupHelper(Context context, String keyPrefix) { + mContext = context; + mKeyPrefix = keyPrefix; + } + + public void performBackup(ParcelFileDescriptor oldSnapshot, ParcelFileDescriptor newSnapshot, BackupDataOutput data, String[] prefGroups) { + Context context = mContext; + // make filenames for the prefGroups final int N = prefGroups.length; String[] files = new String[N]; for (int i=0; i<N; i++) { - files[i] = context.getSharedPrefsFile(prefGroups[i]).toString(); + files[i] = context.getSharedPrefsFile(prefGroups[i]).getAbsolutePath(); } + // make keys if necessary + String[] keys = FileBackupHelper.makeKeys(mKeyPrefix, prefGroups); + + // go FileBackupHelper.performBackup_checked(oldSnapshot, data, newSnapshot, files, prefGroups); } } diff --git a/core/jni/Android.mk b/core/jni/Android.mk index ed8deb1..fee8396 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -117,6 +117,7 @@ LOCAL_SRC_FILES:= \ android_location_GpsLocationProvider.cpp \ com_android_internal_os_ZygoteInit.cpp \ com_android_internal_graphics_NativeUtils.cpp \ + android_backup_BackupDataInput.cpp \ android_backup_BackupDataOutput.cpp \ android_backup_FileBackupHelper.cpp diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 302f39e..6d829fc 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -155,6 +155,7 @@ extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env); extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env); extern int register_android_util_Base64(JNIEnv* env); 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); @@ -1127,6 +1128,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_ddm_DdmHandleNativeHeap), REG_JNI(register_android_util_Base64), REG_JNI(register_android_location_GpsLocationProvider), + REG_JNI(register_android_backup_BackupDataInput), REG_JNI(register_android_backup_BackupDataOutput), REG_JNI(register_android_backup_FileBackupHelper), }; diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp new file mode 100644 index 0000000..cb0ab5f --- /dev/null +++ b/core/jni/android_backup_BackupDataInput.cpp @@ -0,0 +1,162 @@ +/* + * 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; + +// android.backup.BackupDataInput$EntityHeader +static jfieldID s_keyField = 0; +static jfieldID s_dataSizeField = 0; + +static int +ctor_native(JNIEnv* env, jobject clazz, jobject fileDescriptor) +{ + int err; + + int fd = env->GetIntField(fileDescriptor, s_descriptorField); + if (fd == -1) { + return NULL; + } + + return (int)new BackupDataReader(fd); +} + +static void +dtor_native(JNIEnv* env, jobject clazz, int r) +{ + delete (BackupDataReader*)r; +} + +static jint +readNextHeader_native(JNIEnv* env, jobject clazz, int r, jobject entity) +{ + int err; + BackupDataReader* reader = (BackupDataReader*)r; + + err = reader->Status(); + if (err != 0) { + return err < 0 ? err : -1; + } + + while (reader->HasEntities()) { + int type; + + err = reader->ReadNextHeader(&type); + 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) { + 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; + } + } + // done + return 1; +} + +static jint +readEntityData_native(JNIEnv* env, jobject clazz, int r, jbyteArray data, int size) +{ + int err; + BackupDataReader* reader = (BackupDataReader*)r; + + if (env->GetArrayLength(data) > size) { + // size mismatch + return -1; + } + + jbyte* dataBytes = env->GetByteArrayElements(data, NULL); + if (dataBytes == NULL) { + return -1; + } + + err = reader->ReadEntityData(dataBytes, size); + + env->ReleaseByteArrayElements(data, dataBytes, 0); + + return err; +} + +static const JNINativeMethod g_methods[] = { + { "ctor", "(Ljava/io/FileDescriptor;)I", (void*)ctor_native }, + { "dtor", "(I)V", (void*)dtor_native }, + { "readNextHeader_native", "(ILandroid/backup/BackupDataInput$EntityHeader;)I", + (void*)readNextHeader_native }, + { "readEntityData_native", "(I[BI)I", (void*)readEntityData_native }, +}; + +int register_android_backup_BackupDataInput(JNIEnv* env) +{ + LOGD("register_android_backup_BackupDataInput"); + + 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"); + + clazz = env->FindClass("android/backup/BackupDataInput$EntityHeader"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.backup.BackupDataInput.EntityHeader"); + s_keyField = env->GetFieldID(clazz, "key", "Ljava/lang/String;"); + LOG_FATAL_IF(s_keyField == NULL, + "Unable to find key field in android.backup.BackupDataInput.EntityHeader"); + s_dataSizeField = env->GetFieldID(clazz, "dataSize", "Ljava/lang/String;"); + LOG_FATAL_IF(s_dataSizeField == NULL, + "Unable to find dataSize field in android.backup.BackupDataInput.EntityHeader"); + + return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupDataInput", + g_methods, NELEM(g_methods)); +} + +} diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp index aab1233..9660326 100644 --- a/core/jni/android_backup_BackupDataOutput.cpp +++ b/core/jni/android_backup_BackupDataOutput.cpp @@ -28,7 +28,7 @@ namespace android static jfieldID s_descriptorField = 0; static int -ctor_native(JNIEnv* env, jobject This, jobject fileDescriptor) +ctor_native(JNIEnv* env, jobject clazz, jobject fileDescriptor) { int err; @@ -41,14 +41,57 @@ ctor_native(JNIEnv* env, jobject This, jobject fileDescriptor) } static void -dtor_native(JNIEnv* env, jobject This, int fd) +dtor_native(JNIEnv* env, jobject clazz, int w) { - delete (BackupDataWriter*)fd; + delete (BackupDataWriter*)w; +} + +static jint +writeEntityHeader_native(JNIEnv* env, jobject clazz, int w, jstring key, int dataSize) +{ + int err; + BackupDataWriter* writer = (BackupDataWriter*)w; + + const char* keyUTF = env->GetStringUTFChars(key, NULL); + if (keyUTF == NULL) { + return -1; + } + + err = writer->WriteEntityHeader(String8(keyUTF), dataSize); + + env->ReleaseStringUTFChars(key, keyUTF); + + return err; +} + +static jint +writeEntityData_native(JNIEnv* env, jobject clazz, int w, jbyteArray data, int size) +{ + int err; + BackupDataWriter* writer = (BackupDataWriter*)w; + + if (env->GetArrayLength(data) > size) { + // size mismatch + return -1; + } + + jbyte* dataBytes = env->GetByteArrayElements(data, NULL); + if (dataBytes == NULL) { + return -1; + } + + err = writer->WriteEntityData(dataBytes, size); + + env->ReleaseByteArrayElements(data, dataBytes, JNI_ABORT); + + return err; } static const JNINativeMethod g_methods[] = { { "ctor", "(Ljava/io/FileDescriptor;)I", (void*)ctor_native }, { "dtor", "(I)V", (void*)dtor_native }, + { "writeEntityHeader_native", "(ILjava/lang/String;I)I", (void*)writeEntityHeader_native }, + { "writeEntityData_native", "(I[BI)I", (void*)writeEntityData_native }, }; int register_android_backup_BackupDataOutput(JNIEnv* env) diff --git a/core/jni/android_backup_FileBackupHelper.cpp b/core/jni/android_backup_FileBackupHelper.cpp index 2ee064b..418db8a 100644 --- a/core/jni/android_backup_FileBackupHelper.cpp +++ b/core/jni/android_backup_FileBackupHelper.cpp @@ -25,6 +25,7 @@ namespace android { +// java.io.FileDescriptor static jfieldID s_descriptorField = 0; static int @@ -34,7 +35,6 @@ performBackup_native(JNIEnv* env, jobject clazz, jobject oldState, int data, int err; // all parameters have already been checked against null - LOGD("oldState=%p newState=%p data=%p\n", oldState, newState, data); int oldStateFD = oldState != NULL ? env->GetIntField(oldState, s_descriptorField) : -1; int newStateFD = env->GetIntField(newState, s_descriptorField); BackupDataWriter* dataStream = (BackupDataWriter*)data; @@ -74,8 +74,6 @@ static const JNINativeMethod g_methods[] = { int register_android_backup_FileBackupHelper(JNIEnv* env) { - LOGD("register_android_backup_FileBackupHelper"); - jclass clazz; clazz = env->FindClass("java/io/FileDescriptor"); diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index 120f23d..8c9f875 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -327,7 +327,7 @@ BackupDataReader::ReadAppHeader(String8* packageName, int* cookie) } size_t size = m_header.app.packageLen; char* buf = packageName->lockBuffer(size); - if (packageName == NULL) { + if (buf == NULL) { packageName->unlockBuffer(); m_status = ENOMEM; return m_status; diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java index 11e520e..a370d69 100644 --- a/tests/backup/src/com/android/backuptest/BackupTestAgent.java +++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java @@ -30,7 +30,8 @@ public class BackupTestAgent extends BackupAgent public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) { Log.d(TAG, "onBackup"); - FileBackupHelper.performBackup(this, oldState, data, newState, new String[] { + FileBackupHelper helper = new FileBackupHelper(this); + helper.performBackup(oldState, data, newState, new String[] { BackupTestActivity.FILE_NAME }); } |