/* * 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.android.server.backup.testing.TransportData.backupTransport; import static com.android.server.backup.testing.TransportData.d2dTransport; import static com.android.server.backup.testing.TransportData.localTransport; import static com.android.server.backup.testing.TransportTestUtils.setUpTransports; 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.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; 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.BackupManagerService; import com.android.server.backup.TransportManager; import com.android.server.backup.testing.TransportTestUtils; import com.android.server.backup.testing.TransportData; import com.android.server.backup.testing.TransportTestUtils.TransportMock; import com.android.server.backup.transport.TransportClient; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderPackages; 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.Iterator; import java.util.List; import java.util.stream.Stream; @RunWith(FrameworkRobolectricTestRunner.class) @Config(manifest = Config.NONE, sdk = 26) @SystemLoaderPackages({"com.android.server.backup"}) @Presubmit public class PerformInitializeTaskTest { @Mock private BackupManagerService mBackupManagerService; @Mock private TransportManager mTransportManager; @Mock private OnTaskFinishedListener mListener; @Mock private IBackupTransport mTransportBinder; @Mock private IBackupObserver mObserver; @Mock private AlarmManager mAlarmManager; @Mock private PendingIntent mRunInitIntent; private File mBaseStateDir; private TransportData mTransport; private String mTransportName; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mTransport = backupTransport(); mTransportName = mTransport.transportName; 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(mTransport); configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK); PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); verify(mTransportBinder).initializeDevice(); verify(mTransportBinder).finishBackup(); } @Test public void testRun_callsBackupManagerCorrectly() throws Exception { setUpTransport(mTransport); configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK); PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); verify(mBackupManagerService) .recordInitPending(false, mTransportName, mTransport.transportDirName); verify(mBackupManagerService) .resetBackupState(eq(new File(mBaseStateDir, mTransport.transportDirName))); } @Test public void testRun_callsObserverAndListenerCorrectly() throws Exception { setUpTransport(mTransport); configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK); PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_OK)); verify(mObserver).backupFinished(eq(TRANSPORT_OK)); verify(mListener).onFinished(any()); } @Test public void testRun_whenInitializeDeviceFails() throws Exception { setUpTransport(mTransport); configureTransport(mTransportBinder, TRANSPORT_ERROR, 0); PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); verify(mTransportBinder).initializeDevice(); verify(mTransportBinder, never()).finishBackup(); verify(mBackupManagerService) .recordInitPending(true, mTransportName, mTransport.transportDirName); } @Test public void testRun_whenInitializeDeviceFails_callsObserverAndListenerCorrectly() throws Exception { setUpTransport(mTransport); configureTransport(mTransportBinder, TRANSPORT_ERROR, 0); PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_ERROR)); verify(mObserver).backupFinished(eq(TRANSPORT_ERROR)); verify(mListener).onFinished(any()); } @Test public void testRun_whenInitializeDeviceFails_schedulesAlarm() throws Exception { setUpTransport(mTransport); configureTransport(mTransportBinder, TRANSPORT_ERROR, 0); PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); verify(mAlarmManager).set(anyInt(), anyLong(), eq(mRunInitIntent)); } @Test public void testRun_whenFinishBackupFails() throws Exception { setUpTransport(mTransport); configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR); PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); verify(mTransportBinder).initializeDevice(); verify(mTransportBinder).finishBackup(); verify(mBackupManagerService) .recordInitPending(true, mTransportName, mTransport.transportDirName); } @Test public void testRun_whenFinishBackupFails_callsObserverAndListenerCorrectly() throws Exception { setUpTransport(mTransport); configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR); PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_ERROR)); verify(mObserver).backupFinished(eq(TRANSPORT_ERROR)); verify(mListener).onFinished(any()); } @Test public void testRun_whenFinishBackupFails_schedulesAlarm() throws Exception { setUpTransport(mTransport); configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR); PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); verify(mAlarmManager).set(anyInt(), anyLong(), eq(mRunInitIntent)); } @Test public void testRun_whenOnlyOneTransportFails() throws Exception { TransportData transport1 = backupTransport(); TransportData transport2 = d2dTransport(); List transportMocks = setUpTransports(mTransportManager, transport1, transport2); configureTransport(transportMocks.get(0).transport, TRANSPORT_ERROR, 0); configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK); PerformInitializeTask performInitializeTask = createPerformInitializeTask(transport1.transportName, transport2.transportName); performInitializeTask.run(); verify(transportMocks.get(1).transport).initializeDevice(); verify(mObserver).onResult(eq(transport1.transportName), eq(TRANSPORT_ERROR)); verify(mObserver).onResult(eq(transport2.transportName), eq(TRANSPORT_OK)); verify(mObserver).backupFinished(eq(TRANSPORT_ERROR)); } @Test public void testRun_withMultipleTransports() throws Exception { List transportMocks = setUpTransports( mTransportManager, backupTransport(), d2dTransport(), localTransport()); configureTransport(transportMocks.get(0).transport, TRANSPORT_OK, TRANSPORT_OK); configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK); configureTransport(transportMocks.get(2).transport, TRANSPORT_OK, TRANSPORT_OK); String[] transportNames = Stream.of(new TransportData[] {backupTransport(), d2dTransport(), localTransport()}) .map(t -> t.transportName) .toArray(String[]::new); PerformInitializeTask performInitializeTask = createPerformInitializeTask(transportNames); performInitializeTask.run(); Iterator transportsIterator = Arrays.asList( new TransportData[] { backupTransport(), d2dTransport(), localTransport() }) .iterator(); for (TransportMock transportMock : transportMocks) { TransportData transport = transportsIterator.next(); verify(mTransportManager).getTransportClient(eq(transport.transportName), any()); verify(mTransportManager) .disposeOfTransportClient(eq(transportMock.transportClient), any()); } } @Test public void testRun_whenOnlyOneTransportFails_disposesAllTransports() throws Exception { TransportData transport1 = backupTransport(); TransportData transport2 = d2dTransport(); List transportMocks = setUpTransports(mTransportManager, transport1, transport2); configureTransport(transportMocks.get(0).transport, TRANSPORT_ERROR, 0); configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK); PerformInitializeTask performInitializeTask = createPerformInitializeTask(transport1.transportName, transport2.transportName); performInitializeTask.run(); verify(mTransportManager) .disposeOfTransportClient(eq(transportMocks.get(0).transportClient), any()); verify(mTransportManager) .disposeOfTransportClient(eq(transportMocks.get(1).transportClient), any()); } @Test public void testRun_whenTransportNotRegistered() throws Exception { setUpTransports(mTransportManager, mTransport.unregistered()); PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); 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 { TransportData transport1 = backupTransport().unregistered(); TransportData transport2 = d2dTransport(); List transportMocks = setUpTransports(mTransportManager, transport1, transport2); String registeredTransportName = transport2.transportName; IBackupTransport registeredTransport = transportMocks.get(1).transport; TransportClient registeredTransportClient = transportMocks.get(1).transportClient; PerformInitializeTask performInitializeTask = createPerformInitializeTask(transport1.transportName, transport2.transportName); 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 { TransportMock transportMock = setUpTransport(mTransport.unavailable()); PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); verify(mTransportManager) .disposeOfTransportClient(eq(transportMock.transportClient), any()); verify(mObserver).backupFinished(eq(TRANSPORT_ERROR)); verify(mListener).onFinished(any()); } @Test public void testRun_whenTransportThrowsDeadObjectException() throws Exception { TransportMock transportMock = setUpTransport(mTransport); IBackupTransport transport = transportMock.transport; TransportClient transportClient = transportMock.transportClient; when(transport.initializeDevice()).thenThrow(DeadObjectException.class); PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); 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 TransportMock setUpTransport(TransportData transport) throws Exception { TransportMock transportMock = TransportTestUtils.setUpTransport(mTransportManager, transport); mTransportBinder = transportMock.transport; return transportMock; } }