summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/backup/BackupService.java15
-rw-r--r--core/java/android/backup/IBackupManager.aidl8
-rw-r--r--core/java/android/widget/SimpleAdapter.java6
-rw-r--r--core/java/android/widget/SimpleCursorAdapter.java7
-rw-r--r--services/java/com/android/server/BackupManagerService.java64
5 files changed, 72 insertions, 28 deletions
diff --git a/core/java/android/backup/BackupService.java b/core/java/android/backup/BackupService.java
index 879b544bd27f..5a6886d64ccf 100644
--- a/core/java/android/backup/BackupService.java
+++ b/core/java/android/backup/BackupService.java
@@ -39,7 +39,7 @@ import android.util.Log;
* <!-- Use the class "MyBackupService" to perform backups for my app -->
* <service android:name=".MyBackupService">
* <intent-filter>
- * <action android:name="android.service.action.BACKUP" />
+ * <action android:name="android.backup.BackupService.SERVICE" />
* </intent-filter>
* &lt;/service&gt;</pre>
*
@@ -54,19 +54,18 @@ public abstract class BackupService extends Service {
* IntentFilter} that accepts this action.
*/
@SdkConstant(SdkConstantType.SERVICE_ACTION)
- public static final String SERVICE_ACTION = "android.backup.BackupService";
+ public static final String SERVICE_ACTION = "android.backup.BackupService.SERVICE";
/**
* The application is being asked to write any data changed since the
* last time it performed a backup operation. The state data recorded
- * during the last backup pass is provided in the oldStateFd file descriptor.
- * If oldState.getStatSize() is zero or negative, no old state is available
- * and the application should perform a full backup. In both cases, a representation
- * of the final backup state after this pass should be written to the file pointed
- * to by the newStateFd file descriptor.
+ * during the last backup pass is provided in the oldState file descriptor.
+ * If oldState is null, no old state is available and the application should perform
+ * a full backup. In both cases, a representation of the final backup state after
+ * this pass should be written to the file pointed to by the newStateFd file descriptor.
*
* @param oldState An open, read-only ParcelFileDescriptor pointing to the last backup
- * state provided by the application. May be empty or invalid, in which
+ * state provided by the application. May be null, in which
* case no prior state is being provided and the application should
* perform a full backup.
* @param data An open, read/write ParcelFileDescriptor pointing to the backup data
diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl
index 7efaf589526b..cf22798507a8 100644
--- a/core/java/android/backup/IBackupManager.aidl
+++ b/core/java/android/backup/IBackupManager.aidl
@@ -29,7 +29,13 @@ package android.backup;
interface IBackupManager {
/**
* Tell the system service that the caller has made changes to its
- * data, and therefore needs to undergo a backup pass.
+ * data, and therefore needs to undergo an incremental backup pass.
*/
oneway void dataChanged(String packageName);
+
+ /**
+ * Schedule a full backup of the given package.
+ * !!! TODO: protect with a signature-or-system permission?
+ */
+ oneway void scheduleFullBackup(String packageName);
}
diff --git a/core/java/android/widget/SimpleAdapter.java b/core/java/android/widget/SimpleAdapter.java
index 093c24e0ca58..9dd4d151df1a 100644
--- a/core/java/android/widget/SimpleAdapter.java
+++ b/core/java/android/widget/SimpleAdapter.java
@@ -25,6 +25,7 @@ import android.net.Uri;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.WeakHashMap;
/**
* An easy adapter to map static data to views defined in an XML file. You can specify the data
@@ -57,6 +58,7 @@ public class SimpleAdapter extends BaseAdapter implements Filterable {
private int mResource;
private int mDropDownResource;
private LayoutInflater mInflater;
+ private final WeakHashMap<View, View[]> mHolders = new WeakHashMap<View, View[]>();
private SimpleFilter mFilter;
private ArrayList<Map<String, ?>> mUnfilteredData;
@@ -128,7 +130,7 @@ public class SimpleAdapter extends BaseAdapter implements Filterable {
holder[i] = v.findViewById(to[i]);
}
- v.setTag(holder);
+ mHolders.put(v, holder);
} else {
v = convertView;
}
@@ -160,7 +162,7 @@ public class SimpleAdapter extends BaseAdapter implements Filterable {
}
final ViewBinder binder = mViewBinder;
- final View[] holder = (View[]) view.getTag();
+ final View[] holder = mHolders.get(view);
final String[] from = mFrom;
final int[] to = mTo;
final int count = to.length;
diff --git a/core/java/android/widget/SimpleCursorAdapter.java b/core/java/android/widget/SimpleCursorAdapter.java
index c1bcd4219c58..b6a49e5bd5f7 100644
--- a/core/java/android/widget/SimpleCursorAdapter.java
+++ b/core/java/android/widget/SimpleCursorAdapter.java
@@ -22,6 +22,8 @@ import android.net.Uri;
import android.view.View;
import android.view.ViewGroup;
+import java.util.WeakHashMap;
+
/**
* An easy adapter to map columns from a cursor to TextViews or ImageViews
* defined in an XML file. You can specify which columns you want, which
@@ -64,6 +66,7 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter {
private CursorToStringConverter mCursorToStringConverter;
private ViewBinder mViewBinder;
private String[] mOriginalFrom;
+ private final WeakHashMap<View, View[]> mHolders = new WeakHashMap<View, View[]>();
/**
* Constructor.
@@ -106,7 +109,7 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter {
for (int i = 0; i < count; i++) {
holder[i] = v.findViewById(to[i]);
}
- v.setTag(holder);
+ mHolders.put(v, holder);
return v;
}
@@ -137,7 +140,7 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter {
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
- final View[] holder = (View[]) view.getTag();
+ final View[] holder = mHolders.get(view);
final ViewBinder binder = mViewBinder;
final int count = mTo.length;
final int[] from = mFrom;
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 04cd53f9278e..56caeeaadff2 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -58,7 +58,16 @@ class BackupManagerService extends IBackupManager.Stub {
private SparseArray<HashSet<ServiceInfo>> mBackupParticipants
= new SparseArray<HashSet<ServiceInfo>>();
// set of backup services that have pending changes
- private HashSet<ServiceInfo> mPendingBackups = new HashSet<ServiceInfo>();
+ private class BackupRequest {
+ public ServiceInfo service;
+ public boolean fullBackup;
+
+ BackupRequest(ServiceInfo svc, boolean isFull) {
+ service = svc;
+ fullBackup = isFull;
+ }
+ }
+ private HashSet<BackupRequest> mPendingBackups = new HashSet<BackupRequest>();
private final Object mQueueLock = new Object();
private File mStateDir;
@@ -77,21 +86,21 @@ class BackupManagerService extends IBackupManager.Stub {
case MSG_RUN_BACKUP:
{
// snapshot the pending-backup set and work on that
- HashSet<ServiceInfo> queue;
+ HashSet<BackupRequest> queue;
synchronized (mQueueLock) {
queue = mPendingBackups;
- mPendingBackups = new HashSet<ServiceInfo>();
+ 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 (ServiceInfo service : queue) {
+ for (BackupRequest request : queue) {
mBinding = true;
mTargetService = null;
- backupIntent.setClassName(service.packageName, service.name);
+ backupIntent.setClassName(request.service.packageName, request.service.name);
Log.d(TAG, "binding to " + backupIntent);
if (mContext.bindService(backupIntent, this, 0)) {
synchronized (mBindSignaller) {
@@ -106,15 +115,22 @@ class BackupManagerService extends IBackupManager.Stub {
try {
Log.d(TAG, "invoking doBackup() on " + backupIntent);
- File savedStateName = new File(mStateDir, service.packageName);
- File backupDataName = new File(mDataDir, service.packageName + ".data");
- File newStateName = new File(mStateDir, service.packageName + ".new");
-
- ParcelFileDescriptor savedState =
- ParcelFileDescriptor.open(savedStateName,
- ParcelFileDescriptor.MODE_READ_ONLY |
- ParcelFileDescriptor.MODE_CREATE);
+ // !!! 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,
@@ -231,7 +247,7 @@ class BackupManagerService extends IBackupManager.Stub {
// packages associated with this uid
if (service.packageName.equals(packageName)) {
// add the caller to the set of pending backups
- if (mPendingBackups.add(service)) {
+ if (mPendingBackups.add(new BackupRequest(service, false))) {
// !!! TODO: write to the pending-backup journal file in case of crash
}
}
@@ -240,9 +256,27 @@ class BackupManagerService extends IBackupManager.Stub {
// 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.
- mBackupHandler.removeMessages(MSG_RUN_BACKUP);
mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL);
}
}
}
+
+ // Schedule a backup pass for a given package, even if the caller is not part of
+ // that uid or package itself.
+ public void scheduleFullBackup(String packageName) throws RemoteException {
+ // !!! TODO: protect with a signature-or-system permission?
+ HashSet<ServiceInfo> targets = new HashSet<ServiceInfo>();
+ synchronized (mQueueLock) {
+ int numKeys = mBackupParticipants.size();
+ for (int index = 0; index < numKeys; index++) {
+ int uid = mBackupParticipants.keyAt(index);
+ HashSet<ServiceInfo> servicesAtUid = mBackupParticipants.get(uid);
+ for (ServiceInfo service: servicesAtUid) {
+ if (service.packageName.equals(packageName)) {
+ mPendingBackups.add(new BackupRequest(service, true));
+ }
+ }
+ }
+ }
+ }
}