LocalTransport.java revision 8e55eac96d768a4de68a091f57487deadf6d0a87
1package com.android.internal.backup;
2
3import android.backup.BackupDataInput;
4import android.backup.BackupDataOutput;
5import android.backup.RestoreSet;
6import android.content.Context;
7import android.content.pm.PackageInfo;
8import android.content.pm.PackageManager;
9import android.content.pm.PackageManager.NameNotFoundException;
10import android.os.Environment;
11import android.os.ParcelFileDescriptor;
12import android.os.RemoteException;
13import android.util.Log;
14
15import java.io.File;
16import java.io.FileFilter;
17import java.io.FileInputStream;
18import java.io.FileOutputStream;
19import java.io.IOException;
20import java.util.ArrayList;
21
22/**
23 * Backup transport for stashing stuff into a known location on disk, and
24 * later restoring from there.  For testing only.
25 */
26
27public class LocalTransport extends IBackupTransport.Stub {
28    private static final String TAG = "LocalTransport";
29    private static final boolean DEBUG = true;
30
31    private Context mContext;
32    private PackageManager mPackageManager;
33    private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
34    private FileFilter mDirFileFilter = new FileFilter() {
35        public boolean accept(File f) {
36            return f.isDirectory();
37        }
38    };
39
40
41    public LocalTransport(Context context) {
42        if (DEBUG) Log.v(TAG, "Transport constructed");
43        mContext = context;
44        mPackageManager = context.getPackageManager();
45    }
46
47    public long requestBackupTime() throws RemoteException {
48        // any time is a good time for local backup
49        return 0;
50    }
51
52    public int startSession() throws RemoteException {
53        if (DEBUG) Log.v(TAG, "session started");
54        mDataDir.mkdirs();
55        return 0;
56    }
57
58    public int endSession() throws RemoteException {
59        if (DEBUG) Log.v(TAG, "session ended");
60        return 0;
61    }
62
63    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
64            throws RemoteException {
65        if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
66        int err = 0;
67
68        File packageDir = new File(mDataDir, packageInfo.packageName);
69        packageDir.mkdirs();
70
71        // Each 'record' in the restore set is kept in its own file, named by
72        // the record key.  Wind through the data file, extracting individual
73        // record operations and building a set of all the updates to apply
74        // in this update.
75        BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor());
76        try {
77            int bufSize = 512;
78            byte[] buf = new byte[bufSize];
79            while (changeSet.readNextHeader()) {
80                String key = changeSet.getKey();
81                int dataSize = changeSet.getDataSize();
82                if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize);
83                if (dataSize > bufSize) {
84                    bufSize = dataSize;
85                    buf = new byte[bufSize];
86                }
87                changeSet.readEntityData(buf, dataSize);
88                if (DEBUG) Log.v(TAG, "  + data size " + dataSize);
89
90                File entityFile = new File(packageDir, key);
91                FileOutputStream entity = new FileOutputStream(entityFile);
92                try {
93                    entity.write(buf, 0, dataSize);
94                } catch (IOException e) {
95                    Log.e(TAG, "Unable to update key file "
96                            + entityFile.getAbsolutePath());
97                    err = -1;
98                } finally {
99                    entity.close();
100                }
101            }
102        } catch (IOException e) {
103            // oops, something went wrong.  abort the operation and return error.
104            Log.v(TAG, "Exception reading backup input:");
105            e.printStackTrace();
106            err = -1;
107        }
108
109        return err;
110    }
111
112    // Restore handling
113    public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
114        // one hardcoded restore set
115        RestoreSet[] set = new RestoreSet[1];
116        set[0].device = "flash";
117        set[0].name = "Local disk image";
118        set[0].token = 0;
119        return set;
120    }
121
122    public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
123        if (DEBUG) Log.v(TAG, "getting app set " + token);
124        // the available packages are the extant subdirs of mDatadir
125        File[] packageDirs = mDataDir.listFiles(mDirFileFilter);
126        ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>();
127        for (File dir : packageDirs) {
128            try {
129                PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(),
130                        PackageManager.GET_SIGNATURES);
131                if (pkg != null) {
132                    packages.add(pkg);
133                }
134            } catch (NameNotFoundException e) {
135                // restore set contains data for a package not installed on the
136                // phone -- just ignore it.
137            }
138        }
139
140        if (DEBUG) {
141            Log.v(TAG, "Built app set of " + packages.size() + " entries:");
142            for (PackageInfo p : packages) {
143                Log.v(TAG, "    + " + p.packageName);
144            }
145        }
146
147        PackageInfo[] result = new PackageInfo[packages.size()];
148        return packages.toArray(result);
149    }
150
151    public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor outFd)
152            throws android.os.RemoteException {
153        if (DEBUG) Log.v(TAG, "getting restore data " + token + " : " + packageInfo.packageName);
154        // we only support one hardcoded restore set
155        if (token != 0) return -1;
156
157        // the data for a given package is at a known location
158        File packageDir = new File(mDataDir, packageInfo.packageName);
159
160        // The restore set is the concatenation of the individual record blobs,
161        // each of which is a file in the package's directory
162        File[] blobs = packageDir.listFiles();
163        int err = 0;
164        if (blobs != null && blobs.length > 0) {
165            BackupDataOutput out = new BackupDataOutput(mContext, outFd.getFileDescriptor());
166            try {
167                for (File f : blobs) {
168                    FileInputStream in = new FileInputStream(f);
169                    int size = (int) f.length();
170                    byte[] buf = new byte[size];
171                    in.read(buf);
172                    out.writeEntityHeader(f.getName(), size);
173                    out.writeEntityData(buf, size);
174                }
175            } catch (Exception e) {
176                Log.e(TAG, "Unable to read backup records");
177                err = -1;
178            }
179        }
180        return err;
181    }
182}
183