Flesh out restore interface on manager; work up most of LocalTransport
This commit is contained in:
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
140
core/java/com/android/internal/backup/LocalTransport.java
Normal file
140
core/java/com/android/internal/backup/LocalTransport.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user