diff options
4 files changed, 180 insertions, 63 deletions
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 @@ public class BackupManager { /** * 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.IRestoreSession; 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { // --- 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 @@ class BackupManagerService extends IBackupManager.Stub { } 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; } |
