summaryrefslogtreecommitdiffstats
path: root/core/jni/android_app_backup_FullBackup.cpp
blob: 63b2e2aaee126a6e1da05fee1dcde0b04cb21e2e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
 * 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));
}

}