1 /*
2  * Copyright (C) 2022 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 
17 package com.android.server.backup;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
23 
24 import static org.junit.Assert.fail;
25 import static org.mockito.ArgumentMatchers.any;
26 import static org.mockito.ArgumentMatchers.anyBoolean;
27 import static org.mockito.ArgumentMatchers.anyInt;
28 import static org.mockito.ArgumentMatchers.anyString;
29 import static org.mockito.ArgumentMatchers.eq;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.when;
32 
33 import android.app.backup.BackupAgent;
34 import android.app.backup.BackupAnnotations;
35 import android.app.backup.BackupAnnotations.BackupDestination;
36 import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
37 import android.app.backup.IBackupManagerMonitor;
38 import android.app.backup.IBackupObserver;
39 import android.content.Context;
40 import android.content.pm.ApplicationInfo;
41 import android.content.pm.PackageInfo;
42 import android.content.pm.PackageManager;
43 import android.platform.test.annotations.Presubmit;
44 import android.util.FeatureFlagUtils;
45 
46 import androidx.test.filters.FlakyTest;
47 import androidx.test.runner.AndroidJUnit4;
48 
49 import com.android.server.backup.internal.LifecycleOperationStorage;
50 import com.android.server.backup.internal.OnTaskFinishedListener;
51 import com.android.server.backup.params.BackupParams;
52 import com.android.server.backup.transport.BackupTransportClient;
53 import com.android.server.backup.transport.TransportConnection;
54 import com.android.server.backup.utils.BackupEligibilityRules;
55 import com.android.server.backup.utils.BackupManagerMonitorEventSender;
56 
57 import com.google.common.collect.ImmutableSet;
58 
59 import org.junit.After;
60 import org.junit.Before;
61 import org.junit.Test;
62 import org.junit.runner.RunWith;
63 import org.mockito.Mock;
64 import org.mockito.MockitoAnnotations;
65 import org.mockito.MockitoSession;
66 import org.mockito.quality.Strictness;
67 
68 import java.util.Arrays;
69 import java.util.List;
70 import java.util.function.IntConsumer;
71 
72 @Presubmit
73 @RunWith(AndroidJUnit4.class)
74 public class UserBackupManagerServiceTest {
75     private static final String TEST_PACKAGE = "package1";
76     private static final String[] TEST_PACKAGES = new String[] { TEST_PACKAGE };
77     private static final String TEST_TRANSPORT = "transport";
78     private static final int WORKER_THREAD_TIMEOUT_MILLISECONDS = 1;
79 
80     @Mock Context mContext;
81     @Mock IBackupManagerMonitor mBackupManagerMonitor;
82     @Mock IBackupObserver mBackupObserver;
83     @Mock PackageManager mPackageManager;
84     @Mock TransportConnection mTransportConnection;
85     @Mock TransportManager mTransportManager;
86     @Mock BackupTransportClient mBackupTransport;
87     @Mock BackupEligibilityRules mBackupEligibilityRules;
88     @Mock LifecycleOperationStorage mOperationStorage;
89     @Mock BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
90 
91     private MockitoSession mSession;
92     private TestBackupService mService;
93 
94     @Before
setUp()95     public void setUp() throws Exception {
96         mSession = mockitoSession()
97                 .initMocks(this)
98                 .mockStatic(BackupManagerMonitorEventSender.class)
99                 .mockStatic(FeatureFlagUtils.class)
100                 // TODO(b/263239775): Remove unnecessary stubbing.
101                 .strictness(Strictness.LENIENT)
102                 .startMocking();
103         MockitoAnnotations.initMocks(this);
104 
105         mService = new TestBackupService(mContext, mPackageManager, mOperationStorage,
106                 mTransportManager);
107         mService.setEnabled(true);
108         mService.setSetupComplete(true);
109     }
110 
111     @After
tearDown()112     public void tearDown() {
113         if (mSession != null) {
114             mSession.finishMocking();
115         }
116     }
117 
118     @Test
initializeBackupEnableState_doesntWriteStateToDisk()119     public void initializeBackupEnableState_doesntWriteStateToDisk() {
120         mService.initializeBackupEnableState();
121 
122         assertThat(mService.isEnabledStatePersisted).isFalse();
123     }
124 
125     @Test
updateBackupEnableState_writesStateToDisk()126     public void updateBackupEnableState_writesStateToDisk() {
127         mService.setBackupEnabled(true);
128 
129         assertThat(mService.isEnabledStatePersisted).isTrue();
130     }
131 
132     @Test
getRequestBackupParams_appIsEligibleForFullBackup()133     public void getRequestBackupParams_appIsEligibleForFullBackup() throws Exception {
134         when(mPackageManager.getPackageInfoAsUser(anyString(), anyInt(), anyInt())).thenReturn(
135                 getPackageInfo(TEST_PACKAGE));
136         when(mBackupEligibilityRules.appIsEligibleForBackup(any())).thenReturn(true);
137         when(mBackupEligibilityRules.appGetsFullBackup(any())).thenReturn(true);
138 
139         BackupParams params = mService.getRequestBackupParams(TEST_PACKAGES, mBackupObserver,
140                 mBackupManagerMonitor, /* flags */ 0, mBackupEligibilityRules,
141                 mTransportConnection, /* transportDirName */ "", OnTaskFinishedListener.NOP);
142 
143         assertThat(params.kvPackages).isEmpty();
144         assertThat(params.fullPackages).contains(TEST_PACKAGE);
145         assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules);
146     }
147 
148     @Test
getRequestBackupParams_appIsEligibleForKeyValueBackup()149     public void getRequestBackupParams_appIsEligibleForKeyValueBackup() throws Exception {
150         when(mPackageManager.getPackageInfoAsUser(anyString(), anyInt(), anyInt())).thenReturn(
151                 getPackageInfo(TEST_PACKAGE));
152         when(mBackupEligibilityRules.appIsEligibleForBackup(any())).thenReturn(true);
153         when(mBackupEligibilityRules.appGetsFullBackup(any())).thenReturn(false);
154 
155         BackupParams params = mService.getRequestBackupParams(TEST_PACKAGES, mBackupObserver,
156                 mBackupManagerMonitor, /* flags */ 0, mBackupEligibilityRules,
157                 mTransportConnection, /* transportDirName */ "", OnTaskFinishedListener.NOP);
158 
159         assertThat(params.kvPackages).contains(TEST_PACKAGE);
160         assertThat(params.fullPackages).isEmpty();
161         assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules);
162     }
163 
164     @Test
getRequestBackupParams_appIsNotEligibleForBackup()165     public void getRequestBackupParams_appIsNotEligibleForBackup() throws Exception {
166         when(mPackageManager.getPackageInfoAsUser(anyString(), anyInt(), anyInt())).thenReturn(
167                 getPackageInfo(TEST_PACKAGE));
168         when(mBackupEligibilityRules.appIsEligibleForBackup(any())).thenReturn(false);
169         when(mBackupEligibilityRules.appGetsFullBackup(any())).thenReturn(false);
170 
171         BackupParams params = mService.getRequestBackupParams(TEST_PACKAGES, mBackupObserver,
172                 mBackupManagerMonitor, /* flags */ 0, mBackupEligibilityRules,
173                 mTransportConnection, /* transportDirName */ "", OnTaskFinishedListener.NOP);
174 
175         assertThat(params.kvPackages).isEmpty();
176         assertThat(params.fullPackages).isEmpty();
177         assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules);
178     }
179 
180     @Test
testGetBackupDestinationFromTransport_returnsCloudByDefault()181     public void testGetBackupDestinationFromTransport_returnsCloudByDefault()
182             throws Exception {
183         when(mTransportConnection.connectOrThrow(any())).thenReturn(mBackupTransport);
184         when(mBackupTransport.getTransportFlags()).thenReturn(0);
185 
186         int backupDestination = mService.getBackupDestinationFromTransport(mTransportConnection);
187 
188         assertThat(backupDestination).isEqualTo(BackupDestination.CLOUD);
189     }
190 
191     @Test
testGetBackupDestinationFromTransport_returnsDeviceTransferForD2dTransport()192     public void testGetBackupDestinationFromTransport_returnsDeviceTransferForD2dTransport()
193             throws Exception {
194         // This is a temporary flag to control the new behaviour until it's ready to be fully
195         // rolled out.
196         mService.shouldUseNewBackupEligibilityRules = true;
197 
198         when(mTransportConnection.connectOrThrow(any())).thenReturn(mBackupTransport);
199         when(mBackupTransport.getTransportFlags()).thenReturn(
200                 BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER);
201 
202         int backupDestination = mService.getBackupDestinationFromTransport(mTransportConnection);
203 
204         assertThat(backupDestination).isEqualTo(BackupDestination.DEVICE_TRANSFER);
205     }
206 
207     @Test
208     @FlakyTest
testAgentDisconnected_cancelsCurrentOperations()209     public void testAgentDisconnected_cancelsCurrentOperations() throws Exception {
210         when(mOperationStorage.operationTokensForPackage(eq("com.android.foo"))).thenReturn(
211                 ImmutableSet.of(123, 456, 789)
212         );
213 
214         mService.agentDisconnected("com.android.foo");
215 
216         mService.waitForAsyncOperation();
217         verify(mOperationStorage).cancelOperation(eq(123), eq(true), any(IntConsumer.class));
218         verify(mOperationStorage).cancelOperation(eq(456), eq(true), any());
219         verify(mOperationStorage).cancelOperation(eq(789), eq(true), any());
220     }
221 
222     @Test
testAgentDisconnected_unknownPackageName_cancelsNothing()223     public void testAgentDisconnected_unknownPackageName_cancelsNothing() throws Exception {
224         when(mOperationStorage.operationTokensForPackage(eq("com.android.foo"))).thenReturn(
225                 ImmutableSet.of()
226         );
227 
228         mService.agentDisconnected("com.android.foo");
229 
230         verify(mOperationStorage, never())
231                 .cancelOperation(anyInt(), anyBoolean(), any(IntConsumer.class));
232     }
233 
234     @Test
testReportDelayedRestoreResult_sendsLogsToMonitor()235     public void testReportDelayedRestoreResult_sendsLogsToMonitor() throws Exception {
236         PackageInfo packageInfo = getPackageInfo(TEST_PACKAGE);
237         when(mPackageManager.getPackageInfoAsUser(anyString(),
238                 any(PackageManager.PackageInfoFlags.class), anyInt())).thenReturn(packageInfo);
239         when(mTransportManager.getCurrentTransportName()).thenReturn(TEST_TRANSPORT);
240         when(mTransportManager.getTransportClientOrThrow(eq(TEST_TRANSPORT), anyString()))
241                 .thenReturn(mTransportConnection);
242         when(mTransportConnection.connectOrThrow(any())).thenReturn(mBackupTransport);
243         when(mBackupTransport.getBackupManagerMonitor()).thenReturn(mBackupManagerMonitor);
244 
245 
246         List<DataTypeResult> results = Arrays.asList(new DataTypeResult(/* dataType */ "type_1"),
247                 new DataTypeResult(/* dataType */ "type_2"));
248         mService.reportDelayedRestoreResult(TEST_PACKAGE, results);
249 
250 
251         verify(mBackupManagerMonitorEventSender).sendAgentLoggingResults(
252                 eq(packageInfo), eq(results), eq(BackupAnnotations.OperationType.RESTORE));
253     }
254 
getPackageInfo(String packageName)255     private static PackageInfo getPackageInfo(String packageName) {
256         PackageInfo packageInfo = new PackageInfo();
257         packageInfo.applicationInfo = new ApplicationInfo();
258         packageInfo.packageName = packageName;
259         return packageInfo;
260     }
261 
262     private class TestBackupService extends UserBackupManagerService {
263         boolean isEnabledStatePersisted = false;
264         boolean shouldUseNewBackupEligibilityRules = false;
265 
266         private volatile Thread mWorkerThread = null;
267 
TestBackupService(Context context, PackageManager packageManager, LifecycleOperationStorage operationStorage, TransportManager transportManager)268         TestBackupService(Context context, PackageManager packageManager,
269                 LifecycleOperationStorage operationStorage, TransportManager transportManager) {
270             super(context, packageManager, operationStorage, transportManager);
271         }
272 
273         @Override
writeEnabledState(boolean enable)274         void writeEnabledState(boolean enable) {
275             isEnabledStatePersisted = true;
276         }
277 
278         @Override
readEnabledState()279         boolean readEnabledState() {
280             return false;
281         }
282 
283         @Override
updateStateOnBackupEnabled(boolean wasEnabled, boolean enable)284         void updateStateOnBackupEnabled(boolean wasEnabled, boolean enable) {}
285 
286         @Override
shouldUseNewBackupEligibilityRules()287         boolean shouldUseNewBackupEligibilityRules() {
288             return shouldUseNewBackupEligibilityRules;
289         }
290 
291         @Override
getThreadForAsyncOperation(String operationName, Runnable operation)292         Thread getThreadForAsyncOperation(String operationName, Runnable operation) {
293             mWorkerThread = super.getThreadForAsyncOperation(operationName, operation);
294             return mWorkerThread;
295         }
296 
297         @Override
getBMMEventSender(IBackupManagerMonitor monitor)298         BackupManagerMonitorEventSender getBMMEventSender(IBackupManagerMonitor monitor) {
299             return mBackupManagerMonitorEventSender;
300         }
301 
waitForAsyncOperation()302         private void waitForAsyncOperation() {
303             if (mWorkerThread == null) {
304                 return;
305             }
306 
307             try {
308                 mWorkerThread.join(/* millis */ WORKER_THREAD_TIMEOUT_MILLISECONDS);
309             } catch (InterruptedException e) {
310                 fail("Failed waiting for worker thread to complete: " + e.getMessage());
311             }
312         }
313     }
314 }
315