blob: 63b2e2aaee126a6e1da05fee1dcde0b04cb21e2e [file] [log] [blame]
/*
* Copyright (C) 2011 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 "FullBackup_native"
#include <sys/stat.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <androidfw/BackupHelpers.h>
#include "core_jni_helpers.h"
#include <string.h>
namespace android
{
// android.app.backup.FullBackupDataOutput
static struct {
jfieldID mData; // type android.app.backup.BackupDataOutput
jmethodID addSize;
} sFullBackupDataOutput;
// android.app.backup.BackupDataOutput
static struct {
// This is actually a native pointer to the underlying BackupDataWriter instance
jfieldID mBackupWriter;
} sBackupDataOutput;
/*
* Write files to the given output. This implementation does *not* create
* a standalone archive suitable for restore on its own. In particular, the identification of
* the application's name etc is not in-band here; it's assumed that the calling code has
* taken care of supplying that information previously in the output stream.
*
* The file format is 'tar's, with special semantics applied by use of a "fake" directory
* hierarchy within the tar stream:
*
* apps/packagename/a/Filename.apk - this is an actual application binary, which will be
* installed on the target device at restore time. These need to appear first in the tar
* stream.
* apps/packagename/obb/[relpath] - OBB containers belonging the app
* apps/packagename/r/[relpath] - these are files at the root of the app's data tree
* apps/packagename/f/[relpath] - this is a file within the app's getFilesDir() tree, stored
* at [relpath] relative to the top of that tree.
* apps/packagename/db/[relpath] - as with "files" but for the getDatabasePath() tree
* apps/packagename/sp/[relpath] - as with "files" but for the getSharedPrefsFile() tree
* apps/packagename/c/[relpath] - as with "files" but for the getCacheDir() tree
*
* and for the shared storage hierarchy:
*
* shared/[relpaths] - files belonging in the device's shared storage location. This will
* *not* include .obb files; those are saved with their owning apps.
*
* This method writes one file data block. 'domain' is the name of the appropriate pseudo-
* directory to be applied for this file; 'linkdomain' is the pseudo-dir for a relative
* symlink's antecedent.
*
* packagename: the package name to use as the top level directory tag
* domain: which semantic name the file is to be stored under (a, r, f, db, etc)
* linkdomain: where a symlink points for purposes of rewriting; current unused
* rootpath: prefix to be snipped from full path when encoding in tar
* path: absolute path to the file to be saved
* dataOutput: the FullBackupDataOutput object that we're saving into
*/
static jint backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
jstring domainObj, jstring linkdomain,
jstring rootpathObj, jstring pathObj, jobject dataOutputObj) {
// Extract the various strings, allowing for null object pointers
const char* packagenamechars = (packageNameObj) ? env->GetStringUTFChars(packageNameObj, NULL) : NULL;
const char* rootchars = (rootpathObj) ? env->GetStringUTFChars(rootpathObj, NULL) : NULL;
const char* pathchars = (pathObj) ? env->GetStringUTFChars(pathObj, NULL) : NULL;
const char* domainchars = (domainObj) ? env->GetStringUTFChars(domainObj, NULL) : NULL;
String8 packageName(packagenamechars ? packagenamechars : "");
String8 rootpath(rootchars ? rootchars : "");
String8 path(pathchars ? pathchars : "");
String8 domain(domainchars ? domainchars : "");
if (domainchars) env->ReleaseStringUTFChars(domainObj, domainchars);
if (pathchars) env->ReleaseStringUTFChars(pathObj, pathchars);
if (rootchars) env->ReleaseStringUTFChars(rootpathObj, rootchars);
if (packagenamechars) env->ReleaseStringUTFChars(packageNameObj, packagenamechars);
// Extract the data output fd. 'writer' ends up NULL in the measure-only case.
jobject bdo = env->GetObjectField(dataOutputObj, sFullBackupDataOutput.mData);
BackupDataWriter* writer = (bdo != NULL)
? (BackupDataWriter*) env->GetLongField(bdo, sBackupDataOutput.mBackupWriter)
: NULL;
if (path.length() < rootpath.length()) {
ALOGE("file path [%s] shorter than root path [%s]",
path.string(), rootpath.string());
return (jint) -1;
}
off_t tarSize = 0;
jint err = write_tarfile(packageName, domain, rootpath, path, &tarSize, writer);
if (!err) {
//ALOGI("measured [%s] at %lld", path.string(), (long long) tarSize);
env->CallVoidMethod(dataOutputObj, sFullBackupDataOutput.addSize, (jlong) tarSize);
}
return err;
}
static const JNINativeMethod g_methods[] = {
{ "backupToTar",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/FullBackupDataOutput;)I",
(void*)backupToTar },
};
int register_android_app_backup_FullBackup(JNIEnv* env)
{
jclass fbdoClazz = FindClassOrDie(env, "android/app/backup/FullBackupDataOutput");
sFullBackupDataOutput.mData = GetFieldIDOrDie(env, fbdoClazz, "mData", "Landroid/app/backup/BackupDataOutput;");
sFullBackupDataOutput.addSize = GetMethodIDOrDie(env, fbdoClazz, "addSize", "(J)V");
jclass bdoClazz = FindClassOrDie(env, "android/app/backup/BackupDataOutput");
sBackupDataOutput.mBackupWriter = GetFieldIDOrDie(env, bdoClazz, "mBackupWriter", "J");
return RegisterMethodsOrDie(env, "android/app/backup/FullBackup", g_methods, NELEM(g_methods));
}
}