summaryrefslogtreecommitdiffstats
path: root/core/java/com
diff options
context:
space:
mode:
authorChristopher Tate <ctate@google.com>2014-06-16 01:13:29 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2014-06-13 21:09:46 +0000
commit2110ca327a14fd5f52cdbbd5f0e1a6c7a3eca6a5 (patch)
treedd938b1339487539837f76601d164b8f6c66791b /core/java/com
parent0a3316bcfdac9f5f40d1349d97d10329c70f7e30 (diff)
parent9ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46 (diff)
downloadframeworks_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.aidl4
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java177
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;
+ }
}