Flesh out restore interface on manager; work up most of LocalTransport
diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java
index c3b6a02..8df7eae 100644
--- a/core/java/android/backup/BackupManager.java
+++ b/core/java/android/backup/BackupManager.java
@@ -45,7 +45,7 @@
/**
* Defined backup transports understood by {@link IBackupManager.selectBackupTransport}.
*/
- public static final int TRANSPORT_ADB = 1;
+ public static final int TRANSPORT_LOCAL = 1;
public static final int TRANSPORT_GOOGLE = 2;
/**
diff --git a/core/java/com/android/internal/backup/AdbTransport.java b/core/java/com/android/internal/backup/AdbTransport.java
deleted file mode 100644
index 8d3bd1c..0000000
--- a/core/java/com/android/internal/backup/AdbTransport.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.android.internal.backup;
-
-import android.backup.RestoreSet;
-import android.content.pm.PackageInfo;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-
-/**
- * Backup transport for full backup over adb. This transport pipes everything to
- * a file in a known location in /cache, which 'adb backup' then pulls to the desktop
- * (deleting it afterwards).
- */
-
-public class AdbTransport extends IBackupTransport.Stub {
-
- public long requestBackupTime() throws RemoteException {
- return 0;
- }
-
- public int startSession() throws RemoteException {
- // TODO Auto-generated method stub
- return 0;
- }
-
- public int endSession() throws RemoteException {
- // TODO Auto-generated method stub
- return 0;
- }
-
- public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
- throws RemoteException {
- // TODO Auto-generated method stub
- return 0;
- }
-
- // Restore handling
- public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
- RestoreSet[] set = new RestoreSet[1];
- set[0].device = "USB";
- set[0].name = "adb";
- set[0].token = 0;
- return set;
- }
-
- public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
- // !!! TODO: real implementation
- return new PackageInfo[0];
- }
-
- public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor data)
- throws android.os.RemoteException {
- // !!! TODO: real implementation
- return 0;
- }
-}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
new file mode 100644
index 0000000..62fba4a
--- /dev/null
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -0,0 +1,140 @@
+package com.android.internal.backup;
+
+import android.backup.RestoreSet;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Backup transport for stashing stuff into a known location on disk, and
+ * later restoring from there. For testing only.
+ */
+
+public class LocalTransport extends IBackupTransport.Stub {
+ private static final String TAG = "LocalTransport";
+ private static final String DATA_FILE_NAME = "data";
+
+ private Context mContext;
+ private PackageManager mPackageManager;
+ private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
+ private FileFilter mDirFileFilter = new FileFilter() {
+ public boolean accept(File f) {
+ return f.isDirectory();
+ }
+ };
+
+
+ public LocalTransport(Context context) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ }
+
+ public long requestBackupTime() throws RemoteException {
+ // any time is a good time for local backup
+ return 0;
+ }
+
+ public int startSession() throws RemoteException {
+ return 0;
+ }
+
+ public int endSession() throws RemoteException {
+ return 0;
+ }
+
+ public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
+ throws RemoteException {
+ File packageDir = new File(mDataDir, packageInfo.packageName);
+ File imageFileName = new File(packageDir, DATA_FILE_NAME);
+
+ //!!! TODO: process the (partial) update into the persistent restore set:
+
+ // Parse out the existing image file into the key/value map
+
+ // Parse out the backup data into the key/value updates
+
+ // Apply the backup key/value updates to the image
+
+ // Write out the image in the canonical format
+
+ return -1;
+ }
+
+ // Restore handling
+ public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
+ // one hardcoded restore set
+ RestoreSet[] set = new RestoreSet[1];
+ set[0].device = "flash";
+ set[0].name = "Local disk image";
+ set[0].token = 0;
+ return set;
+ }
+
+ public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
+ // the available packages are the extant subdirs of mDatadir
+ File[] packageDirs = mDataDir.listFiles(mDirFileFilter);
+ ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>();
+ for (File dir : packageDirs) {
+ try {
+ PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(),
+ PackageManager.GET_SIGNATURES);
+ if (pkg != null) {
+ packages.add(pkg);
+ }
+ } catch (NameNotFoundException e) {
+ // restore set contains data for a package not installed on the
+ // phone -- just ignore it.
+ }
+ }
+
+ Log.v(TAG, "Built app set of " + packages.size() + " entries:");
+ for (PackageInfo p : packages) {
+ Log.v(TAG, " + " + p.packageName);
+ }
+
+ PackageInfo[] result = new PackageInfo[packages.size()];
+ return packages.toArray(result);
+ }
+
+ public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor output)
+ throws android.os.RemoteException {
+ // we only support one hardcoded restore set
+ if (token != 0) return -1;
+
+ // the data for a given package is at a known location
+ File packageDir = new File(mDataDir, packageInfo.packageName);
+ File imageFile = new File(packageDir, DATA_FILE_NAME);
+
+ // restore is relatively easy: we already maintain the full data set in
+ // the canonical form understandable to the BackupAgent
+ return copyFileToFD(imageFile, output);
+ }
+
+ private int copyFileToFD(File source, ParcelFileDescriptor dest) {
+ try {
+ FileInputStream in = new FileInputStream(source);
+ FileOutputStream out = new FileOutputStream(dest.getFileDescriptor());
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = in.read(buffer)) >= 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ } catch (IOException e) {
+ // something went wrong; claim failure
+ return -1;
+ }
+ return 0;
+ }
+}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index f871496..d3067ec 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -47,7 +47,7 @@
import android.backup.BackupManager;
import android.backup.RestoreSet;
-import com.android.internal.backup.AdbTransport;
+import com.android.internal.backup.LocalTransport;
import com.android.internal.backup.GoogleTransport;
import com.android.internal.backup.IBackupTransport;
@@ -72,6 +72,7 @@
private static final int MSG_RUN_BACKUP = 1;
private static final int MSG_RUN_FULL_BACKUP = 2;
+ private static final int MSG_RUN_RESTORE = 3;
// Timeout interval for deciding that a bind or clear-data has taken too long
static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -131,7 +132,9 @@
mStateDir = new File(Environment.getDataDirectory(), "backup");
mStateDir.mkdirs();
mDataDir = Environment.getDownloadCacheDirectory();
- mTransportId = BackupManager.TRANSPORT_GOOGLE;
+
+ //!!! TODO: default to cloud transport, not local
+ mTransportId = BackupManager.TRANSPORT_LOCAL;
// Build our mapping of uid to backup client services
synchronized (mBackupParticipants) {
@@ -212,6 +215,14 @@
case MSG_RUN_FULL_BACKUP:
break;
+
+ case MSG_RUN_RESTORE:
+ {
+ int token = msg.arg1;
+ IBackupTransport transport = (IBackupTransport)msg.obj;
+ (new PerformRestoreThread(transport, token)).run();
+ break;
+ }
}
}
}
@@ -331,9 +342,9 @@
private IBackupTransport createTransport(int transportID) {
IBackupTransport transport = null;
switch (transportID) {
- case BackupManager.TRANSPORT_ADB:
- if (DEBUG) Log.v(TAG, "Initializing adb transport");
- transport = new AdbTransport();
+ case BackupManager.TRANSPORT_LOCAL:
+ if (DEBUG) Log.v(TAG, "Initializing local transport");
+ transport = new LocalTransport(mContext);
break;
case BackupManager.TRANSPORT_GOOGLE:
@@ -585,10 +596,12 @@
class PerformRestoreThread extends Thread {
private IBackupTransport mTransport;
+ private int mToken;
private RestoreSet mImage;
- PerformRestoreThread(IBackupTransport transport) {
+ PerformRestoreThread(IBackupTransport transport, int restoreSetToken) {
mTransport = transport;
+ mToken = restoreSetToken;
}
@Override
@@ -622,7 +635,7 @@
try {
RestoreSet[] images = mTransport.getAvailableRestoreSets();
if (images.length > 0) {
- // !!! for now we always take the first set
+ // !!! TODO: pick out the set for this token
mImage = images[0];
// build the set of apps we will attempt to restore
@@ -870,6 +883,9 @@
// --- Binder interface ---
public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
+ mContext.enforceCallingPermission("android.permission.BACKUP",
+ "getAvailableRestoreSets");
+
synchronized(this) {
if (mRestoreSets == null) {
mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
@@ -879,10 +895,26 @@
}
public int performRestore(int token) throws android.os.RemoteException {
+ mContext.enforceCallingPermission("android.permission.BACKUP", "performRestore");
+
+ if (mRestoreSets != null) {
+ for (int i = 0; i < mRestoreSets.length; i++) {
+ if (token == mRestoreSets[i].token) {
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE,
+ mRestoreTransport);
+ msg.arg1 = token;
+ mBackupHandler.sendMessage(msg);
+ return 0;
+ }
+ }
+ }
return -1;
}
public void endRestoreSession() throws android.os.RemoteException {
+ mContext.enforceCallingPermission("android.permission.BACKUP",
+ "endRestoreSession");
+
mRestoreTransport.endSession();
mRestoreTransport = null;
}