Merge change 5459 into donut

* changes:
  Make the BackupHelperDispatcher properly handle multiple helpers.
This commit is contained in:
Android (Google) Code Review
2009-06-25 18:04:05 -07:00
7 changed files with 331 additions and 9 deletions

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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) \

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_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),
};
/*

View 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));
}
}

View File

@@ -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"));
}
}