FullBackupAgent.java revision 4a627c71ff53a4fca1f961f4b1dcc0461df18a06
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.util.HashSet;
32import 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
42public 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}
184