Merge change 5459 into donut
* changes: Make the BackupHelperDispatcher properly handle multiple helpers.
This commit is contained in:
@@ -67,7 +67,7 @@ public abstract class BackupAgent extends ContextWrapper {
|
||||
* here after writing the requested data to dataFd.
|
||||
*/
|
||||
public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
|
||||
ParcelFileDescriptor newState);
|
||||
ParcelFileDescriptor newState) throws IOException;
|
||||
|
||||
/**
|
||||
* The application is being restored from backup, and should replace any
|
||||
@@ -120,6 +120,9 @@ public abstract class BackupAgent extends ContextWrapper {
|
||||
BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
|
||||
try {
|
||||
BackupAgent.this.onBackup(oldState, output, newState);
|
||||
} catch (IOException ex) {
|
||||
Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
|
||||
throw new RuntimeException(ex);
|
||||
} catch (RuntimeException ex) {
|
||||
Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
|
||||
throw ex;
|
||||
|
||||
@@ -34,7 +34,7 @@ public class BackupHelperAgent extends BackupAgent {
|
||||
|
||||
@Override
|
||||
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
|
||||
ParcelFileDescriptor newState) {
|
||||
ParcelFileDescriptor newState) throws IOException {
|
||||
mDispatcher.performBackup(oldState, data, newState);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,10 @@ package android.backup;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.FileDescriptor;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -27,6 +30,11 @@ import java.util.Map;
|
||||
public class BackupHelperDispatcher {
|
||||
private static final String TAG = "BackupHelperDispatcher";
|
||||
|
||||
private static class Header {
|
||||
int chunkSize; // not including the header
|
||||
String keyPrefix;
|
||||
}
|
||||
|
||||
TreeMap<String,BackupHelper> mHelpers = new TreeMap<String,BackupHelper>();
|
||||
|
||||
public BackupHelperDispatcher() {
|
||||
@@ -36,13 +44,63 @@ public class BackupHelperDispatcher {
|
||||
mHelpers.put(keyPrefix, helper);
|
||||
}
|
||||
|
||||
/** TODO: Make this save and restore the key prefix. */
|
||||
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
|
||||
ParcelFileDescriptor newState) {
|
||||
// Write out the state files -- mHelpers is a TreeMap, so the order is well defined.
|
||||
for (Map.Entry<String,BackupHelper> entry: mHelpers.entrySet()) {
|
||||
data.setKeyPrefix(entry.getKey());
|
||||
entry.getValue().performBackup(oldState, data, newState);
|
||||
ParcelFileDescriptor newState) throws IOException {
|
||||
// First, do the helpers that we've already done, since they're already in the state
|
||||
// file.
|
||||
int err;
|
||||
Header header = new Header();
|
||||
TreeMap<String,BackupHelper> helpers = (TreeMap<String,BackupHelper>)mHelpers.clone();
|
||||
FileDescriptor oldStateFD = null;
|
||||
FileDescriptor newStateFD = newState.getFileDescriptor();
|
||||
|
||||
if (oldState != null) {
|
||||
oldStateFD = oldState.getFileDescriptor();
|
||||
while ((err = readHeader_native(header, oldStateFD)) >= 0) {
|
||||
if (err == 0) {
|
||||
BackupHelper helper = helpers.get(header.keyPrefix);
|
||||
Log.d(TAG, "handling existing helper '" + header.keyPrefix + "' " + helper);
|
||||
if (helper != null) {
|
||||
doOneBackup(oldState, data, newState, header, helper);
|
||||
helpers.remove(header.keyPrefix);
|
||||
} else {
|
||||
skipChunk_native(oldStateFD, header.chunkSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then go through and do the rest that we haven't done.
|
||||
for (Map.Entry<String,BackupHelper> entry: helpers.entrySet()) {
|
||||
header.keyPrefix = entry.getKey();
|
||||
Log.d(TAG, "handling new helper '" + header.keyPrefix + "'");
|
||||
BackupHelper helper = entry.getValue();
|
||||
doOneBackup(oldState, data, newState, header, helper);
|
||||
}
|
||||
}
|
||||
|
||||
private void doOneBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
|
||||
ParcelFileDescriptor newState, Header header, BackupHelper helper)
|
||||
throws IOException {
|
||||
int err;
|
||||
FileDescriptor newStateFD = newState.getFileDescriptor();
|
||||
|
||||
// allocate space for the header in the file
|
||||
int pos = allocateHeader_native(header, newStateFD);
|
||||
if (pos < 0) {
|
||||
throw new IOException("allocateHeader_native failed (error " + pos + ")");
|
||||
}
|
||||
|
||||
data.setKeyPrefix(header.keyPrefix);
|
||||
|
||||
// do the backup
|
||||
helper.performBackup(oldState, data, newState);
|
||||
|
||||
// fill in the header (seeking back to pos). The file pointer will be returned to
|
||||
// where it was at the end of performBackup. Header.chunkSize will not be filled in.
|
||||
err = writeHeader_native(header, newStateFD, pos);
|
||||
if (err != 0) {
|
||||
throw new IOException("writeHeader_native failed (error " + err + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,5 +141,11 @@ public class BackupHelperDispatcher {
|
||||
helper.writeRestoreSnapshot(newState);
|
||||
}
|
||||
}
|
||||
|
||||
private static native int readHeader_native(Header h, FileDescriptor fd);
|
||||
private static native int skipChunk_native(FileDescriptor fd, int bytesToSkip);
|
||||
|
||||
private static native int allocateHeader_native(Header h, FileDescriptor fd);
|
||||
private static native int writeHeader_native(Header h, FileDescriptor fd, int pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,8 @@ LOCAL_SRC_FILES:= \
|
||||
com_android_internal_graphics_NativeUtils.cpp \
|
||||
android_backup_BackupDataInput.cpp \
|
||||
android_backup_BackupDataOutput.cpp \
|
||||
android_backup_FileBackupHelperBase.cpp
|
||||
android_backup_FileBackupHelperBase.cpp \
|
||||
android_backup_BackupHelperDispatcher.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_FileBackupHelperBase(JNIEnv *env);
|
||||
extern int register_android_backup_BackupHelperDispatcher(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_FileBackupHelperBase),
|
||||
REG_JNI(register_android_backup_BackupHelperDispatcher),
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
251
core/jni/android_backup_BackupHelperDispatcher.cpp
Normal file
251
core/jni/android_backup_BackupHelperDispatcher.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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 "BackupHelperDispatcher_native"
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include "JNIHelp.h"
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define VERSION_1_HEADER 0x01706c48 // 'Hlp'1 little endian
|
||||
|
||||
namespace android
|
||||
{
|
||||
|
||||
struct chunk_header_v1 {
|
||||
int headerSize;
|
||||
int version;
|
||||
int dataSize; // corresponds to Header.chunkSize
|
||||
int nameLength; // not including the NULL terminator, which is not written to the file
|
||||
};
|
||||
|
||||
// java.io.FileDescriptor
|
||||
static jfieldID s_descriptorField = 0;
|
||||
static jfieldID s_chunkSizeField = 0;
|
||||
static jfieldID s_keyPrefixField = 0;
|
||||
|
||||
static int
|
||||
readHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
|
||||
{
|
||||
chunk_header_v1 flattenedHeader;
|
||||
int fd;
|
||||
ssize_t amt;
|
||||
String8 keyPrefix;
|
||||
char* buf;
|
||||
|
||||
fd = env->GetIntField(fdObj, s_descriptorField);
|
||||
|
||||
amt = read(fd, &flattenedHeader.headerSize, sizeof(flattenedHeader.headerSize));
|
||||
if (amt != sizeof(flattenedHeader.headerSize)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int remainingHeader = flattenedHeader.headerSize - sizeof(flattenedHeader.headerSize);
|
||||
|
||||
if (flattenedHeader.headerSize < (int)sizeof(chunk_header_v1)) {
|
||||
LOGW("Skipping unknown header: %d bytes", flattenedHeader.headerSize);
|
||||
if (remainingHeader > 0) {
|
||||
lseek(fd, remainingHeader, SEEK_CUR);
|
||||
// >0 means skip this chunk
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
amt = read(fd, &flattenedHeader.version,
|
||||
sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize));
|
||||
if (amt <= 0) {
|
||||
LOGW("Failed reading chunk header");
|
||||
return -1;
|
||||
}
|
||||
remainingHeader -= sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize);
|
||||
|
||||
if (flattenedHeader.version != VERSION_1_HEADER) {
|
||||
LOGW("Skipping unknown header version: 0x%08x, %d bytes", flattenedHeader.version,
|
||||
flattenedHeader.headerSize);
|
||||
if (remainingHeader > 0) {
|
||||
lseek(fd, remainingHeader, SEEK_CUR);
|
||||
// >0 means skip this chunk
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (flattenedHeader.dataSize < 0 || flattenedHeader.nameLength < 0 ||
|
||||
remainingHeader < flattenedHeader.nameLength) {
|
||||
LOGW("Malformed V1 header remainingHeader=%d dataSize=%d nameLength=%d", remainingHeader,
|
||||
flattenedHeader.dataSize, flattenedHeader.nameLength);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = keyPrefix.lockBuffer(flattenedHeader.nameLength);
|
||||
if (buf == NULL) {
|
||||
LOGW("unable to allocate %d bytes", flattenedHeader.nameLength);
|
||||
return -1;
|
||||
}
|
||||
|
||||
amt = read(fd, buf, flattenedHeader.nameLength);
|
||||
|
||||
keyPrefix.unlockBuffer(flattenedHeader.nameLength);
|
||||
|
||||
remainingHeader -= flattenedHeader.nameLength;
|
||||
|
||||
LOGD("remainingHeader=%d", remainingHeader);
|
||||
|
||||
if (remainingHeader > 0) {
|
||||
lseek(fd, remainingHeader, SEEK_CUR);
|
||||
}
|
||||
|
||||
env->SetIntField(headerObj, s_chunkSizeField, flattenedHeader.dataSize);
|
||||
env->SetObjectField(headerObj, s_keyPrefixField, env->NewStringUTF(keyPrefix.string()));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
skipChunk_native(JNIEnv* env, jobject clazz, jobject fdObj, jint bytesToSkip)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = env->GetIntField(fdObj, s_descriptorField);
|
||||
|
||||
lseek(fd, bytesToSkip, SEEK_CUR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
padding_len(int len)
|
||||
{
|
||||
len = len % 4;
|
||||
return len == 0 ? len : 4 - len;
|
||||
}
|
||||
|
||||
static int
|
||||
allocateHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
|
||||
{
|
||||
int pos;
|
||||
jstring nameObj;
|
||||
int nameLength;
|
||||
int namePadding;
|
||||
int headerSize;
|
||||
int fd;
|
||||
|
||||
fd = env->GetIntField(fdObj, s_descriptorField);
|
||||
|
||||
nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
|
||||
|
||||
nameLength = env->GetStringUTFLength(nameObj);
|
||||
namePadding = padding_len(nameLength);
|
||||
|
||||
headerSize = sizeof(chunk_header_v1) + nameLength + namePadding;
|
||||
|
||||
pos = lseek(fd, 0, SEEK_CUR);
|
||||
|
||||
lseek(fd, headerSize, SEEK_CUR);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static int
|
||||
writeHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj, jint pos)
|
||||
{
|
||||
int err;
|
||||
chunk_header_v1 header;
|
||||
int fd;
|
||||
int namePadding;
|
||||
int prevPos;
|
||||
jstring nameObj;
|
||||
const char* buf;
|
||||
|
||||
fd = env->GetIntField(fdObj, s_descriptorField);
|
||||
prevPos = lseek(fd, 0, SEEK_CUR);
|
||||
|
||||
nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
|
||||
header.nameLength = env->GetStringUTFLength(nameObj);
|
||||
namePadding = padding_len(header.nameLength);
|
||||
|
||||
header.headerSize = sizeof(chunk_header_v1) + header.nameLength + namePadding;
|
||||
header.version = VERSION_1_HEADER;
|
||||
|
||||
lseek(fd, pos, SEEK_SET);
|
||||
err = write(fd, &header, sizeof(chunk_header_v1));
|
||||
if (err != sizeof(chunk_header_v1)) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
buf = env->GetStringUTFChars(nameObj, NULL);
|
||||
err = write(fd, buf, header.nameLength);
|
||||
env->ReleaseStringUTFChars(nameObj, buf);
|
||||
if (err != header.nameLength) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (namePadding != 0) {
|
||||
int zero = 0;
|
||||
err = write(fd, &zero, namePadding);
|
||||
if (err != namePadding) {
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
|
||||
lseek(fd, prevPos, SEEK_SET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const JNINativeMethod g_methods[] = {
|
||||
{ "readHeader_native",
|
||||
"(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
|
||||
(void*)readHeader_native },
|
||||
{ "skipChunk_native",
|
||||
"(Ljava/io/FileDescriptor;I)I",
|
||||
(void*)skipChunk_native },
|
||||
{ "allocateHeader_native",
|
||||
"(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
|
||||
(void*)allocateHeader_native },
|
||||
{ "writeHeader_native",
|
||||
"(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;I)I",
|
||||
(void*)writeHeader_native },
|
||||
};
|
||||
|
||||
int register_android_backup_BackupHelperDispatcher(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");
|
||||
|
||||
clazz = env->FindClass("android/backup/BackupHelperDispatcher$Header");
|
||||
LOG_FATAL_IF(clazz == NULL,
|
||||
"Unable to find class android.backup.BackupHelperDispatcher.Header");
|
||||
s_chunkSizeField = env->GetFieldID(clazz, "chunkSize", "I");
|
||||
LOG_FATAL_IF(s_chunkSizeField == NULL,
|
||||
"Unable to find chunkSize field in android.backup.BackupHelperDispatcher.Header");
|
||||
s_keyPrefixField = env->GetFieldID(clazz, "keyPrefix", "Ljava/lang/String;");
|
||||
LOG_FATAL_IF(s_keyPrefixField == NULL,
|
||||
"Unable to find keyPrefix field in android.backup.BackupHelperDispatcher.Header");
|
||||
|
||||
return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupHelperDispatcher",
|
||||
g_methods, NELEM(g_methods));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,7 @@ public class BackupTestAgent extends BackupHelperAgent
|
||||
{
|
||||
public void onCreate() {
|
||||
addHelper("data_files", new FileBackupHelper(this, BackupTestActivity.FILE_NAME));
|
||||
addHelper("more_data_files", new FileBackupHelper(this, "another_file.txt"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user