1b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tatepackage com.android.sharedstoragebackup; 2b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate 3b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tateimport android.app.backup.FullBackupAgent; 479ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tateimport android.app.backup.FullBackup; 579ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tateimport android.app.backup.FullBackupDataOutput; 6b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tateimport android.content.Context; 7416c39e8d48048fa4a997c09460c262cca871fc4Christopher Tateimport android.os.Environment; 8b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tateimport android.os.ParcelFileDescriptor; 9b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tateimport android.os.storage.StorageManager; 10b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tateimport android.os.storage.StorageVolume; 11303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williamsimport android.util.ArraySet; 12b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tateimport android.util.Slog; 13b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate 14b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tateimport java.io.File; 15b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tateimport java.io.IOException; 16b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate 17b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tatepublic class SharedStorageAgent extends FullBackupAgent { 18b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate static final String TAG = "SharedStorageAgent"; 19b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate static final boolean DEBUG = true; 20b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate 21b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate StorageVolume[] mVolumes; 22b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate 23b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate @Override 24b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate public void onCreate() { 25b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate StorageManager mgr = (StorageManager) getSystemService(Context.STORAGE_SERVICE); 26b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate if (mgr != null) { 27b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate mVolumes = mgr.getVolumeList(); 28b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } else { 29b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate Slog.e(TAG, "Unable to access Storage Manager"); 30b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } 31b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } 32b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate 3379ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate /** 3479ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * Full backup of the shared-storage filesystem 3579ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate */ 36b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate @Override 3779ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate public void onFullBackup(FullBackupDataOutput output) throws IOException { 38b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate // If there are shared-storage volumes available, run the inherited directory- 39b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate // hierarchy backup process on them. By convention in the Storage Manager, the 40b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate // "primary" shared storage volume is first in the list. 41b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate if (mVolumes != null) { 42fb2ea43112bdf5a7dc121b59e2ef7e8b411bd019Christopher Tate if (DEBUG) Slog.i(TAG, "Backing up " + mVolumes.length + " shared volumes"); 43416c39e8d48048fa4a997c09460c262cca871fc4Christopher Tate // Ignore all apps' getExternalFilesDir() content; it is backed up as part of 44416c39e8d48048fa4a997c09460c262cca871fc4Christopher Tate // each app-specific payload. 45303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams ArraySet<String> externalFilesDirFilter = new ArraySet(); 46416c39e8d48048fa4a997c09460c262cca871fc4Christopher Tate final File externalAndroidRoot = new File(Environment.getExternalStorageDirectory(), 47416c39e8d48048fa4a997c09460c262cca871fc4Christopher Tate Environment.DIRECTORY_ANDROID); 48416c39e8d48048fa4a997c09460c262cca871fc4Christopher Tate externalFilesDirFilter.add(externalAndroidRoot.getCanonicalPath()); 49416c39e8d48048fa4a997c09460c262cca871fc4Christopher Tate 50b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate for (int i = 0; i < mVolumes.length; i++) { 51b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate StorageVolume v = mVolumes[i]; 52b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate // Express the contents of volume N this way in the tar stream: 53b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate // shared/N/path/to/file 54b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate // The restore will then extract to the given volume 55b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate String domain = FullBackup.SHARED_PREFIX + i; 56303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams fullBackupFileTree(null, domain, v.getPath(), 57303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams null /* manifestExcludes */, 58303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams externalFilesDirFilter /* systemExcludes */, output); 59b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } 60b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } 61b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } 62b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate 63b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate /** 64b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate * Full restore of one file to shared storage 65b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate */ 66b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate @Override 67b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate public void onRestoreFile(ParcelFileDescriptor data, long size, 68b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate int type, String domain, String relpath, long mode, long mtime) 69b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate throws IOException { 70fb2ea43112bdf5a7dc121b59e2ef7e8b411bd019Christopher Tate if (DEBUG) Slog.d(TAG, "Shared restore: [ " + domain + " : " + relpath + "]"); 71b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate 72b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate File outFile = null; 73b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate 74b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate // The file path must be in the semantic form [number]/path/to/file... 75b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate int slash = relpath.indexOf('/'); 76b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate if (slash > 0) { 77b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate try { 78b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate int i = Integer.parseInt(relpath.substring(0, slash)); 79b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate if (i <= mVolumes.length) { 80b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate outFile = new File(mVolumes[i].getPath(), relpath.substring(slash + 1)); 81b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate if (DEBUG) Slog.i(TAG, " => " + outFile.getAbsolutePath()); 82b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } else { 83b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate Slog.w(TAG, "Cannot restore data for unavailable volume " + i); 84b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } 85b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } catch (NumberFormatException e) { 86b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate if (DEBUG) Slog.w(TAG, "Bad volume number token: " + relpath.substring(0, slash)); 87b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } 88b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } else { 89b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate if (DEBUG) Slog.i(TAG, "Can't find volume-number token"); 90b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } 91b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate if (outFile == null) { 92b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate Slog.e(TAG, "Skipping data with malformed path " + relpath); 93b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } 94b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate 9579ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate FullBackup.restoreFile(data, size, type, -1, mtime, outFile); 96b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate } 97b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate} 98