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         runPhase("testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk");
540     }
541 
542     @Test
testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk()543     public void testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk()
544             throws Exception {
545         assumeTrue("Device does not support file-system checkpoint",
546                 mHostUtils.isCheckpointSupported());
547 
548         runPhase("testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk");
549     }
550 
551     @Test
testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk()552     public void testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk() throws Exception {
553         assumeTrue("Device does not support file-system checkpoint",
554                 mHostUtils.isCheckpointSupported());
555 
556         runPhase("testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk");
557     }
558 
559     // Test for installing multiple staged sessions at the same time
560     @Test
561     @LargeTest
testMultipleStagedInstall_ApkOnly()562     public void testMultipleStagedInstall_ApkOnly() throws Exception {
563         assumeTrue("Device does not support file-system checkpoint",
564                 mHostUtils.isCheckpointSupported());
565 
566         runPhase("testMultipleStagedInstall_ApkOnly_Commit");
567         getDevice().reboot();
568         runPhase("testMultipleStagedInstall_ApkOnly_VerifyPostReboot");
569     }
570 
571     // If apk installation fails in one staged session, then all staged session should fail.
572     @Test
573     @LargeTest
testInstallMultipleStagedSession_PartialFail_ApkOnly()574     public void testInstallMultipleStagedSession_PartialFail_ApkOnly() throws Exception {
575         assumeTrue("Device does not support file-system checkpoint",
576                 mHostUtils.isCheckpointSupported());
577 
578         runPhase("testInstallMultipleStagedSession_PartialFail_ApkOnly_Commit");
579         getDevice().reboot();
580         runPhase("testInstallMultipleStagedSession_PartialFail_ApkOnly_VerifyPostReboot");
581     }
582 
583     // Failure reason of staged install should be be persisted for single sessions
584     @Test
585     @LargeTest
testFailureReasonPersists_SingleSession()586     public void testFailureReasonPersists_SingleSession() throws Exception {
587         assumeTrue("Device does not support file-system checkpoint",
588                 mHostUtils.isCheckpointSupported());
589 
590         runPhase("testFailureReasonPersists_SingleSession_Commit");
591         getDevice().reboot();
592         runPhase("testFailureReasonPersists_SingleSession_VerifyPostReboot");
593     }
594 
595     // Failure reason of staged install should be be persisted for multi session staged install
596     @Test
597     @LargeTest
testFailureReasonPersists_MultiSession()598     public void testFailureReasonPersists_MultiSession() throws Exception {
599         assumeTrue("Device does not support file-system checkpoint",
600                 mHostUtils.isCheckpointSupported());
601 
602         runPhase("testFailureReasonPersists_MultipleSession_Commit");
603         getDevice().reboot();
604         runPhase("testFailureReasonPersists_MultipleSession_VerifyPostReboot");
605     }
606 
607     @Test
608     @LargeTest
testSamegradeSystemApex()609     public void testSamegradeSystemApex() throws Exception {
610         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
611 
612         runPhase("testSamegradeSystemApex_Commit");
613         getDevice().reboot();
614         runPhase("testSamegradeSystemApex_VerifyPostReboot");
615     }
616 
617     @Test
618     @LargeTest
testInstallApkChangingFingerprint()619     public void testInstallApkChangingFingerprint() throws Exception {
620         try {
621             getDevice().executeShellCommand("setprop persist.pm.mock-upgrade true");
622             runPhase("testInstallApkChangingFingerprint");
623             getDevice().reboot();
624             runPhase("testInstallApkChangingFingerprint_VerifyAborted");
625         } finally {
626             getDevice().executeShellCommand("setprop persist.pm.mock-upgrade false");
627         }
628     }
629 
630     @Test
631     @LargeTest
testInstallStagedNoHashtreeApex()632     public void testInstallStagedNoHashtreeApex() throws Exception {
633         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
634 
635         runPhase("testInstallStagedNoHashtreeApex_Commit");
636         getDevice().reboot();
637         runPhase("testInstallStagedNoHashtreeApex_VerifyPostReboot");
638     }
639 
640     /**
641      * Should fail to verify apex targeting older dev sdk
642      */
643     @Test
testApexTargetingOldDevSdkFailsVerification()644     public void testApexTargetingOldDevSdkFailsVerification() throws Exception {
645         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
646 
647         runPhase("testApexTargetingOldDevSdkFailsVerification");
648     }
649 
650     /**
651      * Apex should fail to install if apk-in-apex fails to get scanned
652      */
653     @Test
654     @LargeTest
testApexFailsToInstallIfApkInApexFailsToScan()655     public void testApexFailsToInstallIfApkInApexFailsToScan() throws Exception {
656         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
657 
658         runPhase("testApexFailsToInstallIfApkInApexFailsToScan_Commit");
659         getDevice().reboot();
660         runPhase("testApexFailsToInstallIfApkInApexFailsToScan_VerifyPostReboot");
661     }
662 
663     @Test
testCorruptedApexFailsVerification_b146895998()664     public void testCorruptedApexFailsVerification_b146895998() throws Exception {
665         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
666 
667         runPhase("testCorruptedApexFailsVerification_b146895998");
668     }
669 
670     /**
671      * Should fail to pass apk signature check
672      */
673     @Test
testApexWithUnsignedApkFailsVerification()674     public void testApexWithUnsignedApkFailsVerification() throws Exception {
675         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
676 
677         runPhase("testApexWithUnsignedApkFailsVerification");
678     }
679 
680     /**
681      * Should fail to verify apex signed payload with a different key
682      */
683     @Test
testApexSignPayloadWithDifferentKeyFailsVerification()684     public void testApexSignPayloadWithDifferentKeyFailsVerification() throws Exception {
685         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
686 
687         runPhase("testApexSignPayloadWithDifferentKeyFailsVerification");
688     }
689 
690     /**
691      * Should fail to verify apex with unsigned payload
692      */
693     @Test
testApexWithUnsignedPayloadFailsVerification()694     public void testApexWithUnsignedPayloadFailsVerification() throws Exception {
695         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
696 
697         runPhase("testApexWithUnsignedPayloadFailsVerification");
698     }
699 
700     @Test
701     @LargeTest
testApexSetsUpdatedSystemAppFlag()702     public void testApexSetsUpdatedSystemAppFlag() throws Exception {
703         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
704 
705         runPhase("testApexSetsUpdatedSystemAppFlag_preUpdate");
706         installV2Apex();
707         runPhase("testApexSetsUpdatedSystemAppFlag_postUpdate");
708     }
709 
710     /**
711      * Test non-priv apps cannot access /data/app-staging folder contents
712      */
713     @Test
testAppStagingDirCannotBeReadByNonPrivApps()714     public void testAppStagingDirCannotBeReadByNonPrivApps() throws Exception {
715         runPhase("testAppStagingDirCannotBeReadByNonPrivApps");
716     }
717 
718     @Test
719     @LargeTest
testUpdatedApexFromDataApexActiveCanBePulled()720     public void testUpdatedApexFromDataApexActiveCanBePulled() throws Exception {
721         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
722 
723         installV2Apex();
724 
725         final ITestDevice.ApexInfo shimApex = mHostUtils.getShimApex().orElseThrow(
726                 () -> new AssertionError("Can't find " + SHIM_APEX_PACKAGE_NAME)
727         );
728 
729         assertThat(shimApex.sourceDir).startsWith("/data/apex/active");
730         assertThat(getDevice().pullFile(shimApex.sourceDir)).isNotNull();
731     }
732 
733     @Test
testApexInfoList()734     public void testApexInfoList() throws Exception {
735         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
736 
737         // Check that content of /apex/apex-info-list.xml matches output of
738         // `adb shell pm list packages --apex-only --show-versioncode -f`.
739         List<ApexInfo> apexInfoList = readApexInfoList();
740         Set<ITestDevice.ApexInfo> activeApexes = getDevice().getActiveApexes();
741         assertThat(apexInfoList.size()).isEqualTo(activeApexes.size());
742         for (ITestDevice.ApexInfo apex : activeApexes) {
743             // Note: we can't assert equality of the apex.name and apexInfo.getModuleName() since
744             // they represent different concepts (the former is package name, while latter is apex
745             // module name)
746             List<ApexInfo> temp =
747                     apexInfoList.stream()
748                             .filter(a -> a.getModulePath().equals(apex.sourceDir))
749                             .collect(Collectors.toList());
750             assertThat(temp).hasSize(1);
751             ApexInfo apexInfo = temp.get(0);
752             assertThat(apexInfo.getModulePath()).isEqualTo(apex.sourceDir);
753             assertThat(apexInfo.getVersionCode()).isEqualTo(apex.versionCode);
754             assertThat(apexInfo.getIsActive()).isTrue();
755             assertThat(apexInfo.getLastUpdateMillis()).isGreaterThan(0);
756         }
757     }
758 
759     @Test
testApexInfoListAfterUpdate()760     public void testApexInfoListAfterUpdate() throws Exception {
761         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
762 
763         ApexInfo shimBeforeUpdate = getShimApexInfo();
764 
765         installV2Apex();
766 
767         List<ApexInfo> shimApexInfo =
768                 readApexInfoList().stream()
769                         .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME))
770                         .collect(Collectors.toList());
771 
772         assertThat(shimApexInfo).hasSize(2);
773 
774         ApexInfo factoryShimApexInfo =
775                 shimApexInfo.stream()
776                         .filter(ApexInfo::getIsFactory)
777                         .findAny()
778                         .orElseThrow(() ->
779                                 new AssertionError(
780                                         "No factory version of " + SHIM_APEX_PACKAGE_NAME
781                                                 + " found in /apex/apex-info-list.xml"));
782         assertThat(factoryShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME);
783         assertThat(factoryShimApexInfo.getIsActive()).isFalse();
784         assertThat(factoryShimApexInfo.getIsFactory()).isTrue();
785         assertThat(factoryShimApexInfo.getVersionCode()).isEqualTo(1);
786         assertThat(factoryShimApexInfo.getModulePath())
787                 .isEqualTo(factoryShimApexInfo.getPreinstalledModulePath());
788         assertThat(factoryShimApexInfo.getLastUpdateMillis())
789                 .isEqualTo(shimBeforeUpdate.getLastUpdateMillis());
790 
791         ApexInfo activeShimApexInfo =
792                 shimApexInfo.stream()
793                         .filter(ApexInfo::getIsActive)
794                         .findAny()
795                         .orElseThrow(() ->
796                                 new AssertionError(
797                                         "No active version of " + SHIM_APEX_PACKAGE_NAME
798                                                 + " found in /apex/apex-info-list.xml"));
799         assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME);
800         assertThat(activeShimApexInfo.getIsActive()).isTrue();
801         assertThat(activeShimApexInfo.getIsFactory()).isFalse();
802         assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(2);
803         assertThat(activeShimApexInfo.getPreinstalledModulePath())
804                 .isEqualTo(factoryShimApexInfo.getModulePath());
805         assertThat(activeShimApexInfo.getLastUpdateMillis())
806                 .isNotEqualTo(shimBeforeUpdate.getLastUpdateMillis());
807     }
808 
809     @Test
810     @LargeTest
testRebootlessUpdate()811     public void testRebootlessUpdate() throws Exception {
812         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
813 
814         runPhase("testRebootlessUpdate");
815         ApexInfo activeShimApexInfo = getActiveShimApexInfo();
816         assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME);
817         assertThat(activeShimApexInfo.getIsActive()).isTrue();
818         assertThat(activeShimApexInfo.getIsFactory()).isFalse();
819         assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(2);
820     }
821 
822     @Test
testRebootlessUpdate_fromV2ToV3_sameBoot()823     public void testRebootlessUpdate_fromV2ToV3_sameBoot() throws Exception {
824         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
825 
826         runPhase("testRebootlessUpdate");
827         runPhase("testRebootlessUpdate_installV3");
828         ApexInfo activeShimApexInfo = getActiveShimApexInfo();
829         assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME);
830         assertThat(activeShimApexInfo.getIsActive()).isTrue();
831         assertThat(activeShimApexInfo.getIsFactory()).isFalse();
832         assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(3);
833     }
834 
835     @Test
836     @LargeTest
testRebootlessUpdate_fromV2ToV3_rebootInBetween()837     public void testRebootlessUpdate_fromV2ToV3_rebootInBetween() throws Exception {
838         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
839 
840         runPhase("testRebootlessUpdate");
841         getDevice().reboot();
842         runPhase("testRebootlessUpdate_installV3");
843         ApexInfo activeShimApexInfo = getActiveShimApexInfo();
844         assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME);
845         assertThat(activeShimApexInfo.getIsActive()).isTrue();
846         assertThat(activeShimApexInfo.getIsFactory()).isFalse();
847         assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(3);
848     }
849 
850     @Test
851     @LargeTest
testRebootlessUpdate_downgrage_fails()852     public void testRebootlessUpdate_downgrage_fails() throws Exception {
853         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
854 
855         runPhase("testRebootlessUpdate_installV3");
856         runPhase("testRebootlessUpdate_downgradeToV2_fails");
857     }
858 
859     @Test
testRebootlessUpdate_noPermission_fails()860     public void testRebootlessUpdate_noPermission_fails() throws Exception {
861         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
862 
863         runPhase("testRebootlessUpdate_noPermission_fails");
864     }
865 
866     @Test
testRebootlessUpdate_noPreInstalledApex_fails()867     public void testRebootlessUpdate_noPreInstalledApex_fails() throws Exception {
868         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
869 
870         runPhase("testRebootlessUpdate_noPreInstalledApex_fails");
871     }
872 
873     @Test
testRebootlessUpdate_unsignedPayload_fails()874     public void testRebootlessUpdate_unsignedPayload_fails() throws Exception {
875         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
876 
877         runPhase("testRebootlessUpdate_unsignedPayload_fails");
878     }
879 
880     @Test
testRebootlessUpdate_payloadSignedWithDifferentKey_fails()881     public void testRebootlessUpdate_payloadSignedWithDifferentKey_fails() throws Exception {
882         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
883 
884         runPhase("testRebootlessUpdate_payloadSignedWithDifferentKey_fails");
885     }
886 
887     @Test
testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails()888     public void testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails()
889             throws Exception {
890         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
891 
892         runPhase("testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails");
893     }
894 
895     @Test
testRebootlessUpdate_outerContainerUnsigned_fails()896     public void testRebootlessUpdate_outerContainerUnsigned_fails() throws Exception {
897         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
898 
899         runPhase("testRebootlessUpdate_outerContainerUnsigned_fails");
900     }
901 
902     @Test
testRebootlessUpdate_targetsOlderSdk_fails()903     public void testRebootlessUpdate_targetsOlderSdk_fails() throws Exception {
904         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
905 
906         runPhase("testRebootlessUpdate_targetsOlderSdk_fails");
907     }
908 
readApexInfoList()909     private List<ApexInfo> readApexInfoList() throws Exception {
910         File file = getDevice().pullFile("/apex/apex-info-list.xml");
911         try (FileInputStream stream = new FileInputStream(file)) {
912             return XmlParser.readApexInfoList(stream).getApexInfo();
913         }
914     }
915 
getShimApexInfo()916     private ApexInfo getShimApexInfo() throws Exception {
917         List<ApexInfo> temp =
918                 readApexInfoList().stream()
919                             .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME))
920                             .collect(Collectors.toList());
921         assertThat(temp).hasSize(1);
922         return temp.get(0);
923     }
924 
getActiveShimApexInfo()925     private ApexInfo getActiveShimApexInfo() throws Exception {
926         return readApexInfoList().stream()
927                     .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME))
928                     .filter(ApexInfo::getIsActive)
929                     .findAny()
930                     .orElseThrow(() ->
931                             new AssertionError(
932                                     "No active version of " + SHIM_APEX_PACKAGE_NAME
933                                             + " found in /apex/apex-info-list.xml"));
934     }
935 
936     /**
937      * Store the component name of the default launcher. This value will be used to reset the
938      * default launcher to its correct component upon test completion.
939      */
storeDefaultLauncher()940     private void storeDefaultLauncher() throws DeviceNotAvailableException {
941         final String PREFIX = "Launcher: ComponentInfo{";
942         final String POSTFIX = "}";
943         for (String s : getDevice().executeShellCommand("cmd shortcut get-default-launcher")
944                 .split("\n")) {
945             if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) {
946                 mDefaultLauncher = s.substring(PREFIX.length(), s.length() - POSTFIX.length());
947             }
948         }
949     }
950 
951     /**
952      * Set the default launcher to a given component.
953      * If set to the broadcast receiver component of this test app, this will allow the test app to
954      * receive SESSION_COMMITTED broadcasts.
955      */
setDefaultLauncher(String launcherComponent)956     private void setDefaultLauncher(String launcherComponent) throws DeviceNotAvailableException {
957         assertThat(launcherComponent).isNotEmpty();
958         int user = getDevice().getCurrentUser();
959         getDevice().executeShellCommand(
960                 "cmd package set-home-activity --user " + user + " " + launcherComponent);
961     }
962 
963     private static final class FailedTestLogHook extends TestWatcher {
964 
965         private final BaseHostJUnit4Test mInstance;
966         private String mStagedSessionsBeforeTest;
967 
FailedTestLogHook(BaseHostJUnit4Test instance)968         private FailedTestLogHook(BaseHostJUnit4Test instance) {
969             this.mInstance = instance;
970         }
971 
972         @Override
failed(Throwable e, Description description)973         protected void failed(Throwable e, Description description) {
974             String stagedSessionsAfterTest = getStagedSessions();
975             Log.e(TAG, "Test " + description + " failed.\n"
976                     + "Staged sessions before test started:\n" + mStagedSessionsBeforeTest + "\n"
977                     + "Staged sessions after test failed:\n" + stagedSessionsAfterTest);
978         }
979 
980         @Override
starting(Description description)981         protected void starting(Description description) {
982             mStagedSessionsBeforeTest = getStagedSessions();
983         }
984 
getStagedSessions()985         private String getStagedSessions() {
986             try {
987                 return mInstance.getDevice().executeShellV2Command("pm get-stagedsessions").getStdout();
988             } catch (DeviceNotAvailableException e) {
989                 Log.e(TAG, e);
990                 return "Failed to get staged sessions";
991             }
992         }
993     }
994 
isDebuggable()995     private boolean isDebuggable() throws Exception {
996         return getDevice().getIntProperty("ro.debuggable", 0) == 1;
997     }
998 }
999