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