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