Make BackupTransport more resilient
Currently, if the transport implementation throws an exception, the
framework will wait for response for the full timeout duration (10
minutes) before aborting the operation (see attached bug).
1. Add exception handlers to the transport wrapper that runs on the
remote side (i.e. transport implementation side) - while exceptions
are no longer propagated to system_server with introduction of async
transport, we'd still like to know when one occurs to stop waiting
for response (instead of relying on timeout).
2. Reduce the 10-minute timeout to 5 minutes - our data shows this is a
reasonable value.
Bug: 229670840
Test: 1. Manual (test B&R of any app)
2. atest CtsBackupHostTestCases
Change-Id: Id97fa470fae65efc0ad587615a6e9edfd1e5f298
This commit is contained in:
@@ -665,184 +665,292 @@ public class BackupTransport {
|
||||
|
||||
@Override
|
||||
public void name(AndroidFuture<String> resultFuture) throws RemoteException {
|
||||
String result = BackupTransport.this.name();
|
||||
resultFuture.complete(result);
|
||||
try {
|
||||
String result = BackupTransport.this.name();
|
||||
resultFuture.complete(result);
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configurationIntent(AndroidFuture<Intent> resultFuture)
|
||||
throws RemoteException {
|
||||
Intent result = BackupTransport.this.configurationIntent();
|
||||
resultFuture.complete(result);
|
||||
try {
|
||||
Intent result = BackupTransport.this.configurationIntent();
|
||||
resultFuture.complete(result);
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void currentDestinationString(AndroidFuture<String> resultFuture)
|
||||
throws RemoteException {
|
||||
String result = BackupTransport.this.currentDestinationString();
|
||||
resultFuture.complete(result);
|
||||
try {
|
||||
String result = BackupTransport.this.currentDestinationString();
|
||||
resultFuture.complete(result);
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataManagementIntent(AndroidFuture<Intent> resultFuture)
|
||||
throws RemoteException {
|
||||
Intent result = BackupTransport.this.dataManagementIntent();
|
||||
resultFuture.complete(result);
|
||||
try {
|
||||
Intent result = BackupTransport.this.dataManagementIntent();
|
||||
resultFuture.complete(result);
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataManagementIntentLabel(AndroidFuture<CharSequence> resultFuture)
|
||||
throws RemoteException {
|
||||
CharSequence result = BackupTransport.this.dataManagementIntentLabel();
|
||||
resultFuture.complete(result);
|
||||
try {
|
||||
CharSequence result = BackupTransport.this.dataManagementIntentLabel();
|
||||
resultFuture.complete(result);
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transportDirName(AndroidFuture<String> resultFuture) throws RemoteException {
|
||||
String result = BackupTransport.this.transportDirName();
|
||||
resultFuture.complete(result);
|
||||
try {
|
||||
String result = BackupTransport.this.transportDirName();
|
||||
resultFuture.complete(result);
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestBackupTime(AndroidFuture<Long> resultFuture) throws RemoteException {
|
||||
long result = BackupTransport.this.requestBackupTime();
|
||||
resultFuture.complete(result);
|
||||
try {
|
||||
long result = BackupTransport.this.requestBackupTime();
|
||||
resultFuture.complete(result);
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeDevice(ITransportStatusCallback callback) throws RemoteException {
|
||||
int result = BackupTransport.this.initializeDevice();
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
try {
|
||||
int result = BackupTransport.this.initializeDevice();
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
} catch (RuntimeException e) {
|
||||
callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags,
|
||||
ITransportStatusCallback callback) throws RemoteException {
|
||||
int result = BackupTransport.this.performBackup(packageInfo, inFd, flags);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
try {
|
||||
int result = BackupTransport.this.performBackup(packageInfo, inFd, flags);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
} catch (RuntimeException e) {
|
||||
callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearBackupData(PackageInfo packageInfo, ITransportStatusCallback callback)
|
||||
throws RemoteException {
|
||||
int result = BackupTransport.this.clearBackupData(packageInfo);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
try {
|
||||
int result = BackupTransport.this.clearBackupData(packageInfo);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
} catch (RuntimeException e) {
|
||||
callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishBackup(ITransportStatusCallback callback) throws RemoteException {
|
||||
int result = BackupTransport.this.finishBackup();
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
try {
|
||||
int result = BackupTransport.this.finishBackup();
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
} catch (RuntimeException e) {
|
||||
callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAvailableRestoreSets(AndroidFuture<List<RestoreSet>> resultFuture)
|
||||
throws RemoteException {
|
||||
RestoreSet[] result = BackupTransport.this.getAvailableRestoreSets();
|
||||
resultFuture.complete(Arrays.asList(result));
|
||||
try {
|
||||
RestoreSet[] result = BackupTransport.this.getAvailableRestoreSets();
|
||||
resultFuture.complete(Arrays.asList(result));
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getCurrentRestoreSet(AndroidFuture<Long> resultFuture)
|
||||
throws RemoteException {
|
||||
long result = BackupTransport.this.getCurrentRestoreSet();
|
||||
resultFuture.complete(result);
|
||||
try {
|
||||
long result = BackupTransport.this.getCurrentRestoreSet();
|
||||
resultFuture.complete(result);
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startRestore(long token, PackageInfo[] packages,
|
||||
ITransportStatusCallback callback) throws RemoteException {
|
||||
int result = BackupTransport.this.startRestore(token, packages);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
try {
|
||||
int result = BackupTransport.this.startRestore(token, packages);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
} catch (RuntimeException e) {
|
||||
callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextRestorePackage(AndroidFuture<RestoreDescription> resultFuture)
|
||||
throws RemoteException {
|
||||
RestoreDescription result = BackupTransport.this.nextRestorePackage();
|
||||
resultFuture.complete(result);
|
||||
try {
|
||||
RestoreDescription result = BackupTransport.this.nextRestorePackage();
|
||||
resultFuture.complete(result);
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getRestoreData(ParcelFileDescriptor outFd,
|
||||
ITransportStatusCallback callback) throws RemoteException {
|
||||
int result = BackupTransport.this.getRestoreData(outFd);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
try {
|
||||
int result = BackupTransport.this.getRestoreData(outFd);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
} catch (RuntimeException e) {
|
||||
callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishRestore(ITransportStatusCallback callback)
|
||||
throws RemoteException {
|
||||
BackupTransport.this.finishRestore();
|
||||
callback.onOperationComplete();
|
||||
try {
|
||||
BackupTransport.this.finishRestore();
|
||||
callback.onOperationComplete();
|
||||
} catch (RuntimeException e) {
|
||||
callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFullBackupTime(AndroidFuture<Long> resultFuture)
|
||||
throws RemoteException {
|
||||
long result = BackupTransport.this.requestFullBackupTime();
|
||||
resultFuture.complete(result);
|
||||
try {
|
||||
long result = BackupTransport.this.requestFullBackupTime();
|
||||
resultFuture.complete(result);
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket,
|
||||
int flags, ITransportStatusCallback callback) throws RemoteException {
|
||||
int result = BackupTransport.this.performFullBackup(targetPackage, socket, flags);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
try {
|
||||
int result = BackupTransport.this.performFullBackup(targetPackage, socket, flags);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
} catch (RuntimeException e) {
|
||||
callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkFullBackupSize(long size, ITransportStatusCallback callback)
|
||||
throws RemoteException {
|
||||
int result = BackupTransport.this.checkFullBackupSize(size);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
try {
|
||||
int result = BackupTransport.this.checkFullBackupSize(size);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
} catch (RuntimeException e) {
|
||||
callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBackupData(int numBytes, ITransportStatusCallback callback)
|
||||
throws RemoteException {
|
||||
int result = BackupTransport.this.sendBackupData(numBytes);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
try {
|
||||
int result = BackupTransport.this.sendBackupData(numBytes);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
} catch (RuntimeException e) {
|
||||
callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelFullBackup(ITransportStatusCallback callback) throws RemoteException {
|
||||
BackupTransport.this.cancelFullBackup();
|
||||
callback.onOperationComplete();
|
||||
try {
|
||||
BackupTransport.this.cancelFullBackup();
|
||||
callback.onOperationComplete();
|
||||
} catch (RuntimeException e) {
|
||||
callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup,
|
||||
AndroidFuture<Boolean> resultFuture) throws RemoteException {
|
||||
boolean result = BackupTransport.this.isAppEligibleForBackup(targetPackage,
|
||||
isFullBackup);
|
||||
resultFuture.complete(result);
|
||||
try {
|
||||
boolean result = BackupTransport.this.isAppEligibleForBackup(targetPackage,
|
||||
isFullBackup);
|
||||
resultFuture.complete(result);
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getBackupQuota(String packageName, boolean isFullBackup,
|
||||
AndroidFuture<Long> resultFuture) throws RemoteException {
|
||||
long result = BackupTransport.this.getBackupQuota(packageName, isFullBackup);
|
||||
resultFuture.complete(result);
|
||||
try {
|
||||
long result = BackupTransport.this.getBackupQuota(packageName, isFullBackup);
|
||||
resultFuture.complete(result);
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getTransportFlags(AndroidFuture<Integer> resultFuture) throws RemoteException {
|
||||
int result = BackupTransport.this.getTransportFlags();
|
||||
resultFuture.complete(result);
|
||||
try {
|
||||
int result = BackupTransport.this.getTransportFlags();
|
||||
resultFuture.complete(result);
|
||||
} catch (RuntimeException e) {
|
||||
resultFuture.cancel(/* mayInterruptIfRunning */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getNextFullRestoreDataChunk(ParcelFileDescriptor socket,
|
||||
ITransportStatusCallback callback) throws RemoteException {
|
||||
int result = BackupTransport.this.getNextFullRestoreDataChunk(socket);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
try {
|
||||
int result = BackupTransport.this.getNextFullRestoreDataChunk(socket);
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
} catch (RuntimeException e) {
|
||||
callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abortFullRestore(ITransportStatusCallback callback) throws RemoteException {
|
||||
int result = BackupTransport.this.abortFullRestore();
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
try {
|
||||
int result = BackupTransport.this.abortFullRestore();
|
||||
callback.onOperationCompleteWithStatus(result);
|
||||
} catch (RuntimeException e) {
|
||||
callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import com.android.internal.backup.ITransportStatusCallback;
|
||||
|
||||
public class TransportStatusCallback extends ITransportStatusCallback.Stub {
|
||||
private static final String TAG = "TransportStatusCallback";
|
||||
private static final int TIMEOUT_MILLIS = 600 * 1000; // 10 minutes.
|
||||
private static final int TIMEOUT_MILLIS = 300 * 1000; // 5 minutes.
|
||||
private static final int OPERATION_STATUS_DEFAULT = 0;
|
||||
|
||||
private final int mOperationTimeout;
|
||||
|
||||
Reference in New Issue
Block a user