1package com.android.sharedstoragebackup; 2 3import android.app.backup.FullBackupAgent; 4import android.app.backup.FullBackup; 5import android.app.backup.FullBackupDataOutput; 6import android.content.Context; 7import android.os.ParcelFileDescriptor; 8import android.os.storage.StorageManager; 9import android.os.storage.StorageVolume; 10import android.util.Slog; 11 12import java.io.File; 13import java.io.IOException; 14 15public class SharedStorageAgent extends FullBackupAgent { 16 static final String TAG = "SharedStorageAgent"; 17 static final boolean DEBUG = true; 18 19 StorageVolume[] mVolumes; 20 21 @Override 22 public void onCreate() { 23 StorageManager mgr = (StorageManager) getSystemService(Context.STORAGE_SERVICE); 24 if (mgr != null) { 25 mVolumes = mgr.getVolumeList(); 26 } else { 27 Slog.e(TAG, "Unable to access Storage Manager"); 28 } 29 } 30 31 /** 32 * Full backup of the shared-storage filesystem 33 */ 34 @Override 35 public void onFullBackup(FullBackupDataOutput output) throws IOException { 36 // If there are shared-storage volumes available, run the inherited directory- 37 // hierarchy backup process on them. By convention in the Storage Manager, the 38 // "primary" shared storage volume is first in the list. 39 if (mVolumes != null) { 40 if (DEBUG) Slog.i(TAG, "Backing up " + mVolumes.length + " shared volumes"); 41 for (int i = 0; i < mVolumes.length; i++) { 42 StorageVolume v = mVolumes[i]; 43 // Express the contents of volume N this way in the tar stream: 44 // shared/N/path/to/file 45 // The restore will then extract to the given volume 46 String domain = FullBackup.SHARED_PREFIX + i; 47 fullBackupFileTree(null, domain, v.getPath(), null, output); 48 } 49 } 50 } 51 52 /** 53 * Full restore of one file to shared storage 54 */ 55 @Override 56 public void onRestoreFile(ParcelFileDescriptor data, long size, 57 int type, String domain, String relpath, long mode, long mtime) 58 throws IOException { 59 if (DEBUG) Slog.d(TAG, "Shared restore: [ " + domain + " : " + relpath + "]"); 60 61 File outFile = null; 62 63 // The file path must be in the semantic form [number]/path/to/file... 64 int slash = relpath.indexOf('/'); 65 if (slash > 0) { 66 try { 67 int i = Integer.parseInt(relpath.substring(0, slash)); 68 if (i <= mVolumes.length) { 69 outFile = new File(mVolumes[i].getPath(), relpath.substring(slash + 1)); 70 if (DEBUG) Slog.i(TAG, " => " + outFile.getAbsolutePath()); 71 } else { 72 Slog.w(TAG, "Cannot restore data for unavailable volume " + i); 73 } 74 } catch (NumberFormatException e) { 75 if (DEBUG) Slog.w(TAG, "Bad volume number token: " + relpath.substring(0, slash)); 76 } 77 } else { 78 if (DEBUG) Slog.i(TAG, "Can't find volume-number token"); 79 } 80 if (outFile == null) { 81 Slog.e(TAG, "Skipping data with malformed path " + relpath); 82 } 83 84 FullBackup.restoreFile(data, size, type, -1, mtime, outFile); 85 } 86} 87