summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ruslan Tkhakokhov <rthakohov@google.com> 2021-12-15 11:22:08 +0000
committer Ruslan Tkhakokhov <rthakohov@google.com> 2021-12-19 18:40:59 +0000
commit6aba9656ec6752d65b53aefad6bf4bb6ec63c4ef (patch)
tree300ab81facb7117abff1e0e005dac51bfbccde51
parent16a8f596d53a970338e12925f405d2a16c19fb82 (diff)
Update BackupTransportCallback to use async AIDL
Bug: 202716271 Test: 1. Unit tests WIP 2. atest CtsBackupTestCases CtsBackupHostTestCases GtsBackupTestCases GtsBackupHostTestCases Change-Id: Ifa45f35a415ef3c5fbab03696456fd7085568a7b
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java226
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java91
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java73
3 files changed, 359 insertions, 31 deletions
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
index 85ab48c5f7fb..89633373b152 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
@@ -17,6 +17,7 @@
package com.android.server.backup.transport;
import android.annotation.Nullable;
+import android.app.backup.BackupTransport;
import android.app.backup.RestoreDescription;
import android.app.backup.RestoreSet;
import android.content.Intent;
@@ -24,50 +25,70 @@ import android.content.pm.PackageInfo;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.backup.IBackupTransport;
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* Client to {@link com.android.internal.backup.IBackupTransport}. Manages the call to the remote
* transport service and delivers the results.
*/
public class BackupTransportClient {
+ private static final String TAG = "BackupTransportClient";
+
private final IBackupTransport mTransportBinder;
+ private final TransportStatusCallbackPool mCallbackPool;
BackupTransportClient(IBackupTransport transportBinder) {
mTransportBinder = transportBinder;
-
- // This is a temporary fix to allow blocking calls.
- // TODO: b/147702043. Redesign IBackupTransport so as to make the calls non-blocking.
- Binder.allowBlocking(mTransportBinder.asBinder());
+ mCallbackPool = new TransportStatusCallbackPool();
}
/**
* See {@link IBackupTransport#name()}.
*/
public String name() throws RemoteException {
- return mTransportBinder.name();
+ AndroidFuture<String> resultFuture = new AndroidFuture<>();
+ mTransportBinder.name(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
* See {@link IBackupTransport#configurationIntent()}
*/
public Intent configurationIntent() throws RemoteException {
- return mTransportBinder.configurationIntent();
+ AndroidFuture<Intent> resultFuture = new AndroidFuture<>();
+ mTransportBinder.configurationIntent(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
* See {@link IBackupTransport#currentDestinationString()}
*/
public String currentDestinationString() throws RemoteException {
- return mTransportBinder.currentDestinationString();
+ AndroidFuture<String> resultFuture = new AndroidFuture<>();
+ mTransportBinder.currentDestinationString(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
* See {@link IBackupTransport#dataManagementIntent()}
*/
public Intent dataManagementIntent() throws RemoteException {
- return mTransportBinder.dataManagementIntent();
+ AndroidFuture<Intent> resultFuture = new AndroidFuture<>();
+ mTransportBinder.dataManagementIntent(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
@@ -75,42 +96,67 @@ public class BackupTransportClient {
*/
@Nullable
public CharSequence dataManagementIntentLabel() throws RemoteException {
- return mTransportBinder.dataManagementIntentLabel();
+ AndroidFuture<CharSequence> resultFuture = new AndroidFuture<>();
+ mTransportBinder.dataManagementIntentLabel(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
* See {@link IBackupTransport#transportDirName()}
*/
public String transportDirName() throws RemoteException {
- return mTransportBinder.transportDirName();
+ AndroidFuture<String> resultFuture = new AndroidFuture<>();
+ mTransportBinder.transportDirName(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
* See {@link IBackupTransport#initializeDevice()}
*/
public int initializeDevice() throws RemoteException {
- return mTransportBinder.initializeDevice();
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.initializeDevice(callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#clearBackupData(PackageInfo)}
*/
public int clearBackupData(PackageInfo packageInfo) throws RemoteException {
- return mTransportBinder.clearBackupData(packageInfo);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.clearBackupData(packageInfo, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#finishBackup()}
*/
public int finishBackup() throws RemoteException {
- return mTransportBinder.finishBackup();
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.finishBackup(callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#requestBackupTime()}
*/
public long requestBackupTime() throws RemoteException {
- return mTransportBinder.requestBackupTime();
+ AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+ mTransportBinder.requestBackupTime(resultFuture);
+ Long result = getFutureResult(resultFuture);
+ return result == null ? BackupTransport.TRANSPORT_ERROR : result;
}
/**
@@ -118,56 +164,91 @@ public class BackupTransportClient {
*/
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags)
throws RemoteException {
- return mTransportBinder.performBackup(packageInfo, inFd, flags);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.performBackup(packageInfo, inFd, flags, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#getAvailableRestoreSets()}
*/
public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
- return mTransportBinder.getAvailableRestoreSets();
+ AndroidFuture<List<RestoreSet>> resultFuture = new AndroidFuture<>();
+ mTransportBinder.getAvailableRestoreSets(resultFuture);
+ List<RestoreSet> result = getFutureResult(resultFuture);
+ return result == null ? null : result.toArray(new RestoreSet[] {});
}
/**
* See {@link IBackupTransport#getCurrentRestoreSet()}
*/
public long getCurrentRestoreSet() throws RemoteException {
- return mTransportBinder.getCurrentRestoreSet();
+ AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+ mTransportBinder.getCurrentRestoreSet(resultFuture);
+ Long result = getFutureResult(resultFuture);
+ return result == null ? BackupTransport.TRANSPORT_ERROR : result;
}
/**
* See {@link IBackupTransport#startRestore(long, PackageInfo[])}
*/
public int startRestore(long token, PackageInfo[] packages) throws RemoteException {
- return mTransportBinder.startRestore(token, packages);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.startRestore(token, packages, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#nextRestorePackage()}
*/
public RestoreDescription nextRestorePackage() throws RemoteException {
- return mTransportBinder.nextRestorePackage();
+ AndroidFuture<RestoreDescription> resultFuture = new AndroidFuture<>();
+ mTransportBinder.nextRestorePackage(resultFuture);
+ return getFutureResult(resultFuture);
}
/**
* See {@link IBackupTransport#getRestoreData(ParcelFileDescriptor)}
*/
public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
- return mTransportBinder.getRestoreData(outFd);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.getRestoreData(outFd, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#finishRestore()}
*/
public void finishRestore() throws RemoteException {
- mTransportBinder.finishRestore();
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.finishRestore(callback);
+ callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#requestFullBackupTime()}
*/
public long requestFullBackupTime() throws RemoteException {
- return mTransportBinder.requestFullBackupTime();
+ AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+ mTransportBinder.requestFullBackupTime(resultFuture);
+ Long result = getFutureResult(resultFuture);
+ return result == null ? BackupTransport.TRANSPORT_ERROR : result;
}
/**
@@ -175,28 +256,52 @@ public class BackupTransportClient {
*/
public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket,
int flags) throws RemoteException {
- return mTransportBinder.performFullBackup(targetPackage, socket, flags);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.performFullBackup(targetPackage, socket, flags, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#checkFullBackupSize(long)}
*/
public int checkFullBackupSize(long size) throws RemoteException {
- return mTransportBinder.checkFullBackupSize(size);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.checkFullBackupSize(size, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#sendBackupData(int)}
*/
public int sendBackupData(int numBytes) throws RemoteException {
- return mTransportBinder.sendBackupData(numBytes);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ mTransportBinder.sendBackupData(numBytes, callback);
+ try {
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#cancelFullBackup()}
*/
public void cancelFullBackup() throws RemoteException {
- mTransportBinder.cancelFullBackup();
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.cancelFullBackup(callback);
+ callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
@@ -204,34 +309,93 @@ public class BackupTransportClient {
*/
public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup)
throws RemoteException {
- return mTransportBinder.isAppEligibleForBackup(targetPackage, isFullBackup);
+ AndroidFuture<Boolean> resultFuture = new AndroidFuture<>();
+ mTransportBinder.isAppEligibleForBackup(targetPackage, isFullBackup, resultFuture);
+ Boolean result = getFutureResult(resultFuture);
+ return result != null && result;
}
/**
* See {@link IBackupTransport#getBackupQuota(String, boolean)}
*/
public long getBackupQuota(String packageName, boolean isFullBackup) throws RemoteException {
- return mTransportBinder.getBackupQuota(packageName, isFullBackup);
+ AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+ mTransportBinder.getBackupQuota(packageName, isFullBackup, resultFuture);
+ Long result = getFutureResult(resultFuture);
+ return result == null ? BackupTransport.TRANSPORT_ERROR : result;
}
/**
* See {@link IBackupTransport#getNextFullRestoreDataChunk(ParcelFileDescriptor)}
*/
public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) throws RemoteException {
- return mTransportBinder.getNextFullRestoreDataChunk(socket);
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.getNextFullRestoreDataChunk(socket, callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#abortFullRestore()}
*/
public int abortFullRestore() throws RemoteException {
- return mTransportBinder.abortFullRestore();
+ TransportStatusCallback callback = mCallbackPool.acquire();
+ try {
+ mTransportBinder.abortFullRestore(callback);
+ return callback.getOperationStatus();
+ } finally {
+ mCallbackPool.recycle(callback);
+ }
}
/**
* See {@link IBackupTransport#getTransportFlags()}
*/
public int getTransportFlags() throws RemoteException {
- return mTransportBinder.getTransportFlags();
+ AndroidFuture<Integer> resultFuture = new AndroidFuture<>();
+ mTransportBinder.getTransportFlags(resultFuture);
+ Integer result = getFutureResult(resultFuture);
+ return result == null ? BackupTransport.TRANSPORT_ERROR : result;
+ }
+
+ private <T> T getFutureResult(AndroidFuture<T> future) {
+ try {
+ return future.get(600, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Slog.w(TAG, "Failed to get result from transport:", e);
+ return null;
+ }
+ }
+
+ private static class TransportStatusCallbackPool {
+ private static final int MAX_POOL_SIZE = 100;
+
+ private final Object mPoolLock = new Object();
+ private final Queue<TransportStatusCallback> mCallbackPool = new ArrayDeque<>();
+
+ TransportStatusCallback acquire() {
+ synchronized (mPoolLock) {
+ if (mCallbackPool.isEmpty()) {
+ return new TransportStatusCallback();
+ } else {
+ return mCallbackPool.poll();
+ }
+ }
+ }
+
+ void recycle(TransportStatusCallback callback) {
+ synchronized (mPoolLock) {
+ if (mCallbackPool.size() > MAX_POOL_SIZE) {
+ Slog.d(TAG, "TransportStatusCallback pool size exceeded");
+ return;
+ }
+
+ callback.reset();
+ mCallbackPool.add(callback);
+ }
+ }
}
}
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
new file mode 100644
index 000000000000..a55178c27eef
--- /dev/null
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 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.server.backup.transport;
+
+import android.app.backup.BackupTransport;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+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 OPERATION_STATUS_DEFAULT = 0;
+
+ private final int mOperationTimeout;
+
+ @GuardedBy("this")
+ private int mOperationStatus = OPERATION_STATUS_DEFAULT;
+ @GuardedBy("this")
+ private boolean mHasCompletedOperation = false;
+
+ public TransportStatusCallback() {
+ mOperationTimeout = TIMEOUT_MILLIS;
+ }
+
+ @VisibleForTesting
+ TransportStatusCallback(int operationTimeout) {
+ mOperationTimeout = operationTimeout;
+ }
+
+ @Override
+ public synchronized void onOperationCompleteWithStatus(int status) throws RemoteException {
+ mHasCompletedOperation = true;
+ mOperationStatus = status;
+
+ notifyAll();
+ }
+
+ @Override
+ public synchronized void onOperationComplete() throws RemoteException {
+ onOperationCompleteWithStatus(OPERATION_STATUS_DEFAULT);
+ }
+
+ synchronized int getOperationStatus() {
+ if (mHasCompletedOperation) {
+ return mOperationStatus;
+ }
+
+ long timeoutLeft = mOperationTimeout;
+ try {
+ while (!mHasCompletedOperation && timeoutLeft > 0) {
+ long waitStartTime = System.currentTimeMillis();
+ wait(timeoutLeft);
+ if (mHasCompletedOperation) {
+ return mOperationStatus;
+ }
+ timeoutLeft -= System.currentTimeMillis() - waitStartTime;
+ }
+
+ Slog.w(TAG, "Couldn't get operation status from transport");
+ return BackupTransport.TRANSPORT_ERROR;
+ } catch (InterruptedException e) {
+ Slog.w(TAG, "Couldn't get operation status from transport: ", e);
+ return BackupTransport.TRANSPORT_ERROR;
+ } finally {
+ reset();
+ }
+ }
+
+ synchronized void reset() {
+ mHasCompletedOperation = false;
+ mOperationStatus = OPERATION_STATUS_DEFAULT;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java b/services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java
new file mode 100644
index 000000000000..7f7901f893a3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 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.server.backup.transport;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.backup.BackupTransport;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class TransportStatusCallbackTest {
+ private static final int OPERATION_TIMEOUT_MILLIS = 10;
+ private static final int OPERATION_COMPLETE_STATUS = 123;
+
+ private TransportStatusCallback mTransportStatusCallback;
+
+ @Before
+ public void setUp() {
+ mTransportStatusCallback = new TransportStatusCallback();
+ }
+
+ @Test
+ public void testGetOperationStatus_withPreCompletedOperation_returnsStatus() throws Exception {
+ mTransportStatusCallback.onOperationCompleteWithStatus(OPERATION_COMPLETE_STATUS);
+
+ int result = mTransportStatusCallback.getOperationStatus();
+
+ assertThat(result).isEqualTo(OPERATION_COMPLETE_STATUS);
+ }
+
+ @Test
+ public void testGetOperationStatus_completeOperation_returnsStatus() throws Exception {
+ Thread thread = new Thread(() -> {
+ int result = mTransportStatusCallback.getOperationStatus();
+ assertThat(result).isEqualTo(OPERATION_COMPLETE_STATUS);
+ });
+ thread.start();
+
+ mTransportStatusCallback.onOperationCompleteWithStatus(OPERATION_COMPLETE_STATUS);
+
+ thread.join();
+ }
+
+ @Test
+ public void testGetOperationStatus_operationTimesOut_returnsError() throws Exception {
+ TransportStatusCallback callback = new TransportStatusCallback(OPERATION_TIMEOUT_MILLIS);
+
+ int result = callback.getOperationStatus();
+
+ assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+}