1 /*
2  * Copyright (C) 2019 Google Inc.
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 android.cts.backup.restoresessionapp;
18 
19 import static androidx.test.InstrumentationRegistry.getInstrumentation;
20 import static androidx.test.InstrumentationRegistry.getTargetContext;
21 
22 import static com.google.common.truth.Truth.assertThat;
23 
24 import static junit.framework.Assert.assertEquals;
25 import static junit.framework.Assert.assertTrue;
26 
27 import static org.junit.Assert.assertNotEquals;
28 
29 import android.app.UiAutomation;
30 import android.app.backup.BackupManager;
31 import android.app.backup.BackupManagerMonitor;
32 import android.app.backup.RestoreObserver;
33 import android.app.backup.RestoreSession;
34 import android.app.backup.RestoreSet;
35 import android.content.Context;
36 import android.os.Bundle;
37 import android.platform.test.annotations.AppModeFull;
38 
39 import androidx.annotation.Nullable;
40 import androidx.test.runner.AndroidJUnit4;
41 
42 import org.junit.After;
43 import org.junit.Before;
44 import org.junit.Test;
45 import org.junit.runner.RunWith;
46 
47 import java.util.Arrays;
48 import java.util.HashSet;
49 import java.util.List;
50 import java.util.Set;
51 import java.util.concurrent.CountDownLatch;
52 import java.util.concurrent.TimeUnit;
53 
54 /**
55  * Device side routines to be invoked by the host side RestoreSessionHostSideTest. These are not
56  * designed to be called in any other way, as they rely on state set up by the host side test.
57  */
58 @RunWith(AndroidJUnit4.class)
59 @AppModeFull
60 public class RestoreSessionTest {
61     private static final String[] PACKAGES = new String[] {
62         "android.cts.backup.restoresessionapp1",
63         "android.cts.backup.restoresessionapp2",
64     };
65 
66     private static final int PACKAGES_COUNT = 2;
67     private static final int RESTORE_TIMEOUT_SECONDS = 20;
68 
69     private BackupManager mBackupManager;
70     private Set<String> mRestorePackages;
71     private Set<String> mNonRestorePackages;
72     private CountDownLatch mRestoreSetsLatch;
73     private CountDownLatch mRestoreObserverLatch;
74     private RestoreSession mRestoreSession;
75     private UiAutomation mUiAutomation;
76     private long mRestoreToken;
77 
78     private final RestoreObserver mRestoreObserver =
79             new RestoreObserver() {
80                 @Override
81                 public void restoreSetsAvailable(RestoreSet[] result) {
82                     super.restoreSetsAvailable(result);
83 
84                     long token = 0L;
85 
86                     for (RestoreSet restoreSet : result) {
87                         long restoreToken = restoreSet.token;
88                         if (doesRestoreSetContainAllPackages(restoreToken, mRestorePackages)
89                                 && doesRestoreSetContainAllPackages(
90                                         restoreToken, mNonRestorePackages)) {
91                             token = restoreSet.token;
92                             break;
93                         }
94                     }
95 
96                     mRestoreToken = token;
97 
98                     mRestoreSetsLatch.countDown();
99                 }
100 
101                 @Override
102                 public void restoreStarting(int numPackages) {
103                     super.restoreStarting(numPackages);
104 
105                     assertEquals(
106                             "Wrong number of packages in the restore set",
107                             mRestorePackages.size(),
108                             numPackages);
109                     mRestoreObserverLatch.countDown();
110                 }
111 
112                 @Override
113                 public void onUpdate(int nowBeingRestored, String currentPackage) {
114                     super.onUpdate(nowBeingRestored, currentPackage);
115 
116                     assertTrue(
117                             "Restoring package that is not in mRestorePackages",
118                             mRestorePackages.contains(currentPackage));
119                     mRestoreObserverLatch.countDown();
120                 }
121 
122                 @Override
123                 public void restoreFinished(int error) {
124                     super.restoreFinished(error);
125 
126                     assertEquals(
127                             "Restore finished with error: " + error, BackupManager.SUCCESS, error);
128                     mRestoreObserverLatch.countDown();
129                 }
130             };
131 
132     @Before
setUp()133     public void setUp() throws InterruptedException {
134         Context context = getTargetContext();
135         mBackupManager = new BackupManager(context);
136 
137         mRestoreToken = 0L;
138 
139         mUiAutomation = getInstrumentation().getUiAutomation();
140         mUiAutomation.adoptShellPermissionIdentity();
141         mRestoreSession = mBackupManager.beginRestoreSession();
142     }
143 
144     @After
tearDown()145     public void tearDown() {
146         mUiAutomation.dropShellPermissionIdentity();
147         mRestoreSession.endRestoreSession();
148     }
149 
150     /**
151      * Restore packages added to mRestorePackages and verify only those packages are restored. Use
152      * {@link RestoreSession#restorePackage(String, RestoreObserver)}
153      */
154     @Test
testRestorePackage()155     public void testRestorePackage() throws InterruptedException {
156         initPackagesToRestore(/* packagesCount */ 1);
157         testRestorePackagesInternal((BackupManagerMonitor monitor) -> {
158             mRestoreSession.restorePackage(
159                 mRestorePackages.iterator().next(),
160                 mRestoreObserver);
161         }, false);
162     }
163 
164     /**
165      * Restore packages added to mRestorePackages and verify only those packages are restored. Use
166      * {@link RestoreSession#restorePackage(String, RestoreObserver, BackupManagerMonitor)}
167      */
168     @Test
testRestorePackageWithMonitorParam()169     public void testRestorePackageWithMonitorParam() throws InterruptedException {
170         initPackagesToRestore(/* packagesCount */ 1);
171         testRestorePackagesInternal((BackupManagerMonitor monitor) -> {
172             mRestoreSession.restorePackage(
173                 mRestorePackages.iterator().next(),
174                 mRestoreObserver,
175                 monitor);
176         }, true);
177     }
178 
179     /**
180      * Restore packages added to mRestorePackages and verify only those packages are restored. Use
181      * {@link RestoreSession#restorePackages(long, RestoreObserver, Set)}
182      */
183     @Test
testRestorePackages()184     public void testRestorePackages() throws InterruptedException {
185         initPackagesToRestore(/* packagesCount */ 1);
186         testRestorePackagesInternal((BackupManagerMonitor monitor) -> {
187             mRestoreSession.restorePackages(
188                 mRestoreToken,
189                 mRestoreObserver,
190                 mRestorePackages);
191         }, false);
192     }
193 
194     /**
195      * Restore packages added to mRestorePackages and verify only those packages are restored. Use
196      * {@link RestoreSession#restorePackages(long, RestoreObserver, Set, BackupManagerMonitor)}
197      */
198     @Test
testRestorePackagesWithMonitorParam()199     public void testRestorePackagesWithMonitorParam() throws InterruptedException {
200         initPackagesToRestore(/* packagesCount */ 1);
201         testRestorePackagesInternal((BackupManagerMonitor monitor) -> {
202             mRestoreSession.restorePackages(
203                 mRestoreToken,
204                 mRestoreObserver,
205                 mRestorePackages,
206                 monitor);
207         }, true);
208     }
209 
testRestorePackagesInternal(RestoreRunner restoreRunner, boolean useMonitorParam)210     private void testRestorePackagesInternal(RestoreRunner restoreRunner, boolean useMonitorParam)
211             throws InterruptedException {
212         CountDownLatch backupMonitorLatch = null;
213         if (useMonitorParam) {
214             // Wait for the callbacks from BackupManagerMonitor: one for each package.
215             backupMonitorLatch = new CountDownLatch(mRestorePackages.size());
216             BackupManagerMonitor backupMonitor = new TestBackupMonitor(backupMonitorLatch);
217 
218             loadAvailableRestoreSets(backupMonitor);
219 
220             restoreRunner.runRestore(backupMonitor);
221         } else {
222             loadAvailableRestoreSets(null);
223             restoreRunner.runRestore(null);
224         }
225 
226         awaitResultAndAssertSuccess(mRestoreObserverLatch);
227         if (backupMonitorLatch != null) {
228             awaitResultAndAssertSuccess(backupMonitorLatch);
229         }
230     }
231 
loadAvailableRestoreSets(@ullable BackupManagerMonitor monitor)232     private void loadAvailableRestoreSets(@Nullable BackupManagerMonitor monitor)
233             throws InterruptedException {
234         mRestoreSetsLatch = new CountDownLatch(1);
235         assertEquals(
236                 BackupManager.SUCCESS, monitor == null
237                 ? mRestoreSession.getAvailableRestoreSets(mRestoreObserver)
238                 : mRestoreSession.getAvailableRestoreSets(mRestoreObserver, monitor));
239         awaitResultAndAssertSuccess(mRestoreSetsLatch);
240 
241         assertNotEquals("Restore set not found", 0L, mRestoreToken);
242     }
243 
doesRestoreSetContainAllPackages(long restoreToken, Set<String> packages)244     private boolean doesRestoreSetContainAllPackages(long restoreToken, Set<String> packages) {
245         for (String restorePackage : packages) {
246             if (mBackupManager.getAvailableRestoreToken(restorePackage) != restoreToken) {
247                 return false;
248             }
249         }
250         return true;
251     }
252 
awaitResultAndAssertSuccess(CountDownLatch latch)253     private void awaitResultAndAssertSuccess(CountDownLatch latch) throws InterruptedException {
254         boolean waitResult = latch.await(RESTORE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
255         assertTrue("Restore timed out", waitResult);
256     }
257 
initPackagesToRestore(int packagesCount)258     private void initPackagesToRestore(int packagesCount) {
259         mRestorePackages = new HashSet<>();
260         mNonRestorePackages = new HashSet<>();
261 
262         for (int i = 0; i < PACKAGES_COUNT; i++) {
263             if (i < packagesCount) {
264                 mRestorePackages.add(PACKAGES[i]);
265             } else {
266                 mNonRestorePackages.add(PACKAGES[i]);
267             }
268         }
269 
270         // Wait for the callbacks from RestoreObserver: one for each package from
271         // mRestorePackages plus restoreStarting and restoreFinished.
272         mRestoreObserverLatch = new CountDownLatch(mRestorePackages.size() + 2);
273     }
274 
275     private static class TestBackupMonitor extends BackupManagerMonitor {
276         private final CountDownLatch mLatch;
277 
TestBackupMonitor(CountDownLatch latch)278         TestBackupMonitor(CountDownLatch latch) {
279             mLatch = latch;
280         }
281 
282         @Override
onEvent(Bundle event)283         public void onEvent(Bundle event) {
284             // Note: this logic will break if the test cases using TestBackupMonitor
285             // start restoring more than 1 package because the logic currently expects
286             // exactly 1 onEvent() call per package
287             super.onEvent(event);
288             mLatch.countDown();
289         }
290     }
291 
292     @FunctionalInterface
293     private interface RestoreRunner {
runRestore(BackupManagerMonitor monitor)294         void runRestore(BackupManagerMonitor monitor) throws InterruptedException;
295     }
296 }
297