Merge change 26164 into eclair
* changes:
Reset backup tracking in response to transport data-wipe notification
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 0000000..3ee11bd
--- /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 250bc91..47496a9 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -81,10 +81,14 @@
* 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 981ea82..603f691f 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -56,7 +56,7 @@
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 @@
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 @@
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 854a6f9..861f66d 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -57,6 +57,7 @@
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 @@
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 @@
}
}
+ // 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 @@
// 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 @@
}
}
- 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 @@
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 @@
// 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 @@
} 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 @@
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