With this, the BackupService onBackup method is called.
It took a bunch of refactoring inside BackupManagerService, which is unfortunately all temporary anyway, but it unblocks a bunch of stuff.
This commit is contained in:
@@ -46,6 +46,8 @@ import java.io.FileDescriptor;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.String;
|
import java.lang.String;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -74,137 +76,16 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
fullBackup = isFull;
|
fullBackup = isFull;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private HashSet<BackupRequest> mPendingBackups = new HashSet<BackupRequest>();
|
// Backups that we haven't started yet.
|
||||||
|
private HashMap<ComponentName,BackupRequest> mPendingBackups = new HashMap();
|
||||||
|
// Backups that we have started. These are separate to prevent starvation
|
||||||
|
// if an app keeps re-enqueuing itself.
|
||||||
|
private ArrayList<BackupRequest> mBackupQueue;
|
||||||
private final Object mQueueLock = new Object();
|
private final Object mQueueLock = new Object();
|
||||||
|
|
||||||
private File mStateDir;
|
private File mStateDir;
|
||||||
private File mDataDir;
|
private File mDataDir;
|
||||||
|
|
||||||
// ----- Handler that runs the actual backup process asynchronously -----
|
|
||||||
|
|
||||||
private class BackupHandler extends Handler implements ServiceConnection {
|
|
||||||
private volatile Object mBindSignaller = new Object();
|
|
||||||
private volatile boolean mBinding = false;
|
|
||||||
private IBackupService mTargetService = null;
|
|
||||||
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
|
|
||||||
switch (msg.what) {
|
|
||||||
case MSG_RUN_BACKUP:
|
|
||||||
{
|
|
||||||
// snapshot the pending-backup set and work on that
|
|
||||||
HashSet<BackupRequest> queue;
|
|
||||||
synchronized (mQueueLock) {
|
|
||||||
queue = mPendingBackups;
|
|
||||||
mPendingBackups = new HashSet<BackupRequest>();
|
|
||||||
// !!! TODO: start a new backup-queue journal file too
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk the set of pending backups, setting up the relevant files and
|
|
||||||
// invoking the backup service in each participant
|
|
||||||
Intent backupIntent = new Intent(BackupService.SERVICE_ACTION);
|
|
||||||
for (BackupRequest request : queue) {
|
|
||||||
mBinding = true;
|
|
||||||
mTargetService = null;
|
|
||||||
|
|
||||||
backupIntent.setClassName(request.service.packageName, request.service.name);
|
|
||||||
Log.d(TAG, "binding to " + backupIntent);
|
|
||||||
if (mContext.bindService(backupIntent, this, 0)) {
|
|
||||||
synchronized (mBindSignaller) {
|
|
||||||
while (mTargetService == null && mBinding == true) {
|
|
||||||
try {
|
|
||||||
mBindSignaller.wait();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mTargetService != null) {
|
|
||||||
try {
|
|
||||||
Log.d(TAG, "invoking doBackup() on " + backupIntent);
|
|
||||||
|
|
||||||
// !!! TODO right now these naming schemes limit applications to
|
|
||||||
// one backup service per package
|
|
||||||
File savedStateName = new File(mStateDir,
|
|
||||||
request.service.packageName);
|
|
||||||
File backupDataName = new File(mDataDir,
|
|
||||||
request.service.packageName + ".data");
|
|
||||||
File newStateName = new File(mStateDir,
|
|
||||||
request.service.packageName + ".new");
|
|
||||||
|
|
||||||
// In a full backup, we pass a null ParcelFileDescriptor as
|
|
||||||
// the saved-state "file"
|
|
||||||
ParcelFileDescriptor savedState = (request.fullBackup) ? null
|
|
||||||
: ParcelFileDescriptor.open(savedStateName,
|
|
||||||
ParcelFileDescriptor.MODE_READ_ONLY |
|
|
||||||
ParcelFileDescriptor.MODE_CREATE);
|
|
||||||
|
|
||||||
backupDataName.delete();
|
|
||||||
ParcelFileDescriptor backupData =
|
|
||||||
ParcelFileDescriptor.open(backupDataName,
|
|
||||||
ParcelFileDescriptor.MODE_READ_WRITE |
|
|
||||||
ParcelFileDescriptor.MODE_CREATE);
|
|
||||||
|
|
||||||
newStateName.delete();
|
|
||||||
ParcelFileDescriptor newState =
|
|
||||||
ParcelFileDescriptor.open(newStateName,
|
|
||||||
ParcelFileDescriptor.MODE_READ_WRITE |
|
|
||||||
ParcelFileDescriptor.MODE_CREATE);
|
|
||||||
|
|
||||||
// Run the target's backup pass
|
|
||||||
try {
|
|
||||||
mTargetService.doBackup(savedState, backupData, newState);
|
|
||||||
} finally {
|
|
||||||
savedState.close();
|
|
||||||
backupData.close();
|
|
||||||
newState.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// !!! TODO: Now propagate the newly-backed-up data to the transport
|
|
||||||
|
|
||||||
// !!! TODO: After successful transport, delete the now-stale data
|
|
||||||
// and juggle the files so that next time the new state is passed
|
|
||||||
backupDataName.delete();
|
|
||||||
newStateName.renameTo(savedStateName);
|
|
||||||
|
|
||||||
} catch (FileNotFoundException fnf) {
|
|
||||||
Log.d(TAG, "File not found on backup: ");
|
|
||||||
fnf.printStackTrace();
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.d(TAG, "Remote target " + backupIntent
|
|
||||||
+ " threw during backup:");
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(TAG, "Final exception guard in backup: ");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
mContext.unbindService(this);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "Unable to bind to " + backupIntent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
||||||
synchronized (mBindSignaller) {
|
|
||||||
mTargetService = IBackupService.Stub.asInterface(service);
|
|
||||||
mBinding = false;
|
|
||||||
mBindSignaller.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
|
||||||
synchronized (mBindSignaller) {
|
|
||||||
mTargetService = null;
|
|
||||||
mBinding = false;
|
|
||||||
mBindSignaller.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public BackupManagerService(Context context) {
|
public BackupManagerService(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mPackageManager = context.getPackageManager();
|
mPackageManager = context.getPackageManager();
|
||||||
@@ -268,6 +149,140 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ----- Run the actual backup process asynchronously -----
|
||||||
|
|
||||||
|
private class BackupHandler extends Handler implements ServiceConnection {
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
|
||||||
|
switch (msg.what) {
|
||||||
|
case MSG_RUN_BACKUP:
|
||||||
|
// snapshot the pending-backup set and work on that
|
||||||
|
synchronized (mQueueLock) {
|
||||||
|
mBackupQueue = new ArrayList();
|
||||||
|
for (BackupRequest b: mPendingBackups.values()) {
|
||||||
|
mBackupQueue.add(b);
|
||||||
|
}
|
||||||
|
mPendingBackups = new HashMap<ComponentName,BackupRequest>();
|
||||||
|
// !!! TODO: start a new backup-queue journal file too
|
||||||
|
// WARNING: If we crash after this line, anything in mPendingBackups will
|
||||||
|
// be lost. FIX THIS.
|
||||||
|
}
|
||||||
|
startOneService();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
|
Log.d(TAG, "onServiceConnected name=" + name + " service=" + service);
|
||||||
|
IBackupService bs = IBackupService.Stub.asInterface(service);
|
||||||
|
processOneBackup(name, bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
// TODO: handle backup being interrupted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startOneService() {
|
||||||
|
// Loop until we find someone to start or the queue empties out.
|
||||||
|
Intent intent = new Intent(BackupService.SERVICE_ACTION);
|
||||||
|
while (true) {
|
||||||
|
BackupRequest request;
|
||||||
|
synchronized (mQueueLock) {
|
||||||
|
int queueSize = mBackupQueue.size();
|
||||||
|
if (queueSize == 0) {
|
||||||
|
mBackupQueue = null;
|
||||||
|
// TODO: Anything else to do here?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
request = mBackupQueue.get(0);
|
||||||
|
// Take it off the queue when we're done.
|
||||||
|
}
|
||||||
|
|
||||||
|
intent.setClassName(request.service.packageName, request.service.name);
|
||||||
|
Log.d(TAG, "binding to " + intent);
|
||||||
|
try {
|
||||||
|
if (mContext.bindService(intent, mBackupHandler, Context.BIND_AUTO_CREATE)) {
|
||||||
|
Log.d(TAG, "awaiting service object for " + intent);
|
||||||
|
// success
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (SecurityException ex) {
|
||||||
|
// Try for the next one.
|
||||||
|
Log.d(TAG, "error in bind", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void processOneBackup(ComponentName name, IBackupService bs) {
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "processOneBackup doBackup() on " + name);
|
||||||
|
|
||||||
|
BackupRequest request;
|
||||||
|
synchronized (mQueueLock) {
|
||||||
|
request = mBackupQueue.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// !!! TODO right now these naming schemes limit applications to
|
||||||
|
// one backup service per package
|
||||||
|
File savedStateName = new File(mStateDir, request.service.packageName);
|
||||||
|
File backupDataName = new File(mDataDir, request.service.packageName + ".data");
|
||||||
|
File newStateName = new File(mStateDir, request.service.packageName + ".new");
|
||||||
|
|
||||||
|
// In a full backup, we pass a null ParcelFileDescriptor as
|
||||||
|
// the saved-state "file"
|
||||||
|
ParcelFileDescriptor savedState = (request.fullBackup) ? null
|
||||||
|
: ParcelFileDescriptor.open(savedStateName,
|
||||||
|
ParcelFileDescriptor.MODE_READ_ONLY |
|
||||||
|
ParcelFileDescriptor.MODE_CREATE);
|
||||||
|
|
||||||
|
backupDataName.delete();
|
||||||
|
ParcelFileDescriptor backupData =
|
||||||
|
ParcelFileDescriptor.open(backupDataName,
|
||||||
|
ParcelFileDescriptor.MODE_READ_WRITE |
|
||||||
|
ParcelFileDescriptor.MODE_CREATE);
|
||||||
|
|
||||||
|
newStateName.delete();
|
||||||
|
ParcelFileDescriptor newState =
|
||||||
|
ParcelFileDescriptor.open(newStateName,
|
||||||
|
ParcelFileDescriptor.MODE_READ_WRITE |
|
||||||
|
ParcelFileDescriptor.MODE_CREATE);
|
||||||
|
|
||||||
|
// Run the target's backup pass
|
||||||
|
try {
|
||||||
|
// TODO: Make this oneway
|
||||||
|
bs.doBackup(savedState, backupData, newState);
|
||||||
|
} finally {
|
||||||
|
if (savedState != null) {
|
||||||
|
savedState.close();
|
||||||
|
}
|
||||||
|
backupData.close();
|
||||||
|
newState.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// !!! TODO: Now propagate the newly-backed-up data to the transport
|
||||||
|
|
||||||
|
// !!! TODO: After successful transport, delete the now-stale data
|
||||||
|
// and juggle the files so that next time the new state is passed
|
||||||
|
backupDataName.delete();
|
||||||
|
newStateName.renameTo(savedStateName);
|
||||||
|
|
||||||
|
} catch (FileNotFoundException fnf) {
|
||||||
|
Log.d(TAG, "File not found on backup: ");
|
||||||
|
fnf.printStackTrace();
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.d(TAG, "Remote target " + name + " threw during backup:");
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Final exception guard in backup: ");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
synchronized (mQueueLock) {
|
||||||
|
mBackupQueue.remove(0);
|
||||||
|
}
|
||||||
|
mContext.unbindService(mBackupHandler);
|
||||||
|
}
|
||||||
|
|
||||||
// Add the backup services in the given package to our set of known backup participants.
|
// Add the backup services in the given package to our set of known backup participants.
|
||||||
// If 'packageName' is null, adds all backup services in the system.
|
// If 'packageName' is null, adds all backup services in the system.
|
||||||
void addPackageParticipantsLocked(String packageName) {
|
void addPackageParticipantsLocked(String packageName) {
|
||||||
@@ -305,7 +320,8 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
removePackageParticipantsLockedInner(packageName, services);
|
removePackageParticipantsLockedInner(packageName, services);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removePackageParticipantsLockedInner(String packageName, List<ResolveInfo> services) {
|
private void removePackageParticipantsLockedInner(String packageName,
|
||||||
|
List<ResolveInfo> services) {
|
||||||
for (ResolveInfo ri : services) {
|
for (ResolveInfo ri : services) {
|
||||||
if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) {
|
if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) {
|
||||||
int uid = ri.serviceInfo.applicationInfo.uid;
|
int uid = ri.serviceInfo.applicationInfo.uid;
|
||||||
@@ -353,12 +369,13 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
// validate the caller-supplied package name against the known set of
|
// validate the caller-supplied package name against the known set of
|
||||||
// packages associated with this uid
|
// packages associated with this uid
|
||||||
if (service.packageName.equals(packageName)) {
|
if (service.packageName.equals(packageName)) {
|
||||||
// add the caller to the set of pending backups
|
// Add the caller to the set of pending backups. If there is
|
||||||
if (mPendingBackups.add(new BackupRequest(service, false))) {
|
// one already there, then overwrite it, but no harm done.
|
||||||
|
mPendingBackups.put(new ComponentName(service.packageName, service.name),
|
||||||
|
new BackupRequest(service, true));
|
||||||
// !!! TODO: write to the pending-backup journal file in case of crash
|
// !!! TODO: write to the pending-backup journal file in case of crash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(TAG, "Scheduling backup for " + mPendingBackups.size() + " participants");
|
Log.d(TAG, "Scheduling backup for " + mPendingBackups.size() + " participants");
|
||||||
// Schedule a backup pass in a few minutes. As backup-eligible data
|
// Schedule a backup pass in a few minutes. As backup-eligible data
|
||||||
@@ -381,7 +398,8 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
HashSet<ServiceInfo> servicesAtUid = mBackupParticipants.get(uid);
|
HashSet<ServiceInfo> servicesAtUid = mBackupParticipants.get(uid);
|
||||||
for (ServiceInfo service: servicesAtUid) {
|
for (ServiceInfo service: servicesAtUid) {
|
||||||
if (service.packageName.equals(packageName)) {
|
if (service.packageName.equals(packageName)) {
|
||||||
mPendingBackups.add(new BackupRequest(service, true));
|
mPendingBackups.put(new ComponentName(service.packageName, service.name),
|
||||||
|
new BackupRequest(service, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.example.android.apis.app;
|
package com.android.backuptest;
|
||||||
|
|
||||||
import android.backup.BackupService;
|
import android.backup.BackupService;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
|||||||
Reference in New Issue
Block a user