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