• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.stagedinstall.host;
18 
19 import static com.android.cts.shim.lib.ShimPackage.SHIM_APEX_PACKAGE_NAME;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.hamcrest.CoreMatchers.endsWith;
24 import static org.hamcrest.CoreMatchers.equalTo;
25 import static org.hamcrest.CoreMatchers.not;
26 import static org.junit.Assume.assumeFalse;
27 import static org.junit.Assume.assumeThat;
28 import static org.junit.Assume.assumeTrue;
29 
30 import android.cts.install.lib.host.InstallUtilsHost;
31 import android.platform.test.annotations.LargeTest;
32 
33 import com.android.apex.ApexInfo;
34 import com.android.apex.XmlParser;
35 import com.android.ddmlib.Log;
36 import com.android.tradefed.device.DeviceNotAvailableException;
37 import com.android.tradefed.device.ITestDevice;
38 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
39 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
40 
41 
42 import org.junit.After;
43 import org.junit.Before;
44 import org.junit.Rule;
45 import org.junit.Test;
46 import org.junit.rules.TestWatcher;
47 import org.junit.runner.Description;
48 import org.junit.runner.RunWith;
49 
50 import java.io.File;
51 import java.io.FileInputStream;
52 import java.util.List;
53 import java.util.Set;
54 import java.util.stream.Collectors;
55 
56 @RunWith(DeviceJUnit4ClassRunner.class)
57 public class StagedInstallTest extends BaseHostJUnit4Test {
58 
59     private static final String TAG = "StagedInstallTest";
60 
61     private static final String PACKAGE_NAME = "com.android.tests.stagedinstall";
62 
63     private static final String BROADCAST_RECEIVER_COMPONENT = PACKAGE_NAME + "/"
64             + PACKAGE_NAME + ".LauncherActivity";
65 
66     private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this);
67 
68     private String mDefaultLauncher = null;
69 
70     @Rule
71     public final FailedTestLogHook mFailedTestLogHook = new FailedTestLogHook(this);
72 
73     /**
74      * Runs the given phase of a test by calling into the device.
75      * Throws an exception if the test phase fails.
76      * <p>
77      * For example, <code>runPhase("testInstallStagedApkCommit");</code>
78      */
runPhase(String phase)79     private void runPhase(String phase) throws Exception {
80         assertThat(runDeviceTests(PACKAGE_NAME,
81                 "com.android.tests.stagedinstall.StagedInstallTest",
82                 phase)).isTrue();
83     }
84 
85     // We do not assert the success of cleanup phase since it might fail due to flaky reasons.
cleanUp()86     private void cleanUp() throws Exception {
87         try {
88             runDeviceTests(PACKAGE_NAME,
89                     "com.android.tests.stagedinstall.StagedInstallTest",
90                     "cleanUp");
91         } catch (AssertionError e) {
92             Log.e(TAG, e);
93         }
94     }
95 
96     @Before
setUp()97     public void setUp() throws Exception {
98         cleanUp();
99         mHostUtils.uninstallShimApexIfNecessary();
100         storeDefaultLauncher();
101     }
102 
103     @After
tearDown()104     public void tearDown() throws Exception {
105         cleanUp();
106         mHostUtils.uninstallShimApexIfNecessary();
107         setDefaultLauncher(mDefaultLauncher);
108     }
109 
110     /**
111      * Tests for staged install involving only one apk.
112      */
113     @Test
114     @LargeTest
testInstallStagedApk()115     public void testInstallStagedApk() throws Exception {
116         assumeSystemUser();
117 
118         setDefaultLauncher(BROADCAST_RECEIVER_COMPONENT);
119         runPhase("testInstallStagedApk_Commit");
120         getDevice().reboot();
121         runPhase("testInstallStagedApk_VerifyPostReboot");
122         runPhase("testInstallStagedApk_AbandonSessionIsNoop");
123     }
124 
125     @Test
testFailInstallIfNoPermission()126     public void testFailInstallIfNoPermission() throws Exception {
127         runPhase("testFailInstallIfNoPermission");
128     }
129 
130     @Test
131     @LargeTest
testAbandonStagedApkBeforeReboot()132     public void testAbandonStagedApkBeforeReboot() throws Exception {
133         runPhase("testAbandonStagedApkBeforeReboot_CommitAndAbandon");
134         getDevice().reboot();
135         runPhase("testAbandonStagedApkBeforeReboot_VerifyPostReboot");
136     }
137 
138     @Test
139     @LargeTest
testAbandonStagedApkBeforeReady()140     public void testAbandonStagedApkBeforeReady() throws Exception {
141         runPhase("testAbandonStagedApkBeforeReady_CommitAndAbandon");
142         getDevice().reboot();
143         runPhase("testAbandonStagedApkBeforeReady_VerifyPostReboot");
144     }
145 
146     @Test
testStageAnotherSessionImmediatelyAfterAbandon()147     public void testStageAnotherSessionImmediatelyAfterAbandon() throws Exception {
148         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
149         runPhase("testStageAnotherSessionImmediatelyAfterAbandon");
150     }
151 
152     @Test
testStageAnotherSessionImmediatelyAfterAbandonMultiPackage()153     public void testStageAnotherSessionImmediatelyAfterAbandonMultiPackage() throws Exception {
154         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
155         runPhase("testStageAnotherSessionImmediatelyAfterAbandonMultiPackage");
156     }
157 
158     @Test
testNoSessionUpdatedBroadcastSentForStagedSessionAbandon()159     public void testNoSessionUpdatedBroadcastSentForStagedSessionAbandon() throws Exception {
160         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
161         runPhase("testNoSessionUpdatedBroadcastSentForStagedSessionAbandon");
162     }
163 
164     @Test
165     @LargeTest
testInstallMultipleStagedApks()166     public void testInstallMultipleStagedApks() throws Exception {
167         assumeSystemUser();
168 
169         setDefaultLauncher(BROADCAST_RECEIVER_COMPONENT);
170         runPhase("testInstallMultipleStagedApks_Commit");
171         getDevice().reboot();
172         runPhase("testInstallMultipleStagedApks_VerifyPostReboot");
173     }
174 
assumeSystemUser()175     private void assumeSystemUser() throws Exception {
176         String systemUser = "0";
177         assumeThat("Current user is not system user",
178                 getDevice().executeShellCommand("am get-current-user").trim(), equalTo(systemUser));
179     }
180 
181     @Test
testGetActiveStagedSessions()182     public void testGetActiveStagedSessions() throws Exception {
183         assumeTrue("Device does not support file-system checkpoint",
184                 mHostUtils.isCheckpointSupported());
185 
186         runPhase("testGetActiveStagedSessions");
187     }
188 
189     /**
190      * Verifies that active staged session fulfils conditions stated at
191      * {@link PackageInstaller.SessionInfo#isStagedSessionActive}
192      */
193     @Test
testIsStagedSessionActive()194     public void testIsStagedSessionActive() throws Exception {
195         runPhase("testIsStagedSessionActive");
196     }
197 
198     @Test
testGetActiveStagedSessionsNoSessionActive()199     public void testGetActiveStagedSessionsNoSessionActive() throws Exception {
200         runPhase("testGetActiveStagedSessionsNoSessionActive");
201     }
202 
203     @Test
testGetActiveStagedSessions_MultiApkSession()204     public void testGetActiveStagedSessions_MultiApkSession() throws Exception {
205         assumeTrue("Device does not support file-system checkpoint",
206                 mHostUtils.isCheckpointSupported());
207 
208         runPhase("testGetActiveStagedSessions_MultiApkSession");
209     }
210 
211     @Test
testStagedInstallDowngrade_DowngradeNotRequested_Fails()212     public void testStagedInstallDowngrade_DowngradeNotRequested_Fails() throws Exception {
213         runPhase("testStagedInstallDowngrade_DowngradeNotRequested_Fails_Commit");
214     }
215 
216     @Test
217     @LargeTest
testStagedInstallDowngrade_DowngradeRequested_DebugBuild()218     public void testStagedInstallDowngrade_DowngradeRequested_DebugBuild() throws Exception {
219         assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user")));
220 
221         runPhase("testStagedInstallDowngrade_DowngradeRequested_Commit");
222         getDevice().reboot();
223         runPhase("testStagedInstallDowngrade_DowngradeRequested_DebugBuild_VerifyPostReboot");
224     }
225 
226     @Test
testStagedInstallDowngrade_DowngradeRequested_UserBuild()227     public void testStagedInstallDowngrade_DowngradeRequested_UserBuild() throws Exception {
228         assumeThat(getDevice().getBuildFlavor(), endsWith("-user"));
229         assumeFalse("Device is debuggable", isDebuggable());
230 
231         runPhase("testStagedInstallDowngrade_DowngradeRequested_Fails_Commit");
232     }
233 
234     @Test
testShimApexShouldPreInstalledIfUpdatingApexIsSupported()235     public void testShimApexShouldPreInstalledIfUpdatingApexIsSupported() throws Exception {
236         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
237 
238         final ITestDevice.ApexInfo shimApex = mHostUtils.getShimApex().orElseThrow(
239                 () -> new AssertionError("Can't find " + SHIM_APEX_PACKAGE_NAME)
240         );
241         assertThat(shimApex.versionCode).isEqualTo(1);
242     }
243 
244     @Test
245     @LargeTest
testInstallStagedApex()246     public void testInstallStagedApex() throws Exception {
247         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
248 
249         setDefaultLauncher(BROADCAST_RECEIVER_COMPONENT);
250         runPhase("testInstallStagedApex_Commit");
251         getDevice().reboot();
252         runPhase("testInstallStagedApex_VerifyPostReboot");
253     }
254 
255     @Test
256     // Don't mark as @LargeTest since we want at least one test to install apex during pre-submit.
testInstallStagedApexAndApk()257     public void testInstallStagedApexAndApk() throws Exception {
258         assumeSystemUser();
259         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
260 
261         setDefaultLauncher(BROADCAST_RECEIVER_COMPONENT);
262         runPhase("testInstallStagedApexAndApk_Commit");
263         getDevice().reboot();
264         runPhase("testInstallStagedApexAndApk_VerifyPostReboot");
265     }
266 
267     @Test
testsFailsNonStagedApexInstall()268     public void testsFailsNonStagedApexInstall() throws Exception {
269         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
270 
271         runPhase("testsFailsNonStagedApexInstall");
272     }
273 
274     @Test
testInstallStagedNonPreInstalledApex_Fails()275     public void testInstallStagedNonPreInstalledApex_Fails() throws Exception {
276         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
277 
278         runPhase("testInstallStagedNonPreInstalledApex_Fails");
279     }
280 
281     @Test
testInstallStagedDifferentPackageNameWithInstalledApex_Fails()282     public void testInstallStagedDifferentPackageNameWithInstalledApex_Fails() throws Exception {
283         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
284 
285         runPhase("testInstallStagedDifferentPackageNameWithInstalledApex_Fails");
286     }
287 
288     @Test
289     @LargeTest
testStageApkWithSameNameAsApexShouldFail()290     public void testStageApkWithSameNameAsApexShouldFail() throws Exception {
291         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
292 
293         runPhase("testStageApkWithSameNameAsApexShouldFail_Commit");
294         getDevice().reboot();
295         runPhase("testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot");
296     }
297 
298     @Test
testNonStagedInstallApkWithSameNameAsApexShouldFail()299     public void testNonStagedInstallApkWithSameNameAsApexShouldFail() throws Exception {
300         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
301         runPhase("testNonStagedInstallApkWithSameNameAsApexShouldFail");
302     }
303 
304     @Test
305     @LargeTest
testStagedInstallDowngradeApex_DowngradeNotRequested_Fails()306     public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails() throws Exception {
307         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
308 
309         installV3Apex();
310         runPhase("testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_Commit");
311         getDevice().reboot();
312         runPhase("testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_VerifyPostReboot");
313     }
314 
315     @Test
316     @LargeTest
testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild()317     public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild() throws Exception {
318         assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user")));
319         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
320 
321         installV3Apex();
322         runPhase("testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_Commit");
323         getDevice().reboot();
324         runPhase("testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_VerifyPostReboot");
325     }
326 
327     @Test
328     @LargeTest
testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails()329     public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails()
330             throws Exception {
331         assumeThat(getDevice().getBuildFlavor(), endsWith("-user"));
332         assumeFalse("Device is debuggable", isDebuggable());
333         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
334 
335         installV3Apex();
336         runPhase("testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_Commit");
337         getDevice().reboot();
338         runPhase("testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_"
339                 + "VerifyPostReboot");
340     }
341 
342     @Test
343     @LargeTest
testStagedInstallDowngradeApexToSystemVersion_DebugBuild()344     public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild() throws Exception {
345         assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user")));
346         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
347 
348         installV2Apex();
349         runPhase("testStagedInstallDowngradeApexToSystemVersion_DebugBuild_Commit");
350         getDevice().reboot();
351         runPhase("testStagedInstallDowngradeApexToSystemVersion_DebugBuild_VerifyPostReboot");
352     }
353 
354     @Test
355     @LargeTest
testInstallStagedApex_SameGrade()356     public void testInstallStagedApex_SameGrade() throws Exception {
357         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
358         installV3Apex();
359         ApexInfo shim1 =
360                 readApexInfoList().stream()
361                         .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME))
362                         .filter(ApexInfo::getIsActive)
363                         .findAny()
364                         .orElseThrow(() ->
365                                 new AssertionError(
366                                         "No active version of " + SHIM_APEX_PACKAGE_NAME
367                                                 + " found in /apex/apex-info-list.xml"));
368 
369         installV3Apex();
370         ApexInfo shim2 =
371                 readApexInfoList().stream()
372                         .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME))
373                         .filter(ApexInfo::getIsActive)
374                         .findAny()
375                         .orElseThrow(() ->
376                                 new AssertionError(
377                                         "No active version of " + SHIM_APEX_PACKAGE_NAME
378                                                 + " found in /apex/apex-info-list.xml"));
379         assertThat(shim1.getLastUpdateMillis()).isNotEqualTo(shim2.getLastUpdateMillis());
380     }
381 
382     @Test
383     @LargeTest
testInstallStagedApex_SameGrade_NewOneWins()384     public void testInstallStagedApex_SameGrade_NewOneWins() throws Exception {
385         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
386 
387         installV2Apex();
388 
389         runPhase("testInstallStagedApex_SameGrade_NewOneWins_Commit");
390         getDevice().reboot();
391         runPhase("testInstallStagedApex_SameGrade_NewOneWins_VerifyPostReboot");
392     }
393 
394     @Test
testInstallApex_DeviceDoesNotSupportApex_Fails()395     public void testInstallApex_DeviceDoesNotSupportApex_Fails() throws Exception {
396         assumeFalse("Device supports updating APEX", mHostUtils.isApexUpdateSupported());
397 
398         runPhase("testInstallApex_DeviceDoesNotSupportApex_Fails");
399     }
400 
installV2Apex()401     private void installV2Apex()throws Exception {
402         runPhase("testInstallV2Apex_Commit");
403         getDevice().reboot();
404         runPhase("testInstallV2Apex_VerifyPostReboot");
405     }
406 
installV2SignedBobApex()407     private void installV2SignedBobApex() throws Exception {
408         runPhase("testInstallV2SignedBobApex_Commit");
409         getDevice().reboot();
410         runPhase("testInstallV2SignedBobApex_VerifyPostReboot");
411     }
412 
installV3Apex()413     private void installV3Apex()throws Exception {
414         runPhase("testInstallV3Apex_Commit");
415         getDevice().reboot();
416         runPhase("testInstallV3Apex_VerifyPostReboot");
417     }
418 
419     @Test
testFailsInvalidApexInstall()420     public void testFailsInvalidApexInstall() throws Exception {
421         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
422         runPhase("testFailsInvalidApexInstall_Commit");
423         runPhase("testFailsInvalidApexInstall_AbandonSessionIsNoop");
424     }
425 
426     @Test
testStagedApkSessionCallbacks()427     public void testStagedApkSessionCallbacks() throws Exception {
428         runPhase("testStagedApkSessionCallbacks");
429     }
430 
431     @Test
432     @LargeTest
testInstallStagedApexWithoutApexSuffix()433     public void testInstallStagedApexWithoutApexSuffix() throws Exception {
434         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
435 
436         runPhase("testInstallStagedApexWithoutApexSuffix_Commit");
437         getDevice().reboot();
438         runPhase("testInstallStagedApexWithoutApexSuffix_VerifyPostReboot");
439     }
440 
441     @Test
testRejectsApexDifferentCertificate()442     public void testRejectsApexDifferentCertificate() throws Exception {
443         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
444 
445         runPhase("testRejectsApexDifferentCertificate");
446     }
447 
448     /**
449      * Tests for staged install involving rotated keys.
450      *
451      * Here alice means the original default key that cts.shim.v1 package was signed with and
452      * bob is the new key alice rotates to. Where ambiguous, we will refer keys as alice and bob
453      * instead of "old key" and "new key".
454      *
455      * By default, rotated keys have rollback capability enabled for old keys. When we remove
456      * rollback capability from a key, it is called "Distrusting Event" and the distrusted key can
457      * not update the app anymore.
458      */
459 
460     // Should not be able to update with a key that has not been rotated.
461     @Test
testUpdateWithDifferentKeyButNoRotation()462     public void testUpdateWithDifferentKeyButNoRotation() throws Exception {
463         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
464 
465         runPhase("testUpdateWithDifferentKeyButNoRotation");
466     }
467 
468     // Should be able to update with a key that has been rotated.
469     @Test
470     @LargeTest
testUpdateWithDifferentKey()471     public void testUpdateWithDifferentKey() throws Exception {
472         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
473 
474         runPhase("testUpdateWithDifferentKey_Commit");
475         getDevice().reboot();
476         runPhase("testUpdateWithDifferentKey_VerifyPostReboot");
477     }
478 
479     // Should not be able to update with a key that is no longer trusted (i.e, has no
480     // rollback capability)
481     @Test
482     @LargeTest
testUntrustedOldKeyIsRejected()483     public void testUntrustedOldKeyIsRejected() throws Exception {
484         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
485 
486         installV2SignedBobApex();
487         runPhase("testUntrustedOldKeyIsRejected");
488     }
489 
490     // Should be able to update with an old key which is trusted
491     @Test
492     @LargeTest
testTrustedOldKeyIsAccepted()493     public void testTrustedOldKeyIsAccepted() throws Exception {
494         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
495 
496         runPhase("testTrustedOldKeyIsAccepted_Commit");
497         getDevice().reboot();
498         runPhase("testTrustedOldKeyIsAccepted_CommitPostReboot");
499         getDevice().reboot();
500         runPhase("testTrustedOldKeyIsAccepted_VerifyPostReboot");
501     }
502 
503     // Should be able to update further with rotated key
504     @Test
505     @LargeTest
testAfterRotationNewKeyCanUpdateFurther()506     public void testAfterRotationNewKeyCanUpdateFurther() throws Exception {
507         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
508 
509         installV2SignedBobApex();
510         runPhase("testAfterRotationNewKeyCanUpdateFurther_CommitPostReboot");
511         getDevice().reboot();
512         runPhase("testAfterRotationNewKeyCanUpdateFurther_VerifyPostReboot");
513     }
514 
515     @Test
516     @LargeTest
testAfterRotationNewKeyCanUpdateFurtherWithoutLineage()517     public void testAfterRotationNewKeyCanUpdateFurtherWithoutLineage() throws Exception {
518         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
519 
520         installV2SignedBobApex();
521         runPhase("testAfterRotationNewKeyCanUpdateFurtherWithoutLineage");
522     }
523 
524     /**
525      * Tests for staging and installing multiple staged sessions.
526      */
527 
528     // Should fail to stage multiple sessions when check-point is not available
529     @Test
testFailStagingMultipleSessionsIfNoCheckPoint()530     public void testFailStagingMultipleSessionsIfNoCheckPoint() throws Exception {
531         assumeFalse("Device supports file-system checkpoint",
532                 mHostUtils.isCheckpointSupported());
533 
534         runPhase("testFailStagingMultipleSessionsIfNoCheckPoint");
535     }
536 
537     @Test
testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk()538     public void testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk() throws Exception {
539         assumeTrue("Device does not support file-system checkpoint",
540                 mHostUtils.isCheckpointSupported());
541 
542         runPhase("testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk");
543     }
544 
545     @Test
testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk()546     public void testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk()
547             throws Exception {
548         assumeTrue("Device does not support file-system checkpoint",
549                 mHostUtils.isCheckpointSupported());
550 
551         runPhase("testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk");
552     }
553 
554     @Test
testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk()555     public void testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk() throws Exception {
556         assumeTrue("Device does not support file-system checkpoint",
557                 mHostUtils.isCheckpointSupported());
558 
559         runPhase("testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk");
560     }
561 
562     // Test for installing multiple staged sessions at the same time
563     @Test
564     @LargeTest
testMultipleStagedInstall_ApkOnly()565     public void testMultipleStagedInstall_ApkOnly() throws Exception {
566         assumeTrue("Device does not support file-system checkpoint",
567                 mHostUtils.isCheckpointSupported());
568 
569         runPhase("testMultipleStagedInstall_ApkOnly_Commit");
570         getDevice().reboot();
571         runPhase("testMultipleStagedInstall_ApkOnly_VerifyPostReboot");
572     }
573 
574     // If apk installation fails in one staged session, then all staged session should fail.
575     @Test
576     @LargeTest
testInstallMultipleStagedSession_PartialFail_ApkOnly()577     public void testInstallMultipleStagedSession_PartialFail_ApkOnly() throws Exception {
578         assumeTrue("Device does not support file-system checkpoint",
579                 mHostUtils.isCheckpointSupported());
580 
581         runPhase("testInstallMultipleStagedSession_PartialFail_ApkOnly_Commit");
582         getDevice().reboot();
583         runPhase("testInstallMultipleStagedSession_PartialFail_ApkOnly_VerifyPostReboot");
584     }
585 
586     // Failure reason of staged install should be be persisted for single sessions
587     @Test
588     @LargeTest
testFailureReasonPersists_SingleSession()589     public void testFailureReasonPersists_SingleSession() throws Exception {
590         assumeTrue("Device does not support file-system checkpoint",
591                 mHostUtils.isCheckpointSupported());
592 
593         runPhase("testFailureReasonPersists_SingleSession_Commit");
594         getDevice().reboot();
595         runPhase("testFailureReasonPersists_SingleSession_VerifyPostReboot");
596     }
597 
598     // Failure reason of staged install should be be persisted for multi session staged install
599     @Test
600     @LargeTest
testFailureReasonPersists_MultiSession()601     public void testFailureReasonPersists_MultiSession() throws Exception {
602         assumeTrue("Device does not support file-system checkpoint",
603                 mHostUtils.isCheckpointSupported());
604 
605         runPhase("testFailureReasonPersists_MultipleSession_Commit");
606         getDevice().reboot();
607         runPhase("testFailureReasonPersists_MultipleSession_VerifyPostReboot");
608     }
609 
610     @Test
611     @LargeTest
testSamegradeSystemApex()612     public void testSamegradeSystemApex() throws Exception {
613         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
614 
615         runPhase("testSamegradeSystemApex_Commit");
616         getDevice().reboot();
617         runPhase("testSamegradeSystemApex_VerifyPostReboot");
618     }
619 
620     @Test
621     @LargeTest
testInstallApkChangingFingerprint()622     public void testInstallApkChangingFingerprint() throws Exception {
623         try {
624             getDevice().executeShellCommand("setprop persist.pm.mock-upgrade true");
625             runPhase("testInstallApkChangingFingerprint");
626             getDevice().reboot();
627             runPhase("testInstallApkChangingFingerprint_VerifyAborted");
628         } finally {
629             getDevice().executeShellCommand("setprop persist.pm.mock-upgrade false");
630         }
631     }
632 
633     @Test
634     @LargeTest
testInstallStagedNoHashtreeApex()635     public void testInstallStagedNoHashtreeApex() throws Exception {
636         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
637 
638         runPhase("testInstallStagedNoHashtreeApex_Commit");
639         getDevice().reboot();
640         runPhase("testInstallStagedNoHashtreeApex_VerifyPostReboot");
641     }
642 
643     /**
644      * Should fail to verify apex targeting older dev sdk
645      */
646     @Test
testApexTargetingOldDevSdkFailsVerification()647     public void testApexTargetingOldDevSdkFailsVerification() throws Exception {
648         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
649 
650         runPhase("testApexTargetingOldDevSdkFailsVerification");
651     }
652 
653     /**
654      * Apex should fail to install if apk-in-apex fails to get scanned
655      */
656     @Test
657     @LargeTest
testApexFailsToInstallIfApkInApexFailsToScan()658     public void testApexFailsToInstallIfApkInApexFailsToScan() throws Exception {
659         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
660 
661         runPhase("testApexFailsToInstallIfApkInApexFailsToScan_Commit");
662         getDevice().reboot();
663         runPhase("testApexFailsToInstallIfApkInApexFailsToScan_VerifyPostReboot");
664     }
665 
666     @Test
testCorruptedApexFailsVerification_b146895998()667     public void testCorruptedApexFailsVerification_b146895998() throws Exception {
668         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
669 
670         runPhase("testCorruptedApexFailsVerification_b146895998");
671     }
672 
673     /**
674      * Should fail to pass apk signature check
675      */
676     @Test
testApexWithUnsignedApkFailsVerification()677     public void testApexWithUnsignedApkFailsVerification() throws Exception {
678         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
679 
680         runPhase("testApexWithUnsignedApkFailsVerification");
681     }
682 
683     /**
684      * Should fail to verify apex signed payload with a different key
685      */
686     @Test
testApexSignPayloadWithDifferentKeyFailsVerification()687     public void testApexSignPayloadWithDifferentKeyFailsVerification() throws Exception {
688         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
689 
690         runPhase("testApexSignPayloadWithDifferentKeyFailsVerification");
691     }
692 
693     /**
694      * Should fail to verify apex with unsigned payload
695      */
696     @Test
testApexWithUnsignedPayloadFailsVerification()697     public void testApexWithUnsignedPayloadFailsVerification() throws Exception {
698         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
699 
700         runPhase("testApexWithUnsignedPayloadFailsVerification");
701     }
702 
703     @Test
704     @LargeTest
testApexSetsUpdatedSystemAppFlag()705     public void testApexSetsUpdatedSystemAppFlag() throws Exception {
706         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
707 
708         runPhase("testApexSetsUpdatedSystemAppFlag_preUpdate");
709         installV2Apex();
710         runPhase("testApexSetsUpdatedSystemAppFlag_postUpdate");
711     }
712 
713     /**
714      * Test non-priv apps cannot access /data/app-staging folder contents
715      */
716     @Test
testAppStagingDirCannotBeReadByNonPrivApps()717     public void testAppStagingDirCannotBeReadByNonPrivApps() throws Exception {
718         runPhase("testAppStagingDirCannotBeReadByNonPrivApps");
719     }
720 
721     @Test
722     @LargeTest
testUpdatedApexFromDataApexActiveCanBePulled()723     public void testUpdatedApexFromDataApexActiveCanBePulled() throws Exception {
724         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
725 
726         installV2Apex();
727 
728         final ITestDevice.ApexInfo shimApex = mHostUtils.getShimApex().orElseThrow(
729                 () -> new AssertionError("Can't find " + SHIM_APEX_PACKAGE_NAME)
730         );
731 
732         assertThat(shimApex.sourceDir).startsWith("/data/apex/active");
733         assertThat(getDevice().pullFile(shimApex.sourceDir)).isNotNull();
734     }
735 
736     @Test
testApexInfoList()737     public void testApexInfoList() throws Exception {
738         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
739 
740         // Check that content of /apex/apex-info-list.xml matches output of
741         // `adb shell pm list packages --apex-only --show-versioncode -f`.
742         List<ApexInfo> apexInfoList =
743                 readApexInfoList().stream()
744                     .filter(a -> a.getIsActive())
745                     .collect(Collectors.toList());
746         Set<ITestDevice.ApexInfo> activeApexes = getDevice().getActiveApexes();
747         assertThat(apexInfoList.size()).isEqualTo(activeApexes.size());
748         for (ITestDevice.ApexInfo apex : activeApexes) {
749             // Note: we can't assert equality of the apex.name and apexInfo.getModuleName() since
750             // they represent different concepts (the former is package name, while latter is apex
751             // module name)
752             List<ApexInfo> temp =
753                     apexInfoList.stream()
754                             .filter(a -> a.getModulePath().equals(apex.sourceDir))
755                             .collect(Collectors.toList());
756             assertThat(temp).hasSize(1);
757             ApexInfo apexInfo = temp.get(0);
758             assertThat(apexInfo.getModulePath()).isEqualTo(apex.sourceDir);
759             assertThat(apexInfo.getVersionCode()).isEqualTo(apex.versionCode);
760             assertThat(apexInfo.getIsActive()).isTrue();
761             assertThat(apexInfo.getLastUpdateMillis()).isGreaterThan(0);
762         }
763     }
764 
765     @Test
766     @LargeTest
testApexInfoListAfterUpdate()767     public void testApexInfoListAfterUpdate() throws Exception {
768         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
769 
770         ApexInfo shimBeforeUpdate = getShimApexInfo();
771 
772         installV2Apex();
773 
774         List<ApexInfo> shimApexInfo =
775                 readApexInfoList().stream()
776                         .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME))
777                         .collect(Collectors.toList());
778 
779         assertThat(shimApexInfo).hasSize(2);
780 
781         ApexInfo factoryShimApexInfo =
782                 shimApexInfo.stream()
783                         .filter(ApexInfo::getIsFactory)
784                         .findAny()
785                         .orElseThrow(() ->
786                                 new AssertionError(
787                                         "No factory version of " + SHIM_APEX_PACKAGE_NAME
788                                                 + " found in /apex/apex-info-list.xml"));
789         assertThat(factoryShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME);
790         assertThat(factoryShimApexInfo.getIsActive()).isFalse();
791         assertThat(factoryShimApexInfo.getIsFactory()).isTrue();
792         assertThat(factoryShimApexInfo.getVersionCode()).isEqualTo(1);
793         assertThat(factoryShimApexInfo.getModulePath())
794                 .isEqualTo(factoryShimApexInfo.getPreinstalledModulePath());
795         assertThat(factoryShimApexInfo.getLastUpdateMillis())
796                 .isEqualTo(shimBeforeUpdate.getLastUpdateMillis());
797 
798         ApexInfo activeShimApexInfo =
799                 shimApexInfo.stream()
800                         .filter(ApexInfo::getIsActive)
801                         .findAny()
802                         .orElseThrow(() ->
803                                 new AssertionError(
804                                         "No active version of " + SHIM_APEX_PACKAGE_NAME
805                                                 + " found in /apex/apex-info-list.xml"));
806         assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME);
807         assertThat(activeShimApexInfo.getIsActive()).isTrue();
808         assertThat(activeShimApexInfo.getIsFactory()).isFalse();
809         assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(2);
810         assertThat(activeShimApexInfo.getPreinstalledModulePath())
811                 .isEqualTo(factoryShimApexInfo.getModulePath());
812         assertThat(activeShimApexInfo.getLastUpdateMillis())
813                 .isNotEqualTo(shimBeforeUpdate.getLastUpdateMillis());
814     }
815 
816     @Test
817     @LargeTest
testRebootlessUpdate()818     public void testRebootlessUpdate() throws Exception {
819         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
820 
821         runPhase("testRebootlessUpdate");
822         ApexInfo activeShimApexInfo = getActiveShimApexInfo();
823         assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME);
824         assertThat(activeShimApexInfo.getIsActive()).isTrue();
825         assertThat(activeShimApexInfo.getIsFactory()).isFalse();
826         assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(2);
827     }
828 
829     @Test
830     @LargeTest
testRebootlessUpdate_fromV2ToV3_sameBoot()831     public void testRebootlessUpdate_fromV2ToV3_sameBoot() throws Exception {
832         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
833 
834         runPhase("testRebootlessUpdate");
835         runPhase("testRebootlessUpdate_installV3");
836         ApexInfo activeShimApexInfo = getActiveShimApexInfo();
837         assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME);
838         assertThat(activeShimApexInfo.getIsActive()).isTrue();
839         assertThat(activeShimApexInfo.getIsFactory()).isFalse();
840         assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(3);
841     }
842 
843     @Test
844     @LargeTest
testRebootlessUpdate_fromV2ToV3_rebootInBetween()845     public void testRebootlessUpdate_fromV2ToV3_rebootInBetween() throws Exception {
846         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
847 
848         runPhase("testRebootlessUpdate");
849         getDevice().reboot();
850         runPhase("testRebootlessUpdate_installV3");
851         ApexInfo activeShimApexInfo = getActiveShimApexInfo();
852         assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME);
853         assertThat(activeShimApexInfo.getIsActive()).isTrue();
854         assertThat(activeShimApexInfo.getIsFactory()).isFalse();
855         assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(3);
856     }
857 
858     @Test
859     @LargeTest
testRebootlessUpdate_downgrage_fails()860     public void testRebootlessUpdate_downgrage_fails() throws Exception {
861         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
862 
863         runPhase("testRebootlessUpdate_installV3");
864         runPhase("testRebootlessUpdate_downgradeToV2_fails");
865     }
866 
867     @Test
testRebootlessUpdate_noPermission_fails()868     public void testRebootlessUpdate_noPermission_fails() throws Exception {
869         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
870 
871         runPhase("testRebootlessUpdate_noPermission_fails");
872     }
873 
874     @Test
testRebootlessUpdate_noPreInstalledApex_fails()875     public void testRebootlessUpdate_noPreInstalledApex_fails() throws Exception {
876         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
877 
878         runPhase("testRebootlessUpdate_noPreInstalledApex_fails");
879     }
880 
881     @Test
testRebootlessUpdate_unsignedPayload_fails()882     public void testRebootlessUpdate_unsignedPayload_fails() throws Exception {
883         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
884 
885         runPhase("testRebootlessUpdate_unsignedPayload_fails");
886     }
887 
888     @Test
testRebootlessUpdate_payloadSignedWithDifferentKey_fails()889     public void testRebootlessUpdate_payloadSignedWithDifferentKey_fails() throws Exception {
890         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
891 
892         runPhase("testRebootlessUpdate_payloadSignedWithDifferentKey_fails");
893     }
894 
895     @Test
testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails()896     public void testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails()
897             throws Exception {
898         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
899 
900         runPhase("testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails");
901     }
902 
903     @Test
testRebootlessUpdate_outerContainerUnsigned_fails()904     public void testRebootlessUpdate_outerContainerUnsigned_fails() throws Exception {
905         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
906 
907         runPhase("testRebootlessUpdate_outerContainerUnsigned_fails");
908     }
909 
910     @Test
testRebootlessUpdate_targetsOlderSdk_fails()911     public void testRebootlessUpdate_targetsOlderSdk_fails() throws Exception {
912         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
913 
914         runPhase("testRebootlessUpdate_targetsOlderSdk_fails");
915     }
916 
917     @Test
918     @LargeTest
testGetInactiveApexFactoryPackagesAfterApexInstall_containsNoDuplicates()919     public void testGetInactiveApexFactoryPackagesAfterApexInstall_containsNoDuplicates()
920             throws Exception {
921         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
922 
923         installV2Apex();
924         runPhase("testGetInactiveApexFactoryPackagesAfterApexInstall_containsNoDuplicates");
925     }
926 
readApexInfoList()927     private List<ApexInfo> readApexInfoList() throws Exception {
928         File file = getDevice().pullFile("/apex/apex-info-list.xml");
929         try (FileInputStream stream = new FileInputStream(file)) {
930             return XmlParser.readApexInfoList(stream).getApexInfo();
931         }
932     }
933 
getShimApexInfo()934     private ApexInfo getShimApexInfo() throws Exception {
935         List<ApexInfo> temp =
936                 readApexInfoList().stream()
937                             .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME))
938                             .collect(Collectors.toList());
939         assertThat(temp).hasSize(1);
940         return temp.get(0);
941     }
942 
getActiveShimApexInfo()943     private ApexInfo getActiveShimApexInfo() throws Exception {
944         return readApexInfoList().stream()
945                     .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME))
946                     .filter(ApexInfo::getIsActive)
947                     .findAny()
948                     .orElseThrow(() ->
949                             new AssertionError(
950                                     "No active version of " + SHIM_APEX_PACKAGE_NAME
951                                             + " found in /apex/apex-info-list.xml"));
952     }
953 
954     /**
955      * Store the component name of the default launcher. This value will be used to reset the
956      * default launcher to its correct component upon test completion.
957      */
storeDefaultLauncher()958     private void storeDefaultLauncher() throws DeviceNotAvailableException {
959         final String PREFIX = "Launcher: ComponentInfo{";
960         final String POSTFIX = "}";
961         for (String s : getDevice().executeShellCommand("cmd shortcut get-default-launcher")
962                 .split("\n")) {
963             if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) {
964                 mDefaultLauncher = s.substring(PREFIX.length(), s.length() - POSTFIX.length());
965             }
966         }
967     }
968 
969     /**
970      * Set the default launcher to a given component.
971      * If set to the broadcast receiver component of this test app, this will allow the test app to
972      * receive SESSION_COMMITTED broadcasts.
973      */
setDefaultLauncher(String launcherComponent)974     private void setDefaultLauncher(String launcherComponent) throws DeviceNotAvailableException {
975         assertThat(launcherComponent).isNotEmpty();
976         int user = getDevice().getCurrentUser();
977         getDevice().executeShellCommand(
978                 "cmd package set-home-activity --user " + user + " " + launcherComponent);
979     }
980 
981     private static final class FailedTestLogHook extends TestWatcher {
982 
983         private final BaseHostJUnit4Test mInstance;
984         private String mStagedSessionsBeforeTest;
985 
FailedTestLogHook(BaseHostJUnit4Test instance)986         private FailedTestLogHook(BaseHostJUnit4Test instance) {
987             this.mInstance = instance;
988         }
989 
990         @Override
failed(Throwable e, Description description)991         protected void failed(Throwable e, Description description) {
992             String stagedSessionsAfterTest = getStagedSessions();
993             Log.e(TAG, "Test " + description + " failed.\n"
994                     + "Staged sessions before test started:\n" + mStagedSessionsBeforeTest + "\n"
995                     + "Staged sessions after test failed:\n" + stagedSessionsAfterTest);
996         }
997 
998         @Override
starting(Description description)999         protected void starting(Description description) {
1000             mStagedSessionsBeforeTest = getStagedSessions();
1001         }
1002 
getStagedSessions()1003         private String getStagedSessions() {
1004             try {
1005                 return mInstance.getDevice().executeShellV2Command("pm get-stagedsessions").getStdout();
1006             } catch (DeviceNotAvailableException e) {
1007                 Log.e(TAG, e);
1008                 return "Failed to get staged sessions";
1009             }
1010         }
1011     }
1012 
isDebuggable()1013     private boolean isDebuggable() throws Exception {
1014         return getDevice().getIntProperty("ro.debuggable", 0) == 1;
1015     }
1016 }
1017