1 /*
2  * Copyright (C) 2018 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.tests.rollback;
18 
19 import static com.android.tests.rollback.RollbackTestUtils.assertPackageRollbackInfoEquals;
20 import static com.android.tests.rollback.RollbackTestUtils.assertRollbackInfoEquals;
21 import static com.android.tests.rollback.RollbackTestUtils.getUniqueRollbackInfoForPackage;
22 import static com.android.tests.rollback.RollbackTestUtils.processUserData;
23 
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.assertNull;
27 import static org.junit.Assert.fail;
28 
29 import android.Manifest;
30 import android.content.BroadcastReceiver;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.content.pm.VersionedPackage;
35 import android.content.rollback.RollbackInfo;
36 import android.content.rollback.RollbackManager;
37 import android.provider.DeviceConfig;
38 import android.provider.Settings;
39 import android.util.Log;
40 
41 import androidx.test.InstrumentationRegistry;
42 
43 import org.junit.Ignore;
44 import org.junit.Test;
45 import org.junit.runner.RunWith;
46 import org.junit.runners.JUnit4;
47 
48 import java.util.Collections;
49 import java.util.concurrent.TimeUnit;
50 
51 /**
52  * Test system Rollback APIs.
53  * TODO: Should this be a cts test instead? Where should it live?
54  */
55 @RunWith(JUnit4.class)
56 public class RollbackTest {
57 
58     private static final String TAG = "RollbackTest";
59 
60     private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A";
61     private static final String TEST_APP_B = "com.android.tests.rollback.testapp.B";
62     private static final String INSTRUMENTED_APP = "com.android.tests.rollback";
63 
64     // copied from PackageManagerService#PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS
65     // TODO: find a better place for the property so that it can be imported in tests
66     // maybe android.content.pm.PackageManager?
67     private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS =
68             "enable_rollback_timeout";
69 
70     /**
71      * Test basic rollbacks.
72      */
73     @Test
testBasic()74     public void testBasic() throws Exception {
75         // Make sure an app can't listen to or disturb the internal
76         // ACTION_PACKAGE_ENABLE_ROLLBACK broadcast.
77         Context context = InstrumentationRegistry.getContext();
78         IntentFilter enableRollbackFilter = new IntentFilter();
79         enableRollbackFilter.addAction("android.intent.action.PACKAGE_ENABLE_ROLLBACK");
80         enableRollbackFilter.addDataType("application/vnd.android.package-archive");
81         enableRollbackFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
82         BroadcastReceiver enableRollbackReceiver = new BroadcastReceiver() {
83             @Override
84             public void onReceive(Context context, Intent intent) {
85                 abortBroadcast();
86             }
87         };
88         context.registerReceiver(enableRollbackReceiver, enableRollbackFilter);
89 
90         try {
91             RollbackTestUtils.adoptShellPermissionIdentity(
92                     Manifest.permission.INSTALL_PACKAGES,
93                     Manifest.permission.DELETE_PACKAGES,
94                     Manifest.permission.TEST_MANAGE_ROLLBACKS,
95                     Manifest.permission.MANAGE_ROLLBACKS);
96 
97             // Register a broadcast receiver for notification when the
98             // rollback has been committed.
99             RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
100             RollbackManager rm = RollbackTestUtils.getRollbackManager();
101 
102             // Uninstall TEST_APP_A
103             RollbackTestUtils.uninstall(TEST_APP_A);
104             assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
105 
106             // TODO: There is currently a race condition between when the app is
107             // uninstalled and when rollback manager deletes the rollback. Fix it
108             // so that's not the case!
109             for (int i = 0; i < 5; ++i) {
110                 RollbackInfo rollback = getUniqueRollbackInfoForPackage(
111                         rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
112                 if (rollback != null) {
113                     Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
114                     Thread.sleep(1000);
115                 }
116             }
117 
118             // The app should not be available for rollback.
119             assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A));
120 
121             // There should be no recently committed rollbacks for this package.
122             assertNull(getUniqueRollbackInfoForPackage(
123                         rm.getRecentlyCommittedRollbacks(), TEST_APP_A));
124 
125             // Install v1 of the app (without rollbacks enabled).
126             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
127             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
128 
129             // Upgrade from v1 to v2, with rollbacks enabled.
130             RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
131             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
132 
133             // The app should now be available for rollback.
134             RollbackInfo rollback = getUniqueRollbackInfoForPackage(
135                     rm.getAvailableRollbacks(), TEST_APP_A);
136             assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
137 
138             // We should not have received any rollback requests yet.
139             // TODO: Possibly flaky if, by chance, some other app on device
140             // happens to be rolled back at the same time?
141             assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
142 
143             // Roll back the app.
144             RollbackTestUtils.rollback(rollback.getRollbackId());
145             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
146 
147             // Verify we received a broadcast for the rollback.
148             // TODO: Race condition between the timeout and when the broadcast is
149             // received could lead to test flakiness.
150             Intent broadcast = broadcastReceiver.poll(5, TimeUnit.SECONDS);
151             assertNotNull(broadcast);
152             assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
153 
154             // Verify the recent rollback has been recorded.
155             rollback = getUniqueRollbackInfoForPackage(
156                     rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
157             assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
158 
159             broadcastReceiver.unregister();
160             context.unregisterReceiver(enableRollbackReceiver);
161         } finally {
162             RollbackTestUtils.dropShellPermissionIdentity();
163         }
164     }
165 
166     /**
167      * Test that multiple available rollbacks are properly persisted.
168      */
169     @Test
testAvailableRollbackPersistence()170     public void testAvailableRollbackPersistence() throws Exception {
171         try {
172             RollbackTestUtils.adoptShellPermissionIdentity(
173                     Manifest.permission.INSTALL_PACKAGES,
174                     Manifest.permission.DELETE_PACKAGES,
175                     Manifest.permission.TEST_MANAGE_ROLLBACKS);
176 
177             RollbackManager rm = RollbackTestUtils.getRollbackManager();
178 
179             RollbackTestUtils.uninstall(TEST_APP_A);
180             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
181             RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
182             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
183 
184             RollbackTestUtils.uninstall(TEST_APP_B);
185             RollbackTestUtils.install("RollbackTestAppBv1.apk", false);
186             RollbackTestUtils.install("RollbackTestAppBv2.apk", true);
187             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
188 
189             // Both test apps should now be available for rollback.
190             RollbackInfo rollbackA = getUniqueRollbackInfoForPackage(
191                     rm.getAvailableRollbacks(), TEST_APP_A);
192             assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA);
193 
194             RollbackInfo rollbackB = getUniqueRollbackInfoForPackage(
195                     rm.getAvailableRollbacks(), TEST_APP_B);
196             assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB);
197 
198             // Reload the persisted data.
199             rm.reloadPersistedData();
200 
201             // The apps should still be available for rollback.
202             rollbackA = getUniqueRollbackInfoForPackage(
203                     rm.getAvailableRollbacks(), TEST_APP_A);
204             assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA);
205 
206             rollbackB = getUniqueRollbackInfoForPackage(
207                     rm.getAvailableRollbacks(), TEST_APP_B);
208             assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB);
209 
210             // Rollback of B should not rollback A
211             RollbackTestUtils.rollback(rollbackB.getRollbackId());
212             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
213             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
214         } finally {
215             RollbackTestUtils.dropShellPermissionIdentity();
216         }
217     }
218 
219     /**
220      * Test that available multi-package rollbacks are properly persisted.
221      */
222     @Test
testAvailableMultiPackageRollbackPersistence()223     public void testAvailableMultiPackageRollbackPersistence() throws Exception {
224         try {
225             RollbackTestUtils.adoptShellPermissionIdentity(
226                     Manifest.permission.INSTALL_PACKAGES,
227                     Manifest.permission.DELETE_PACKAGES,
228                     Manifest.permission.TEST_MANAGE_ROLLBACKS);
229 
230             RollbackManager rm = RollbackTestUtils.getRollbackManager();
231 
232             RollbackTestUtils.uninstall(TEST_APP_A);
233             RollbackTestUtils.uninstall(TEST_APP_B);
234             RollbackTestUtils.installMultiPackage(false,
235                     "RollbackTestAppAv1.apk",
236                     "RollbackTestAppBv1.apk");
237             RollbackTestUtils.installMultiPackage(true,
238                     "RollbackTestAppAv2.apk",
239                     "RollbackTestAppBv2.apk");
240             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
241             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
242 
243             // The app should now be available for rollback.
244             RollbackInfo rollbackA = getUniqueRollbackInfoForPackage(
245                     rm.getAvailableRollbacks(), TEST_APP_A);
246             assertRollbackInfoForAandB(rollbackA);
247 
248             RollbackInfo rollbackB = getUniqueRollbackInfoForPackage(
249                     rm.getAvailableRollbacks(), TEST_APP_B);
250             assertRollbackInfoForAandB(rollbackB);
251 
252             // Reload the persisted data.
253             rm.reloadPersistedData();
254 
255             // The apps should still be available for rollback.
256             rollbackA = getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A);
257             assertRollbackInfoForAandB(rollbackA);
258 
259             rollbackB = getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_B);
260             assertRollbackInfoForAandB(rollbackB);
261 
262             // Rollback of B should rollback A as well
263             RollbackTestUtils.rollback(rollbackB.getRollbackId());
264             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
265             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
266         } finally {
267             RollbackTestUtils.dropShellPermissionIdentity();
268         }
269     }
270 
271     /**
272      * Test that recently committed rollback data is properly persisted.
273      */
274     @Test
testRecentlyCommittedRollbackPersistence()275     public void testRecentlyCommittedRollbackPersistence() throws Exception {
276         try {
277             RollbackTestUtils.adoptShellPermissionIdentity(
278                     Manifest.permission.INSTALL_PACKAGES,
279                     Manifest.permission.DELETE_PACKAGES,
280                     Manifest.permission.TEST_MANAGE_ROLLBACKS);
281 
282             RollbackManager rm = RollbackTestUtils.getRollbackManager();
283 
284             RollbackTestUtils.uninstall(TEST_APP_A);
285             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
286             RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
287             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
288 
289             // The app should now be available for rollback.
290             RollbackInfo rollback = getUniqueRollbackInfoForPackage(
291                     rm.getAvailableRollbacks(), TEST_APP_A);
292 
293             // Roll back the app.
294             VersionedPackage cause = new VersionedPackage(
295                     "com.android.tests.rollback.testapp.Foo", 42);
296             RollbackTestUtils.rollback(rollback.getRollbackId(), cause);
297             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
298 
299             // Verify the recent rollback has been recorded.
300             rollback = getUniqueRollbackInfoForPackage(
301                     rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
302             assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback, cause);
303 
304             // Reload the persisted data.
305             rm.reloadPersistedData();
306 
307             // Verify the recent rollback is still recorded.
308             rollback = getUniqueRollbackInfoForPackage(
309                     rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
310             assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback, cause);
311         } finally {
312             RollbackTestUtils.dropShellPermissionIdentity();
313         }
314     }
315 
316     /**
317      * Test the scheduling aspect of rollback expiration.
318      */
319     @Test
testRollbackExpiresAfterLifetime()320     public void testRollbackExpiresAfterLifetime() throws Exception {
321         long expirationTime = TimeUnit.SECONDS.toMillis(30);
322         long defaultExpirationTime = TimeUnit.HOURS.toMillis(48);
323         RollbackManager rm = RollbackTestUtils.getRollbackManager();
324 
325         try {
326             RollbackTestUtils.adoptShellPermissionIdentity(
327                     Manifest.permission.INSTALL_PACKAGES,
328                     Manifest.permission.DELETE_PACKAGES,
329                     Manifest.permission.TEST_MANAGE_ROLLBACKS,
330                     Manifest.permission.WRITE_DEVICE_CONFIG);
331 
332             DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
333                     RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
334                     Long.toString(expirationTime), false /* makeDefault*/);
335 
336             // Pull the new expiration time from DeviceConfig
337             rm.reloadPersistedData();
338 
339             // Uninstall TEST_APP_A
340             RollbackTestUtils.uninstall(TEST_APP_A);
341             assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
342 
343             // Install v1 of the app (without rollbacks enabled).
344             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
345             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
346 
347             // Upgrade from v1 to v2, with rollbacks enabled.
348             RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
349             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
350 
351             // Check that the rollback data has not expired
352             Thread.sleep(1000);
353             RollbackInfo rollback = getUniqueRollbackInfoForPackage(
354                     rm.getAvailableRollbacks(), TEST_APP_A);
355             assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
356 
357             // Give it a little more time, but still not the long enough to expire
358             Thread.sleep(expirationTime / 2);
359             rollback = getUniqueRollbackInfoForPackage(
360                 rm.getAvailableRollbacks(), TEST_APP_A);
361             assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
362 
363             // Check that the data has expired after the expiration time (with a buffer of 1 second)
364             Thread.sleep(expirationTime / 2);
365             assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A));
366 
367         } finally {
368             DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
369                     RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
370                     Long.toString(defaultExpirationTime), false /* makeDefault*/);
371             RollbackTestUtils.dropShellPermissionIdentity();
372         }
373     }
374 
375     /**
376      * Test that changing time on device does not affect the duration of time that we keep
377      * rollback available
378      */
379     @Test
testTimeChangeDoesNotAffectLifetime()380     public void testTimeChangeDoesNotAffectLifetime() throws Exception {
381         long expirationTime = TimeUnit.SECONDS.toMillis(30);
382         long defaultExpirationTime = TimeUnit.HOURS.toMillis(48);
383         RollbackManager rm = RollbackTestUtils.getRollbackManager();
384 
385         try {
386             RollbackTestUtils.adoptShellPermissionIdentity(
387                     Manifest.permission.INSTALL_PACKAGES,
388                     Manifest.permission.DELETE_PACKAGES,
389                     Manifest.permission.TEST_MANAGE_ROLLBACKS,
390                     Manifest.permission.WRITE_DEVICE_CONFIG,
391                     Manifest.permission.SET_TIME);
392 
393             DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
394                     RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
395                     Long.toString(expirationTime), false /* makeDefault*/);
396 
397             // Pull the new expiration time from DeviceConfig
398             rm.reloadPersistedData();
399 
400             // Install app A with rollback enabled
401             RollbackTestUtils.uninstall(TEST_APP_A);
402             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
403             RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
404             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
405 
406             Thread.sleep(expirationTime / 2);
407 
408             // Install app B with rollback enabled
409             RollbackTestUtils.uninstall(TEST_APP_B);
410             RollbackTestUtils.install("RollbackTestAppBv1.apk", false);
411             RollbackTestUtils.install("RollbackTestAppBv2.apk", true);
412             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
413             // 1 second buffer
414             Thread.sleep(1000);
415 
416             try {
417                 // Change the time
418                 RollbackTestUtils.forwardTimeBy(expirationTime);
419 
420                 // 1 second buffer to allow Rollback Manager to handle time change before loading
421                 // persisted data
422                 Thread.sleep(1000);
423 
424                 // Load timestamps from storage
425                 rm.reloadPersistedData();
426 
427                 // Wait until rollback for app A has expired
428                 // This will trigger an expiration run that should expire app A but not B
429                 Thread.sleep(expirationTime / 2);
430                 assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A));
431 
432                 // Rollback for app B should not be expired
433                 RollbackInfo rollback = getUniqueRollbackInfoForPackage(
434                         rm.getAvailableRollbacks(), TEST_APP_B);
435                 assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollback);
436 
437                 // Wait until rollback for app B has expired
438                 Thread.sleep(expirationTime / 2);
439                 assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_B));
440             } finally {
441                 RollbackTestUtils.forwardTimeBy(-expirationTime);
442             }
443         } finally {
444             DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
445                     RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
446                     Long.toString(defaultExpirationTime), false /* makeDefault*/);
447             RollbackTestUtils.dropShellPermissionIdentity();
448         }
449     }
450 
451     /**
452      * Test explicit expiration of rollbacks.
453      * Does not test the scheduling aspects of rollback expiration.
454      */
455     @Test
testRollbackExpiration()456     public void testRollbackExpiration() throws Exception {
457         try {
458             RollbackTestUtils.adoptShellPermissionIdentity(
459                     Manifest.permission.INSTALL_PACKAGES,
460                     Manifest.permission.DELETE_PACKAGES,
461                     Manifest.permission.TEST_MANAGE_ROLLBACKS);
462 
463             RollbackManager rm = RollbackTestUtils.getRollbackManager();
464             RollbackTestUtils.uninstall(TEST_APP_A);
465             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
466             RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
467             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
468 
469             // The app should now be available for rollback.
470             RollbackInfo rollback = getUniqueRollbackInfoForPackage(
471                     rm.getAvailableRollbacks(), TEST_APP_A);
472             assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
473 
474             // Expire the rollback.
475             rm.expireRollbackForPackage(TEST_APP_A);
476 
477             // The rollback should no longer be available.
478             assertNull(getUniqueRollbackInfoForPackage(
479                         rm.getAvailableRollbacks(), TEST_APP_A));
480         } finally {
481             RollbackTestUtils.dropShellPermissionIdentity();
482         }
483     }
484 
485     /**
486      * Test that app user data is rolled back.
487      */
488     @Test
testUserDataRollback()489     public void testUserDataRollback() throws Exception {
490         try {
491             RollbackTestUtils.adoptShellPermissionIdentity(
492                     Manifest.permission.INSTALL_PACKAGES,
493                     Manifest.permission.DELETE_PACKAGES,
494                     Manifest.permission.TEST_MANAGE_ROLLBACKS);
495 
496             RollbackTestUtils.uninstall(TEST_APP_A);
497             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
498             processUserData(TEST_APP_A);
499             RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
500             processUserData(TEST_APP_A);
501 
502             RollbackManager rm = RollbackTestUtils.getRollbackManager();
503             RollbackInfo rollback = getUniqueRollbackInfoForPackage(
504                     rm.getAvailableRollbacks(), TEST_APP_A);
505             RollbackTestUtils.rollback(rollback.getRollbackId());
506             processUserData(TEST_APP_A);
507         } finally {
508             RollbackTestUtils.dropShellPermissionIdentity();
509         }
510     }
511 
512     /**
513      * Test rollback of apks involving splits.
514      */
515     @Test
testRollbackWithSplits()516     public void testRollbackWithSplits() throws Exception {
517         try {
518             RollbackTestUtils.adoptShellPermissionIdentity(
519                     Manifest.permission.INSTALL_PACKAGES,
520                     Manifest.permission.DELETE_PACKAGES,
521                     Manifest.permission.TEST_MANAGE_ROLLBACKS);
522 
523             RollbackTestUtils.uninstall(TEST_APP_A);
524             RollbackTestUtils.installSplit(false,
525                     "RollbackTestAppASplitV1.apk",
526                     "RollbackTestAppASplitV1_anydpi.apk");
527             processUserData(TEST_APP_A);
528 
529             RollbackTestUtils.installSplit(true,
530                     "RollbackTestAppASplitV2.apk",
531                     "RollbackTestAppASplitV2_anydpi.apk");
532             processUserData(TEST_APP_A);
533 
534             RollbackManager rm = RollbackTestUtils.getRollbackManager();
535             RollbackInfo rollback = getUniqueRollbackInfoForPackage(
536                     rm.getAvailableRollbacks(), TEST_APP_A);
537             assertNotNull(rollback);
538             RollbackTestUtils.rollback(rollback.getRollbackId());
539             processUserData(TEST_APP_A);
540         } finally {
541             RollbackTestUtils.dropShellPermissionIdentity();
542         }
543     }
544 
545     /**
546      * Test restrictions on rollback broadcast sender.
547      * A random app should not be able to send a ROLLBACK_COMMITTED broadcast.
548      */
549     @Test
testRollbackBroadcastRestrictions()550     public void testRollbackBroadcastRestrictions() throws Exception {
551         RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
552         Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
553         try {
554             InstrumentationRegistry.getContext().sendBroadcast(broadcast);
555             fail("Succeeded in sending restricted broadcast from app context.");
556         } catch (SecurityException se) {
557             // Expected behavior.
558         }
559 
560         // Confirm that we really haven't received the broadcast.
561         // TODO: How long to wait for the expected timeout?
562         assertNull(broadcastReceiver.poll(5, TimeUnit.SECONDS));
563 
564         // TODO: Do we need to do this? Do we need to ensure this is always
565         // called, even when the test fails?
566         broadcastReceiver.unregister();
567     }
568 
569     /**
570      * Regression test for rollback in the case when multiple apps are
571      * available for rollback at the same time.
572      */
573     @Test
testMultipleRollbackAvailable()574     public void testMultipleRollbackAvailable() throws Exception {
575         try {
576             RollbackTestUtils.adoptShellPermissionIdentity(
577                     Manifest.permission.INSTALL_PACKAGES,
578                     Manifest.permission.DELETE_PACKAGES,
579                     Manifest.permission.TEST_MANAGE_ROLLBACKS);
580             RollbackManager rm = RollbackTestUtils.getRollbackManager();
581 
582             // Prep installation of the test apps.
583             RollbackTestUtils.uninstall(TEST_APP_A);
584             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
585             RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
586             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
587 
588             RollbackTestUtils.uninstall(TEST_APP_B);
589             RollbackTestUtils.install("RollbackTestAppBv1.apk", false);
590             RollbackTestUtils.install("RollbackTestAppBv2.apk", true);
591             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
592 
593             // Both test apps should now be available for rollback, and the
594             // RollbackInfo returned for the rollbacks should be correct.
595             RollbackInfo rollbackA = getUniqueRollbackInfoForPackage(
596                     rm.getAvailableRollbacks(), TEST_APP_A);
597             assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA);
598 
599             RollbackInfo rollbackB = getUniqueRollbackInfoForPackage(
600                     rm.getAvailableRollbacks(), TEST_APP_B);
601             assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB);
602 
603             // Executing rollback should roll back the correct package.
604             RollbackTestUtils.rollback(rollbackA.getRollbackId());
605             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
606             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
607 
608             RollbackTestUtils.uninstall(TEST_APP_A);
609             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
610             RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
611             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
612 
613             RollbackTestUtils.rollback(rollbackB.getRollbackId());
614             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
615             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
616         } finally {
617             RollbackTestUtils.dropShellPermissionIdentity();
618         }
619     }
620 
621     /**
622      * Test that the MANAGE_ROLLBACKS permission is required to call
623      * RollbackManager APIs.
624      */
625     @Test
testManageRollbacksPermission()626     public void testManageRollbacksPermission() throws Exception {
627         // We shouldn't be allowed to call any of the RollbackManager APIs
628         // without the MANAGE_ROLLBACKS permission.
629         RollbackManager rm = RollbackTestUtils.getRollbackManager();
630 
631         try {
632             rm.getAvailableRollbacks();
633             fail("expected SecurityException");
634         } catch (SecurityException e) {
635             // Expected.
636         }
637 
638         try {
639             rm.getRecentlyCommittedRollbacks();
640             fail("expected SecurityException");
641         } catch (SecurityException e) {
642             // Expected.
643         }
644 
645         try {
646             // TODO: What if the implementation checks arguments for non-null
647             // first? Then this test isn't valid.
648             rm.commitRollback(0, Collections.emptyList(), null);
649             fail("expected SecurityException");
650         } catch (SecurityException e) {
651             // Expected.
652         }
653 
654         try {
655             rm.reloadPersistedData();
656             fail("expected SecurityException");
657         } catch (SecurityException e) {
658             // Expected.
659         }
660 
661         try {
662             rm.expireRollbackForPackage(TEST_APP_A);
663             fail("expected SecurityException");
664         } catch (SecurityException e) {
665             // Expected.
666         }
667     }
668 
669     /**
670      * Test that you cannot enable rollback for a package without the
671      * MANAGE_ROLLBACKS permission.
672      */
673     @Test
testEnableRollbackPermission()674     public void testEnableRollbackPermission() throws Exception {
675         try {
676             RollbackTestUtils.adoptShellPermissionIdentity(
677                     Manifest.permission.INSTALL_PACKAGES,
678                     Manifest.permission.DELETE_PACKAGES);
679 
680             RollbackTestUtils.uninstall(TEST_APP_A);
681             RollbackTestUtils.install("RollbackTestAppAv1.apk", /* enableRollback */ false);
682             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
683 
684             RollbackTestUtils.install("RollbackTestAppAv2.apk", /* enableRollback */ true);
685 
686             // We expect v2 of the app was installed, but rollback has not
687             // been enabled.
688             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
689 
690             RollbackTestUtils.adoptShellPermissionIdentity(
691                     Manifest.permission.TEST_MANAGE_ROLLBACKS);
692             RollbackManager rm = RollbackTestUtils.getRollbackManager();
693             assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A));
694         } finally {
695             RollbackTestUtils.dropShellPermissionIdentity();
696         }
697     }
698 
699     /**
700      * Test that you cannot enable rollback for a non-module package when
701      * holding the MANAGE_ROLLBACKS permission.
702      */
703     @Test
testNonModuleEnableRollback()704     public void testNonModuleEnableRollback() throws Exception {
705         try {
706             RollbackTestUtils.adoptShellPermissionIdentity(
707                     Manifest.permission.INSTALL_PACKAGES,
708                     Manifest.permission.DELETE_PACKAGES,
709                     Manifest.permission.MANAGE_ROLLBACKS);
710 
711             RollbackTestUtils.uninstall(TEST_APP_A);
712             RollbackTestUtils.install("RollbackTestAppAv1.apk", /* enableRollback */ false);
713             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
714 
715             RollbackTestUtils.install("RollbackTestAppAv2.apk", /* enableRollback */ true);
716 
717             // We expect v2 of the app was installed, but rollback has not
718             // been enabled because the test app is not a module.
719             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
720 
721             RollbackManager rm = RollbackTestUtils.getRollbackManager();
722             assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A));
723         } finally {
724             RollbackTestUtils.dropShellPermissionIdentity();
725         }
726     }
727 
728     /**
729      * Test rollback of multi-package installs is implemented.
730      */
731     @Test
testMultiPackage()732     public void testMultiPackage() throws Exception {
733         try {
734             RollbackTestUtils.adoptShellPermissionIdentity(
735                     Manifest.permission.INSTALL_PACKAGES,
736                     Manifest.permission.DELETE_PACKAGES,
737                     Manifest.permission.TEST_MANAGE_ROLLBACKS);
738             RollbackManager rm = RollbackTestUtils.getRollbackManager();
739 
740             // Prep installation of the test apps.
741             RollbackTestUtils.uninstall(TEST_APP_A);
742             RollbackTestUtils.uninstall(TEST_APP_B);
743             RollbackTestUtils.installMultiPackage(false,
744                     "RollbackTestAppAv1.apk",
745                     "RollbackTestAppBv1.apk");
746             processUserData(TEST_APP_A);
747             processUserData(TEST_APP_B);
748             RollbackTestUtils.installMultiPackage(true,
749                     "RollbackTestAppAv2.apk",
750                     "RollbackTestAppBv2.apk");
751             processUserData(TEST_APP_A);
752             processUserData(TEST_APP_B);
753             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
754             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
755 
756             // TEST_APP_A should now be available for rollback.
757             RollbackInfo rollback = getUniqueRollbackInfoForPackage(
758                     rm.getAvailableRollbacks(), TEST_APP_A);
759             assertRollbackInfoForAandB(rollback);
760 
761             // Rollback the app. It should cause both test apps to be rolled
762             // back.
763             RollbackTestUtils.rollback(rollback.getRollbackId());
764             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
765             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
766 
767             // We should see recent rollbacks listed for both A and B.
768             Thread.sleep(1000);
769             RollbackInfo rollbackA = getUniqueRollbackInfoForPackage(
770                     rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
771 
772             RollbackInfo rollbackB = getUniqueRollbackInfoForPackage(
773                     rm.getRecentlyCommittedRollbacks(), TEST_APP_B);
774             assertRollbackInfoForAandB(rollbackB);
775 
776             assertEquals(rollbackA.getRollbackId(), rollbackB.getRollbackId());
777 
778             processUserData(TEST_APP_A);
779             processUserData(TEST_APP_B);
780         } finally {
781             RollbackTestUtils.dropShellPermissionIdentity();
782         }
783     }
784 
785     /**
786      * Test failure to enable rollback for multi-package installs.
787      * If any one of the packages fail to enable rollback, we shouldn't enable
788      * rollback for any package.
789      */
790     @Test
testMultiPackageEnableFail()791     public void testMultiPackageEnableFail() throws Exception {
792         try {
793             RollbackTestUtils.adoptShellPermissionIdentity(
794                     Manifest.permission.INSTALL_PACKAGES,
795                     Manifest.permission.DELETE_PACKAGES,
796                     Manifest.permission.TEST_MANAGE_ROLLBACKS);
797             RollbackManager rm = RollbackTestUtils.getRollbackManager();
798 
799             RollbackTestUtils.uninstall(TEST_APP_A);
800             RollbackTestUtils.uninstall(TEST_APP_B);
801             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
802 
803             // We should fail to enable rollback here because TestApp B is not
804             // already installed.
805             RollbackTestUtils.installMultiPackage(true,
806                     "RollbackTestAppAv2.apk",
807                     "RollbackTestAppBv2.apk");
808 
809             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
810             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
811 
812             assertNull(getUniqueRollbackInfoForPackage(
813                     rm.getAvailableRollbacks(), TEST_APP_A));
814             assertNull(getUniqueRollbackInfoForPackage(
815                     rm.getAvailableRollbacks(), TEST_APP_B));
816         } finally {
817             RollbackTestUtils.dropShellPermissionIdentity();
818         }
819     }
820 
821     @Test
822     @Ignore("b/120200473")
823     /**
824      * Test rollback when app is updated to its same version.
825      */
testSameVersionUpdate()826     public void testSameVersionUpdate() throws Exception {
827         try {
828             RollbackTestUtils.adoptShellPermissionIdentity(
829                     Manifest.permission.INSTALL_PACKAGES,
830                     Manifest.permission.DELETE_PACKAGES,
831                     Manifest.permission.TEST_MANAGE_ROLLBACKS);
832             RollbackManager rm = RollbackTestUtils.getRollbackManager();
833 
834             RollbackTestUtils.uninstall(TEST_APP_A);
835             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
836             RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
837             RollbackTestUtils.install("RollbackTestAppACrashingV2.apk", true);
838             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
839 
840             RollbackInfo rollback = getUniqueRollbackInfoForPackage(
841                     rm.getAvailableRollbacks(), TEST_APP_A);
842             assertRollbackInfoEquals(TEST_APP_A, 2, 2, rollback);
843 
844             RollbackTestUtils.rollback(rollback.getRollbackId());
845             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
846 
847             rollback = getUniqueRollbackInfoForPackage(
848                     rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
849             assertRollbackInfoEquals(TEST_APP_A, 2, 2, rollback);
850         } finally {
851             RollbackTestUtils.dropShellPermissionIdentity();
852         }
853     }
854 
855     /**
856      * Test bad update automatic rollback.
857      */
858     @Test
testBadUpdateRollback()859     public void testBadUpdateRollback() throws Exception {
860         BroadcastReceiver crashCountReceiver = null;
861         Context context = InstrumentationRegistry.getContext();
862         try {
863             RollbackTestUtils.adoptShellPermissionIdentity(
864                     Manifest.permission.INSTALL_PACKAGES,
865                     Manifest.permission.DELETE_PACKAGES,
866                     Manifest.permission.MANAGE_ROLLBACKS,
867                     Manifest.permission.TEST_MANAGE_ROLLBACKS,
868                     Manifest.permission.KILL_BACKGROUND_PROCESSES,
869                     Manifest.permission.RESTART_PACKAGES);
870             RollbackManager rm = RollbackTestUtils.getRollbackManager();
871 
872             // Prep installation of the test apps.
873             RollbackTestUtils.uninstall(TEST_APP_A);
874             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
875             RollbackTestUtils.install("RollbackTestAppACrashingV2.apk", true);
876             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
877 
878             RollbackTestUtils.uninstall(TEST_APP_B);
879             RollbackTestUtils.install("RollbackTestAppBv1.apk", false);
880             RollbackTestUtils.install("RollbackTestAppBv2.apk", true);
881             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
882 
883             // Both test apps should now be available for rollback, and the
884             // targetPackage returned for rollback should be correct.
885             RollbackInfo rollbackA = getUniqueRollbackInfoForPackage(
886                     rm.getAvailableRollbacks(), TEST_APP_A);
887             assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA);
888 
889             RollbackInfo rollbackB = getUniqueRollbackInfoForPackage(
890                     rm.getAvailableRollbacks(), TEST_APP_B);
891             assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB);
892 
893             // Register rollback committed receiver
894             RollbackBroadcastReceiver rollbackReceiver = new RollbackBroadcastReceiver();
895 
896             // Crash TEST_APP_A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
897             crashCountReceiver = RollbackTestUtils.sendCrashBroadcast(context, TEST_APP_A, 5);
898 
899             // Verify we received a broadcast for the rollback.
900             rollbackReceiver.take();
901 
902             // TEST_APP_A is automatically rolled back by the RollbackPackageHealthObserver
903             assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
904             // Instrumented app is still the package installer
905             String installer = context.getPackageManager().getInstallerPackageName(TEST_APP_A);
906             assertEquals(INSTRUMENTED_APP, installer);
907             // TEST_APP_B is untouched
908             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
909         } finally {
910             RollbackTestUtils.dropShellPermissionIdentity();
911             if (crashCountReceiver != null) {
912                 context.unregisterReceiver(crashCountReceiver);
913             }
914         }
915     }
916 
917     /**
918      * Test race between roll back and roll forward.
919      */
920     @Test
testRollForwardRace()921     public void testRollForwardRace() throws Exception {
922         try {
923             RollbackTestUtils.adoptShellPermissionIdentity(
924                     Manifest.permission.INSTALL_PACKAGES,
925                     Manifest.permission.DELETE_PACKAGES,
926                     Manifest.permission.TEST_MANAGE_ROLLBACKS,
927                     Manifest.permission.MANAGE_ROLLBACKS);
928 
929             RollbackManager rm = RollbackTestUtils.getRollbackManager();
930 
931             RollbackTestUtils.uninstall(TEST_APP_A);
932             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
933             RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
934             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
935 
936             RollbackInfo rollback = getUniqueRollbackInfoForPackage(
937                     rm.getAvailableRollbacks(), TEST_APP_A);
938             assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
939 
940             // Install a new version of package A, then immediately rollback
941             // the previous version. We expect the rollback to fail, because
942             // it is no longer available.
943             // There are a couple different ways this could fail depending on
944             // thread interleaving, so don't ignore flaky failures.
945             RollbackTestUtils.install("RollbackTestAppAv3.apk", false);
946             try {
947                 RollbackTestUtils.rollback(rollback.getRollbackId());
948                 // Note: Don't ignore flaky failures here.
949                 fail("Expected rollback to fail, but it did not.");
950             } catch (AssertionError e) {
951                 Log.i(TAG, "Note expected failure: ", e);
952                 // Expected
953             }
954 
955             // Note: Don't ignore flaky failures here.
956             assertEquals(3, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
957         } finally {
958             RollbackTestUtils.dropShellPermissionIdentity();
959         }
960     }
961 
962     @Test
testEnableRollbackTimeoutFailsRollback()963     public void testEnableRollbackTimeoutFailsRollback() throws Exception {
964         try {
965             RollbackTestUtils.adoptShellPermissionIdentity(
966                     Manifest.permission.INSTALL_PACKAGES,
967                     Manifest.permission.DELETE_PACKAGES,
968                     Manifest.permission.TEST_MANAGE_ROLLBACKS,
969                     Manifest.permission.MANAGE_ROLLBACKS,
970                     Manifest.permission.WRITE_DEVICE_CONFIG);
971 
972             //setting the timeout to a very short amount that will definitely be triggered
973             DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
974                     PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
975                     Long.toString(1), false /* makeDefault*/);
976             RollbackManager rm = RollbackTestUtils.getRollbackManager();
977 
978             RollbackTestUtils.uninstall(TEST_APP_A);
979             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
980             RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
981 
982             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
983 
984             assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A));
985         } finally {
986             //setting the timeout back to default
987             DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
988                     PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
989                     null, false /* makeDefault*/);
990             RollbackTestUtils.dropShellPermissionIdentity();
991         }
992     }
993 
994     // Helper function to test that the given rollback info is a rollback for
995     // the atomic set {A2, B2} -> {A1, B1}.
assertRollbackInfoForAandB(RollbackInfo rollback)996     private void assertRollbackInfoForAandB(RollbackInfo rollback) {
997         assertNotNull(rollback);
998         assertEquals(2, rollback.getPackages().size());
999         if (TEST_APP_A.equals(rollback.getPackages().get(0).getPackageName())) {
1000             assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.getPackages().get(0));
1001             assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollback.getPackages().get(1));
1002         } else {
1003             assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollback.getPackages().get(0));
1004             assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.getPackages().get(1));
1005         }
1006     }
1007 }
1008