2009-04-29 14:03:25 -07:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2009 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package com.android.server;
|
|
|
|
|
2009-05-14 11:12:14 -07:00
|
|
|
import android.app.ActivityManagerNative;
|
|
|
|
import android.app.IActivityManager;
|
|
|
|
import android.app.IApplicationThread;
|
|
|
|
import android.app.IBackupAgent;
|
2009-05-06 16:13:56 -07:00
|
|
|
import android.content.BroadcastReceiver;
|
2009-04-29 14:03:25 -07:00
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
2009-05-06 16:13:56 -07:00
|
|
|
import android.content.IntentFilter;
|
2009-05-14 11:12:14 -07:00
|
|
|
import android.content.pm.ApplicationInfo;
|
2009-06-07 13:52:37 -07:00
|
|
|
import android.content.pm.PackageInfo;
|
2009-04-29 14:03:25 -07:00
|
|
|
import android.content.pm.PackageManager;
|
2009-06-02 16:11:00 -07:00
|
|
|
import android.content.pm.PackageManager.NameNotFoundException;
|
2009-05-06 16:13:56 -07:00
|
|
|
import android.net.Uri;
|
2009-04-29 14:03:25 -07:00
|
|
|
import android.os.Binder;
|
2009-05-06 16:13:56 -07:00
|
|
|
import android.os.Bundle;
|
2009-05-04 16:41:53 -07:00
|
|
|
import android.os.Environment;
|
2009-04-29 14:03:25 -07:00
|
|
|
import android.os.Handler;
|
|
|
|
import android.os.IBinder;
|
|
|
|
import android.os.Message;
|
2009-05-04 16:41:53 -07:00
|
|
|
import android.os.ParcelFileDescriptor;
|
2009-06-02 16:11:00 -07:00
|
|
|
import android.os.Process;
|
2009-04-29 14:03:25 -07:00
|
|
|
import android.os.RemoteException;
|
|
|
|
import android.util.Log;
|
|
|
|
import android.util.SparseArray;
|
|
|
|
|
|
|
|
import android.backup.IBackupManager;
|
2009-06-07 19:33:20 -07:00
|
|
|
import android.backup.IRestoreSession;
|
2009-06-02 16:11:00 -07:00
|
|
|
import android.backup.BackupManager;
|
2009-06-08 15:24:01 -07:00
|
|
|
import android.backup.RestoreSet;
|
2009-06-02 16:11:00 -07:00
|
|
|
|
|
|
|
import com.android.internal.backup.AdbTransport;
|
|
|
|
import com.android.internal.backup.GoogleTransport;
|
|
|
|
import com.android.internal.backup.IBackupTransport;
|
2009-04-29 14:03:25 -07:00
|
|
|
|
2009-05-04 16:41:53 -07:00
|
|
|
import java.io.File;
|
2009-05-06 18:06:21 -07:00
|
|
|
import java.io.FileDescriptor;
|
2009-05-04 16:41:53 -07:00
|
|
|
import java.io.FileNotFoundException;
|
2009-05-06 18:06:21 -07:00
|
|
|
import java.io.PrintWriter;
|
2009-04-29 14:03:25 -07:00
|
|
|
import java.lang.String;
|
2009-05-13 01:41:44 -04:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.HashMap;
|
2009-04-29 14:03:25 -07:00
|
|
|
import java.util.HashSet;
|
2009-05-14 11:12:14 -07:00
|
|
|
import java.util.Iterator;
|
2009-04-29 14:03:25 -07:00
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
class BackupManagerService extends IBackupManager.Stub {
|
|
|
|
private static final String TAG = "BackupManagerService";
|
|
|
|
private static final boolean DEBUG = true;
|
|
|
|
|
2009-05-06 18:06:21 -07:00
|
|
|
private static final long COLLECTION_INTERVAL = 1000;
|
|
|
|
//private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
|
2009-04-29 14:03:25 -07:00
|
|
|
|
|
|
|
private static final int MSG_RUN_BACKUP = 1;
|
2009-06-02 16:11:00 -07:00
|
|
|
private static final int MSG_RUN_FULL_BACKUP = 2;
|
2009-04-29 14:03:25 -07:00
|
|
|
|
|
|
|
private Context mContext;
|
|
|
|
private PackageManager mPackageManager;
|
2009-05-14 11:12:14 -07:00
|
|
|
private final IActivityManager mActivityManager;
|
2009-04-29 14:03:25 -07:00
|
|
|
private final BackupHandler mBackupHandler = new BackupHandler();
|
|
|
|
// map UIDs to the set of backup client services within that UID's app set
|
2009-05-14 11:12:14 -07:00
|
|
|
private SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
|
|
|
|
= new SparseArray<HashSet<ApplicationInfo>>();
|
2009-04-29 14:03:25 -07:00
|
|
|
// set of backup services that have pending changes
|
2009-05-06 11:22:00 -07:00
|
|
|
private class BackupRequest {
|
2009-05-14 11:12:14 -07:00
|
|
|
public ApplicationInfo appInfo;
|
2009-05-06 11:22:00 -07:00
|
|
|
public boolean fullBackup;
|
|
|
|
|
2009-05-14 11:12:14 -07:00
|
|
|
BackupRequest(ApplicationInfo app, boolean isFull) {
|
|
|
|
appInfo = app;
|
2009-05-06 11:22:00 -07:00
|
|
|
fullBackup = isFull;
|
|
|
|
}
|
2009-05-14 11:12:14 -07:00
|
|
|
|
|
|
|
public String toString() {
|
|
|
|
return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}";
|
|
|
|
}
|
2009-05-06 11:22:00 -07:00
|
|
|
}
|
2009-05-13 01:41:44 -04:00
|
|
|
// Backups that we haven't started yet.
|
2009-05-14 11:12:14 -07:00
|
|
|
private HashMap<ApplicationInfo,BackupRequest> mPendingBackups
|
|
|
|
= new HashMap<ApplicationInfo,BackupRequest>();
|
2009-05-13 01:41:44 -04:00
|
|
|
// Backups that we have started. These are separate to prevent starvation
|
|
|
|
// if an app keeps re-enqueuing itself.
|
|
|
|
private ArrayList<BackupRequest> mBackupQueue;
|
2009-04-29 14:03:25 -07:00
|
|
|
private final Object mQueueLock = new Object();
|
|
|
|
|
2009-06-02 16:11:00 -07:00
|
|
|
// The thread performing the sequence of queued backups binds to each app's agent
|
|
|
|
// in succession. Bind notifications are asynchronously delivered through the
|
|
|
|
// Activity Manager; use this lock object to signal when a requested binding has
|
|
|
|
// completed.
|
|
|
|
private final Object mAgentConnectLock = new Object();
|
|
|
|
private IBackupAgent mConnectedAgent;
|
|
|
|
private volatile boolean mConnecting;
|
|
|
|
|
|
|
|
private int mTransportId;
|
|
|
|
|
2009-05-04 16:41:53 -07:00
|
|
|
private File mStateDir;
|
2009-05-05 15:50:03 -07:00
|
|
|
private File mDataDir;
|
2009-04-29 14:03:25 -07:00
|
|
|
|
|
|
|
public BackupManagerService(Context context) {
|
|
|
|
mContext = context;
|
|
|
|
mPackageManager = context.getPackageManager();
|
2009-05-14 11:12:14 -07:00
|
|
|
mActivityManager = ActivityManagerNative.getDefault();
|
2009-04-29 14:03:25 -07:00
|
|
|
|
2009-05-04 16:41:53 -07:00
|
|
|
// Set up our bookkeeping
|
2009-05-05 15:50:03 -07:00
|
|
|
mStateDir = new File(Environment.getDataDirectory(), "backup");
|
2009-05-04 16:41:53 -07:00
|
|
|
mStateDir.mkdirs();
|
2009-05-05 15:50:03 -07:00
|
|
|
mDataDir = Environment.getDownloadCacheDirectory();
|
2009-06-02 16:11:00 -07:00
|
|
|
mTransportId = BackupManager.TRANSPORT_GOOGLE;
|
2009-05-04 16:41:53 -07:00
|
|
|
|
2009-05-06 16:13:56 -07:00
|
|
|
// Build our mapping of uid to backup client services
|
|
|
|
synchronized (mBackupParticipants) {
|
|
|
|
addPackageParticipantsLocked(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register for broadcasts about package install, etc., so we can
|
|
|
|
// update the provider list.
|
|
|
|
IntentFilter filter = new IntentFilter();
|
|
|
|
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
|
|
|
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
|
|
|
filter.addDataScheme("package");
|
|
|
|
mContext.registerReceiver(mBroadcastReceiver, filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----- Track installation/removal of packages -----
|
|
|
|
BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
if (DEBUG) Log.d(TAG, "Received broadcast " + intent);
|
|
|
|
|
|
|
|
Uri uri = intent.getData();
|
|
|
|
if (uri == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
String pkgName = uri.getSchemeSpecificPart();
|
|
|
|
if (pkgName == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
String action = intent.getAction();
|
|
|
|
if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
|
|
|
|
synchronized (mBackupParticipants) {
|
|
|
|
Bundle extras = intent.getExtras();
|
|
|
|
if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
|
|
|
|
// The package was just upgraded
|
|
|
|
updatePackageParticipantsLocked(pkgName);
|
|
|
|
} else {
|
|
|
|
// The package was just added
|
|
|
|
addPackageParticipantsLocked(pkgName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
|
|
|
|
Bundle extras = intent.getExtras();
|
|
|
|
if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
|
|
|
|
// The package is being updated. We'll receive a PACKAGE_ADDED shortly.
|
|
|
|
} else {
|
|
|
|
synchronized (mBackupParticipants) {
|
|
|
|
removePackageParticipantsLocked(pkgName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-05-13 01:41:44 -04:00
|
|
|
// ----- Run the actual backup process asynchronously -----
|
|
|
|
|
2009-05-14 11:12:14 -07:00
|
|
|
private class BackupHandler extends Handler {
|
2009-05-13 01:41:44 -04:00
|
|
|
public void handleMessage(Message msg) {
|
|
|
|
|
|
|
|
switch (msg.what) {
|
|
|
|
case MSG_RUN_BACKUP:
|
|
|
|
// snapshot the pending-backup set and work on that
|
|
|
|
synchronized (mQueueLock) {
|
2009-05-19 13:41:21 -07:00
|
|
|
if (mBackupQueue == null) {
|
2009-06-02 16:11:00 -07:00
|
|
|
mBackupQueue = new ArrayList<BackupRequest>();
|
2009-05-19 13:41:21 -07:00
|
|
|
for (BackupRequest b: mPendingBackups.values()) {
|
|
|
|
mBackupQueue.add(b);
|
|
|
|
}
|
2009-05-14 11:12:14 -07:00
|
|
|
mPendingBackups = new HashMap<ApplicationInfo,BackupRequest>();
|
2009-05-13 01:41:44 -04:00
|
|
|
}
|
|
|
|
// !!! TODO: start a new backup-queue journal file too
|
|
|
|
// WARNING: If we crash after this line, anything in mPendingBackups will
|
|
|
|
// be lost. FIX THIS.
|
|
|
|
}
|
2009-06-02 16:11:00 -07:00
|
|
|
(new PerformBackupThread(mTransportId, mBackupQueue)).run();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_RUN_FULL_BACKUP:
|
2009-05-13 01:41:44 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-02 16:11:00 -07:00
|
|
|
void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
|
|
|
|
final String packageName = request.appInfo.packageName;
|
2009-05-14 11:12:14 -07:00
|
|
|
Log.d(TAG, "processOneBackup doBackup() on " + packageName);
|
2009-05-13 01:41:44 -04:00
|
|
|
|
2009-05-14 11:12:14 -07:00
|
|
|
try {
|
2009-06-07 13:52:37 -07:00
|
|
|
// Look up the package info & signatures. This is first so that if it
|
|
|
|
// throws an exception, there's no file setup yet that would need to
|
|
|
|
// be unraveled.
|
|
|
|
PackageInfo packInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
|
|
|
|
|
2009-06-02 16:11:00 -07:00
|
|
|
// !!! TODO: get the state file dir from the transport
|
|
|
|
File savedStateName = new File(mStateDir, packageName);
|
|
|
|
File backupDataName = new File(mDataDir, packageName + ".data");
|
|
|
|
File newStateName = new File(mStateDir, packageName + ".new");
|
2009-05-13 01:41:44 -04:00
|
|
|
|
|
|
|
// 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
|
2009-06-02 16:11:00 -07:00
|
|
|
boolean success = false;
|
2009-05-13 01:41:44 -04:00
|
|
|
try {
|
2009-06-02 16:11:00 -07:00
|
|
|
agent.doBackup(savedState, backupData, newState);
|
|
|
|
success = true;
|
2009-05-13 01:41:44 -04:00
|
|
|
} finally {
|
|
|
|
if (savedState != null) {
|
|
|
|
savedState.close();
|
|
|
|
}
|
|
|
|
backupData.close();
|
|
|
|
newState.close();
|
|
|
|
}
|
|
|
|
|
2009-06-02 16:11:00 -07:00
|
|
|
// Now propagate the newly-backed-up data to the transport
|
|
|
|
if (success) {
|
2009-06-04 15:00:33 -07:00
|
|
|
if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
|
2009-06-02 16:11:00 -07:00
|
|
|
backupData =
|
|
|
|
ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
|
2009-06-07 13:52:37 -07:00
|
|
|
int error = transport.performBackup(packInfo, backupData);
|
2009-06-02 16:11:00 -07:00
|
|
|
|
|
|
|
// !!! 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);
|
|
|
|
}
|
2009-06-07 13:52:37 -07:00
|
|
|
} catch (NameNotFoundException e) {
|
|
|
|
Log.e(TAG, "Package not found on backup: " + packageName);
|
2009-05-13 01:41:44 -04:00
|
|
|
} catch (FileNotFoundException fnf) {
|
2009-06-07 13:52:37 -07:00
|
|
|
Log.w(TAG, "File not found on backup: ");
|
2009-05-13 01:41:44 -04:00
|
|
|
fnf.printStackTrace();
|
|
|
|
} catch (RemoteException e) {
|
2009-06-02 16:11:00 -07:00
|
|
|
Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:");
|
2009-05-13 01:41:44 -04:00
|
|
|
e.printStackTrace();
|
|
|
|
} catch (Exception e) {
|
|
|
|
Log.w(TAG, "Final exception guard in backup: ");
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-14 11:12:14 -07:00
|
|
|
// Add the backup agents in the given package to our set of known backup participants.
|
|
|
|
// If 'packageName' is null, adds all backup agents in the whole system.
|
2009-05-06 16:13:56 -07:00
|
|
|
void addPackageParticipantsLocked(String packageName) {
|
2009-05-14 11:12:14 -07:00
|
|
|
// Look for apps that define the android:backupAgent attribute
|
2009-06-02 16:11:00 -07:00
|
|
|
if (DEBUG) Log.v(TAG, "addPackageParticipantsLocked: " + packageName);
|
2009-05-14 11:12:14 -07:00
|
|
|
List<ApplicationInfo> targetApps = allAgentApps();
|
|
|
|
addPackageParticipantsLockedInner(packageName, targetApps);
|
2009-05-06 16:13:56 -07:00
|
|
|
}
|
|
|
|
|
2009-05-14 11:12:14 -07:00
|
|
|
private void addPackageParticipantsLockedInner(String packageName,
|
|
|
|
List<ApplicationInfo> targetApps) {
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.v(TAG, "Adding " + targetApps.size() + " backup participants:");
|
|
|
|
for (ApplicationInfo a : targetApps) {
|
|
|
|
Log.v(TAG, " " + a + " agent=" + a.backupAgentName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ApplicationInfo app : targetApps) {
|
|
|
|
if (packageName == null || app.packageName.equals(packageName)) {
|
|
|
|
int uid = app.uid;
|
|
|
|
HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
|
2009-05-06 16:13:56 -07:00
|
|
|
if (set == null) {
|
2009-05-14 11:12:14 -07:00
|
|
|
set = new HashSet<ApplicationInfo>();
|
2009-05-06 16:13:56 -07:00
|
|
|
mBackupParticipants.put(uid, set);
|
|
|
|
}
|
2009-05-14 11:12:14 -07:00
|
|
|
set.add(app);
|
2009-04-29 14:03:25 -07:00
|
|
|
}
|
|
|
|
}
|
2009-05-06 16:13:56 -07:00
|
|
|
}
|
2009-04-29 14:03:25 -07:00
|
|
|
|
2009-05-06 16:13:56 -07:00
|
|
|
// Remove the given package's backup services from our known active set. If
|
|
|
|
// 'packageName' is null, *all* backup services will be removed.
|
|
|
|
void removePackageParticipantsLocked(String packageName) {
|
2009-06-02 16:11:00 -07:00
|
|
|
if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName);
|
2009-05-14 11:12:14 -07:00
|
|
|
List<ApplicationInfo> allApps = null;
|
|
|
|
if (packageName != null) {
|
|
|
|
allApps = new ArrayList<ApplicationInfo>();
|
|
|
|
try {
|
|
|
|
ApplicationInfo app = mPackageManager.getApplicationInfo(packageName, 0);
|
|
|
|
allApps.add(app);
|
|
|
|
} catch (Exception e) {
|
|
|
|
// just skip it
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// all apps with agents
|
|
|
|
allApps = allAgentApps();
|
|
|
|
}
|
|
|
|
removePackageParticipantsLockedInner(packageName, allApps);
|
2009-05-06 16:13:56 -07:00
|
|
|
}
|
|
|
|
|
2009-05-13 01:41:44 -04:00
|
|
|
private void removePackageParticipantsLockedInner(String packageName,
|
2009-05-14 11:12:14 -07:00
|
|
|
List<ApplicationInfo> agents) {
|
2009-06-02 16:11:00 -07:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.v(TAG, "removePackageParticipantsLockedInner (" + packageName
|
|
|
|
+ ") removing " + agents.size() + " entries");
|
|
|
|
for (ApplicationInfo a : agents) {
|
|
|
|
Log.v(TAG, " - " + a);
|
|
|
|
}
|
|
|
|
}
|
2009-05-14 11:12:14 -07:00
|
|
|
for (ApplicationInfo app : agents) {
|
|
|
|
if (packageName == null || app.packageName.equals(packageName)) {
|
|
|
|
int uid = app.uid;
|
|
|
|
HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
|
2009-05-06 16:13:56 -07:00
|
|
|
if (set != null) {
|
2009-06-05 13:57:54 -07:00
|
|
|
// Find the existing entry with the same package name, and remove it.
|
|
|
|
// We can't just remove(app) because the instances are different.
|
|
|
|
for (ApplicationInfo entry: set) {
|
|
|
|
if (entry.packageName.equals(app.packageName)) {
|
|
|
|
set.remove(entry);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-05-06 16:13:56 -07:00
|
|
|
if (set.size() == 0) {
|
2009-06-02 16:11:00 -07:00
|
|
|
mBackupParticipants.delete(uid); }
|
2009-05-06 16:13:56 -07:00
|
|
|
}
|
2009-04-29 14:03:25 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-14 11:12:14 -07:00
|
|
|
// Returns the set of all applications that define an android:backupAgent attribute
|
|
|
|
private List<ApplicationInfo> allAgentApps() {
|
|
|
|
List<ApplicationInfo> allApps = mPackageManager.getInstalledApplications(0);
|
|
|
|
int N = allApps.size();
|
|
|
|
if (N > 0) {
|
|
|
|
for (int a = N-1; a >= 0; a--) {
|
|
|
|
ApplicationInfo app = allApps.get(a);
|
|
|
|
if (app.backupAgentName == null) {
|
|
|
|
allApps.remove(a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return allApps;
|
|
|
|
}
|
|
|
|
|
2009-05-06 16:13:56 -07:00
|
|
|
// Reset the given package's known backup participants. Unlike add/remove, the update
|
|
|
|
// action cannot be passed a null package name.
|
|
|
|
void updatePackageParticipantsLocked(String packageName) {
|
|
|
|
if (packageName == null) {
|
|
|
|
Log.e(TAG, "updatePackageParticipants called with null package name");
|
|
|
|
return;
|
|
|
|
}
|
2009-06-02 16:11:00 -07:00
|
|
|
if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName);
|
2009-05-06 16:13:56 -07:00
|
|
|
|
|
|
|
// brute force but small code size
|
2009-05-14 11:12:14 -07:00
|
|
|
List<ApplicationInfo> allApps = allAgentApps();
|
|
|
|
removePackageParticipantsLockedInner(packageName, allApps);
|
|
|
|
addPackageParticipantsLockedInner(packageName, allApps);
|
2009-05-06 16:13:56 -07:00
|
|
|
}
|
|
|
|
|
2009-06-07 19:33:20 -07:00
|
|
|
// Instantiate the given transport
|
|
|
|
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();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BackupManager.TRANSPORT_GOOGLE:
|
|
|
|
if (DEBUG) Log.v(TAG, "Initializing Google transport");
|
|
|
|
//!!! TODO: stand up the google backup transport for real here
|
|
|
|
transport = new GoogleTransport();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Log.e(TAG, "creating unknown transport " + transportID);
|
|
|
|
}
|
|
|
|
return transport;
|
|
|
|
}
|
|
|
|
|
2009-06-02 16:11:00 -07:00
|
|
|
// ----- Back up a set of applications via a worker thread -----
|
|
|
|
|
|
|
|
class PerformBackupThread extends Thread {
|
|
|
|
private static final String TAG = "PerformBackupThread";
|
|
|
|
int mTransport;
|
|
|
|
ArrayList<BackupRequest> mQueue;
|
|
|
|
|
|
|
|
public PerformBackupThread(int transportId, ArrayList<BackupRequest> queue) {
|
|
|
|
mTransport = transportId;
|
|
|
|
mQueue = queue;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
/*
|
|
|
|
* 1. start up the current transport
|
|
|
|
* 2. for each item in the queue:
|
|
|
|
* 2a. bind the agent [wait for async attach]
|
|
|
|
* 2b. set up the files and call doBackup()
|
|
|
|
* 2c. unbind the agent
|
|
|
|
* 3. tear down the transport
|
|
|
|
* 4. done!
|
|
|
|
*/
|
|
|
|
if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
|
|
|
|
|
|
|
|
// stand up the current transport
|
2009-06-07 19:33:20 -07:00
|
|
|
IBackupTransport transport = createTransport(mTransport);
|
|
|
|
if (transport == null) {
|
2009-06-02 16:11:00 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The transport is up and running; now run all the backups in our queue
|
|
|
|
doQueuedBackups(transport);
|
|
|
|
|
|
|
|
// Finally, tear down the transport
|
|
|
|
try {
|
|
|
|
transport.endSession();
|
|
|
|
} catch (Exception e) {
|
|
|
|
Log.e(TAG, "Error ending transport");
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void doQueuedBackups(IBackupTransport transport) {
|
|
|
|
for (BackupRequest request : mQueue) {
|
|
|
|
Log.d(TAG, "starting agent for " + request);
|
|
|
|
// !!! TODO: need to handle the restore case?
|
|
|
|
|
|
|
|
IBackupAgent agent = null;
|
|
|
|
int mode = (request.fullBackup)
|
|
|
|
? IApplicationThread.BACKUP_MODE_FULL
|
|
|
|
: IApplicationThread.BACKUP_MODE_INCREMENTAL;
|
|
|
|
try {
|
|
|
|
synchronized(mAgentConnectLock) {
|
|
|
|
mConnecting = true;
|
|
|
|
mConnectedAgent = null;
|
|
|
|
if (mActivityManager.bindBackupAgent(request.appInfo, mode)) {
|
|
|
|
Log.d(TAG, "awaiting agent for " + request);
|
|
|
|
|
|
|
|
// success; wait for the agent to arrive
|
|
|
|
while (mConnecting && mConnectedAgent == null) {
|
|
|
|
try {
|
|
|
|
mAgentConnectLock.wait(10000);
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
// just retry
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we timed out with no connect, abort and move on
|
|
|
|
if (mConnecting == true) {
|
|
|
|
Log.w(TAG, "Timeout waiting for agent " + request);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
agent = mConnectedAgent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (RemoteException e) {
|
|
|
|
// can't happen; activity manager is local
|
|
|
|
} catch (SecurityException ex) {
|
|
|
|
// Try for the next one.
|
|
|
|
Log.d(TAG, "error in bind", ex);
|
|
|
|
}
|
|
|
|
|
|
|
|
// successful bind? run the backup for this agent
|
|
|
|
if (agent != null) {
|
|
|
|
processOneBackup(request, agent, transport);
|
|
|
|
}
|
|
|
|
|
|
|
|
// send the unbind even on timeout, just in case
|
|
|
|
try {
|
|
|
|
mActivityManager.unbindBackupAgent(request.appInfo);
|
|
|
|
} catch (RemoteException e) {
|
|
|
|
// can't happen
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-29 14:03:25 -07:00
|
|
|
// ----- IBackupManager binder interface -----
|
|
|
|
|
2009-04-30 11:36:21 -07:00
|
|
|
public void dataChanged(String packageName) throws RemoteException {
|
2009-04-29 14:03:25 -07:00
|
|
|
// Record that we need a backup pass for the caller. Since multiple callers
|
|
|
|
// may share a uid, we need to note all candidates within that uid and schedule
|
|
|
|
// a backup pass for each of them.
|
2009-05-06 18:06:21 -07:00
|
|
|
|
|
|
|
Log.d(TAG, "dataChanged packageName=" + packageName);
|
2009-04-29 14:03:25 -07:00
|
|
|
|
2009-05-14 11:12:14 -07:00
|
|
|
HashSet<ApplicationInfo> targets = mBackupParticipants.get(Binder.getCallingUid());
|
2009-04-29 14:03:25 -07:00
|
|
|
if (targets != null) {
|
|
|
|
synchronized (mQueueLock) {
|
|
|
|
// Note that this client has made data changes that need to be backed up
|
2009-05-14 11:12:14 -07:00
|
|
|
for (ApplicationInfo app : targets) {
|
2009-04-30 11:36:21 -07:00
|
|
|
// validate the caller-supplied package name against the known set of
|
|
|
|
// packages associated with this uid
|
2009-05-14 11:12:14 -07:00
|
|
|
if (app.packageName.equals(packageName)) {
|
2009-05-13 01:41:44 -04:00
|
|
|
// Add the caller to the set of pending backups. If there is
|
|
|
|
// one already there, then overwrite it, but no harm done.
|
2009-05-14 11:12:14 -07:00
|
|
|
BackupRequest req = new BackupRequest(app, false);
|
|
|
|
mPendingBackups.put(app, req);
|
2009-05-13 01:41:44 -04:00
|
|
|
// !!! TODO: write to the pending-backup journal file in case of crash
|
2009-04-29 14:03:25 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-14 11:12:14 -07:00
|
|
|
if (DEBUG) {
|
|
|
|
int numKeys = mPendingBackups.size();
|
|
|
|
Log.d(TAG, "Scheduling backup for " + numKeys + " participants:");
|
|
|
|
for (BackupRequest b : mPendingBackups.values()) {
|
|
|
|
Log.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName);
|
|
|
|
}
|
|
|
|
}
|
2009-04-29 14:03:25 -07:00
|
|
|
// Schedule a backup pass in a few minutes. As backup-eligible data
|
|
|
|
// keeps changing, continue to defer the backup pass until things
|
|
|
|
// settle down, to avoid extra overhead.
|
2009-06-02 16:11:00 -07:00
|
|
|
mBackupHandler.removeMessages(MSG_RUN_BACKUP);
|
2009-04-29 14:03:25 -07:00
|
|
|
mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-05-06 11:22:00 -07:00
|
|
|
|
2009-06-02 16:11:00 -07:00
|
|
|
// Schedule a backup pass for a given package. This method will schedule a
|
|
|
|
// full backup even for apps that do not declare an android:backupAgent, so
|
|
|
|
// use with care.
|
2009-05-06 11:22:00 -07:00
|
|
|
public void scheduleFullBackup(String packageName) throws RemoteException {
|
2009-06-02 16:11:00 -07:00
|
|
|
mContext.enforceCallingPermission("android.permission.BACKUP", "scheduleFullBackup");
|
|
|
|
|
|
|
|
if (DEBUG) Log.v(TAG, "Scheduling immediate full backup for " + packageName);
|
2009-05-06 11:22:00 -07:00
|
|
|
synchronized (mQueueLock) {
|
2009-06-02 16:11:00 -07:00
|
|
|
try {
|
|
|
|
ApplicationInfo app = mPackageManager.getApplicationInfo(packageName, 0);
|
|
|
|
mPendingBackups.put(app, new BackupRequest(app, true));
|
|
|
|
mBackupHandler.sendEmptyMessage(MSG_RUN_FULL_BACKUP);
|
|
|
|
} catch (NameNotFoundException e) {
|
|
|
|
Log.w(TAG, "Could not find app for " + packageName + " to schedule full backup");
|
2009-05-06 11:22:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-05-06 18:06:21 -07:00
|
|
|
|
2009-06-02 16:11:00 -07:00
|
|
|
// Select which transport to use for the next backup operation
|
|
|
|
public int selectBackupTransport(int transportId) {
|
|
|
|
mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
|
|
|
|
|
|
|
|
int prevTransport = mTransportId;
|
|
|
|
mTransportId = transportId;
|
|
|
|
return prevTransport;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Callback: a requested backup agent has been instantiated. This should only
|
|
|
|
// be called from the Activity Manager.
|
2009-05-14 11:12:14 -07:00
|
|
|
public void agentConnected(String packageName, IBinder agentBinder) {
|
2009-06-02 16:11:00 -07:00
|
|
|
synchronized(mAgentConnectLock) {
|
|
|
|
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
|
|
|
|
Log.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
|
|
|
|
IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
|
|
|
|
mConnectedAgent = agent;
|
|
|
|
mConnecting = false;
|
|
|
|
} else {
|
|
|
|
Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
|
|
|
|
+ " claiming agent connected");
|
|
|
|
}
|
|
|
|
mAgentConnectLock.notifyAll();
|
|
|
|
}
|
2009-05-14 11:12:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Callback: a backup agent has failed to come up, or has unexpectedly quit.
|
|
|
|
// If the agent failed to come up in the first place, the agentBinder argument
|
2009-06-02 16:11:00 -07:00
|
|
|
// will be null. This should only be called from the Activity Manager.
|
2009-05-14 11:12:14 -07:00
|
|
|
public void agentDisconnected(String packageName) {
|
|
|
|
// TODO: handle backup being interrupted
|
2009-06-02 16:11:00 -07:00
|
|
|
synchronized(mAgentConnectLock) {
|
|
|
|
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
|
|
|
|
mConnectedAgent = null;
|
|
|
|
mConnecting = false;
|
|
|
|
} else {
|
|
|
|
Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
|
|
|
|
+ " claiming agent disconnected");
|
|
|
|
}
|
|
|
|
mAgentConnectLock.notifyAll();
|
|
|
|
}
|
2009-05-14 11:12:14 -07:00
|
|
|
}
|
|
|
|
|
2009-06-07 19:33:20 -07:00
|
|
|
// Hand off a restore session
|
|
|
|
public IRestoreSession beginRestoreSession(int transportID) {
|
|
|
|
mContext.enforceCallingPermission("android.permission.BACKUP", "beginRestoreSession");
|
|
|
|
return null;
|
|
|
|
}
|
2009-06-02 16:11:00 -07:00
|
|
|
|
2009-06-08 15:24:01 -07:00
|
|
|
// ----- Restore session -----
|
|
|
|
|
|
|
|
class RestoreSession extends IRestoreSession.Stub {
|
|
|
|
private IBackupTransport mRestoreTransport = null;
|
|
|
|
RestoreSet[] mRestoreSets = null;
|
|
|
|
|
|
|
|
RestoreSession(int transportID) {
|
|
|
|
mRestoreTransport = createTransport(transportID);
|
|
|
|
}
|
|
|
|
|
|
|
|
// --- Binder interface ---
|
|
|
|
public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
|
|
|
|
synchronized(this) {
|
|
|
|
if (mRestoreSets == null) {
|
|
|
|
mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
|
|
|
|
}
|
|
|
|
return mRestoreSets;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public int performRestore(int token) throws android.os.RemoteException {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void endRestoreSession() throws android.os.RemoteException {
|
|
|
|
mRestoreTransport.endSession();
|
|
|
|
mRestoreTransport = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-02 16:11:00 -07:00
|
|
|
|
2009-05-06 18:06:21 -07:00
|
|
|
@Override
|
|
|
|
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
|
|
|
synchronized (mQueueLock) {
|
|
|
|
int N = mBackupParticipants.size();
|
|
|
|
pw.println("Participants:");
|
|
|
|
for (int i=0; i<N; i++) {
|
|
|
|
int uid = mBackupParticipants.keyAt(i);
|
|
|
|
pw.print(" uid: ");
|
|
|
|
pw.println(uid);
|
2009-05-14 11:12:14 -07:00
|
|
|
HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
|
|
|
|
for (ApplicationInfo app: participants) {
|
2009-05-06 18:06:21 -07:00
|
|
|
pw.print(" ");
|
2009-05-14 11:12:14 -07:00
|
|
|
pw.println(app.toString());
|
2009-05-06 18:06:21 -07:00
|
|
|
}
|
|
|
|
}
|
2009-05-14 11:12:14 -07:00
|
|
|
pw.println("Pending:");
|
|
|
|
Iterator<BackupRequest> br = mPendingBackups.values().iterator();
|
|
|
|
while (br.hasNext()) {
|
|
|
|
pw.print(" ");
|
|
|
|
pw.println(br);
|
|
|
|
}
|
2009-05-06 18:06:21 -07:00
|
|
|
}
|
|
|
|
}
|
2009-04-29 14:03:25 -07:00
|
|
|
}
|