Christopher Tate | 4a627c7 | 2011-04-01 14:43:32 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #define LOG_TAG "FullBackup_native" |
| 18 | #include <utils/Log.h> |
| 19 | #include <utils/String8.h> |
| 20 | |
| 21 | #include "JNIHelp.h" |
| 22 | #include <android_runtime/AndroidRuntime.h> |
| 23 | |
Mathias Agopian | b13b9bd | 2012-02-17 18:27:36 -0800 | [diff] [blame] | 24 | #include <androidfw/BackupHelpers.h> |
Christopher Tate | 4a627c7 | 2011-04-01 14:43:32 -0700 | [diff] [blame] | 25 | |
| 26 | #include <string.h> |
| 27 | |
| 28 | namespace android |
| 29 | { |
| 30 | |
| 31 | // android.app.backup.BackupDataOutput |
| 32 | static struct { |
| 33 | // This is actually a native pointer to the underlying BackupDataWriter instance |
| 34 | jfieldID mBackupWriter; |
| 35 | } sBackupDataOutput; |
| 36 | |
| 37 | /* |
| 38 | * Write files to the given output. This implementation does *not* create |
| 39 | * a standalone archive suitable for restore on its own. In particular, the identification of |
| 40 | * the application's name etc is not in-band here; it's assumed that the calling code has |
| 41 | * taken care of supplying that information previously in the output stream. |
| 42 | * |
| 43 | * The file format is 'tar's, with special semantics applied by use of a "fake" directory |
| 44 | * hierarchy within the tar stream: |
| 45 | * |
| 46 | * apps/packagename/a/Filename.apk - this is an actual application binary, which will be |
| 47 | * installed on the target device at restore time. These need to appear first in the tar |
| 48 | * stream. |
| 49 | * apps/packagename/obb/[relpath] - OBB containers belonging the app |
| 50 | * apps/packagename/r/[relpath] - these are files at the root of the app's data tree |
| 51 | * apps/packagename/f/[relpath] - this is a file within the app's getFilesDir() tree, stored |
| 52 | * at [relpath] relative to the top of that tree. |
| 53 | * apps/packagename/db/[relpath] - as with "files" but for the getDatabasePath() tree |
| 54 | * apps/packagename/sp/[relpath] - as with "files" but for the getSharedPrefsFile() tree |
| 55 | * apps/packagename/c/[relpath] - as with "files" but for the getCacheDir() tree |
| 56 | * |
| 57 | * and for the shared storage hierarchy: |
| 58 | * |
| 59 | * shared/[relpaths] - files belonging in the device's shared storage location. This will |
| 60 | * *not* include .obb files; those are saved with their owning apps. |
| 61 | * |
| 62 | * This method writes one file data block. 'domain' is the name of the appropriate pseudo- |
| 63 | * directory to be applied for this file; 'linkdomain' is the pseudo-dir for a relative |
| 64 | * symlink's antecedent. |
| 65 | * |
| 66 | * packagename: the package name to use as the top level directory tag |
| 67 | * domain: which semantic name the file is to be stored under (a, r, f, db, etc) |
| 68 | * linkdomain: where a symlink points for purposes of rewriting; current unused |
| 69 | * rootpath: prefix to be snipped from full path when encoding in tar |
| 70 | * path: absolute path to the file to be saved |
| 71 | * dataOutput: the BackupDataOutput object that we're saving into |
| 72 | */ |
Ashok Bhat | 58b8b24 | 2014-01-02 16:52:41 +0000 | [diff] [blame^] | 73 | static jint backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj, |
Christopher Tate | 4a627c7 | 2011-04-01 14:43:32 -0700 | [diff] [blame] | 74 | jstring domainObj, jstring linkdomain, |
| 75 | jstring rootpathObj, jstring pathObj, jobject dataOutputObj) { |
Christopher Tate | b0628bf | 2011-06-02 15:08:13 -0700 | [diff] [blame] | 76 | int ret; |
| 77 | |
Christopher Tate | 4a627c7 | 2011-04-01 14:43:32 -0700 | [diff] [blame] | 78 | // Extract the various strings, allowing for null object pointers |
Christopher Tate | ca1605e | 2011-09-23 13:12:49 -0700 | [diff] [blame] | 79 | const char* packagenamechars = (packageNameObj) ? env->GetStringUTFChars(packageNameObj, NULL) : NULL; |
| 80 | const char* rootchars = (rootpathObj) ? env->GetStringUTFChars(rootpathObj, NULL) : NULL; |
| 81 | const char* pathchars = (pathObj) ? env->GetStringUTFChars(pathObj, NULL) : NULL; |
| 82 | const char* domainchars = (domainObj) ? env->GetStringUTFChars(domainObj, NULL) : NULL; |
Christopher Tate | 4a627c7 | 2011-04-01 14:43:32 -0700 | [diff] [blame] | 83 | |
| 84 | String8 packageName(packagenamechars ? packagenamechars : ""); |
| 85 | String8 rootpath(rootchars ? rootchars : ""); |
| 86 | String8 path(pathchars ? pathchars : ""); |
| 87 | String8 domain(domainchars ? domainchars : ""); |
| 88 | |
| 89 | if (domainchars) env->ReleaseStringUTFChars(domainObj, domainchars); |
| 90 | if (pathchars) env->ReleaseStringUTFChars(pathObj, pathchars); |
| 91 | if (rootchars) env->ReleaseStringUTFChars(rootpathObj, rootchars); |
| 92 | if (packagenamechars) env->ReleaseStringUTFChars(packageNameObj, packagenamechars); |
| 93 | |
| 94 | // Extract the data output fd |
Ashok Bhat | 58b8b24 | 2014-01-02 16:52:41 +0000 | [diff] [blame^] | 95 | BackupDataWriter* writer = (BackupDataWriter*) env->GetLongField(dataOutputObj, |
Christopher Tate | 4a627c7 | 2011-04-01 14:43:32 -0700 | [diff] [blame] | 96 | sBackupDataOutput.mBackupWriter); |
| 97 | |
| 98 | // Validate |
| 99 | if (!writer) { |
Steve Block | 3762c31 | 2012-01-06 19:20:56 +0000 | [diff] [blame] | 100 | ALOGE("No output stream provided [%s]", path.string()); |
Ashok Bhat | 58b8b24 | 2014-01-02 16:52:41 +0000 | [diff] [blame^] | 101 | return (jint) -1; |
Christopher Tate | 4a627c7 | 2011-04-01 14:43:32 -0700 | [diff] [blame] | 102 | } |
| 103 | |
| 104 | if (path.length() < rootpath.length()) { |
Steve Block | 3762c31 | 2012-01-06 19:20:56 +0000 | [diff] [blame] | 105 | ALOGE("file path [%s] shorter than root path [%s]", |
Christopher Tate | 4a627c7 | 2011-04-01 14:43:32 -0700 | [diff] [blame] | 106 | path.string(), rootpath.string()); |
Ashok Bhat | 58b8b24 | 2014-01-02 16:52:41 +0000 | [diff] [blame^] | 107 | return (jint) -1; |
Christopher Tate | 4a627c7 | 2011-04-01 14:43:32 -0700 | [diff] [blame] | 108 | } |
| 109 | |
Ashok Bhat | 58b8b24 | 2014-01-02 16:52:41 +0000 | [diff] [blame^] | 110 | return (jint) write_tarfile(packageName, domain, rootpath, path, writer); |
Christopher Tate | 4a627c7 | 2011-04-01 14:43:32 -0700 | [diff] [blame] | 111 | } |
| 112 | |
| 113 | static const JNINativeMethod g_methods[] = { |
| 114 | { "backupToTar", |
| 115 | "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/BackupDataOutput;)I", |
| 116 | (void*)backupToTar }, |
| 117 | }; |
| 118 | |
| 119 | int register_android_app_backup_FullBackup(JNIEnv* env) |
| 120 | { |
| 121 | jclass clazz = env->FindClass("android/app/backup/BackupDataOutput"); |
| 122 | LOG_FATAL_IF(clazz == NULL, "Unable to find class android.app.backup.BackupDataOutput"); |
| 123 | |
Ashok Bhat | 58b8b24 | 2014-01-02 16:52:41 +0000 | [diff] [blame^] | 124 | sBackupDataOutput.mBackupWriter = env->GetFieldID(clazz, "mBackupWriter", "J"); |
Christopher Tate | 4a627c7 | 2011-04-01 14:43:32 -0700 | [diff] [blame] | 125 | LOG_FATAL_IF(sBackupDataOutput.mBackupwriter == NULL, |
| 126 | "Unable to find mBackupWriter field in android.app.backup.BackupDataOutput"); |
| 127 | |
| 128 | return AndroidRuntime::registerNativeMethods(env, "android/app/backup/FullBackup", |
| 129 | g_methods, NELEM(g_methods)); |
| 130 | } |
| 131 | |
| 132 | } |