1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.backup.internal;
18
19import static android.app.backup.BackupTransport.TRANSPORT_ERROR;
20import static android.app.backup.BackupTransport.TRANSPORT_OK;
21
22import static com.android.server.backup.testing.TransportData.backupTransport;
23import static com.android.server.backup.testing.TransportData.d2dTransport;
24import static com.android.server.backup.testing.TransportData.localTransport;
25import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
26
27import static com.google.common.truth.Truth.assertThat;
28
29import static org.mockito.ArgumentMatchers.any;
30import static org.mockito.ArgumentMatchers.anyInt;
31import static org.mockito.ArgumentMatchers.anyLong;
32import static org.mockito.ArgumentMatchers.eq;
33import static org.mockito.Mockito.never;
34import static org.mockito.Mockito.verify;
35import static org.mockito.Mockito.when;
36
37import android.app.AlarmManager;
38import android.app.Application;
39import android.app.PendingIntent;
40import android.app.backup.IBackupObserver;
41import android.os.DeadObjectException;
42import android.platform.test.annotations.Presubmit;
43
44import com.android.internal.backup.IBackupTransport;
45import com.android.server.backup.BackupManagerService;
46import com.android.server.backup.TransportManager;
47import com.android.server.backup.testing.TransportTestUtils;
48import com.android.server.backup.testing.TransportData;
49import com.android.server.backup.testing.TransportTestUtils.TransportMock;
50import com.android.server.backup.transport.TransportClient;
51import com.android.server.testing.FrameworkRobolectricTestRunner;
52import com.android.server.testing.SystemLoaderPackages;
53
54import org.junit.Before;
55import org.junit.Test;
56import org.junit.runner.RunWith;
57import org.mockito.Mock;
58import org.mockito.MockitoAnnotations;
59import org.robolectric.RuntimeEnvironment;
60import org.robolectric.annotation.Config;
61
62import java.io.File;
63import java.util.Arrays;
64import java.util.Iterator;
65import java.util.List;
66import java.util.stream.Stream;
67
68@RunWith(FrameworkRobolectricTestRunner.class)
69@Config(manifest = Config.NONE, sdk = 26)
70@SystemLoaderPackages({"com.android.server.backup"})
71@Presubmit
72public class PerformInitializeTaskTest {
73    @Mock private BackupManagerService mBackupManagerService;
74    @Mock private TransportManager mTransportManager;
75    @Mock private OnTaskFinishedListener mListener;
76    @Mock private IBackupTransport mTransportBinder;
77    @Mock private IBackupObserver mObserver;
78    @Mock private AlarmManager mAlarmManager;
79    @Mock private PendingIntent mRunInitIntent;
80    private File mBaseStateDir;
81    private TransportData mTransport;
82    private String mTransportName;
83
84    @Before
85    public void setUp() throws Exception {
86        MockitoAnnotations.initMocks(this);
87
88        mTransport = backupTransport();
89        mTransportName = mTransport.transportName;
90
91        Application context = RuntimeEnvironment.application;
92        mBaseStateDir = new File(context.getCacheDir(), "base_state_dir");
93        assertThat(mBaseStateDir.mkdir()).isTrue();
94
95        when(mBackupManagerService.getAlarmManager()).thenReturn(mAlarmManager);
96        when(mBackupManagerService.getRunInitIntent()).thenReturn(mRunInitIntent);
97    }
98
99    @Test
100    public void testRun_callsTransportCorrectly() throws Exception {
101        setUpTransport(mTransport);
102        configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK);
103        PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
104
105        performInitializeTask.run();
106
107        verify(mTransportBinder).initializeDevice();
108        verify(mTransportBinder).finishBackup();
109    }
110
111    @Test
112    public void testRun_callsBackupManagerCorrectly() throws Exception {
113        setUpTransport(mTransport);
114        configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK);
115        PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
116
117        performInitializeTask.run();
118
119        verify(mBackupManagerService)
120                .recordInitPending(false, mTransportName, mTransport.transportDirName);
121        verify(mBackupManagerService)
122                .resetBackupState(eq(new File(mBaseStateDir, mTransport.transportDirName)));
123    }
124
125    @Test
126    public void testRun_callsObserverAndListenerCorrectly() throws Exception {
127        setUpTransport(mTransport);
128        configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK);
129        PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
130
131        performInitializeTask.run();
132
133        verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_OK));
134        verify(mObserver).backupFinished(eq(TRANSPORT_OK));
135        verify(mListener).onFinished(any());
136    }
137
138    @Test
139    public void testRun_whenInitializeDeviceFails() throws Exception {
140        setUpTransport(mTransport);
141        configureTransport(mTransportBinder, TRANSPORT_ERROR, 0);
142        PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
143
144        performInitializeTask.run();
145
146        verify(mTransportBinder).initializeDevice();
147        verify(mTransportBinder, never()).finishBackup();
148        verify(mBackupManagerService)
149                .recordInitPending(true, mTransportName, mTransport.transportDirName);
150    }
151
152    @Test
153    public void testRun_whenInitializeDeviceFails_callsObserverAndListenerCorrectly()
154            throws Exception {
155        setUpTransport(mTransport);
156        configureTransport(mTransportBinder, TRANSPORT_ERROR, 0);
157        PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
158
159        performInitializeTask.run();
160
161        verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_ERROR));
162        verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
163        verify(mListener).onFinished(any());
164    }
165
166    @Test
167    public void testRun_whenInitializeDeviceFails_schedulesAlarm() throws Exception {
168        setUpTransport(mTransport);
169        configureTransport(mTransportBinder, TRANSPORT_ERROR, 0);
170        PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
171
172        performInitializeTask.run();
173
174        verify(mAlarmManager).set(anyInt(), anyLong(), eq(mRunInitIntent));
175    }
176
177    @Test
178    public void testRun_whenFinishBackupFails() throws Exception {
179        setUpTransport(mTransport);
180        configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR);
181        PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
182
183        performInitializeTask.run();
184
185        verify(mTransportBinder).initializeDevice();
186        verify(mTransportBinder).finishBackup();
187        verify(mBackupManagerService)
188                .recordInitPending(true, mTransportName, mTransport.transportDirName);
189    }
190
191    @Test
192    public void testRun_whenFinishBackupFails_callsObserverAndListenerCorrectly() throws Exception {
193        setUpTransport(mTransport);
194        configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR);
195        PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
196
197        performInitializeTask.run();
198
199        verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_ERROR));
200        verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
201        verify(mListener).onFinished(any());
202    }
203
204    @Test
205    public void testRun_whenFinishBackupFails_schedulesAlarm() throws Exception {
206        setUpTransport(mTransport);
207        configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR);
208        PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
209
210        performInitializeTask.run();
211
212        verify(mAlarmManager).set(anyInt(), anyLong(), eq(mRunInitIntent));
213    }
214
215    @Test
216    public void testRun_whenOnlyOneTransportFails() throws Exception {
217        TransportData transport1 = backupTransport();
218        TransportData transport2 = d2dTransport();
219        List<TransportMock> transportMocks =
220                setUpTransports(mTransportManager, transport1, transport2);
221        configureTransport(transportMocks.get(0).transport, TRANSPORT_ERROR, 0);
222        configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK);
223        PerformInitializeTask performInitializeTask =
224                createPerformInitializeTask(transport1.transportName, transport2.transportName);
225
226        performInitializeTask.run();
227
228        verify(transportMocks.get(1).transport).initializeDevice();
229        verify(mObserver).onResult(eq(transport1.transportName), eq(TRANSPORT_ERROR));
230        verify(mObserver).onResult(eq(transport2.transportName), eq(TRANSPORT_OK));
231        verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
232    }
233
234    @Test
235    public void testRun_withMultipleTransports() throws Exception {
236        List<TransportMock> transportMocks =
237                setUpTransports(
238                        mTransportManager, backupTransport(), d2dTransport(), localTransport());
239        configureTransport(transportMocks.get(0).transport, TRANSPORT_OK, TRANSPORT_OK);
240        configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK);
241        configureTransport(transportMocks.get(2).transport, TRANSPORT_OK, TRANSPORT_OK);
242        String[] transportNames =
243                Stream.of(new TransportData[] {backupTransport(), d2dTransport(), localTransport()})
244                        .map(t -> t.transportName)
245                        .toArray(String[]::new);
246        PerformInitializeTask performInitializeTask = createPerformInitializeTask(transportNames);
247
248        performInitializeTask.run();
249
250        Iterator<TransportData> transportsIterator =
251                Arrays.asList(
252                                new TransportData[] {
253                                    backupTransport(), d2dTransport(), localTransport()
254                                })
255                        .iterator();
256        for (TransportMock transportMock : transportMocks) {
257            TransportData transport = transportsIterator.next();
258            verify(mTransportManager).getTransportClient(eq(transport.transportName), any());
259            verify(mTransportManager)
260                    .disposeOfTransportClient(eq(transportMock.transportClient), any());
261        }
262    }
263
264    @Test
265    public void testRun_whenOnlyOneTransportFails_disposesAllTransports() throws Exception {
266        TransportData transport1 = backupTransport();
267        TransportData transport2 = d2dTransport();
268        List<TransportMock> transportMocks =
269                setUpTransports(mTransportManager, transport1, transport2);
270        configureTransport(transportMocks.get(0).transport, TRANSPORT_ERROR, 0);
271        configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK);
272        PerformInitializeTask performInitializeTask =
273                createPerformInitializeTask(transport1.transportName, transport2.transportName);
274
275        performInitializeTask.run();
276
277        verify(mTransportManager)
278                .disposeOfTransportClient(eq(transportMocks.get(0).transportClient), any());
279        verify(mTransportManager)
280                .disposeOfTransportClient(eq(transportMocks.get(1).transportClient), any());
281    }
282
283    @Test
284    public void testRun_whenTransportNotRegistered() throws Exception {
285        setUpTransports(mTransportManager, mTransport.unregistered());
286        PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
287
288        performInitializeTask.run();
289
290        verify(mTransportManager, never()).disposeOfTransportClient(any(), any());
291        verify(mObserver, never()).onResult(any(), anyInt());
292        verify(mObserver).backupFinished(eq(TRANSPORT_OK));
293    }
294
295    @Test
296    public void testRun_whenOnlyOneTransportNotRegistered() throws Exception {
297        TransportData transport1 = backupTransport().unregistered();
298        TransportData transport2 = d2dTransport();
299        List<TransportMock> transportMocks =
300                setUpTransports(mTransportManager, transport1, transport2);
301        String registeredTransportName = transport2.transportName;
302        IBackupTransport registeredTransport = transportMocks.get(1).transport;
303        TransportClient registeredTransportClient = transportMocks.get(1).transportClient;
304        PerformInitializeTask performInitializeTask =
305                createPerformInitializeTask(transport1.transportName, transport2.transportName);
306
307        performInitializeTask.run();
308
309        verify(registeredTransport).initializeDevice();
310        verify(mTransportManager).disposeOfTransportClient(eq(registeredTransportClient), any());
311        verify(mObserver).onResult(eq(registeredTransportName), eq(TRANSPORT_OK));
312    }
313
314    @Test
315    public void testRun_whenTransportNotAvailable() throws Exception {
316        TransportMock transportMock = setUpTransport(mTransport.unavailable());
317        PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
318
319        performInitializeTask.run();
320
321        verify(mTransportManager)
322                .disposeOfTransportClient(eq(transportMock.transportClient), any());
323        verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
324        verify(mListener).onFinished(any());
325    }
326
327    @Test
328    public void testRun_whenTransportThrowsDeadObjectException() throws Exception {
329        TransportMock transportMock = setUpTransport(mTransport);
330        IBackupTransport transport = transportMock.transport;
331        TransportClient transportClient = transportMock.transportClient;
332        when(transport.initializeDevice()).thenThrow(DeadObjectException.class);
333        PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
334
335        performInitializeTask.run();
336
337        verify(mTransportManager).disposeOfTransportClient(eq(transportClient), any());
338        verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
339        verify(mListener).onFinished(any());
340    }
341
342    private PerformInitializeTask createPerformInitializeTask(String... transportNames) {
343        return new PerformInitializeTask(
344                mBackupManagerService,
345                mTransportManager,
346                transportNames,
347                mObserver,
348                mListener,
349                mBaseStateDir);
350    }
351
352    private void configureTransport(
353            IBackupTransport transportMock, int initializeDeviceStatus, int finishBackupStatus)
354            throws Exception {
355        when(transportMock.initializeDevice()).thenReturn(initializeDeviceStatus);
356        when(transportMock.finishBackup()).thenReturn(finishBackupStatus);
357    }
358
359    private TransportMock setUpTransport(TransportData transport) throws Exception {
360        TransportMock transportMock =
361                TransportTestUtils.setUpTransport(mTransportManager, transport);
362        mTransportBinder = transportMock.transport;
363        return transportMock;
364    }
365}
366