Christopher Tate | b0628bf | 2011-06-02 15:08:13 -0700 | [diff] [blame] | 1 | package com.android.sharedstoragebackup; |
| 2 | |
Christopher Tate | b0628bf | 2011-06-02 15:08:13 -0700 | [diff] [blame] | 3 | import android.app.backup.FullBackupAgent; |
Christopher Tate | 79ec80d | 2011-06-24 14:58:49 -0700 | [diff] [blame] | 4 | import android.app.backup.FullBackup; |
Christopher Tate | 79ec80d | 2011-06-24 14:58:49 -0700 | [diff] [blame] | 5 | import android.app.backup.FullBackupDataOutput; |
Christopher Tate | b0628bf | 2011-06-02 15:08:13 -0700 | [diff] [blame] | 6 | import android.content.Context; |
Christopher Tate | 416c39e | 2013-02-14 16:55:46 -0800 | [diff] [blame] | 7 | import android.os.Environment; |
Christopher Tate | b0628bf | 2011-06-02 15:08:13 -0700 | [diff] [blame] | 8 | import android.os.ParcelFileDescriptor; |
| 9 | import android.os.storage.StorageManager; |
| 10 | import android.os.storage.StorageVolume; |
Matthew Williams | 303650c | 2015-04-17 18:22:51 -0700 | [diff] [blame] | 11 | import android.util.ArraySet; |
Christopher Tate | b0628bf | 2011-06-02 15:08:13 -0700 | [diff] [blame] | 12 | import android.util.Slog; |
| 13 | |
| 14 | import java.io.File; |
| 15 | import java.io.IOException; |
| 16 | |
| 17 | public 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 Tate | 79ec80d | 2011-06-24 14:58:49 -0700 | [diff] [blame] | 33 | /** |
| 34 | * Full backup of the shared-storage filesystem |
| 35 | */ |
Christopher Tate | b0628bf | 2011-06-02 15:08:13 -0700 | [diff] [blame] | 36 | @Override |
Christopher Tate | 79ec80d | 2011-06-24 14:58:49 -0700 | [diff] [blame] | 37 | public void onFullBackup(FullBackupDataOutput output) throws IOException { |
Christopher Tate | b0628bf | 2011-06-02 15:08:13 -0700 | [diff] [blame] | 38 | // 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 Tate | fb2ea43 | 2011-10-17 16:35:58 -0700 | [diff] [blame] | 42 | if (DEBUG) Slog.i(TAG, "Backing up " + mVolumes.length + " shared volumes"); |
Christopher Tate | 416c39e | 2013-02-14 16:55:46 -0800 | [diff] [blame] | 43 | // Ignore all apps' getExternalFilesDir() content; it is backed up as part of |
| 44 | // each app-specific payload. |
Matthew Williams | 303650c | 2015-04-17 18:22:51 -0700 | [diff] [blame] | 45 | ArraySet<String> externalFilesDirFilter = new ArraySet(); |
Christopher Tate | 416c39e | 2013-02-14 16:55:46 -0800 | [diff] [blame] | 46 | final File externalAndroidRoot = new File(Environment.getExternalStorageDirectory(), |
| 47 | Environment.DIRECTORY_ANDROID); |
| 48 | externalFilesDirFilter.add(externalAndroidRoot.getCanonicalPath()); |
| 49 | |
Christopher Tate | b0628bf | 2011-06-02 15:08:13 -0700 | [diff] [blame] | 50 | 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 Williams | 303650c | 2015-04-17 18:22:51 -0700 | [diff] [blame] | 56 | fullBackupFileTree(null, domain, v.getPath(), |
| 57 | null /* manifestExcludes */, |
| 58 | externalFilesDirFilter /* systemExcludes */, output); |
Christopher Tate | b0628bf | 2011-06-02 15:08:13 -0700 | [diff] [blame] | 59 | } |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | /** |
Christopher Tate | b0628bf | 2011-06-02 15:08:13 -0700 | [diff] [blame] | 64 | * 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 Tate | fb2ea43 | 2011-10-17 16:35:58 -0700 | [diff] [blame] | 70 | if (DEBUG) Slog.d(TAG, "Shared restore: [ " + domain + " : " + relpath + "]"); |
Christopher Tate | b0628bf | 2011-06-02 15:08:13 -0700 | [diff] [blame] | 71 | |
| 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 Tate | 79ec80d | 2011-06-24 14:58:49 -0700 | [diff] [blame] | 95 | FullBackup.restoreFile(data, size, type, -1, mtime, outFile); |
Christopher Tate | b0628bf | 2011-06-02 15:08:13 -0700 | [diff] [blame] | 96 | } |
| 97 | } |