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}.
|
* 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;
|
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.BackupManager;
|
||||||
import android.backup.RestoreSet;
|
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.GoogleTransport;
|
||||||
import com.android.internal.backup.IBackupTransport;
|
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_BACKUP = 1;
|
||||||
private static final int MSG_RUN_FULL_BACKUP = 2;
|
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
|
// Timeout interval for deciding that a bind or clear-data has taken too long
|
||||||
static final long TIMEOUT_INTERVAL = 10 * 1000;
|
static final long TIMEOUT_INTERVAL = 10 * 1000;
|
||||||
@@ -131,7 +132,9 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
mStateDir = new File(Environment.getDataDirectory(), "backup");
|
mStateDir = new File(Environment.getDataDirectory(), "backup");
|
||||||
mStateDir.mkdirs();
|
mStateDir.mkdirs();
|
||||||
mDataDir = Environment.getDownloadCacheDirectory();
|
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
|
// Build our mapping of uid to backup client services
|
||||||
synchronized (mBackupParticipants) {
|
synchronized (mBackupParticipants) {
|
||||||
@@ -212,6 +215,14 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
|
|
||||||
case MSG_RUN_FULL_BACKUP:
|
case MSG_RUN_FULL_BACKUP:
|
||||||
break;
|
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) {
|
private IBackupTransport createTransport(int transportID) {
|
||||||
IBackupTransport transport = null;
|
IBackupTransport transport = null;
|
||||||
switch (transportID) {
|
switch (transportID) {
|
||||||
case BackupManager.TRANSPORT_ADB:
|
case BackupManager.TRANSPORT_LOCAL:
|
||||||
if (DEBUG) Log.v(TAG, "Initializing adb transport");
|
if (DEBUG) Log.v(TAG, "Initializing local transport");
|
||||||
transport = new AdbTransport();
|
transport = new LocalTransport(mContext);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BackupManager.TRANSPORT_GOOGLE:
|
case BackupManager.TRANSPORT_GOOGLE:
|
||||||
@@ -585,10 +596,12 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
|
|
||||||
class PerformRestoreThread extends Thread {
|
class PerformRestoreThread extends Thread {
|
||||||
private IBackupTransport mTransport;
|
private IBackupTransport mTransport;
|
||||||
|
private int mToken;
|
||||||
private RestoreSet mImage;
|
private RestoreSet mImage;
|
||||||
|
|
||||||
PerformRestoreThread(IBackupTransport transport) {
|
PerformRestoreThread(IBackupTransport transport, int restoreSetToken) {
|
||||||
mTransport = transport;
|
mTransport = transport;
|
||||||
|
mToken = restoreSetToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -622,7 +635,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
try {
|
try {
|
||||||
RestoreSet[] images = mTransport.getAvailableRestoreSets();
|
RestoreSet[] images = mTransport.getAvailableRestoreSets();
|
||||||
if (images.length > 0) {
|
if (images.length > 0) {
|
||||||
// !!! for now we always take the first set
|
// !!! TODO: pick out the set for this token
|
||||||
mImage = images[0];
|
mImage = images[0];
|
||||||
|
|
||||||
// build the set of apps we will attempt to restore
|
// build the set of apps we will attempt to restore
|
||||||
@@ -870,6 +883,9 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
|
|
||||||
// --- Binder interface ---
|
// --- Binder interface ---
|
||||||
public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
|
public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
|
||||||
|
mContext.enforceCallingPermission("android.permission.BACKUP",
|
||||||
|
"getAvailableRestoreSets");
|
||||||
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if (mRestoreSets == null) {
|
if (mRestoreSets == null) {
|
||||||
mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
|
mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
|
||||||
@@ -879,10 +895,26 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int performRestore(int token) throws android.os.RemoteException {
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void endRestoreSession() throws android.os.RemoteException {
|
public void endRestoreSession() throws android.os.RemoteException {
|
||||||
|
mContext.enforceCallingPermission("android.permission.BACKUP",
|
||||||
|
"endRestoreSession");
|
||||||
|
|
||||||
mRestoreTransport.endSession();
|
mRestoreTransport.endSession();
|
||||||
mRestoreTransport = null;
|
mRestoreTransport = null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user