diff options
| -rw-r--r-- | core/java/android/backup/BackupService.java | 15 | ||||
| -rw-r--r-- | core/java/android/backup/IBackupManager.aidl | 8 | ||||
| -rw-r--r-- | core/java/android/widget/SimpleAdapter.java | 6 | ||||
| -rw-r--r-- | core/java/android/widget/SimpleCursorAdapter.java | 7 | ||||
| -rw-r--r-- | services/java/com/android/server/BackupManagerService.java | 64 |
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> * </service></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)); + } + } + } + } + } } |