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