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.Slog;
     12 
     13 import java.io.File;
     14 import java.io.IOException;
     15 import java.util.HashSet;
     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             HashSet<String> externalFilesDirFilter = new HashSet<String>();
     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(), externalFilesDirFilter, output);
     57             }
     58         }
     59     }
     60 
     61     /**
     62      * Full restore of one file to shared storage
     63      */
     64     @Override
     65     public void onRestoreFile(ParcelFileDescriptor data, long size,
     66             int type, String domain, String relpath, long mode, long mtime)
     67             throws IOException {
     68         if (DEBUG) Slog.d(TAG, "Shared restore: [ " + domain + " : " + relpath + "]");
     69 
     70         File outFile = null;
     71 
     72         // The file path must be in the semantic form [number]/path/to/file...
     73         int slash = relpath.indexOf('/');
     74         if (slash > 0) {
     75             try {
     76                 int i = Integer.parseInt(relpath.substring(0, slash));
     77                 if (i <= mVolumes.length) {
     78                     outFile = new File(mVolumes[i].getPath(), relpath.substring(slash + 1));
     79                     if (DEBUG) Slog.i(TAG, " => " + outFile.getAbsolutePath());
     80                 } else {
     81                     Slog.w(TAG, "Cannot restore data for unavailable volume " + i);
     82                 }
     83             } catch (NumberFormatException e) {
     84                 if (DEBUG) Slog.w(TAG, "Bad volume number token: " + relpath.substring(0, slash));
     85             }
     86         } else {
     87             if (DEBUG) Slog.i(TAG, "Can't find volume-number token");
     88         }
     89         if (outFile == null) {
     90             Slog.e(TAG, "Skipping data with malformed path " + relpath);
     91         }
     92 
     93         FullBackup.restoreFile(data, size, type, -1, mtime, outFile);
     94     }
     95 }
     96