summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Bernardo Rufino <brufino@google.com> 2018-01-02 11:34:33 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2018-01-02 11:34:33 +0000
commita67d10763a9a65639c37aa118482c297b85decdd (patch)
tree7b328d78ab84bbdbcc7d7302fa1ed09dd84eee69
parentea713a3882d11675ca067ad63ab01a664d012f3b (diff)
parent79155590e67a30f520677d9d22e215365d595a73 (diff)
Merge "Binding on-demand #7: PerformInitializeTask usage"
-rw-r--r--services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java52
-rw-r--r--services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java93
-rw-r--r--services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java32
-rw-r--r--services/robotests/src/com/android/server/backup/TransportManagerTest.java3
-rw-r--r--services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java411
-rw-r--r--services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java5
6 files changed, 523 insertions, 73 deletions
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 35f1185e4c1f..3a37459843be 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -1086,34 +1086,34 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
}
/**
- * Maintain persistent state around whether need to do an initialize operation.
- * Must be called with the queue lock held.
+ * Maintain persistent state around whether need to do an initialize operation. This will lock
+ * on {@link #getQueueLock()}.
*/
- @GuardedBy("mQueueLock")
- public void recordInitPendingLocked(
+ public void recordInitPending(
boolean isPending, String transportName, String transportDirName) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "recordInitPendingLocked: " + isPending
- + " on transport " + transportName);
- }
+ synchronized (mQueueLock) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "recordInitPending(" + isPending + ") on transport " + transportName);
+ }
- File stateDir = new File(mBaseStateDir, transportDirName);
- File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+ File stateDir = new File(mBaseStateDir, transportDirName);
+ File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
- if (isPending) {
- // We need an init before we can proceed with sending backup data.
- // Record that with an entry in our set of pending inits, as well as
- // journaling it via creation of a sentinel file.
- mPendingInits.add(transportName);
- try {
- (new FileOutputStream(initPendingFile)).close();
- } catch (IOException ioe) {
- // Something is badly wrong with our permissions; just try to move on
+ if (isPending) {
+ // We need an init before we can proceed with sending backup data.
+ // Record that with an entry in our set of pending inits, as well as
+ // journaling it via creation of a sentinel file.
+ mPendingInits.add(transportName);
+ try {
+ (new FileOutputStream(initPendingFile)).close();
+ } catch (IOException ioe) {
+ // Something is badly wrong with our permissions; just try to move on
+ }
+ } else {
+ // No more initialization needed; wipe the journal and reset our state.
+ initPendingFile.delete();
+ mPendingInits.remove(transportName);
}
- } else {
- // No more initialization needed; wipe the journal and reset our state.
- initPendingFile.delete();
- mPendingInits.remove(transportName);
}
}
@@ -2321,7 +2321,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
final long oldId = Binder.clearCallingIdentity();
try {
mWakelock.acquire();
- mBackupHandler.post(new PerformInitializeTask(this, transportNames, observer));
+ OnTaskFinishedListener listener = caller -> mWakelock.release();
+ mBackupHandler.post(
+ new PerformInitializeTask(this, transportNames, observer, listener));
} finally {
Binder.restoreCallingIdentity(oldId);
}
@@ -2812,7 +2814,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
// build the set of transports for which we are posting an init
for (int i = 0; i < transportNames.size(); i++) {
- recordInitPendingLocked(
+ recordInitPending(
true,
transportNames.get(i),
transportDirNames.get(i));
diff --git a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
index b21b0724acc2..c6246981ef44 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
@@ -18,6 +18,7 @@ package com.android.server.backup.internal;
import static com.android.server.backup.RefactoredBackupManagerService.TAG;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.backup.BackupTransport;
import android.app.backup.IBackupObserver;
@@ -26,23 +27,63 @@ import android.os.SystemClock;
import android.util.EventLog;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.server.EventLogTags;
import com.android.server.backup.RefactoredBackupManagerService;
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.transport.TransportClient;
import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+/**
+ * Attempts to call {@link BackupTransport#initializeDevice()} followed by
+ * {@link BackupTransport#finishBackup()} for the transport names passed in with the intent of
+ * wiping backup data from the transport.
+ *
+ * If the transport returns error, it will record the operation as pending and schedule it to run in
+ * a future time according to {@link BackupTransport#requestBackupTime()}. The result status
+ * reported to observers will be the last unsuccessful status reported by the transports. If every
+ * operation was successful then it's {@link BackupTransport#TRANSPORT_OK}.
+ */
public class PerformInitializeTask implements Runnable {
+ private final RefactoredBackupManagerService mBackupManagerService;
+ private final TransportManager mTransportManager;
+ private final String[] mQueue;
+ private final File mBaseStateDir;
+ private final OnTaskFinishedListener mListener;
+ @Nullable private IBackupObserver mObserver;
- private RefactoredBackupManagerService backupManagerService;
- String[] mQueue;
- IBackupObserver mObserver;
+ public PerformInitializeTask(
+ RefactoredBackupManagerService backupManagerService,
+ String[] transportNames,
+ @Nullable IBackupObserver observer,
+ OnTaskFinishedListener listener) {
+ this(
+ backupManagerService,
+ backupManagerService.getTransportManager(),
+ transportNames,
+ observer,
+ listener,
+ backupManagerService.getBaseStateDir());
+ }
- public PerformInitializeTask(RefactoredBackupManagerService backupManagerService,
- String[] transportNames, IBackupObserver observer) {
- this.backupManagerService = backupManagerService;
+ @VisibleForTesting
+ PerformInitializeTask(
+ RefactoredBackupManagerService backupManagerService,
+ TransportManager transportManager,
+ String[] transportNames,
+ @Nullable IBackupObserver observer,
+ OnTaskFinishedListener listener,
+ File baseStateDir) {
+ mBackupManagerService = backupManagerService;
+ mTransportManager = transportManager;
mQueue = transportNames;
mObserver = observer;
+ mListener = listener;
+ mBaseStateDir = baseStateDir;
}
private void notifyResult(String target, int status) {
@@ -67,21 +108,25 @@ public class PerformInitializeTask implements Runnable {
public void run() {
// mWakelock is *acquired* when execution begins here
+ String callerLogString = "PerformInitializeTask.run()";
+ List<TransportClient> transportClientsToDisposeOf = new ArrayList<>(mQueue.length);
int result = BackupTransport.TRANSPORT_OK;
try {
for (String transportName : mQueue) {
- IBackupTransport transport =
- backupManagerService.getTransportManager().getTransportBinder(
- transportName);
- if (transport == null) {
+ TransportClient transportClient =
+ mTransportManager.getTransportClient(transportName, callerLogString);
+ if (transportClient == null) {
Slog.e(TAG, "Requested init for " + transportName + " but not found");
continue;
}
+ transportClientsToDisposeOf.add(transportClient);
Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
- String transportDirName = transport.transportDirName();
+ String transportDirName = transportClient.getTransportDirName();
EventLog.writeEvent(EventLogTags.BACKUP_START, transportDirName);
long startRealtime = SystemClock.elapsedRealtime();
+
+ IBackupTransport transport = transportClient.connectOrThrow(callerLogString);
int status = transport.initializeDevice();
if (status == BackupTransport.TRANSPORT_OK) {
@@ -93,42 +138,38 @@ public class PerformInitializeTask implements Runnable {
Slog.i(TAG, "Device init successful");
int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
- backupManagerService
- .resetBackupState(new File(backupManagerService.getBaseStateDir(),
- transportDirName));
+ File stateFileDir = new File(mBaseStateDir, transportDirName);
+ mBackupManagerService.resetBackupState(stateFileDir);
EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
- synchronized (backupManagerService.getQueueLock()) {
- backupManagerService.recordInitPendingLocked(
- false, transportName, transportDirName);
- }
+ mBackupManagerService.recordInitPending(false, transportName, transportDirName);
notifyResult(transportName, BackupTransport.TRANSPORT_OK);
} else {
// If this didn't work, requeue this one and try again
// after a suitable interval
Slog.e(TAG, "Transport error in initializeDevice()");
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
- synchronized (backupManagerService.getQueueLock()) {
- backupManagerService.recordInitPendingLocked(
- true, transportName, transportDirName);
- }
+ mBackupManagerService.recordInitPending(true, transportName, transportDirName);
notifyResult(transportName, status);
result = status;
// do this via another alarm to make sure of the wakelock states
long delay = transport.requestBackupTime();
Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay);
- backupManagerService.getAlarmManager().set(AlarmManager.RTC_WAKEUP,
+ mBackupManagerService.getAlarmManager().set(
+ AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + delay,
- backupManagerService.getRunInitIntent());
+ mBackupManagerService.getRunInitIntent());
}
}
} catch (Exception e) {
Slog.e(TAG, "Unexpected error performing init", e);
result = BackupTransport.TRANSPORT_ERROR;
} finally {
- // Done; release the wakelock
+ for (TransportClient transportClient : transportClientsToDisposeOf) {
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ }
notifyFinished(result);
- backupManagerService.getWakelock().release();
+ mListener.onFinished(callerLogString);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
index 1df0bf0c6e08..6c160a332096 100644
--- a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
@@ -23,37 +23,41 @@ import static com.android.server.backup.RefactoredBackupManagerService.TAG;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.PowerManager;
import android.util.ArraySet;
import android.util.Slog;
import com.android.server.backup.RefactoredBackupManagerService;
public class RunInitializeReceiver extends BroadcastReceiver {
-
- private RefactoredBackupManagerService backupManagerService;
+ private final RefactoredBackupManagerService mBackupManagerService;
public RunInitializeReceiver(RefactoredBackupManagerService backupManagerService) {
- this.backupManagerService = backupManagerService;
+ mBackupManagerService = backupManagerService;
}
public void onReceive(Context context, Intent intent) {
if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
- synchronized (backupManagerService.getQueueLock()) {
- final ArraySet<String> pendingInits = backupManagerService.getPendingInits();
+ synchronized (mBackupManagerService.getQueueLock()) {
+ final ArraySet<String> pendingInits = mBackupManagerService.getPendingInits();
if (DEBUG) {
Slog.v(TAG, "Running a device init; " + pendingInits.size() + " pending");
}
if (pendingInits.size() > 0) {
- final String[] transports = pendingInits.toArray(new String[pendingInits.size()]);
- PerformInitializeTask initTask = new PerformInitializeTask(backupManagerService,
- transports, null);
-
- // Acquire the wakelock and pass it to the init thread. it will
- // be released once init concludes.
- backupManagerService.clearPendingInits();
- backupManagerService.getWakelock().acquire();
- backupManagerService.getBackupHandler().post(initTask);
+ final String[] transports =
+ pendingInits.toArray(new String[pendingInits.size()]);
+
+ mBackupManagerService.clearPendingInits();
+
+ PowerManager.WakeLock wakelock = mBackupManagerService.getWakelock();
+ wakelock.acquire();
+ OnTaskFinishedListener listener = caller -> wakelock.release();
+
+ Runnable task =
+ new PerformInitializeTask(
+ mBackupManagerService, transports, null, listener);
+ mBackupManagerService.getBackupHandler().post(task);
}
}
}
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
index ced9b1ec8042..82830fe5c479 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -18,8 +18,6 @@ package com.android.server.backup;
import static com.google.common.truth.Truth.assertThat;
-import static junit.framework.Assert.fail;
-
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.robolectric.shadow.api.Shadow.extract;
@@ -58,7 +56,6 @@ import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPackageManager;
-import org.testng.Assert.ThrowingRunnable;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
new file mode 100644
index 000000000000..73f1c2fbda0a
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2017 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.internal;
+
+import static android.app.backup.BackupTransport.TRANSPORT_ERROR;
+import static android.app.backup.BackupTransport.TRANSPORT_OK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.app.Application;
+import android.app.PendingIntent;
+import android.app.backup.IBackupObserver;
+import android.os.DeadObjectException;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.RefactoredBackupManagerService;
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportNotAvailableException;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderClasses;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderClasses({PerformInitializeTaskTest.class, TransportManager.class})
+@Presubmit
+public class PerformInitializeTaskTest {
+ private static final String[] TRANSPORT_NAMES = {
+ "android/com.android.internal.backup.LocalTransport",
+ "com.google.android.gms/.backup.migrate.service.D2dTransport",
+ "com.google.android.gms/.backup.BackupTransportService"
+ };
+
+ private static final String TRANSPORT_NAME = TRANSPORT_NAMES[0];
+
+ @Mock private RefactoredBackupManagerService mBackupManagerService;
+ @Mock private TransportManager mTransportManager;
+ @Mock private OnTaskFinishedListener mListener;
+ @Mock private IBackupTransport mTransport;
+ @Mock private IBackupObserver mObserver;
+ @Mock private AlarmManager mAlarmManager;
+ @Mock private PendingIntent mRunInitIntent;
+ private File mBaseStateDir;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ Application context = RuntimeEnvironment.application;
+ mBaseStateDir = new File(context.getCacheDir(), "base_state_dir");
+ assertThat(mBaseStateDir.mkdir()).isTrue();
+
+ when(mBackupManagerService.getAlarmManager()).thenReturn(mAlarmManager);
+ when(mBackupManagerService.getRunInitIntent()).thenReturn(mRunInitIntent);
+ }
+
+ @Test
+ public void testRun_callsTransportCorrectly() throws Exception {
+ setUpTransport(TRANSPORT_NAME);
+ configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_OK);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+
+ performInitializeTask.run();
+
+ verify(mTransport).initializeDevice();
+ verify(mTransport).finishBackup();
+ }
+
+ @Test
+ public void testRun_callsBackupManagerCorrectly() throws Exception {
+ setUpTransport(TRANSPORT_NAME);
+ configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_OK);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+
+ performInitializeTask.run();
+
+ verify(mBackupManagerService)
+ .recordInitPending(false, TRANSPORT_NAME, dirName(TRANSPORT_NAME));
+ verify(mBackupManagerService)
+ .resetBackupState(eq(new File(mBaseStateDir, dirName(TRANSPORT_NAME))));
+ }
+
+ @Test
+ public void testRun_callsObserverAndListenerCorrectly() throws Exception {
+ setUpTransport(TRANSPORT_NAME);
+ configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_OK);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+
+ performInitializeTask.run();
+
+ verify(mObserver).onResult(eq(TRANSPORT_NAME), eq(TRANSPORT_OK));
+ verify(mObserver).backupFinished(eq(TRANSPORT_OK));
+ verify(mListener).onFinished(any());
+ }
+
+ @Test
+ public void testRun_whenInitializeDeviceFails() throws Exception {
+ setUpTransport(TRANSPORT_NAME);
+ configureTransport(mTransport, TRANSPORT_ERROR, 0);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+
+ performInitializeTask.run();
+
+ verify(mTransport).initializeDevice();
+ verify(mTransport, never()).finishBackup();
+ verify(mBackupManagerService)
+ .recordInitPending(true, TRANSPORT_NAME, dirName(TRANSPORT_NAME));
+ }
+
+ @Test
+ public void testRun_whenInitializeDeviceFails_callsObserverAndListenerCorrectly()
+ throws Exception {
+ setUpTransport(TRANSPORT_NAME);
+ configureTransport(mTransport, TRANSPORT_ERROR, 0);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+
+ performInitializeTask.run();
+
+ verify(mObserver).onResult(eq(TRANSPORT_NAME), eq(TRANSPORT_ERROR));
+ verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
+ verify(mListener).onFinished(any());
+ }
+
+ @Test
+ public void testRun_whenInitializeDeviceFails_schedulesAlarm() throws Exception {
+ setUpTransport(TRANSPORT_NAME);
+ configureTransport(mTransport, TRANSPORT_ERROR, 0);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+
+ performInitializeTask.run();
+
+ verify(mAlarmManager).set(anyInt(), anyLong(), eq(mRunInitIntent));
+ }
+
+ @Test
+ public void testRun_whenFinishBackupFails() throws Exception {
+ setUpTransport(TRANSPORT_NAME);
+ configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_ERROR);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+
+ performInitializeTask.run();
+
+ verify(mTransport).initializeDevice();
+ verify(mTransport).finishBackup();
+ verify(mBackupManagerService)
+ .recordInitPending(true, TRANSPORT_NAME, dirName(TRANSPORT_NAME));
+ }
+
+ @Test
+ public void testRun_whenFinishBackupFails_callsObserverAndListenerCorrectly() throws Exception {
+ setUpTransport(TRANSPORT_NAME);
+ configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_ERROR);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+
+ performInitializeTask.run();
+
+ verify(mObserver).onResult(eq(TRANSPORT_NAME), eq(TRANSPORT_ERROR));
+ verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
+ verify(mListener).onFinished(any());
+ }
+
+ @Test
+ public void testRun_whenFinishBackupFails_schedulesAlarm() throws Exception {
+ setUpTransport(TRANSPORT_NAME);
+ configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_ERROR);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+
+ performInitializeTask.run();
+
+ verify(mAlarmManager).set(anyInt(), anyLong(), eq(mRunInitIntent));
+ }
+
+ @Test
+ public void testRun_whenOnlyOneTransportFails() throws Exception {
+ List<TransportData> transports = setUpTransports(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
+ configureTransport(transports.get(0).transportMock, TRANSPORT_ERROR, 0);
+ configureTransport(transports.get(1).transportMock, TRANSPORT_OK, TRANSPORT_OK);
+ PerformInitializeTask performInitializeTask =
+ createPerformInitializeTask(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
+
+ performInitializeTask.run();
+
+ verify(transports.get(1).transportMock).initializeDevice();
+ verify(mObserver).onResult(eq(TRANSPORT_NAMES[0]), eq(TRANSPORT_ERROR));
+ verify(mObserver).onResult(eq(TRANSPORT_NAMES[1]), eq(TRANSPORT_OK));
+ verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
+ }
+
+ @Test
+ public void testRun_withMultipleTransports() throws Exception {
+ List<TransportData> transports = setUpTransports(TRANSPORT_NAMES);
+ configureTransport(transports.get(0).transportMock, TRANSPORT_OK, TRANSPORT_OK);
+ configureTransport(transports.get(1).transportMock, TRANSPORT_OK, TRANSPORT_OK);
+ configureTransport(transports.get(2).transportMock, TRANSPORT_OK, TRANSPORT_OK);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAMES);
+
+ performInitializeTask.run();
+
+ for (TransportData transport : transports) {
+ verify(mTransportManager).getTransportClient(eq(transport.transportName), any());
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(transport.transportClientMock), any());
+ }
+ }
+
+ @Test
+ public void testRun_whenOnlyOneTransportFails_disposesAllTransports() throws Exception {
+ List<TransportData> transports = setUpTransports(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
+ configureTransport(transports.get(0).transportMock, TRANSPORT_ERROR, 0);
+ configureTransport(transports.get(1).transportMock, TRANSPORT_OK, TRANSPORT_OK);
+ PerformInitializeTask performInitializeTask =
+ createPerformInitializeTask(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
+
+ performInitializeTask.run();
+
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(transports.get(0).transportClientMock), any());
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(transports.get(1).transportClientMock), any());
+ }
+
+ @Test
+ public void testRun_whenTransportNotRegistered() throws Exception {
+ setUpTransport(new TransportData(TRANSPORT_NAME, null, null));
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+
+ performInitializeTask.run();
+
+ verify(mTransportManager, never()).disposeOfTransportClient(any(), any());
+ verify(mObserver, never()).onResult(any(), anyInt());
+ verify(mObserver).backupFinished(eq(TRANSPORT_OK));
+ }
+
+ @Test
+ public void testRun_whenOnlyOneTransportNotRegistered() throws Exception {
+ List<TransportData> transports =
+ setUpTransports(
+ new TransportData(TRANSPORT_NAMES[0], null, null),
+ new TransportData(TRANSPORT_NAMES[1]));
+ String registeredTransportName = transports.get(1).transportName;
+ IBackupTransport registeredTransport = transports.get(1).transportMock;
+ TransportClient registeredTransportClient = transports.get(1).transportClientMock;
+ PerformInitializeTask performInitializeTask =
+ createPerformInitializeTask(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
+
+ performInitializeTask.run();
+
+ verify(registeredTransport).initializeDevice();
+ verify(mTransportManager).disposeOfTransportClient(eq(registeredTransportClient), any());
+ verify(mObserver).onResult(eq(registeredTransportName), eq(TRANSPORT_OK));
+ }
+
+ @Test
+ public void testRun_whenTransportNotAvailable() throws Exception {
+ TransportClient transportClient = mock(TransportClient.class);
+ setUpTransport(new TransportData(TRANSPORT_NAME, null, transportClient));
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+
+ performInitializeTask.run();
+
+ verify(mTransportManager).disposeOfTransportClient(eq(transportClient), any());
+ verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
+ verify(mListener).onFinished(any());
+ }
+
+ @Test
+ public void testRun_whenTransportThrowsDeadObjectException() throws Exception {
+ TransportClient transportClient = mock(TransportClient.class);
+ setUpTransport(new TransportData(TRANSPORT_NAME, mTransport, transportClient));
+ when(mTransport.initializeDevice()).thenThrow(DeadObjectException.class);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+
+ performInitializeTask.run();
+
+ verify(mTransportManager).disposeOfTransportClient(eq(transportClient), any());
+ verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
+ verify(mListener).onFinished(any());
+ }
+
+ private PerformInitializeTask createPerformInitializeTask(String... transportNames) {
+ return new PerformInitializeTask(
+ mBackupManagerService,
+ mTransportManager,
+ transportNames,
+ mObserver,
+ mListener,
+ mBaseStateDir);
+ }
+
+ private void configureTransport(
+ IBackupTransport transportMock, int initializeDeviceStatus, int finishBackupStatus)
+ throws Exception {
+ when(transportMock.initializeDevice()).thenReturn(initializeDeviceStatus);
+ when(transportMock.finishBackup()).thenReturn(finishBackupStatus);
+ }
+
+ private List<TransportData> setUpTransports(String... transportNames) throws Exception {
+ return setUpTransports(
+ Arrays.stream(transportNames)
+ .map(TransportData::new)
+ .toArray(TransportData[]::new));
+ }
+
+ /** @see #setUpTransport(TransportData) */
+ private List<TransportData> setUpTransports(TransportData... transports) throws Exception {
+ for (TransportData transport : transports) {
+ setUpTransport(transport);
+ }
+ return Arrays.asList(transports);
+ }
+
+ private void setUpTransport(String transportName) throws Exception {
+ setUpTransport(new TransportData(transportName, mTransport, mock(TransportClient.class)));
+ }
+
+ /**
+ * Configures transport according to {@link TransportData}:
+ *
+ * <ul>
+ * <li>{@link TransportData#transportMock} {@code null} means {@link
+ * TransportClient#connectOrThrow(String)} throws {@link TransportNotAvailableException}.
+ * <li>{@link TransportData#transportClientMock} {@code null} means {@link
+ * TransportManager#getTransportClient(String, String)} returns {@code null}.
+ * </ul>
+ */
+ private void setUpTransport(TransportData transport) throws Exception {
+ String transportName = transport.transportName;
+ String transportDirName = dirName(transportName);
+ IBackupTransport transportMock = transport.transportMock;
+ TransportClient transportClientMock = transport.transportClientMock;
+
+ if (transportMock != null) {
+ when(transportMock.name()).thenReturn(transportName);
+ when(transportMock.transportDirName()).thenReturn(transportDirName);
+ }
+
+ if (transportClientMock != null) {
+ when(transportClientMock.getTransportDirName()).thenReturn(transportDirName);
+ if (transportMock != null) {
+ when(transportClientMock.connectOrThrow(any())).thenReturn(transportMock);
+ } else {
+ when(transportClientMock.connectOrThrow(any()))
+ .thenThrow(TransportNotAvailableException.class);
+ }
+ }
+
+ when(mTransportManager.getTransportClient(eq(transportName), any()))
+ .thenReturn(transportClientMock);
+ }
+
+ private String dirName(String transportName) {
+ return transportName + "_dir_name";
+ }
+
+ private static class TransportData {
+ private final String transportName;
+ @Nullable private final IBackupTransport transportMock;
+ @Nullable private final TransportClient transportClientMock;
+
+ private TransportData(
+ String transportName,
+ @Nullable IBackupTransport transportMock,
+ @Nullable TransportClient transportClientMock) {
+ this.transportName = transportName;
+ this.transportMock = transportMock;
+ this.transportClientMock = transportClientMock;
+ }
+
+ private TransportData(String transportName) {
+ this(transportName, mock(IBackupTransport.class), mock(TransportClient.class));
+ }
+ }
+}
diff --git a/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java b/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java
index 78ac4ed92788..6c7313ba639e 100644
--- a/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java
+++ b/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java
@@ -27,14 +27,9 @@ import org.robolectric.internal.bytecode.InstrumentationConfiguration;
import org.robolectric.internal.bytecode.SandboxClassLoader;
import org.robolectric.util.Util;
-import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
import java.net.URL;
-import java.util.Arrays;
import java.util.Set;
import javax.annotation.Nonnull;