Loading core/java/android/backup/IBackupManager.aidl +7 −1 Original line number Original line Diff line number Diff line Loading @@ -29,7 +29,13 @@ package android.backup; interface IBackupManager { interface IBackupManager { /** /** * Tell the system service that the caller has made changes to its * 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); 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); } } services/java/com/android/server/BackupManagerService.java +52 −15 Original line number Original line Diff line number Diff line Loading @@ -58,7 +58,16 @@ class BackupManagerService extends IBackupManager.Stub { private SparseArray<HashSet<ServiceInfo>> mBackupParticipants private SparseArray<HashSet<ServiceInfo>> mBackupParticipants = new SparseArray<HashSet<ServiceInfo>>(); = new SparseArray<HashSet<ServiceInfo>>(); // set of backup services that have pending changes // 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 final Object mQueueLock = new Object(); private File mStateDir; private File mStateDir; Loading @@ -77,21 +86,21 @@ class BackupManagerService extends IBackupManager.Stub { case MSG_RUN_BACKUP: case MSG_RUN_BACKUP: { { // snapshot the pending-backup set and work on that // snapshot the pending-backup set and work on that HashSet<ServiceInfo> queue; HashSet<BackupRequest> queue; synchronized (mQueueLock) { synchronized (mQueueLock) { queue = mPendingBackups; queue = mPendingBackups; mPendingBackups = new HashSet<ServiceInfo>(); mPendingBackups = new HashSet<BackupRequest>(); // !!! TODO: start a new backup-queue journal file too // !!! TODO: start a new backup-queue journal file too } } // Walk the set of pending backups, setting up the relevant files and // Walk the set of pending backups, setting up the relevant files and // invoking the backup service in each participant // invoking the backup service in each participant Intent backupIntent = new Intent(BackupService.SERVICE_ACTION); Intent backupIntent = new Intent(BackupService.SERVICE_ACTION); for (ServiceInfo service : queue) { for (BackupRequest request : queue) { mBinding = true; mBinding = true; mTargetService = null; mTargetService = null; backupIntent.setClassName(service.packageName, service.name); backupIntent.setClassName(request.service.packageName, request.service.name); Log.d(TAG, "binding to " + backupIntent); Log.d(TAG, "binding to " + backupIntent); if (mContext.bindService(backupIntent, this, 0)) { if (mContext.bindService(backupIntent, this, 0)) { synchronized (mBindSignaller) { synchronized (mBindSignaller) { Loading @@ -106,12 +115,22 @@ class BackupManagerService extends IBackupManager.Stub { try { try { Log.d(TAG, "invoking doBackup() on " + backupIntent); Log.d(TAG, "invoking doBackup() on " + backupIntent); File savedStateName = new File(mStateDir, service.packageName); // !!! TODO right now these naming schemes limit applications to File backupDataName = new File(mDataDir, service.packageName + ".data"); // one backup service per package File newStateName = new File(mStateDir, service.packageName + ".new"); File savedStateName = new File(mStateDir, request.service.packageName); File dummyName = new File(mStateDir, "#####"); 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 file that is never writeable, hence // is always zero-sized, as a sentinel to the callee that they must // write all of their data. ParcelFileDescriptor savedState = ParcelFileDescriptor savedState = ParcelFileDescriptor.open(savedStateName, ParcelFileDescriptor.open( (request.fullBackup) ? savedStateName : dummyName, ParcelFileDescriptor.MODE_READ_ONLY | ParcelFileDescriptor.MODE_READ_ONLY | ParcelFileDescriptor.MODE_CREATE); ParcelFileDescriptor.MODE_CREATE); Loading Loading @@ -231,7 +250,7 @@ class BackupManagerService extends IBackupManager.Stub { // 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 (mPendingBackups.add(service)) { if (mPendingBackups.add(new BackupRequest(service, false))) { // !!! TODO: write to the pending-backup journal file in case of crash // !!! TODO: write to the pending-backup journal file in case of crash } } } } Loading @@ -240,9 +259,27 @@ class BackupManagerService extends IBackupManager.Stub { // Schedule a backup pass in a few minutes. As backup-eligible data // Schedule a backup pass in a few minutes. As backup-eligible data // keeps changing, continue to defer the backup pass until things // keeps changing, continue to defer the backup pass until things // settle down, to avoid extra overhead. // settle down, to avoid extra overhead. mBackupHandler.removeMessages(MSG_RUN_BACKUP); mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL); 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)); } } } } } } } Loading
core/java/android/backup/IBackupManager.aidl +7 −1 Original line number Original line Diff line number Diff line Loading @@ -29,7 +29,13 @@ package android.backup; interface IBackupManager { interface IBackupManager { /** /** * Tell the system service that the caller has made changes to its * 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); 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); } }
services/java/com/android/server/BackupManagerService.java +52 −15 Original line number Original line Diff line number Diff line Loading @@ -58,7 +58,16 @@ class BackupManagerService extends IBackupManager.Stub { private SparseArray<HashSet<ServiceInfo>> mBackupParticipants private SparseArray<HashSet<ServiceInfo>> mBackupParticipants = new SparseArray<HashSet<ServiceInfo>>(); = new SparseArray<HashSet<ServiceInfo>>(); // set of backup services that have pending changes // 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 final Object mQueueLock = new Object(); private File mStateDir; private File mStateDir; Loading @@ -77,21 +86,21 @@ class BackupManagerService extends IBackupManager.Stub { case MSG_RUN_BACKUP: case MSG_RUN_BACKUP: { { // snapshot the pending-backup set and work on that // snapshot the pending-backup set and work on that HashSet<ServiceInfo> queue; HashSet<BackupRequest> queue; synchronized (mQueueLock) { synchronized (mQueueLock) { queue = mPendingBackups; queue = mPendingBackups; mPendingBackups = new HashSet<ServiceInfo>(); mPendingBackups = new HashSet<BackupRequest>(); // !!! TODO: start a new backup-queue journal file too // !!! TODO: start a new backup-queue journal file too } } // Walk the set of pending backups, setting up the relevant files and // Walk the set of pending backups, setting up the relevant files and // invoking the backup service in each participant // invoking the backup service in each participant Intent backupIntent = new Intent(BackupService.SERVICE_ACTION); Intent backupIntent = new Intent(BackupService.SERVICE_ACTION); for (ServiceInfo service : queue) { for (BackupRequest request : queue) { mBinding = true; mBinding = true; mTargetService = null; mTargetService = null; backupIntent.setClassName(service.packageName, service.name); backupIntent.setClassName(request.service.packageName, request.service.name); Log.d(TAG, "binding to " + backupIntent); Log.d(TAG, "binding to " + backupIntent); if (mContext.bindService(backupIntent, this, 0)) { if (mContext.bindService(backupIntent, this, 0)) { synchronized (mBindSignaller) { synchronized (mBindSignaller) { Loading @@ -106,12 +115,22 @@ class BackupManagerService extends IBackupManager.Stub { try { try { Log.d(TAG, "invoking doBackup() on " + backupIntent); Log.d(TAG, "invoking doBackup() on " + backupIntent); File savedStateName = new File(mStateDir, service.packageName); // !!! TODO right now these naming schemes limit applications to File backupDataName = new File(mDataDir, service.packageName + ".data"); // one backup service per package File newStateName = new File(mStateDir, service.packageName + ".new"); File savedStateName = new File(mStateDir, request.service.packageName); File dummyName = new File(mStateDir, "#####"); 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 file that is never writeable, hence // is always zero-sized, as a sentinel to the callee that they must // write all of their data. ParcelFileDescriptor savedState = ParcelFileDescriptor savedState = ParcelFileDescriptor.open(savedStateName, ParcelFileDescriptor.open( (request.fullBackup) ? savedStateName : dummyName, ParcelFileDescriptor.MODE_READ_ONLY | ParcelFileDescriptor.MODE_READ_ONLY | ParcelFileDescriptor.MODE_CREATE); ParcelFileDescriptor.MODE_CREATE); Loading Loading @@ -231,7 +250,7 @@ class BackupManagerService extends IBackupManager.Stub { // 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 (mPendingBackups.add(service)) { if (mPendingBackups.add(new BackupRequest(service, false))) { // !!! TODO: write to the pending-backup journal file in case of crash // !!! TODO: write to the pending-backup journal file in case of crash } } } } Loading @@ -240,9 +259,27 @@ class BackupManagerService extends IBackupManager.Stub { // Schedule a backup pass in a few minutes. As backup-eligible data // Schedule a backup pass in a few minutes. As backup-eligible data // keeps changing, continue to defer the backup pass until things // keeps changing, continue to defer the backup pass until things // settle down, to avoid extra overhead. // settle down, to avoid extra overhead. mBackupHandler.removeMessages(MSG_RUN_BACKUP); mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL); 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)); } } } } } } }