• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.odsign;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.truth.Truth.assertWithMessage;
21 
22 import com.android.tradefed.invoker.TestInformation;
23 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
24 import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
25 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
26 import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
27 import com.android.tradefed.util.CommandResult;
28 
29 import org.junit.After;
30 import org.junit.Before;
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 
34 import java.util.HashSet;
35 import java.util.Set;
36 
37 /**
38  * Test to check end-to-end odrefresh invocations, but without odsign and fs-verity involved.
39  */
40 @RunWith(DeviceJUnit4ClassRunner.class)
41 public class OdrefreshHostTest extends BaseHostJUnit4Test {
42     private OdsignTestUtils mTestUtils;
43     private DeviceState mDeviceState;
44 
45     @BeforeClassWithInfo
beforeClassWithDevice(TestInformation testInfo)46     public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
47         OdsignTestUtils testUtils = new OdsignTestUtils(testInfo);
48         testUtils.installTestApex();
49         testUtils.reboot();
50     }
51 
52     @AfterClassWithInfo
afterClassWithDevice(TestInformation testInfo)53     public static void afterClassWithDevice(TestInformation testInfo) throws Exception {
54         OdsignTestUtils testUtils = new OdsignTestUtils(testInfo);
55         testUtils.uninstallTestApex();
56         testUtils.reboot();
57     }
58 
59     @Before
setUp()60     public void setUp() throws Exception {
61         mTestUtils = new OdsignTestUtils(getTestInformation());
62         mDeviceState = new DeviceState(getTestInformation());
63         mDeviceState.backupArtifacts();
64     }
65 
66     @After
tearDown()67     public void tearDown() throws Exception {
68         mDeviceState.restore();
69     }
70 
71     @Test
verifyArtSamegradeUpdateTriggersCompilation()72     public void verifyArtSamegradeUpdateTriggersCompilation() throws Exception {
73         mDeviceState.simulateArtApexUpgrade();
74         long timeMs = mTestUtils.getCurrentTimeMs();
75         mTestUtils.runOdrefresh();
76 
77         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
78         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
79         mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
80     }
81 
82     @Test
verifyOtherApexSamegradeUpdateTriggersCompilation()83     public void verifyOtherApexSamegradeUpdateTriggersCompilation() throws Exception {
84         mDeviceState.simulateApexUpgrade();
85         long timeMs = mTestUtils.getCurrentTimeMs();
86         mTestUtils.runOdrefresh();
87 
88         mTestUtils.assertNotModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
89         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
90         mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
91     }
92 
93     @Test
verifyBootClasspathOtaTriggersCompilation()94     public void verifyBootClasspathOtaTriggersCompilation() throws Exception {
95         mDeviceState.simulateBootClasspathOta();
96         long timeMs = mTestUtils.getCurrentTimeMs();
97         mTestUtils.runOdrefresh();
98 
99         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
100         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
101         mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
102     }
103 
104     @Test
verifySystemServerOtaTriggersCompilation()105     public void verifySystemServerOtaTriggersCompilation() throws Exception {
106         mDeviceState.simulateSystemServerOta();
107         long timeMs = mTestUtils.getCurrentTimeMs();
108         mTestUtils.runOdrefresh();
109 
110         mTestUtils.assertNotModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
111         mTestUtils.assertNotModifiedAfter(
112                 mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
113         mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
114     }
115 
116     @Test
verifyMissingArtifactTriggersCompilation()117     public void verifyMissingArtifactTriggersCompilation() throws Exception {
118         Set<String> missingArtifacts = simulateMissingArtifacts();
119         Set<String> remainingArtifacts = new HashSet<>();
120         remainingArtifacts.addAll(mTestUtils.getExpectedPrimaryBootImage());
121         remainingArtifacts.addAll(mTestUtils.getExpectedBootImageMainlineExtension());
122         remainingArtifacts.addAll(mTestUtils.getSystemServerExpectedArtifacts());
123         remainingArtifacts.removeAll(missingArtifacts);
124 
125         mTestUtils.removeCompilationLogToAvoidBackoff();
126         long timeMs = mTestUtils.getCurrentTimeMs();
127         mTestUtils.runOdrefresh();
128 
129         mTestUtils.assertNotModifiedAfter(remainingArtifacts, timeMs);
130         mTestUtils.assertModifiedAfter(missingArtifacts, timeMs);
131     }
132 
133     @Test
verifyPhenotypeFlagChangeTriggersCompilation()134     public void verifyPhenotypeFlagChangeTriggersCompilation() throws Exception {
135         // Simulate that the flag value is initially empty.
136         mDeviceState.setPhenotypeFlag("odrefresh_test_toggle", null);
137 
138         long timeMs = mTestUtils.getCurrentTimeMs();
139         mTestUtils.runOdrefresh();
140 
141         mDeviceState.setPhenotypeFlag("odrefresh_test_toggle", "false");
142 
143         timeMs = mTestUtils.getCurrentTimeMs();
144         mTestUtils.runOdrefresh();
145 
146         // Artifacts should not be re-compiled.
147         mTestUtils.assertNotModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
148         mTestUtils.assertNotModifiedAfter(
149                 mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
150         mTestUtils.assertNotModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
151 
152         mDeviceState.setPhenotypeFlag("odrefresh_test_toggle", "true");
153 
154         timeMs = mTestUtils.getCurrentTimeMs();
155         mTestUtils.runOdrefresh();
156 
157         // Artifacts should be re-compiled.
158         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
159         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
160         mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
161 
162         // Run odrefresh again with the flag unchanged.
163         timeMs = mTestUtils.getCurrentTimeMs();
164         mTestUtils.runOdrefresh();
165 
166         // Artifacts should not be re-compiled.
167         mTestUtils.assertNotModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
168         mTestUtils.assertNotModifiedAfter(
169                 mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
170         mTestUtils.assertNotModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
171 
172         mDeviceState.setPhenotypeFlag("odrefresh_test_toggle", null);
173 
174         timeMs = mTestUtils.getCurrentTimeMs();
175         mTestUtils.runOdrefresh();
176 
177         // Artifacts should be re-compiled.
178         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
179         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
180         mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
181     }
182 
183     @Test
verifySystemServerCompilerFilterOverrideChangeTriggersCompilation()184     public void verifySystemServerCompilerFilterOverrideChangeTriggersCompilation()
185             throws Exception {
186         mDeviceState.setPhenotypeFlag("systemservercompilerfilter_override", null);
187 
188         long timeMs = mTestUtils.getCurrentTimeMs();
189         mTestUtils.runOdrefresh();
190 
191         // Artifacts should not be re-compiled.
192         mTestUtils.assertNotModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
193         mTestUtils.assertNotModifiedAfter(
194                 mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
195         mTestUtils.assertNotModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
196 
197         mDeviceState.setPhenotypeFlag("systemservercompilerfilter_override", "speed");
198 
199         timeMs = mTestUtils.getCurrentTimeMs();
200         mTestUtils.runOdrefresh();
201 
202         // Artifacts should be re-compiled.
203         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
204         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
205         mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
206 
207         // Run odrefresh again with the flag unchanged.
208         timeMs = mTestUtils.getCurrentTimeMs();
209         mTestUtils.runOdrefresh();
210 
211         // Artifacts should not be re-compiled.
212         mTestUtils.assertNotModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
213         mTestUtils.assertNotModifiedAfter(
214                 mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
215         mTestUtils.assertNotModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
216 
217         mDeviceState.setPhenotypeFlag("systemservercompilerfilter_override", "verify");
218 
219         timeMs = mTestUtils.getCurrentTimeMs();
220         mTestUtils.runOdrefresh();
221 
222         // Artifacts should be re-compiled.
223         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
224         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
225         mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
226     }
227 
228     @Test
verifySystemPropertyMismatchTriggersCompilation()229     public void verifySystemPropertyMismatchTriggersCompilation() throws Exception {
230         // Change a system property from empty to a value.
231         mDeviceState.setProperty("dalvik.vm.foo", "1");
232         long timeMs = mTestUtils.getCurrentTimeMs();
233         mTestUtils.runOdrefresh();
234 
235         // Artifacts should be re-compiled.
236         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
237         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
238         mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
239 
240         // Run again with the same value.
241         timeMs = mTestUtils.getCurrentTimeMs();
242         mTestUtils.runOdrefresh();
243 
244         // Artifacts should not be re-compiled.
245         mTestUtils.assertNotModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
246         mTestUtils.assertNotModifiedAfter(
247                 mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
248         mTestUtils.assertNotModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
249 
250         // Change the system property to another value.
251         mDeviceState.setProperty("dalvik.vm.foo", "2");
252         timeMs = mTestUtils.getCurrentTimeMs();
253         mTestUtils.runOdrefresh();
254 
255         // Artifacts should be re-compiled.
256         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
257         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
258         mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
259 
260         // Run again with the same value.
261         timeMs = mTestUtils.getCurrentTimeMs();
262         mTestUtils.runOdrefresh();
263 
264         // Artifacts should not be re-compiled.
265         mTestUtils.assertNotModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
266         mTestUtils.assertNotModifiedAfter(
267                 mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
268         mTestUtils.assertNotModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
269 
270         // Change the system property to empty.
271         mDeviceState.setProperty("dalvik.vm.foo", "");
272         timeMs = mTestUtils.getCurrentTimeMs();
273         mTestUtils.runOdrefresh();
274 
275         // Artifacts should be re-compiled.
276         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
277         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
278         mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
279 
280         // Run again with the same value.
281         timeMs = mTestUtils.getCurrentTimeMs();
282         mTestUtils.runOdrefresh();
283 
284         // Artifacts should not be re-compiled.
285         mTestUtils.assertNotModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
286         mTestUtils.assertNotModifiedAfter(
287                 mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
288         mTestUtils.assertNotModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
289     }
290 
291     @Test
verifyNoCompilationWhenCacheIsGood()292     public void verifyNoCompilationWhenCacheIsGood() throws Exception {
293         mTestUtils.removeCompilationLogToAvoidBackoff();
294         long timeMs = mTestUtils.getCurrentTimeMs();
295         mTestUtils.runOdrefresh();
296 
297         mTestUtils.assertNotModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
298         mTestUtils.assertNotModifiedAfter(
299                 mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
300         mTestUtils.assertNotModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
301     }
302 
303     @Test
verifyUnexpectedFilesAreCleanedUp()304     public void verifyUnexpectedFilesAreCleanedUp() throws Exception {
305         String unexpected = OdsignTestUtils.ART_APEX_DALVIK_CACHE_DIRNAME + "/unexpected";
306         getDevice().pushString("" /* contents */, unexpected);
307         mTestUtils.runOdrefresh();
308 
309         assertThat(getDevice().doesFileExist(unexpected)).isFalse();
310     }
311 
312     @Test
verifyCacheInfoOmitsIrrelevantApexes()313     public void verifyCacheInfoOmitsIrrelevantApexes() throws Exception {
314         String cacheInfo = getDevice().pullFileContents(OdsignTestUtils.CACHE_INFO_FILE);
315 
316         // cacheInfo should list all APEXes that have compilable JARs and
317         // none that do not.
318 
319         // This should always contain classpath JARs, that's the reason it exists.
320         assertThat(cacheInfo).contains("name=\"com.android.sdkext\"");
321 
322         // This should never contain classpath JARs, it's the native runtime.
323         assertThat(cacheInfo).doesNotContain("name=\"com.android.runtime\"");
324     }
325 
326     @Test
verifyCompilationOsMode()327     public void verifyCompilationOsMode() throws Exception {
328         try {
329             // In CompOS, dex2oat directly writes to the output dir. This is allowed on Microdroid
330             // but not allowed on Android, so we need to bypass the SELinux restriction.
331             mTestUtils.assertCommandSucceeds("setenforce 0");
332 
333             mTestUtils.removeCompilationLogToAvoidBackoff();
334             mDeviceState.simulateApexUpgrade();
335             long timeMs = mTestUtils.getCurrentTimeMs();
336             mTestUtils.runOdrefresh("--compilation-os-mode");
337 
338             mTestUtils.assertNotModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
339             mTestUtils.assertModifiedAfter(
340                     mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
341             mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
342 
343             String cacheInfo = getDevice().pullFileContents(OdsignTestUtils.CACHE_INFO_FILE);
344             assertThat(cacheInfo).contains("compilationOsMode=\"true\"");
345 
346             // Compilation OS does not write the compilation log to the host.
347             mTestUtils.removeCompilationLogToAvoidBackoff();
348 
349             // Simulate the odrefresh invocation on the next boot.
350             timeMs = mTestUtils.getCurrentTimeMs();
351             mTestUtils.runOdrefresh();
352 
353             // odrefresh should not re-compile anything.
354             mTestUtils.assertNotModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
355             mTestUtils.assertNotModifiedAfter(
356                     mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
357             mTestUtils.assertNotModifiedAfter(
358                     mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
359         } finally {
360             mTestUtils.assertCommandSucceeds("setenforce 1");
361         }
362     }
363 
364     @Test
verifyMinimalCompilation()365     public void verifyMinimalCompilation() throws Exception {
366         mTestUtils.removeCompilationLogToAvoidBackoff();
367         getDevice().executeShellV2Command(
368                 "rm -rf " + OdsignTestUtils.ART_APEX_DALVIK_CACHE_DIRNAME);
369         mTestUtils.runOdrefresh("--minimal");
370 
371         mTestUtils.restartZygote();
372 
373         // The minimal boot image should be loaded.
374         mTestUtils.verifyZygotesLoadedMinimalBootImage();
375 
376         // Running the command again should not overwrite the minimal boot image.
377         mTestUtils.removeCompilationLogToAvoidBackoff();
378         long timeMs = mTestUtils.getCurrentTimeMs();
379         mTestUtils.runOdrefresh("--minimal");
380 
381         Set<String> minimalZygoteArtifacts = mTestUtils.getExpectedMinimalBootImage();
382         mTestUtils.assertNotModifiedAfter(minimalZygoteArtifacts, timeMs);
383 
384         // A normal odrefresh invocation should replace the minimal boot image with a full one.
385         mTestUtils.removeCompilationLogToAvoidBackoff();
386         timeMs = mTestUtils.getCurrentTimeMs();
387         mTestUtils.runOdrefresh();
388 
389         for (String artifact : minimalZygoteArtifacts) {
390             assertWithMessage(
391                     String.format(
392                             "Artifact %s should be cleaned up while it still exists", artifact))
393                     .that(getDevice().doesFileExist(artifact))
394                     .isFalse();
395         }
396 
397         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
398         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
399     }
400 
401     @Test
verifyCompilationFailureBackoff()402     public void verifyCompilationFailureBackoff() throws Exception {
403         mDeviceState.makeDex2oatFail();
404         mDeviceState.simulateArtApexUpgrade();
405 
406         // Run odrefresh. It should encounter dex2oat failures.
407         long timeMs = mTestUtils.getCurrentTimeMs();
408         mTestUtils.runOdrefresh();
409 
410         // Artifacts don't exist because the compilation failed.
411         mTestUtils.assertModifiedAfter(Set.of(OdsignTestUtils.CACHE_INFO_FILE), timeMs);
412         mTestUtils.assertFilesNotExist(mTestUtils.getExpectedPrimaryBootImage());
413         mTestUtils.assertFilesNotExist(mTestUtils.getExpectedBootImageMainlineExtension());
414         mTestUtils.assertFilesNotExist(mTestUtils.getSystemServerExpectedArtifacts());
415 
416         // Run odrefresh again.
417         timeMs = mTestUtils.getCurrentTimeMs();
418         mTestUtils.runOdrefresh();
419 
420         // It should not retry.
421         mTestUtils.assertNotModifiedAfter(Set.of(OdsignTestUtils.CACHE_INFO_FILE), timeMs);
422 
423         // Simulate that the backoff time has passed.
424         mTestUtils.removeCompilationLogToAvoidBackoff();
425 
426         // Run odrefresh again.
427         timeMs = mTestUtils.getCurrentTimeMs();
428         mTestUtils.runOdrefresh();
429 
430         // Now it should retry.
431         mTestUtils.assertModifiedAfter(Set.of(OdsignTestUtils.CACHE_INFO_FILE), timeMs);
432     }
433 
434     /**
435      * Regression test of CVE-2021-39689 (b/206090748): if the device doesn't have the odsign
436      * security fix, there's a risk that the existing artifacts may be manipulated, and odsign will
437      * mistakenly sign them. Therefore, odrefresh should clear all artifacts and regenerate them.
438      * I.e., no matter the compilation succeeds or not, no existing artifacts should be left.
439      *
440      * On contrary, if the device has the odsign security fix, odrefresh should keep existing
441      * artifacts (see {@link #verifyMissingArtifactTriggersCompilation}).
442      */
443     @Test
verifyArtifactsClearedWhenNoPartialCompilation()444     public void verifyArtifactsClearedWhenNoPartialCompilation() throws Exception {
445         // Remove arbitrary system server artifacts to trigger compilation.
446         simulateMissingArtifacts();
447 
448         // The successful case.
449         mTestUtils.removeCompilationLogToAvoidBackoff();
450         long timeMs = mTestUtils.getCurrentTimeMs();
451         mTestUtils.runOdrefreshNoPartialCompilation();
452 
453         // Existing artifacts should be replaced with new ones.
454         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
455         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
456         mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
457 
458         // Remove arbitrary system server artifacts to trigger compilation again.
459         simulateMissingArtifacts();
460 
461         // The failed case.
462         mDeviceState.makeDex2oatFail();
463         mTestUtils.removeCompilationLogToAvoidBackoff();
464         timeMs = mTestUtils.getCurrentTimeMs();
465         mTestUtils.runOdrefreshNoPartialCompilation();
466 
467         // Existing artifacts should be gone.
468         mTestUtils.assertFilesNotExist(mTestUtils.getExpectedPrimaryBootImage());
469         mTestUtils.assertFilesNotExist(mTestUtils.getExpectedBootImageMainlineExtension());
470         mTestUtils.assertFilesNotExist(mTestUtils.getSystemServerExpectedArtifacts());
471     }
472 
473     /**
474      * If the compilation is skipped because a previous attempt partially failed, odrefresh should
475      * not clear existing artifacts.
476      */
477     @Test
verifyArtifactsKeptWhenCompilationSkippedNoPartialCompilation()478     public void verifyArtifactsKeptWhenCompilationSkippedNoPartialCompilation() throws Exception {
479         // Simulate that the compilation is partially failed.
480         mDeviceState.simulateBadSystemServerJar();
481         long timeMs = mTestUtils.getCurrentTimeMs();
482         mTestUtils.runOdrefreshNoPartialCompilation();
483 
484         // Verify the test setup: boot images are still generated.
485         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
486         mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
487 
488         // Rerun odrefresh. The compilation is skipped this time.
489         timeMs = mTestUtils.getCurrentTimeMs();
490         CommandResult result = mTestUtils.runOdrefreshNoPartialCompilation();
491 
492         // Existing artifacts should be kept.
493         mTestUtils.assertNotModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
494         mTestUtils.assertNotModifiedAfter(
495                 mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
496 
497         // Note that the existing artifacts may be manipulated (CVE-2021-39689). Make sure odrefresh
498         // returns `kOkay` rather than `kCompilationSuccess` or `kCompilationFailed`, so that odsign
499         // only verifies the artifacts but not sign them.
500         assertThat(result.getExitCode()).isEqualTo(0);
501     }
502 
simulateMissingArtifacts()503     private Set<String> simulateMissingArtifacts() throws Exception {
504         Set<String> missingArtifacts = new HashSet<>();
505         String sample = mTestUtils.getSystemServerExpectedArtifacts().iterator().next();
506         for (String extension : OdsignTestUtils.APP_ARTIFACT_EXTENSIONS) {
507             String artifact = OdsignTestUtils.replaceExtension(sample, extension);
508             getDevice().deleteFile(artifact);
509             missingArtifacts.add(artifact);
510         }
511         return missingArtifacts;
512     }
513 }
514