Christopher Tate | 4a627c7 | 2011-04-01 14:43:32 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2009 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 | package android.app.backup; |
| 18 | |
| 19 | import android.content.pm.ApplicationInfo; |
| 20 | import android.content.pm.PackageManager; |
| 21 | import android.os.Environment; |
| 22 | import android.os.ParcelFileDescriptor; |
| 23 | import android.util.Log; |
| 24 | |
| 25 | import libcore.io.Libcore; |
| 26 | import libcore.io.ErrnoException; |
| 27 | import libcore.io.OsConstants; |
| 28 | import libcore.io.StructStat; |
| 29 | |
| 30 | import java.io.File; |
| 31 | import java.util.HashSet; |
| 32 | import java.util.LinkedList; |
| 33 | |
| 34 | /** |
| 35 | * Backs up an application's entire /data/data/<package>/... file system. This |
| 36 | * class is used by the desktop full backup mechanism and is not intended for direct |
| 37 | * use by applications. |
| 38 | * |
| 39 | * {@hide} |
| 40 | */ |
| 41 | |
| 42 | public class FullBackupAgent extends BackupAgent { |
| 43 | // !!! TODO: turn off debugging |
| 44 | private static final String TAG = "FullBackupAgent"; |
| 45 | private static final boolean DEBUG = true; |
| 46 | |
| 47 | PackageManager mPm; |
| 48 | |
| 49 | private String mMainDir; |
| 50 | private String mFilesDir; |
| 51 | private String mDatabaseDir; |
| 52 | private String mSharedPrefsDir; |
| 53 | private String mCacheDir; |
| 54 | private String mLibDir; |
| 55 | |
| 56 | @Override |
| 57 | public void onCreate() { |
| 58 | mPm = getPackageManager(); |
| 59 | try { |
| 60 | ApplicationInfo appInfo = mPm.getApplicationInfo(getPackageName(), 0); |
| 61 | mMainDir = new File(appInfo.dataDir).getAbsolutePath(); |
| 62 | } catch (PackageManager.NameNotFoundException e) { |
| 63 | Log.e(TAG, "Unable to find package " + getPackageName()); |
| 64 | throw new RuntimeException(e); |
| 65 | } |
| 66 | |
| 67 | mFilesDir = getFilesDir().getAbsolutePath(); |
| 68 | mDatabaseDir = getDatabasePath("foo").getParentFile().getAbsolutePath(); |
| 69 | mSharedPrefsDir = getSharedPrefsFile("foo").getParentFile().getAbsolutePath(); |
| 70 | mCacheDir = getCacheDir().getAbsolutePath(); |
| 71 | |
| 72 | ApplicationInfo app = getApplicationInfo(); |
| 73 | mLibDir = (app.nativeLibraryDir != null) |
| 74 | ? new File(app.nativeLibraryDir).getAbsolutePath() |
| 75 | : null; |
| 76 | } |
| 77 | |
| 78 | @Override |
| 79 | public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, |
| 80 | ParcelFileDescriptor newState) { |
| 81 | // Filters, the scan queue, and the set of resulting entities |
| 82 | HashSet<String> filterSet = new HashSet<String>(); |
| 83 | |
| 84 | // Okay, start with the app's root tree, but exclude all of the canonical subdirs |
| 85 | if (mLibDir != null) { |
| 86 | filterSet.add(mLibDir); |
| 87 | } |
| 88 | filterSet.add(mCacheDir); |
| 89 | filterSet.add(mDatabaseDir); |
| 90 | filterSet.add(mSharedPrefsDir); |
| 91 | filterSet.add(mFilesDir); |
| 92 | processTree(FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data); |
| 93 | |
| 94 | // Now do the same for the files dir, db dir, and shared prefs dir |
| 95 | filterSet.add(mMainDir); |
| 96 | filterSet.remove(mFilesDir); |
| 97 | processTree(FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data); |
| 98 | |
| 99 | filterSet.add(mFilesDir); |
| 100 | filterSet.remove(mDatabaseDir); |
| 101 | processTree(FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data); |
| 102 | |
| 103 | filterSet.add(mDatabaseDir); |
| 104 | filterSet.remove(mSharedPrefsDir); |
| 105 | processTree(FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data); |
| 106 | } |
| 107 | |
| 108 | private void processTree(String domain, String rootPath, |
| 109 | HashSet<String> excludes, BackupDataOutput data) { |
| 110 | // Scan the dir tree (if it actually exists) and process each entry we find |
| 111 | File rootFile = new File(rootPath); |
| 112 | if (rootFile.exists()) { |
| 113 | LinkedList<File> scanQueue = new LinkedList<File>(); |
| 114 | scanQueue.add(rootFile); |
| 115 | |
| 116 | while (scanQueue.size() > 0) { |
| 117 | File file = scanQueue.remove(0); |
| 118 | String filePath = file.getAbsolutePath(); |
| 119 | |
| 120 | // prune this subtree? |
| 121 | if (excludes.contains(filePath)) { |
| 122 | continue; |
| 123 | } |
| 124 | |
| 125 | // If it's a directory, enqueue its contents for scanning. |
| 126 | try { |
| 127 | StructStat stat = Libcore.os.lstat(filePath); |
| 128 | if (OsConstants.S_ISLNK(stat.st_mode)) { |
| 129 | if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file); |
| 130 | continue; |
| 131 | } else if (OsConstants.S_ISDIR(stat.st_mode)) { |
| 132 | File[] contents = file.listFiles(); |
| 133 | if (contents != null) { |
| 134 | for (File entry : contents) { |
| 135 | scanQueue.add(0, entry); |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | } catch (ErrnoException e) { |
| 140 | if (DEBUG) Log.w(TAG, "Error scanning file " + file + " : " + e); |
| 141 | continue; |
| 142 | } |
| 143 | |
| 144 | // Finally, back this file up before proceeding |
| 145 | FullBackup.backupToTar(getPackageName(), domain, null, rootPath, filePath, data); |
| 146 | } |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | @Override |
| 151 | void onSaveApk(BackupDataOutput data) { |
| 152 | ApplicationInfo app = getApplicationInfo(); |
| 153 | if (DEBUG) Log.i(TAG, "APK flags: system=" + ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) |
| 154 | + " updated=" + ((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) |
| 155 | + " locked=" + ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) ); |
| 156 | if (DEBUG) Log.i(TAG, "codepath: " + getPackageCodePath()); |
| 157 | |
| 158 | // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here |
| 159 | final String pkgName = getPackageName(); |
| 160 | final String apkDir = new File(getPackageCodePath()).getParent(); |
| 161 | FullBackup.backupToTar(pkgName, FullBackup.APK_TREE_TOKEN, null, |
| 162 | apkDir, getPackageCodePath(), data); |
| 163 | |
| 164 | // Save associated .obb content if it exists and we did save the apk |
| 165 | // check for .obb and save those too |
| 166 | final File obbDir = Environment.getExternalStorageAppObbDirectory(pkgName); |
| 167 | if (obbDir != null) { |
| 168 | if (DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath()); |
| 169 | File[] obbFiles = obbDir.listFiles(); |
| 170 | if (obbFiles != null) { |
| 171 | final String obbDirName = obbDir.getAbsolutePath(); |
| 172 | for (File obb : obbFiles) { |
| 173 | FullBackup.backupToTar(pkgName, FullBackup.OBB_TREE_TOKEN, null, |
| 174 | obbDirName, obb.getAbsolutePath(), data); |
| 175 | } |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | @Override |
| 181 | public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) { |
| 182 | } |
| 183 | } |