diff options
| author | 2018-01-02 11:34:33 +0000 | |
|---|---|---|
| committer | 2018-01-02 11:34:33 +0000 | |
| commit | a67d10763a9a65639c37aa118482c297b85decdd (patch) | |
| tree | 7b328d78ab84bbdbcc7d7302fa1ed09dd84eee69 | |
| parent | ea713a3882d11675ca067ad63ab01a664d012f3b (diff) | |
| parent | 79155590e67a30f520677d9d22e215365d595a73 (diff) | |
Merge "Binding on-demand #7: PerformInitializeTask usage"
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; |