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