blob: e453cf53a98b5ca0a48850dc334af7cc983bd9b9 [file] [log] [blame]
Christopher Tateb0628bf2011-06-02 15:08:13 -07001package com.android.sharedstoragebackup;
2
Christopher Tateb0628bf2011-06-02 15:08:13 -07003import android.app.backup.FullBackupAgent;
Christopher Tate79ec80d2011-06-24 14:58:49 -07004import android.app.backup.FullBackup;
Christopher Tate79ec80d2011-06-24 14:58:49 -07005import android.app.backup.FullBackupDataOutput;
Christopher Tateb0628bf2011-06-02 15:08:13 -07006import android.content.Context;
Christopher Tate416c39e2013-02-14 16:55:46 -08007import android.os.Environment;
Christopher Tateb0628bf2011-06-02 15:08:13 -07008import android.os.ParcelFileDescriptor;
9import android.os.storage.StorageManager;
10import android.os.storage.StorageVolume;
Matthew Williams303650c2015-04-17 18:22:51 -070011import android.util.ArraySet;
Christopher Tateb0628bf2011-06-02 15:08:13 -070012import android.util.Slog;
13
14import java.io.File;
15import java.io.IOException;
16
17public class SharedStorageAgent extends FullBackupAgent {
18 static final String TAG = "SharedStorageAgent";
19 static final boolean DEBUG = true;
20
21 StorageVolume[] mVolumes;
22
23 @Override
24 public void onCreate() {
25 StorageManager mgr = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
26 if (mgr != null) {
27 mVolumes = mgr.getVolumeList();
28 } else {
29 Slog.e(TAG, "Unable to access Storage Manager");
30 }
31 }
32
Christopher Tate79ec80d2011-06-24 14:58:49 -070033 /**
34 * Full backup of the shared-storage filesystem
35 */
Christopher Tateb0628bf2011-06-02 15:08:13 -070036 @Override
Christopher Tate79ec80d2011-06-24 14:58:49 -070037 public void onFullBackup(FullBackupDataOutput output) throws IOException {
Christopher Tateb0628bf2011-06-02 15:08:13 -070038 // If there are shared-storage volumes available, run the inherited directory-
39 // hierarchy backup process on them. By convention in the Storage Manager, the
40 // "primary" shared storage volume is first in the list.
41 if (mVolumes != null) {
Christopher Tatefb2ea432011-10-17 16:35:58 -070042 if (DEBUG) Slog.i(TAG, "Backing up " + mVolumes.length + " shared volumes");
Christopher Tate416c39e2013-02-14 16:55:46 -080043 // Ignore all apps' getExternalFilesDir() content; it is backed up as part of
44 // each app-specific payload.
Matthew Williams303650c2015-04-17 18:22:51 -070045 ArraySet<String> externalFilesDirFilter = new ArraySet();
Christopher Tate416c39e2013-02-14 16:55:46 -080046 final File externalAndroidRoot = new File(Environment.getExternalStorageDirectory(),
47 Environment.DIRECTORY_ANDROID);
48 externalFilesDirFilter.add(externalAndroidRoot.getCanonicalPath());
49
Christopher Tateb0628bf2011-06-02 15:08:13 -070050 for (int i = 0; i < mVolumes.length; i++) {
51 StorageVolume v = mVolumes[i];
52 // Express the contents of volume N this way in the tar stream:
53 // shared/N/path/to/file
54 // The restore will then extract to the given volume
55 String domain = FullBackup.SHARED_PREFIX + i;
Matthew Williams303650c2015-04-17 18:22:51 -070056 fullBackupFileTree(null, domain, v.getPath(),
57 null /* manifestExcludes */,
58 externalFilesDirFilter /* systemExcludes */, output);
Christopher Tateb0628bf2011-06-02 15:08:13 -070059 }
60 }
61 }
62
63 /**
Christopher Tateb0628bf2011-06-02 15:08:13 -070064 * Full restore of one file to shared storage
65 */
66 @Override
67 public void onRestoreFile(ParcelFileDescriptor data, long size,
68 int type, String domain, String relpath, long mode, long mtime)
69 throws IOException {
Christopher Tatefb2ea432011-10-17 16:35:58 -070070 if (DEBUG) Slog.d(TAG, "Shared restore: [ " + domain + " : " + relpath + "]");
Christopher Tateb0628bf2011-06-02 15:08:13 -070071
72 File outFile = null;
73
74 // The file path must be in the semantic form [number]/path/to/file...
75 int slash = relpath.indexOf('/');
76 if (slash > 0) {
77 try {
78 int i = Integer.parseInt(relpath.substring(0, slash));
79 if (i <= mVolumes.length) {
80 outFile = new File(mVolumes[i].getPath(), relpath.substring(slash + 1));
81 if (DEBUG) Slog.i(TAG, " => " + outFile.getAbsolutePath());
82 } else {
83 Slog.w(TAG, "Cannot restore data for unavailable volume " + i);
84 }
85 } catch (NumberFormatException e) {
86 if (DEBUG) Slog.w(TAG, "Bad volume number token: " + relpath.substring(0, slash));
87 }
88 } else {
89 if (DEBUG) Slog.i(TAG, "Can't find volume-number token");
90 }
91 if (outFile == null) {
92 Slog.e(TAG, "Skipping data with malformed path " + relpath);
93 }
94
Christopher Tate79ec80d2011-06-24 14:58:49 -070095 FullBackup.restoreFile(data, size, type, -1, mtime, outFile);
Christopher Tateb0628bf2011-06-02 15:08:13 -070096 }
97}