diff options
4 files changed, 107 insertions, 13 deletions
diff --git a/core/java/com/android/internal/backup/BackupConstants.java b/core/java/com/android/internal/backup/BackupConstants.java new file mode 100644 index 000000000000..3ee11bd3f949 --- /dev/null +++ b/core/java/com/android/internal/backup/BackupConstants.java @@ -0,0 +1,26 @@ +/* + * 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.internal.backup; + +/** + * Constants used internally between the backup manager and its transports + */ +public class BackupConstants { + public static final int TRANSPORT_OK = 0; + public static final int TRANSPORT_ERROR = 1; + public static final int TRANSPORT_NOT_INITIALIZED = 2; +} diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index 250bc9156c5c..47496a91e89d 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -81,10 +81,14 @@ interface IBackupTransport { * will be erased prior to the storage of the data provided here. The purpose of this * is to provide a guarantee that no stale data exists in the restore set when the * device begins providing backups. - * @return false if errors occurred (the backup should be aborted and rescheduled), - * true if everything is OK so far (but {@link #finishBackup} must be called). + * @return If everything is okay so far, returns zero (but {@link #finishBackup} must + * still be called). If the backend dataset has unexpectedly become unavailable, + * such as when it is deleted after a period of device inactivity, returns {@link + * BackupManager#DATASET_UNAVAILABLE}; in this case, the transport should be + * reinitalized and the entire backup pass restarted. Any other nonzero value is a + * fatal error requiring that this package's backup be aborted and rescheduled. */ - boolean performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd, + int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd, boolean wipeAllFirst); /** diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index 981ea82028a6..603f691f9522 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -56,7 +56,7 @@ public class LocalTransport extends IBackupTransport.Stub { return 0; } - public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data, + public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data, boolean wipeAllFirst) throws RemoteException { if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName); @@ -99,7 +99,7 @@ public class LocalTransport extends IBackupTransport.Stub { entity.write(buf, 0, dataSize); } catch (IOException e) { Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath()); - return false; + return BackupConstants.TRANSPORT_ERROR; } finally { entity.close(); } @@ -107,11 +107,11 @@ public class LocalTransport extends IBackupTransport.Stub { entityFile.delete(); } } - return true; + return BackupConstants.TRANSPORT_OK; } catch (IOException e) { // oops, something went wrong. abort the operation and return error. Log.v(TAG, "Exception reading backup input:", e); - return false; + return BackupConstants.TRANSPORT_ERROR; } } diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 854a6f9ca024..861f66d0144a 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -57,6 +57,7 @@ import android.backup.IRestoreObserver; import android.backup.IRestoreSession; import android.backup.RestoreSet; +import com.android.internal.backup.BackupConstants; import com.android.internal.backup.LocalTransport; import com.android.internal.backup.IBackupTransport; @@ -101,6 +102,7 @@ class BackupManagerService extends IBackupManager.Stub { private static final int BACKUP_AGENT_FAILURE_EVENT = 2823; private static final int BACKUP_PACKAGE_EVENT = 2824; private static final int BACKUP_SUCCESS_EVENT = 2825; + private static final int BACKUP_RESET_EVENT = 2826; private static final int RESTORE_START_EVENT = 2830; private static final int RESTORE_TRANSPORT_FAILURE_EVENT = 2831; @@ -406,6 +408,47 @@ class BackupManagerService extends IBackupManager.Stub { } } + // Reset all of our bookkeeping, in response to having been told that + // the backend data has been wiped [due to idle expiry, for example], + // so we must re-upload all saved settings. + void resetBackupState(File stateFileDir) { + synchronized (mQueueLock) { + // Wipe the "what we've ever backed up" tracking + try { + // close the ever-stored journal... + if (mEverStoredStream != null) { + mEverStoredStream.close(); + } + // ... so we can delete it and start over + mEverStored.delete(); + mEverStoredStream = new RandomAccessFile(mEverStored, "rwd"); + } catch (IOException e) { + Log.e(TAG, "Unable to open known-stored file!"); + mEverStoredStream = null; + } + mEverStoredApps.clear(); + + // Remove all the state files + for (File sf : stateFileDir.listFiles()) { + sf.delete(); + } + + // Enqueue a new backup of every participant + int N = mBackupParticipants.size(); + for (int i=0; i<N; i++) { + int uid = mBackupParticipants.keyAt(i); + HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i); + for (ApplicationInfo app: participants) { + try { + dataChanged(app.packageName); + } catch (RemoteException e) { + // can't happen; we're in the same process + } + } + } + } + } + // Add a transport to our set of available backends private void registerTransport(String name, IBackupTransport transport) { synchronized (mTransports) { @@ -891,8 +934,22 @@ class BackupManagerService extends IBackupManager.Stub { // If we haven't stored anything yet, we need to do an init // operation along with recording the metadata blob. boolean needInit = (mEverStoredApps.size() == 0); - processOneBackup(pmRequest, IBackupAgent.Stub.asInterface(pmAgent.onBind()), + int result = processOneBackup(pmRequest, + IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport, needInit); + if (result == BackupConstants.TRANSPORT_NOT_INITIALIZED) { + // The backend reports that our dataset has been wiped. We need to + // reset all of our bookkeeping and instead run a new backup pass for + // everything. + EventLog.writeEvent(BACKUP_RESET_EVENT, mTransport.transportDirName()); + resetBackupState(mStateDir); + backupNow(); + return; + } else if (result != BackupConstants.TRANSPORT_OK) { + // Give up if we couldn't even process the metadata + Log.e(TAG, "Meta backup err " + result); + return; + } // Now run all the backups in our queue int count = mQueue.size(); @@ -953,7 +1010,7 @@ class BackupManagerService extends IBackupManager.Stub { } } - void processOneBackup(BackupRequest request, IBackupAgent agent, + int processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport, boolean doInit) { final String packageName = request.appInfo.packageName; if (DEBUG) Log.d(TAG, "processOneBackup doBackup(" + doInit + ") on " + packageName); @@ -1007,7 +1064,7 @@ class BackupManagerService extends IBackupManager.Stub { EventLog.writeEvent(BACKUP_AGENT_FAILURE_EVENT, packageName, e.toString()); backupDataName.delete(); newStateName.delete(); - return; + return BackupConstants.TRANSPORT_ERROR; } finally { try { if (savedState != null) savedState.close(); } catch (IOException e) {} try { if (backupData != null) backupData.close(); } catch (IOException e) {} @@ -1027,7 +1084,13 @@ class BackupManagerService extends IBackupManager.Stub { // hold off on finishBackup() until the end, which implies holding off on // renaming *all* the output state files (see below) until that happens. - if (!transport.performBackup(packInfo, backupData, doInit) || + int performOkay = transport.performBackup(packInfo, backupData, doInit); + if (performOkay == BackupConstants.TRANSPORT_NOT_INITIALIZED) { + Log.i(TAG, "Backend not initialized"); + return performOkay; + } + + if ((performOkay != 0) || !transport.finishBackup()) { throw new Exception("Backup transport failed"); } @@ -1044,10 +1107,12 @@ class BackupManagerService extends IBackupManager.Stub { } catch (Exception e) { Log.e(TAG, "Transport error backing up " + packageName, e); EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName); - return; + return BackupConstants.TRANSPORT_ERROR; } finally { try { if (backupData != null) backupData.close(); } catch (IOException e) {} } + + return BackupConstants.TRANSPORT_OK; } } @@ -1590,7 +1655,6 @@ class BackupManagerService extends IBackupManager.Stub { if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass"); synchronized (mQueueLock) { try { - if (DEBUG) Log.v(TAG, "sending immediate backup broadcast"); mRunBackupIntent.send(); } catch (PendingIntent.CanceledException e) { // should never happen |