Home | History | Annotate | Download | only in sharedstoragebackup
      1 package com.android.sharedstoragebackup;
      2 
      3 import android.app.backup.FullBackupAgent;
      4 import android.app.backup.FullBackup;
      5 import android.app.backup.FullBackupDataOutput;
      6 import android.content.Context;
      7 import android.os.Environment;
      8 import android.os.ParcelFileDescriptor;
      9 import android.os.storage.StorageManager;
     10 import android.os.storage.StorageVolume;
     11 import android.util.ArraySet;
     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 
     33     /**
     34      * Full backup of the shared-storage filesystem
     35      */
     36     @Override
     37     public void onFullBackup(FullBackupDataOutput output) throws IOException {
     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) {
     42             if (DEBUG) Slog.i(TAG, "Backing up " + mVolumes.length + " shared volumes");
     43             // Ignore all apps' getExternalFilesDir() content; it is backed up as part of
     44             // each app-specific payload.
     45             ArraySet<String> externalFilesDirFilter = new ArraySet();
     46             final File externalAndroidRoot = new File(Environment.getExternalStorageDirectory(),
     47                     Environment.DIRECTORY_ANDROID);
     48             externalFilesDirFilter.add(externalAndroidRoot.getCanonicalPath());
     49 
     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;
     56                 fullBackupFileTree(null, domain, v.getPath(),
     57                         null /* manifestExcludes */,
     58                         externalFilesDirFilter /* systemExcludes */, output);
     59             }
     60         }
     61     }
     62 
     63     /**
     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 {
     70         if (DEBUG) Slog.d(TAG, "Shared restore: [ " + domain + " : " + relpath + "]");
     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 
     95         FullBackup.restoreFile(data, size, type, -1, mtime, outFile);
     96     }
     97 }
     98