diff options
author | Christopher Tate <ctate@google.com> | 2014-06-16 01:13:29 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-06-13 21:09:46 +0000 |
commit | 2110ca327a14fd5f52cdbbd5f0e1a6c7a3eca6a5 (patch) | |
tree | dd938b1339487539837f76601d164b8f6c66791b /core/java/com | |
parent | 0a3316bcfdac9f5f40d1349d97d10329c70f7e30 (diff) | |
parent | 9ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46 (diff) | |
download | frameworks_base-2110ca327a14fd5f52cdbbd5f0e1a6c7a3eca6a5.zip frameworks_base-2110ca327a14fd5f52cdbbd5f0e1a6c7a3eca6a5.tar.gz frameworks_base-2110ca327a14fd5f52cdbbd5f0e1a6c7a3eca6a5.tar.bz2 |
Merge "Implement full data backup through transport"
Diffstat (limited to 'core/java/com')
-rw-r--r-- | core/java/com/android/internal/backup/IBackupTransport.aidl | 4 | ||||
-rw-r--r-- | core/java/com/android/internal/backup/LocalTransport.java | 177 |
2 files changed, 177 insertions, 4 deletions
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index d10451b..960fa49 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -187,4 +187,8 @@ interface IBackupTransport { * freeing any resources and connections used during the restore process. */ void finishRestore(); + + long requestFullBackupTime(); + int performFullBackup(in PackageInfo targetPackage, in ParcelFileDescriptor socket); + int sendBackupData(int numBytes); } diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index 7292116..c9d621d 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -34,12 +34,17 @@ import android.util.Log; import com.android.org.bouncycastle.util.encoders.Base64; +import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; +import java.util.List; import static android.system.OsConstants.*; @@ -64,16 +69,29 @@ public class LocalTransport extends BackupTransport { private Context mContext; private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup"); private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN)); + private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, "_delta"); + private File mCurrentSetFullDir = new File(mCurrentSetDir, "_full"); private PackageInfo[] mRestorePackages = null; private int mRestorePackage = -1; // Index into mRestorePackages private File mRestoreDataDir; private long mRestoreToken; + // Additional bookkeeping for full backup + private String mFullTargetPackage; + private ParcelFileDescriptor mSocket; + private FileInputStream mSocketInputStream; + private BufferedOutputStream mFullBackupOutputStream; + private byte[] mFullBackupBuffer; + + private File mFullRestoreSetDir; + private HashSet<String> mFullRestorePackages; public LocalTransport(Context context) { mContext = context; mCurrentSetDir.mkdirs(); + mCurrentSetFullDir.mkdir(); + mCurrentSetIncrementalDir.mkdir(); if (!SELinux.restorecon(mCurrentSetDir)) { Log.e(TAG, "SELinux restorecon failed for " + mCurrentSetDir); } @@ -119,7 +137,7 @@ public class LocalTransport extends BackupTransport { } } - File packageDir = new File(mCurrentSetDir, packageInfo.packageName); + File packageDir = new File(mCurrentSetIncrementalDir, packageInfo.packageName); packageDir.mkdirs(); // Each 'record' in the restore set is kept in its own file, named by @@ -200,7 +218,7 @@ public class LocalTransport extends BackupTransport { public int clearBackupData(PackageInfo packageInfo) { if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName); - File packageDir = new File(mCurrentSetDir, packageInfo.packageName); + File packageDir = new File(mCurrentSetIncrementalDir, packageInfo.packageName); final File[] fileset = packageDir.listFiles(); if (fileset != null) { for (File f : fileset) { @@ -208,27 +226,122 @@ public class LocalTransport extends BackupTransport { } packageDir.delete(); } + + packageDir = new File(mCurrentSetFullDir, packageInfo.packageName); + final File[] tarballs = packageDir.listFiles(); + if (tarballs != null) { + for (File f : tarballs) { + f.delete(); + } + packageDir.delete(); + } + return BackupTransport.TRANSPORT_OK; } public int finishBackup() { if (DEBUG) Log.v(TAG, "finishBackup()"); + if (mSocket != null) { + if (DEBUG) { + Log.v(TAG, "Concluding full backup of " + mFullTargetPackage); + } + try { + mFullBackupOutputStream.flush(); + mFullBackupOutputStream.close(); + mSocketInputStream = null; + mFullTargetPackage = null; + mSocket.close(); + } catch (IOException e) { + return BackupTransport.TRANSPORT_ERROR; + } finally { + mSocket = null; + } + } return BackupTransport.TRANSPORT_OK; } + // ------------------------------------------------------------------------------------ + // Full backup handling + public long requestFullBackupTime() { + return 0; + } + + public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) { + if (mSocket != null) { + Log.e(TAG, "Attempt to initiate full backup while one is in progress"); + return BackupTransport.TRANSPORT_ERROR; + } + + if (DEBUG) { + Log.i(TAG, "performFullBackup : " + targetPackage); + } + + // We know a priori that we run in the system process, so we need to make + // sure to dup() our own copy of the socket fd. Transports which run in + // their own processes must not do this. + try { + mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); + mSocketInputStream = new FileInputStream(mSocket.getFileDescriptor()); + } catch (IOException e) { + Log.e(TAG, "Unable to process socket for full backup"); + return BackupTransport.TRANSPORT_ERROR; + } + + mFullTargetPackage = targetPackage.packageName; + FileOutputStream tarstream; + try { + File tarball = new File(mCurrentSetFullDir, mFullTargetPackage); + tarstream = new FileOutputStream(tarball); + } catch (FileNotFoundException e) { + return BackupTransport.TRANSPORT_ERROR; + } + mFullBackupOutputStream = new BufferedOutputStream(tarstream); + mFullBackupBuffer = new byte[4096]; + + return BackupTransport.TRANSPORT_OK; + } + + public int sendBackupData(int numBytes) { + if (mFullBackupBuffer == null) { + Log.w(TAG, "Attempted sendBackupData before performFullBackup"); + return BackupTransport.TRANSPORT_ERROR; + } + + if (numBytes > mFullBackupBuffer.length) { + mFullBackupBuffer = new byte[numBytes]; + } + while (numBytes > 0) { + try { + int nRead = mSocketInputStream.read(mFullBackupBuffer, 0, numBytes); + if (nRead < 0) { + // Something went wrong if we expect data but saw EOD + Log.w(TAG, "Unexpected EOD; failing backup"); + return BackupTransport.TRANSPORT_ERROR; + } + mFullBackupOutputStream.write(mFullBackupBuffer, 0, nRead); + numBytes -= nRead; + } catch (IOException e) { + Log.e(TAG, "Error handling backup data for " + mFullTargetPackage); + return BackupTransport.TRANSPORT_ERROR; + } + } + return BackupTransport.TRANSPORT_OK; + } + + // ------------------------------------------------------------------------------------ // Restore handling static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 }; public RestoreSet[] getAvailableRestoreSets() { long[] existing = new long[POSSIBLE_SETS.length + 1]; int num = 0; - // see which possible non-current sets exist, then put the current set at the end + // see which possible non-current sets exist... for (long token : POSSIBLE_SETS) { if ((new File(mDataDir, Long.toString(token))).exists()) { existing[num++] = token; } } - // and always the currently-active set last + // ...and always the currently-active set last existing[num++] = CURRENT_SET_TOKEN; RestoreSet[] available = new RestoreSet[num]; @@ -345,4 +458,60 @@ public class LocalTransport extends BackupTransport { public void finishRestore() { if (DEBUG) Log.v(TAG, "finishRestore()"); } + + // ------------------------------------------------------------------------------------ + // Full restore handling + + public int prepareFullRestore(long token, String[] targetPackages) { + mRestoreDataDir = new File(mDataDir, Long.toString(token)); + mFullRestoreSetDir = new File(mRestoreDataDir, "_full"); + mFullRestorePackages = new HashSet<String>(); + if (mFullRestoreSetDir.exists()) { + List<String> pkgs = Arrays.asList(mFullRestoreSetDir.list()); + HashSet<String> available = new HashSet<String>(pkgs); + + for (int i = 0; i < targetPackages.length; i++) { + if (available.contains(targetPackages[i])) { + mFullRestorePackages.add(targetPackages[i]); + } + } + } + return BackupTransport.TRANSPORT_OK; + } + + /** + * Ask the transport what package's full data will be restored next. When all apps' + * data has been delivered, the transport should return {@code null} here. + * @return The package name of the next application whose data will be restored, or + * {@code null} if all available package has been delivered. + */ + public String getNextFullRestorePackage() { + return null; + } + + /** + * Ask the transport to provide data for the "current" package being restored. The + * transport then writes some data to the socket supplied to this call, and returns + * the number of bytes written. The system will then read that many bytes and + * stream them to the application's agent for restore, then will call this method again + * to receive the next chunk of the archive. This sequence will be repeated until the + * transport returns zero indicating that all of the package's data has been delivered + * (or returns a negative value indicating some sort of hard error condition at the + * transport level). + * + * <p>After this method returns zero, the system will then call + * {@link #getNextFullRestorePackage()} to begin the restore process for the next + * application, and the sequence begins again. + * + * @param socket The file descriptor that the transport will use for delivering the + * streamed archive. + * @return 0 when no more data for the current package is available. A positive value + * indicates the presence of that much data to be delivered to the app. A negative + * return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR}, + * indicating a fatal error condition that precludes further restore operations + * on the current dataset. + */ + public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) { + return 0; + } } |