FullBackupAgent.java revision 75a99709accef8cf221fd436d646727e7c8dd1f1
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 17package android.app.backup; 18 19import android.content.pm.ApplicationInfo; 20import android.content.pm.PackageManager; 21import android.os.Environment; 22import android.os.ParcelFileDescriptor; 23import android.util.Log; 24 25import libcore.io.Libcore; 26import libcore.io.ErrnoException; 27import libcore.io.OsConstants; 28import libcore.io.StructStat; 29 30import java.io.File; 31import java.io.FileInputStream; 32import java.io.FileOutputStream; 33import java.io.IOException; 34import java.util.HashSet; 35import java.util.LinkedList; 36 37/** 38 * Backs up an application's entire /data/data/<package>/... file system. This 39 * class is used by the desktop full backup mechanism and is not intended for direct 40 * use by applications. 41 * 42 * {@hide} 43 */ 44 45public class FullBackupAgent extends BackupAgent { 46 // !!! TODO: turn off debugging 47 private static final String TAG = "FullBackupAgent"; 48 private static final boolean DEBUG = true; 49 50 PackageManager mPm; 51 52 private String mMainDir; 53 private String mFilesDir; 54 private String mDatabaseDir; 55 private String mSharedPrefsDir; 56 private String mCacheDir; 57 private String mLibDir; 58 59 private File NULL_FILE; 60 61 @Override 62 public void onCreate() { 63 NULL_FILE = new File("/dev/null"); 64 65 mPm = getPackageManager(); 66 try { 67 ApplicationInfo appInfo = mPm.getApplicationInfo(getPackageName(), 0); 68 mMainDir = new File(appInfo.dataDir).getAbsolutePath(); 69 } catch (PackageManager.NameNotFoundException e) { 70 Log.e(TAG, "Unable to find package " + getPackageName()); 71 throw new RuntimeException(e); 72 } 73 74 mFilesDir = getFilesDir().getAbsolutePath(); 75 mDatabaseDir = getDatabasePath("foo").getParentFile().getAbsolutePath(); 76 mSharedPrefsDir = getSharedPrefsFile("foo").getParentFile().getAbsolutePath(); 77 mCacheDir = getCacheDir().getAbsolutePath(); 78 79 ApplicationInfo app = getApplicationInfo(); 80 mLibDir = (app.nativeLibraryDir != null) 81 ? new File(app.nativeLibraryDir).getAbsolutePath() 82 : null; 83 } 84 85 @Override 86 public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, 87 ParcelFileDescriptor newState) { 88 // Filters, the scan queue, and the set of resulting entities 89 HashSet<String> filterSet = new HashSet<String>(); 90 91 // Okay, start with the app's root tree, but exclude all of the canonical subdirs 92 if (mLibDir != null) { 93 filterSet.add(mLibDir); 94 } 95 filterSet.add(mCacheDir); 96 filterSet.add(mDatabaseDir); 97 filterSet.add(mSharedPrefsDir); 98 filterSet.add(mFilesDir); 99 processTree(FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data); 100 101 // Now do the same for the files dir, db dir, and shared prefs dir 102 filterSet.add(mMainDir); 103 filterSet.remove(mFilesDir); 104 processTree(FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data); 105 106 filterSet.add(mFilesDir); 107 filterSet.remove(mDatabaseDir); 108 processTree(FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data); 109 110 filterSet.add(mDatabaseDir); 111 filterSet.remove(mSharedPrefsDir); 112 processTree(FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data); 113 } 114 115 private void processTree(String domain, String rootPath, 116 HashSet<String> excludes, BackupDataOutput data) { 117 // Scan the dir tree (if it actually exists) and process each entry we find 118 File rootFile = new File(rootPath); 119 if (rootFile.exists()) { 120 LinkedList<File> scanQueue = new LinkedList<File>(); 121 scanQueue.add(rootFile); 122 123 while (scanQueue.size() > 0) { 124 File file = scanQueue.remove(0); 125 String filePath = file.getAbsolutePath(); 126 127 // prune this subtree? 128 if (excludes.contains(filePath)) { 129 continue; 130 } 131 132 // If it's a directory, enqueue its contents for scanning. 133 try { 134 StructStat stat = Libcore.os.lstat(filePath); 135 if (OsConstants.S_ISLNK(stat.st_mode)) { 136 if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file); 137 continue; 138 } else if (OsConstants.S_ISDIR(stat.st_mode)) { 139 File[] contents = file.listFiles(); 140 if (contents != null) { 141 for (File entry : contents) { 142 scanQueue.add(0, entry); 143 } 144 } 145 } 146 } catch (ErrnoException e) { 147 if (DEBUG) Log.w(TAG, "Error scanning file " + file + " : " + e); 148 continue; 149 } 150 151 // Finally, back this file up before proceeding 152 FullBackup.backupToTar(getPackageName(), domain, null, rootPath, filePath, data); 153 } 154 } 155 } 156 157 @Override 158 void onSaveApk(BackupDataOutput data) { 159 ApplicationInfo app = getApplicationInfo(); 160 if (DEBUG) Log.i(TAG, "APK flags: system=" + ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) 161 + " updated=" + ((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) 162 + " locked=" + ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) ); 163 if (DEBUG) Log.i(TAG, "codepath: " + getPackageCodePath()); 164 165 // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here 166 final String pkgName = getPackageName(); 167 final String apkDir = new File(getPackageCodePath()).getParent(); 168 FullBackup.backupToTar(pkgName, FullBackup.APK_TREE_TOKEN, null, 169 apkDir, getPackageCodePath(), data); 170 171 // Save associated .obb content if it exists and we did save the apk 172 // check for .obb and save those too 173 final File obbDir = Environment.getExternalStorageAppObbDirectory(pkgName); 174 if (obbDir != null) { 175 if (DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath()); 176 File[] obbFiles = obbDir.listFiles(); 177 if (obbFiles != null) { 178 final String obbDirName = obbDir.getAbsolutePath(); 179 for (File obb : obbFiles) { 180 FullBackup.backupToTar(pkgName, FullBackup.OBB_TREE_TOKEN, null, 181 obbDirName, obb.getAbsolutePath(), data); 182 } 183 } 184 } 185 } 186 187 /** 188 * Dummy -- We're never used for restore of an incremental dataset 189 */ 190 @Override 191 public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) 192 throws IOException { 193 } 194 195 /** 196 * Restore the described file from the given pipe. 197 */ 198 @Override 199 public void onRestoreFile(ParcelFileDescriptor data, long size, 200 int type, String domain, String relpath, long mode, long mtime) 201 throws IOException { 202 String basePath = null; 203 File outFile = null; 204 205 if (DEBUG) Log.d(TAG, "onRestoreFile() size=" + size + " type=" + type 206 + " domain=" + domain + " relpath=" + relpath + " mode=" + mode 207 + " mtime=" + mtime); 208 209 // Parse out the semantic domains into the correct physical location 210 if (domain.equals(FullBackup.DATA_TREE_TOKEN)) basePath = mFilesDir; 211 else if (domain.equals(FullBackup.DATABASE_TREE_TOKEN)) basePath = mDatabaseDir; 212 else if (domain.equals(FullBackup.ROOT_TREE_TOKEN)) basePath = mMainDir; 213 else if (domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)) basePath = mSharedPrefsDir; 214 215 // Not a supported output location? We need to consume the data 216 // anyway, so send it to /dev/null 217 outFile = (basePath != null) ? new File(basePath, relpath) : null; 218 if (DEBUG) Log.i(TAG, "[" + domain + " : " + relpath + "] mapped to " + outFile.getPath()); 219 220 // Now that we've figured out where the data goes, send it on its way 221 FullBackup.restoreToFile(data, size, type, mode, mtime, outFile); 222 } 223} 224