Make RestoreSession.getAvailableRestoreSets() asynchronous
This transaction can involve the transport having to query a remote backend
over the wire, so it can take a Long Time(tm). Make it main-thread-safe by
making it asynchronous, with the results passed as a callback to the invoker's
RestoreObserver. We also make the IRestoreObserver callback interface
properly oneway.
Bug #2550665
Bug #2549422
Change-Id: If18a233a0a3d54c7b55101715c9e6195b762c5a0
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 6c1fa60..ecf1a1d 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -104,7 +104,8 @@
private static final int MSG_RUN_RESTORE = 3;
private static final int MSG_RUN_CLEAR = 4;
private static final int MSG_RUN_INITIALIZE = 5;
- private static final int MSG_TIMEOUT = 6;
+ private static final int MSG_RUN_GET_RESTORE_SETS = 6;
+ private static final int MSG_TIMEOUT = 7;
// Timeout interval for deciding that a bind or clear-data has taken too long
static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -177,6 +178,19 @@
IBackupTransport mLocalTransport, mGoogleTransport;
ActiveRestoreSession mActiveRestoreSession;
+ class RestoreGetSetsParams {
+ public IBackupTransport transport;
+ public ActiveRestoreSession session;
+ public IRestoreObserver observer;
+
+ RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
+ IRestoreObserver _observer) {
+ transport = _transport;
+ session = _session;
+ observer = _observer;
+ }
+ }
+
class RestoreParams {
public IBackupTransport transport;
public IRestoreObserver observer;
@@ -333,6 +347,36 @@
break;
}
+ case MSG_RUN_GET_RESTORE_SETS:
+ {
+ // Like other async operations, this is entered with the wakelock held
+ RestoreSet[] sets = null;
+ RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj;
+ try {
+ sets = params.transport.getAvailableRestoreSets();
+ // cache the result in the active session
+ synchronized (params.session) {
+ params.session.mRestoreSets = sets;
+ }
+ if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error from transport getting set list");
+ } finally {
+ if (params.observer != null) {
+ try {
+ params.observer.restoreSetsAvailable(sets);
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Unable to report listing to observer");
+ } catch (Exception e) {
+ Slog.e(TAG, "Restore observer threw", e);
+ }
+ }
+
+ mWakelock.release();
+ }
+ break;
+ }
+
case MSG_TIMEOUT:
{
synchronized (mCurrentOpLock) {
@@ -2343,24 +2387,28 @@
}
// --- Binder interface ---
- public synchronized RestoreSet[] getAvailableRestoreSets() {
+ public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getAvailableRestoreSets");
+ if (observer == null) {
+ throw new IllegalArgumentException("Observer must not be null");
+ }
long oldId = Binder.clearCallingIdentity();
try {
if (mRestoreTransport == null) {
Slog.w(TAG, "Null transport getting restore sets");
- return null;
+ return -1;
}
- if (mRestoreSets == null) { // valid transport; do the one-time fetch
- mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
- if (mRestoreSets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
- }
- return mRestoreSets;
+ // spin off the transport request to our service thread
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
+ new RestoreGetSetsParams(mRestoreTransport, this, observer));
+ mBackupHandler.sendMessage(msg);
+ return 0;
} catch (Exception e) {
Slog.e(TAG, "Error in getAvailableRestoreSets", e);
- return null;
+ return -1;
} finally {
Binder.restoreCallingIdentity(oldId);
}