1 /* 2 * Copyright (C) 2022 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.server.art; 18 19 import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult; 20 import static com.android.server.art.testing.TestingUtils.deepEq; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static org.mockito.Mockito.any; 25 import static org.mockito.Mockito.anyInt; 26 import static org.mockito.Mockito.argThat; 27 import static org.mockito.Mockito.doAnswer; 28 import static org.mockito.Mockito.doReturn; 29 import static org.mockito.Mockito.eq; 30 import static org.mockito.Mockito.inOrder; 31 import static org.mockito.Mockito.isNull; 32 import static org.mockito.Mockito.lenient; 33 import static org.mockito.Mockito.mock; 34 import static org.mockito.Mockito.never; 35 import static org.mockito.Mockito.same; 36 import static org.mockito.Mockito.times; 37 import static org.mockito.Mockito.verify; 38 import static org.mockito.Mockito.when; 39 40 import android.os.Process; 41 import android.os.ServiceSpecificException; 42 import android.os.UserHandle; 43 44 import androidx.test.filters.SmallTest; 45 import androidx.test.runner.AndroidJUnit4; 46 47 import com.android.server.art.model.ArtFlags; 48 import com.android.server.art.model.DexoptParams; 49 import com.android.server.art.model.DexoptResult; 50 import com.android.server.art.proto.DexMetadataConfig; 51 import com.android.server.art.testing.TestingUtils; 52 53 import org.junit.Before; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.mockito.InOrder; 57 58 import java.nio.file.NoSuchFileException; 59 import java.util.ArrayList; 60 import java.util.List; 61 import java.util.concurrent.ForkJoinPool; 62 import java.util.concurrent.Future; 63 import java.util.concurrent.Semaphore; 64 import java.util.concurrent.TimeUnit; 65 import java.util.stream.Collectors; 66 import java.util.zip.ZipFile; 67 68 @SmallTest 69 @RunWith(AndroidJUnit4.class) 70 public class PrimaryDexopterTest extends PrimaryDexopterTestBase { 71 private final String mDexPath = "/somewhere/app/foo/base.apk"; 72 private final String mDmPath = "/somewhere/app/foo/base.dm"; 73 private final ProfilePath mRefProfile = 74 AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME, "primary"); 75 private final ProfilePath mPrebuiltProfile = AidlUtils.buildProfilePathForPrebuilt(mDexPath); 76 private final ProfilePath mDmProfile = AidlUtils.buildProfilePathForDm(mDexPath); 77 private final DexMetadataPath mDmFile = AidlUtils.buildDexMetadataPath(mDexPath); 78 private final OutputProfile mPublicOutputProfile = 79 AidlUtils.buildOutputProfileForPrimary(PKG_NAME, "primary", Process.SYSTEM_UID, 80 SHARED_GID, true /* isOtherReadable */, false /* isPreReboot */); 81 private final OutputProfile mPrivateOutputProfile = 82 AidlUtils.buildOutputProfileForPrimary(PKG_NAME, "primary", Process.SYSTEM_UID, 83 SHARED_GID, false /* isOtherReadable */, false /* isPreReboot */); 84 85 private final String mSplit0DexPath = "/somewhere/app/foo/split_0.apk"; 86 private final ProfilePath mSplit0RefProfile = 87 AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME, "split_0.split"); 88 89 private final int mDefaultDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER 90 | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION; 91 private final int mBetterOrSameDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER 92 | DexoptTrigger.COMPILER_FILTER_IS_SAME 93 | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION; 94 private final int mForceDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER 95 | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE 96 | DexoptTrigger.COMPILER_FILTER_IS_SAME | DexoptTrigger.COMPILER_FILTER_IS_WORSE 97 | DexoptTrigger.NEED_EXTRACTION; 98 99 private final MergeProfileOptions mMergeProfileOptions = new MergeProfileOptions(); 100 101 private final ArtdDexoptResult mArtdDexoptResult = 102 createArtdDexoptResult(false /* cancelled */); 103 104 private DexoptParams mDexoptParams = 105 new DexoptParams.Builder("install").setCompilerFilter("speed-profile").build(); 106 107 private PrimaryDexopter mPrimaryDexopter; 108 109 private List<ProfilePath> mUsedProfiles; 110 private List<String> mUsedEmbeddedProfiles; 111 112 @Before setUp()113 public void setUp() throws Exception { 114 super.setUp(); 115 116 // By default, none of the profiles are usable. 117 lenient().when(mArtd.isProfileUsable(any(), any())).thenReturn(false); 118 lenient() 119 .when(mArtd.copyAndRewriteProfile(any(), any(), any())) 120 .thenReturn(TestingUtils.createCopyAndRewriteProfileNoProfile()); 121 lenient() 122 .when(mArtd.copyAndRewriteEmbeddedProfile(any(), any())) 123 .thenReturn(TestingUtils.createCopyAndRewriteProfileNoProfile()); 124 125 // By default, no DM file exists. 126 lenient() 127 .when(mDexMetadataHelperInjector.openZipFile(any())) 128 .thenThrow(NoSuchFileException.class); 129 130 // By default, no artifacts exist. 131 lenient().when(mArtd.getArtifactsVisibility(any())).thenReturn(FileVisibility.NOT_FOUND); 132 133 // Dexopt is by default needed and successful. 134 lenient() 135 .when(mArtd.getDexoptNeeded(any(), any(), any(), any(), anyInt())) 136 .thenReturn(dexoptIsNeeded()); 137 lenient() 138 .when(mArtd.dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), 139 any(), any())) 140 .thenReturn(mArtdDexoptResult); 141 142 lenient() 143 .when(mArtd.createCancellationSignal()) 144 .thenReturn(mock(IArtdCancellationSignal.class)); 145 146 lenient() 147 .when(mArtd.getDexFileVisibility(mDexPath)) 148 .thenReturn(FileVisibility.OTHER_READABLE); 149 lenient() 150 .when(mArtd.getDexFileVisibility(mSplit0DexPath)) 151 .thenReturn(FileVisibility.OTHER_READABLE); 152 153 mPrimaryDexopter = 154 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 155 156 mUsedProfiles = new ArrayList<>(); 157 mUsedEmbeddedProfiles = new ArrayList<>(); 158 } 159 160 @Test testDexoptInputVdex()161 public void testDexoptInputVdex() throws Exception { 162 // null. 163 doReturn(dexoptIsNeeded(ArtifactsLocation.NONE_OR_ERROR)) 164 .when(mArtd) 165 .getDexoptNeeded(eq(mDexPath), eq("arm64"), any(), any(), anyInt()); 166 doReturn(mArtdDexoptResult) 167 .when(mArtd) 168 .dexopt(any(), eq(mDexPath), eq("arm64"), any(), any(), any(), isNull(), any(), 169 anyInt(), any(), any()); 170 171 // ArtifactsPath, isInDalvikCache=true. 172 doReturn(dexoptIsNeeded(ArtifactsLocation.DALVIK_CACHE)) 173 .when(mArtd) 174 .getDexoptNeeded(eq(mDexPath), eq("arm"), any(), any(), anyInt()); 175 doReturn(mArtdDexoptResult) 176 .when(mArtd) 177 .dexopt(any(), eq(mDexPath), eq("arm"), any(), any(), any(), 178 deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput( 179 mDexPath, "arm", true /* isInDalvikCache */))), 180 any(), anyInt(), any(), any()); 181 182 // ArtifactsPath, isInDalvikCache=false. 183 doReturn(dexoptIsNeeded(ArtifactsLocation.NEXT_TO_DEX)) 184 .when(mArtd) 185 .getDexoptNeeded(eq(mSplit0DexPath), eq("arm64"), any(), any(), anyInt()); 186 doReturn(mArtdDexoptResult) 187 .when(mArtd) 188 .dexopt(any(), eq(mSplit0DexPath), eq("arm64"), any(), any(), any(), 189 deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput( 190 mSplit0DexPath, "arm64", false /* isInDalvikCache */))), 191 any(), anyInt(), any(), any()); 192 193 // DexMetadataPath. 194 doReturn(dexoptIsNeeded(ArtifactsLocation.DM)) 195 .when(mArtd) 196 .getDexoptNeeded(eq(mSplit0DexPath), eq("arm"), any(), any(), anyInt()); 197 doReturn(mArtdDexoptResult) 198 .when(mArtd) 199 .dexopt(any(), eq(mSplit0DexPath), eq("arm"), any(), any(), any(), isNull(), any(), 200 anyInt(), any(), any()); 201 202 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 203 verifyStatusAllOk(results); 204 } 205 206 @Test testDexoptDm()207 public void testDexoptDm() throws Exception { 208 String dmPath = TestingUtils.createTempZipWithEntry("primary.vdex", new byte[0] /* data */); 209 doReturn(new ZipFile(dmPath)).when(mDexMetadataHelperInjector).openZipFile(mDmPath); 210 211 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 212 verifyStatusAllOk(results); 213 214 verify(mArtd, times(2)) 215 .dexopt(any(), eq(mDexPath), any(), any(), any(), any(), any(), deepEq(mDmFile), 216 anyInt(), 217 argThat(dexoptOptions 218 -> dexoptOptions.compilationReason.equals("install-dm")), 219 any()); 220 verify(mArtd, times(2)) 221 .dexopt(any(), eq(mSplit0DexPath), any(), any(), any(), any(), any(), isNull(), 222 anyInt(), 223 argThat(dexoptOptions -> dexoptOptions.compilationReason.equals("install")), 224 any()); 225 } 226 227 @Test testDexoptUsesRefProfile()228 public void testDexoptUsesRefProfile() throws Exception { 229 makeProfileUsable(mRefProfile); 230 when(mArtd.getProfileVisibility(deepEq(mRefProfile))) 231 .thenReturn(FileVisibility.NOT_OTHER_READABLE); 232 233 // Other profiles are also usable, but they shouldn't be used. 234 makeProfileUsable(mPrebuiltProfile); 235 makeProfileUsable(mDmProfile); 236 makeEmbeddedProfileUsable(mDexPath); 237 238 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 239 verifyStatusAllOk(results); 240 241 verify(mArtd).getDexoptNeeded( 242 eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger)); 243 checkDexoptWithProfile( 244 verify(mArtd), mDexPath, "arm64", mRefProfile, false /* isOtherReadable */); 245 246 verify(mArtd).getDexoptNeeded( 247 eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger)); 248 checkDexoptWithProfile( 249 verify(mArtd), mDexPath, "arm", mRefProfile, false /* isOtherReadable */); 250 251 // There is no profile for split 0, so it should fall back to "verify". 252 verify(mArtd).getDexoptNeeded( 253 eq(mSplit0DexPath), eq("arm64"), any(), eq("verify"), eq(mDefaultDexoptTrigger)); 254 checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm64", "verify"); 255 256 verify(mArtd).getDexoptNeeded( 257 eq(mSplit0DexPath), eq("arm"), any(), eq("verify"), eq(mDefaultDexoptTrigger)); 258 checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm", "verify"); 259 260 verifyProfileNotUsed(mPrebuiltProfile); 261 verifyProfileNotUsed(mDmProfile); 262 verifyEmbeddedProfileNotUsed(mDexPath); 263 } 264 265 @Test testDexoptUsesPublicRefProfile()266 public void testDexoptUsesPublicRefProfile() throws Exception { 267 // The ref profile is usable and public. 268 makeProfileUsable(mRefProfile); 269 when(mArtd.getProfileVisibility(deepEq(mRefProfile))) 270 .thenReturn(FileVisibility.OTHER_READABLE); 271 272 // Other profiles are also usable, but they shouldn't be used. 273 makeProfileUsable(mPrebuiltProfile); 274 makeProfileUsable(mDmProfile); 275 makeEmbeddedProfileUsable(mDexPath); 276 277 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 278 verifyStatusAllOk(results); 279 280 checkDexoptWithProfile( 281 verify(mArtd), mDexPath, "arm64", mRefProfile, true /* isOtherReadable */); 282 checkDexoptWithProfile( 283 verify(mArtd), mDexPath, "arm", mRefProfile, true /* isOtherReadable */); 284 285 verifyProfileNotUsed(mPrebuiltProfile); 286 verifyProfileNotUsed(mDmProfile); 287 verifyEmbeddedProfileNotUsed(mDexPath); 288 } 289 290 @Test testDexoptUsesPrebuiltProfile()291 public void testDexoptUsesPrebuiltProfile() throws Exception { 292 makeProfileUsable(mPrebuiltProfile); 293 makeProfileUsable(mDmProfile); 294 makeEmbeddedProfileUsable(mDexPath); 295 296 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 297 verifyStatusAllOk(results); 298 299 InOrder inOrder = inOrder(mArtd); 300 301 inOrder.verify(mArtd).copyAndRewriteProfile( 302 deepEq(mPrebuiltProfile), deepEq(mPublicOutputProfile), eq(mDexPath)); 303 304 checkDexoptWithProfile(inOrder.verify(mArtd), mDexPath, "arm64", 305 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 306 true /* isOtherReadable */); 307 checkDexoptWithProfile(inOrder.verify(mArtd), mDexPath, "arm", 308 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 309 true /* isOtherReadable */); 310 311 inOrder.verify(mArtd).commitTmpProfile(deepEq(mPublicOutputProfile.profilePath)); 312 313 verifyProfileNotUsed(mRefProfile); 314 verifyProfileNotUsed(mDmProfile); 315 verifyEmbeddedProfileNotUsed(mDexPath); 316 } 317 checkDexoptMergesProfiles()318 private void checkDexoptMergesProfiles() throws Exception { 319 setPackageInstalledForUserIds(0, 2); 320 321 when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(true); 322 323 makeProfileUsable(mRefProfile); 324 when(mArtd.getProfileVisibility(deepEq(mRefProfile))) 325 .thenReturn(FileVisibility.OTHER_READABLE); 326 327 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 328 verifyStatusAllOk(results); 329 330 InOrder inOrder = inOrder(mArtd); 331 332 inOrder.verify(mArtd).mergeProfiles( 333 deepEq(List.of(AidlUtils.buildProfilePathForPrimaryCur( 334 0 /* userId */, PKG_NAME, "primary"), 335 AidlUtils.buildProfilePathForPrimaryCur( 336 2 /* userId */, PKG_NAME, "primary"))), 337 deepEq(mRefProfile), deepEq(mPrivateOutputProfile), deepEq(List.of(mDexPath)), 338 deepEq(mMergeProfileOptions)); 339 340 // It should use `mBetterOrSameDexoptTrigger` and the merged profile for both ISAs. 341 inOrder.verify(mArtd).getDexoptNeeded(eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), 342 eq(mBetterOrSameDexoptTrigger)); 343 checkDexoptWithProfile(inOrder.verify(mArtd), mDexPath, "arm64", 344 ProfilePath.tmpProfilePath(mPrivateOutputProfile.profilePath), 345 false /* isOtherReadable */); 346 347 inOrder.verify(mArtd).getDexoptNeeded(eq(mDexPath), eq("arm"), any(), eq("speed-profile"), 348 eq(mBetterOrSameDexoptTrigger)); 349 checkDexoptWithProfile(inOrder.verify(mArtd), mDexPath, "arm", 350 ProfilePath.tmpProfilePath(mPrivateOutputProfile.profilePath), 351 false /* isOtherReadable */); 352 353 inOrder.verify(mArtd).commitTmpProfile(deepEq(mPrivateOutputProfile.profilePath)); 354 } 355 356 @Test testDexoptMergesProfiles()357 public void testDexoptMergesProfiles() throws Exception { 358 checkDexoptMergesProfiles(); 359 360 verify(mArtd).deleteProfile(deepEq( 361 AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME, "primary"))); 362 verify(mArtd).deleteProfile(deepEq( 363 AidlUtils.buildProfilePathForPrimaryCur(2 /* userId */, PKG_NAME, "primary"))); 364 } 365 366 @Test testDexoptMergesProfilesPreReboot()367 public void testDexoptMergesProfilesPreReboot() throws Exception { 368 when(mInjector.isPreReboot()).thenReturn(true); 369 mPublicOutputProfile.profilePath.finalPath.getForPrimary().isPreReboot = true; 370 mPrivateOutputProfile.profilePath.finalPath.getForPrimary().isPreReboot = true; 371 372 checkDexoptMergesProfiles(); 373 374 verify(mArtd, never()).deleteProfile(any()); 375 } 376 377 @Test testDexoptMergesProfilesMergeFailed()378 public void testDexoptMergesProfilesMergeFailed() throws Exception { 379 setPackageInstalledForUserIds(0, 2); 380 381 when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(false); 382 383 makeProfileUsable(mRefProfile); 384 when(mArtd.getProfileVisibility(deepEq(mRefProfile))) 385 .thenReturn(FileVisibility.OTHER_READABLE); 386 387 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 388 verifyStatusAllOk(results); 389 390 // It should still use "speed-profile", but with the existing reference profile only. 391 verify(mArtd).getDexoptNeeded( 392 eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger)); 393 checkDexoptWithProfile( 394 verify(mArtd), mDexPath, "arm64", mRefProfile, true /* isOtherReadable */); 395 396 verify(mArtd).getDexoptNeeded( 397 eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger)); 398 checkDexoptWithProfile( 399 verify(mArtd), mDexPath, "arm", mRefProfile, true /* isOtherReadable */); 400 401 verify(mArtd, never()).deleteProfile(any()); 402 verify(mArtd, never()).commitTmpProfile(any()); 403 } 404 405 @Test testDexoptMergesProfilesForceMerge()406 public void testDexoptMergesProfilesForceMerge() throws Exception { 407 mDexoptParams = mDexoptParams.toBuilder() 408 .setFlags(ArtFlags.FLAG_FORCE_MERGE_PROFILE, 409 ArtFlags.FLAG_FORCE_MERGE_PROFILE) 410 .build(); 411 mPrimaryDexopter = 412 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 413 414 setPackageInstalledForUserIds(0, 2); 415 416 mMergeProfileOptions.forceMerge = true; 417 when(mArtd.mergeProfiles(any(), any(), any(), any(), deepEq(mMergeProfileOptions))) 418 .thenReturn(true); 419 420 makeProfileUsable(mRefProfile); 421 when(mArtd.getProfileVisibility(deepEq(mRefProfile))) 422 .thenReturn(FileVisibility.OTHER_READABLE); 423 424 mPrimaryDexopter.dexopt(); 425 } 426 checkDexoptUsesDmProfile()427 private void checkDexoptUsesDmProfile() throws Exception { 428 makeProfileUsable(mDmProfile); 429 makeEmbeddedProfileUsable(mDexPath); 430 431 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 432 verifyStatusAllOk(results); 433 434 verify(mArtd).copyAndRewriteProfile( 435 deepEq(mDmProfile), deepEq(mPublicOutputProfile), eq(mDexPath)); 436 437 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm64", 438 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 439 true /* isOtherReadable */); 440 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm", 441 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 442 true /* isOtherReadable */); 443 444 verifyProfileNotUsed(mRefProfile); 445 verifyProfileNotUsed(mPrebuiltProfile); 446 verifyEmbeddedProfileNotUsed(mDexPath); 447 } 448 449 @Test testDexoptUsesDmProfile()450 public void testDexoptUsesDmProfile() throws Exception { 451 checkDexoptUsesDmProfile(); 452 } 453 454 @Test testDexoptUsesDmProfilePreReboot()455 public void testDexoptUsesDmProfilePreReboot() throws Exception { 456 when(mInjector.isPreReboot()).thenReturn(true); 457 mPublicOutputProfile.profilePath.finalPath.getForPrimary().isPreReboot = true; 458 mPrivateOutputProfile.profilePath.finalPath.getForPrimary().isPreReboot = true; 459 460 checkDexoptUsesDmProfile(); 461 } 462 checkDexoptUsesEmbeddedProfile()463 private void checkDexoptUsesEmbeddedProfile() throws Exception { 464 makeEmbeddedProfileUsable(mDexPath); 465 466 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 467 verifyStatusAllOk(results); 468 469 verify(mArtd).copyAndRewriteProfile( 470 deepEq(mDmProfile), deepEq(mPublicOutputProfile), eq(mDexPath)); 471 472 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm64", 473 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 474 true /* isOtherReadable */); 475 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm", 476 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 477 true /* isOtherReadable */); 478 479 verifyProfileNotUsed(mRefProfile); 480 verifyProfileNotUsed(mPrebuiltProfile); 481 verifyProfileNotUsed(mDmProfile); 482 } 483 484 @Test testDexoptUsesEmbeddedProfileNoDm()485 public void testDexoptUsesEmbeddedProfileNoDm() throws Exception { 486 checkDexoptUsesEmbeddedProfile(); 487 } 488 489 @Test testDexoptUsesEmbeddedProfileDmNoConfig()490 public void testDexoptUsesEmbeddedProfileDmNoConfig() throws Exception { 491 String dmPath = TestingUtils.createTempZipWithEntry("primary.vdex", new byte[0] /* data */); 492 doReturn(new ZipFile(dmPath)).when(mDexMetadataHelperInjector).openZipFile(mDmPath); 493 checkDexoptUsesEmbeddedProfile(); 494 } 495 496 @Test testDexoptUsesEmbeddedProfileDmEmptyConfig()497 public void testDexoptUsesEmbeddedProfileDmEmptyConfig() throws Exception { 498 String dmPath = TestingUtils.createTempZipWithEntry("config.pb", new byte[0] /* data */); 499 doReturn(new ZipFile(dmPath)).when(mDexMetadataHelperInjector).openZipFile(mDmPath); 500 checkDexoptUsesEmbeddedProfile(); 501 } 502 503 @Test testDexoptUsesEmbeddedProfileDmBadConfig()504 public void testDexoptUsesEmbeddedProfileDmBadConfig() throws Exception { 505 String dmPath = TestingUtils.createTempZipWithEntry( 506 "config.pb", new byte[] {0x42, 0x43, 0x44} /* data */); 507 doReturn(new ZipFile(dmPath)).when(mDexMetadataHelperInjector).openZipFile(mDmPath); 508 checkDexoptUsesEmbeddedProfile(); 509 } 510 511 @Test testDexoptUsesEmbeddedProfileDmDisableEmbeddedProfile()512 public void testDexoptUsesEmbeddedProfileDmDisableEmbeddedProfile() throws Exception { 513 var config = DexMetadataConfig.newBuilder().setEnableEmbeddedProfile(false).build(); 514 String dmPath = TestingUtils.createTempZipWithEntry("config.pb", config.toByteArray()); 515 doReturn(new ZipFile(dmPath)).when(mDexMetadataHelperInjector).openZipFile(mDmPath); 516 517 makeEmbeddedProfileUsable(mDexPath); 518 519 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 520 verifyStatusAllOk(results); 521 522 checkDexoptWithNoProfile(verify(mArtd), mDexPath, "arm64", "verify"); 523 checkDexoptWithNoProfile(verify(mArtd), mDexPath, "arm", "verify"); 524 525 verifyEmbeddedProfileNotUsed(mDexPath); 526 } 527 528 @Test testDexoptUsesEmbeddedProfileNoEmbeddedProfile()529 public void testDexoptUsesEmbeddedProfileNoEmbeddedProfile() throws Exception { 530 var config = DexMetadataConfig.newBuilder().setEnableEmbeddedProfile(true).build(); 531 String dmPath = TestingUtils.createTempZipWithEntry("config.pb", config.toByteArray()); 532 doReturn(new ZipFile(dmPath)).when(mDexMetadataHelperInjector).openZipFile(mDmPath); 533 534 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 535 verifyStatusAllOk(results); 536 537 checkDexoptWithNoProfile(verify(mArtd), mDexPath, "arm64", "verify"); 538 checkDexoptWithNoProfile(verify(mArtd), mDexPath, "arm", "verify"); 539 540 verifyEmbeddedProfileNotUsed(mDexPath); 541 } 542 543 @Test testDexoptUsesEmbeddedProfilePreReboot()544 public void testDexoptUsesEmbeddedProfilePreReboot() throws Exception { 545 when(mInjector.isPreReboot()).thenReturn(true); 546 mPublicOutputProfile.profilePath.finalPath.getForPrimary().isPreReboot = true; 547 mPrivateOutputProfile.profilePath.finalPath.getForPrimary().isPreReboot = true; 548 549 checkDexoptUsesEmbeddedProfile(); 550 } 551 552 @Test testDexoptExternalProfileErrors()553 public void testDexoptExternalProfileErrors() throws Exception { 554 // Having no profile should not be reported. 555 // Having a bad profile should be reported. 556 lenient() 557 .when(mArtd.copyAndRewriteProfile(deepEq(mDmProfile), any(), any())) 558 .thenReturn(TestingUtils.createCopyAndRewriteProfileBadProfile("error_msg")); 559 560 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 561 562 assertThat(results.get(0).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED); 563 assertThat(results.get(0).getExtendedStatusFlags() 564 & DexoptResult.EXTENDED_BAD_EXTERNAL_PROFILE) 565 .isNotEqualTo(0); 566 assertThat(results.get(0).getExternalProfileErrors()).containsExactly("error_msg"); 567 assertThat(results.get(1).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED); 568 assertThat(results.get(1).getExtendedStatusFlags() 569 & DexoptResult.EXTENDED_BAD_EXTERNAL_PROFILE) 570 .isNotEqualTo(0); 571 assertThat(results.get(1).getExternalProfileErrors()).containsExactly("error_msg"); 572 } 573 574 @Test testDexoptDeletesProfileOnFailure()575 public void testDexoptDeletesProfileOnFailure() throws Exception { 576 makeProfileUsable(mDmProfile); 577 578 when(mArtd.dexopt(any(), eq(mDexPath), any(), any(), any(), any(), any(), any(), anyInt(), 579 any(), any())) 580 .thenThrow(ServiceSpecificException.class); 581 582 mPrimaryDexopter.dexopt(); 583 584 verify(mArtd).deleteProfile( 585 deepEq(ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath))); 586 verify(mArtd, never()).commitTmpProfile(deepEq(mPublicOutputProfile.profilePath)); 587 } 588 589 @Test testDexoptNeedsToBeShared()590 public void testDexoptNeedsToBeShared() throws Exception { 591 when(mDexUseManager.isPrimaryDexUsedByOtherApps(eq(PKG_NAME), eq(mDexPath))) 592 .thenReturn(true); 593 when(mDexUseManager.isPrimaryDexUsedByOtherApps(eq(PKG_NAME), eq(mSplit0DexPath))) 594 .thenReturn(true); 595 596 // The ref profile is usable but shouldn't be used. 597 makeProfileUsable(mRefProfile); 598 599 makeProfileUsable(mDmProfile); 600 601 // The existing artifacts are private. 602 when(mArtd.getArtifactsVisibility( 603 argThat(artifactsPath -> artifactsPath.dexPath == mDexPath))) 604 .thenReturn(FileVisibility.NOT_OTHER_READABLE); 605 606 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 607 verifyStatusAllOk(results); 608 609 verify(mArtd).copyAndRewriteProfile( 610 deepEq(mDmProfile), deepEq(mPublicOutputProfile), eq(mDexPath)); 611 612 // It should re-compile anyway. 613 verify(mArtd).getDexoptNeeded( 614 eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mForceDexoptTrigger)); 615 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm64", 616 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 617 true /* isOtherReadable */); 618 619 verify(mArtd).getDexoptNeeded( 620 eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mForceDexoptTrigger)); 621 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm", 622 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 623 true /* isOtherReadable */); 624 625 checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm64", "speed"); 626 checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm", "speed"); 627 628 verifyProfileNotUsed(mRefProfile); 629 verifyProfileNotUsed(mPrebuiltProfile); 630 } 631 632 @Test testDexoptNeedsToBeSharedArtifactsArePublic()633 public void testDexoptNeedsToBeSharedArtifactsArePublic() throws Exception { 634 // Same setup as above, but the existing artifacts are public. 635 when(mDexUseManager.isPrimaryDexUsedByOtherApps(eq(PKG_NAME), eq(mDexPath))) 636 .thenReturn(true); 637 when(mDexUseManager.isPrimaryDexUsedByOtherApps(eq(PKG_NAME), eq(mSplit0DexPath))) 638 .thenReturn(true); 639 640 makeProfileUsable(mRefProfile); 641 makeProfileUsable(mDmProfile); 642 when(mArtd.getArtifactsVisibility( 643 argThat(artifactsPath -> artifactsPath.dexPath == mDexPath))) 644 .thenReturn(FileVisibility.OTHER_READABLE); 645 646 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 647 verifyStatusAllOk(results); 648 649 // It should use the default dexopt trigger. 650 verify(mArtd).getDexoptNeeded( 651 eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger)); 652 verify(mArtd).getDexoptNeeded( 653 eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger)); 654 } 655 656 @Test testDexoptUsesProfileForSplit()657 public void testDexoptUsesProfileForSplit() throws Exception { 658 makeProfileUsable(mSplit0RefProfile); 659 when(mArtd.getProfileVisibility(deepEq(mSplit0RefProfile))) 660 .thenReturn(FileVisibility.NOT_OTHER_READABLE); 661 662 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 663 verifyStatusAllOk(results); 664 665 verify(mArtd).getDexoptNeeded(eq(mSplit0DexPath), eq("arm64"), any(), eq("speed-profile"), 666 eq(mDefaultDexoptTrigger)); 667 checkDexoptWithProfile(verify(mArtd), mSplit0DexPath, "arm64", mSplit0RefProfile, 668 false /* isOtherReadable */); 669 670 verify(mArtd).getDexoptNeeded(eq(mSplit0DexPath), eq("arm"), any(), eq("speed-profile"), 671 eq(mDefaultDexoptTrigger)); 672 checkDexoptWithProfile(verify(mArtd), mSplit0DexPath, "arm", mSplit0RefProfile, 673 false /* isOtherReadable */); 674 } 675 676 @Test testDexoptCancelledBeforeDexopt()677 public void testDexoptCancelledBeforeDexopt() throws Exception { 678 mCancellationSignal.cancel(); 679 680 var artdCancellationSignal = mock(IArtdCancellationSignal.class); 681 when(mArtd.createCancellationSignal()).thenReturn(artdCancellationSignal); 682 683 doAnswer(invocation -> { 684 verify(artdCancellationSignal).cancel(); 685 return createArtdDexoptResult(true /* cancelled */); 686 }) 687 .when(mArtd) 688 .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(), 689 same(artdCancellationSignal)); 690 691 // The result should only contain one element: the result of the first file with 692 // DEXOPT_CANCELLED. 693 assertThat(mPrimaryDexopter.dexopt() 694 .stream() 695 .map(DexContainerFileDexoptResult::getStatus) 696 .collect(Collectors.toList())) 697 .containsExactly(DexoptResult.DEXOPT_CANCELLED); 698 699 // It shouldn't continue after being cancelled on the first file. 700 verify(mArtd, times(1)).createCancellationSignal(); 701 verify(mArtd, times(1)) 702 .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(), 703 any()); 704 } 705 706 @Test testDexoptCancelledDuringDexopt()707 public void testDexoptCancelledDuringDexopt() throws Exception { 708 Semaphore dexoptStarted = new Semaphore(0); 709 Semaphore dexoptCancelled = new Semaphore(0); 710 final long TIMEOUT_SEC = 10; 711 712 var artdCancellationSignal = mock(IArtdCancellationSignal.class); 713 when(mArtd.createCancellationSignal()).thenReturn(artdCancellationSignal); 714 715 doAnswer(invocation -> { 716 dexoptStarted.release(); 717 assertThat(dexoptCancelled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue(); 718 return createArtdDexoptResult(true /* cancelled */); 719 }) 720 .when(mArtd) 721 .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(), 722 same(artdCancellationSignal)); 723 doAnswer(invocation -> { 724 dexoptCancelled.release(); 725 return null; 726 }) 727 .when(artdCancellationSignal) 728 .cancel(); 729 730 Future<List<DexContainerFileDexoptResult>> results = 731 ForkJoinPool.commonPool().submit(() -> { return mPrimaryDexopter.dexopt(); }); 732 733 assertThat(dexoptStarted.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue(); 734 735 mCancellationSignal.cancel(); 736 737 assertThat(results.get() 738 .stream() 739 .map(DexContainerFileDexoptResult::getStatus) 740 .collect(Collectors.toList())) 741 .containsExactly(DexoptResult.DEXOPT_CANCELLED); 742 743 // It shouldn't continue after being cancelled on the first file. 744 verify(mArtd, times(1)).createCancellationSignal(); 745 verify(mArtd, times(1)) 746 .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(), 747 any()); 748 } 749 750 @Test testDexoptBaseApk()751 public void testDexoptBaseApk() throws Exception { 752 mDexoptParams = 753 new DexoptParams.Builder("install") 754 .setCompilerFilter("speed-profile") 755 .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SINGLE_SPLIT) 756 .setSplitName(null) 757 .build(); 758 mPrimaryDexopter = 759 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 760 761 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 762 verifyStatusAllOk(results); 763 764 verify(mArtd, times(2)) 765 .dexopt(any(), eq(mDexPath), any(), any(), any(), any(), any(), any(), anyInt(), 766 any(), any()); 767 verify(mArtd, never()) 768 .dexopt(any(), eq(mSplit0DexPath), any(), any(), any(), any(), any(), any(), 769 anyInt(), any(), any()); 770 } 771 772 @Test testDexoptSplitApk()773 public void testDexoptSplitApk() throws Exception { 774 mDexoptParams = 775 new DexoptParams.Builder("install") 776 .setCompilerFilter("speed-profile") 777 .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SINGLE_SPLIT) 778 .setSplitName("split_0") 779 .build(); 780 mPrimaryDexopter = 781 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 782 783 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 784 verifyStatusAllOk(results); 785 786 verify(mArtd, never()) 787 .dexopt(any(), eq(mDexPath), any(), any(), any(), any(), any(), any(), anyInt(), 788 any(), any()); 789 verify(mArtd, times(2)) 790 .dexopt(any(), eq(mSplit0DexPath), any(), any(), any(), any(), any(), any(), 791 anyInt(), any(), any()); 792 } 793 794 @Test testDexoptStorageLow()795 public void testDexoptStorageLow() throws Exception { 796 when(mStorageManager.getAllocatableBytes(any())).thenReturn(1l, 0l, 0l, 1l); 797 798 mDexoptParams = 799 new DexoptParams.Builder("install") 800 .setCompilerFilter("speed-profile") 801 .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_SKIP_IF_STORAGE_LOW) 802 .build(); 803 mPrimaryDexopter = 804 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 805 806 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 807 assertThat(results.get(0).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED); 808 assertThat( 809 results.get(0).getExtendedStatusFlags() & DexoptResult.EXTENDED_SKIPPED_STORAGE_LOW) 810 .isEqualTo(0); 811 assertThat(results.get(1).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED); 812 assertThat( 813 results.get(1).getExtendedStatusFlags() & DexoptResult.EXTENDED_SKIPPED_STORAGE_LOW) 814 .isNotEqualTo(0); 815 assertThat(results.get(2).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED); 816 assertThat( 817 results.get(2).getExtendedStatusFlags() & DexoptResult.EXTENDED_SKIPPED_STORAGE_LOW) 818 .isNotEqualTo(0); 819 assertThat(results.get(3).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED); 820 assertThat( 821 results.get(3).getExtendedStatusFlags() & DexoptResult.EXTENDED_SKIPPED_STORAGE_LOW) 822 .isEqualTo(0); 823 824 verify(mArtd, times(2)) 825 .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(), 826 any()); 827 } 828 829 @Test testDexoptDexStatus()830 public void testDexoptDexStatus() throws Exception { 831 lenient() 832 .when(mArtd.getDexoptNeeded(any(), any(), any(), any(), anyInt())) 833 .thenReturn(dexoptIsNotNeeded(false /* hasDexCode */), 834 dexoptIsNotNeeded(false /* hasDexCode */), 835 dexoptIsNotNeeded(true /* hasDexCode */), dexoptIsNeeded()); 836 837 mDexoptParams = new DexoptParams.Builder("install") 838 .setCompilerFilter("speed-profile") 839 .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX) 840 .build(); 841 mPrimaryDexopter = 842 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 843 844 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 845 assertThat(results.get(0).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED); 846 assertThat( 847 results.get(0).getExtendedStatusFlags() & DexoptResult.EXTENDED_SKIPPED_NO_DEX_CODE) 848 .isNotEqualTo(0); 849 assertThat(results.get(1).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED); 850 assertThat( 851 results.get(1).getExtendedStatusFlags() & DexoptResult.EXTENDED_SKIPPED_NO_DEX_CODE) 852 .isNotEqualTo(0); 853 assertThat(results.get(2).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED); 854 assertThat( 855 results.get(2).getExtendedStatusFlags() & DexoptResult.EXTENDED_SKIPPED_NO_DEX_CODE) 856 .isEqualTo(0); 857 assertThat(results.get(3).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED); 858 assertThat( 859 results.get(3).getExtendedStatusFlags() & DexoptResult.EXTENDED_SKIPPED_NO_DEX_CODE) 860 .isEqualTo(0); 861 } 862 863 @Test testDexoptPreRebootDexNotFound()864 public void testDexoptPreRebootDexNotFound() throws Exception { 865 when(mInjector.isPreReboot()).thenReturn(true); 866 doReturn(FileVisibility.NOT_FOUND).when(mArtd).getDexFileVisibility(mDexPath); 867 doReturn(FileVisibility.NOT_FOUND).when(mArtd).getDexFileVisibility(mSplit0DexPath); 868 869 mPrimaryDexopter = 870 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 871 872 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 873 assertThat(results).hasSize(0); 874 } 875 876 @Test testDexoptPreRebootSomeDexNotFound()877 public void testDexoptPreRebootSomeDexNotFound() throws Exception { 878 when(mInjector.isPreReboot()).thenReturn(true); 879 doReturn(FileVisibility.OTHER_READABLE).when(mArtd).getDexFileVisibility(mDexPath); 880 doReturn(FileVisibility.NOT_FOUND).when(mArtd).getDexFileVisibility(mSplit0DexPath); 881 882 mPrimaryDexopter = 883 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 884 885 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 886 assertThat(results).hasSize(2); 887 assertThat(results.get(0).getDexContainerFile()).isEqualTo(mDexPath); 888 assertThat(results.get(0).getAbi()).isEqualTo("arm64-v8a"); 889 assertThat(results.get(1).getDexContainerFile()).isEqualTo(mDexPath); 890 assertThat(results.get(1).getAbi()).isEqualTo("armeabi-v7a"); 891 } 892 893 @Test testDexoptPreRebootArtifactsExist()894 public void testDexoptPreRebootArtifactsExist() throws Exception { 895 when(mInjector.isPreReboot()).thenReturn(true); 896 897 when(mArtd.getArtifactsVisibility(deepEq(AidlUtils.buildArtifactsPathAsInputPreReboot( 898 mDexPath, "arm", false /* isInDalvikCache */)))) 899 .thenReturn(FileVisibility.OTHER_READABLE); 900 901 mPrimaryDexopter = 902 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 903 904 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 905 // Only the one at index 1 is skipped. 906 assertThat(results).hasSize(4); 907 assertThat(results.get(0).getDexContainerFile()).isEqualTo(mDexPath); 908 assertThat(results.get(0).getAbi()).isEqualTo("arm64-v8a"); 909 assertThat(results.get(0).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED); 910 assertThat(results.get(0).getExtendedStatusFlags() 911 & DexoptResult.EXTENDED_SKIPPED_PRE_REBOOT_ALREADY_EXIST) 912 .isEqualTo(0); 913 assertThat(results.get(1).getDexContainerFile()).isEqualTo(mDexPath); 914 assertThat(results.get(1).getAbi()).isEqualTo("armeabi-v7a"); 915 assertThat(results.get(1).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED); 916 assertThat(results.get(1).getExtendedStatusFlags() 917 & DexoptResult.EXTENDED_SKIPPED_PRE_REBOOT_ALREADY_EXIST) 918 .isNotEqualTo(0); 919 assertThat(results.get(2).getDexContainerFile()).isEqualTo(mSplit0DexPath); 920 assertThat(results.get(2).getAbi()).isEqualTo("arm64-v8a"); 921 assertThat(results.get(2).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED); 922 assertThat(results.get(2).getExtendedStatusFlags() 923 & DexoptResult.EXTENDED_SKIPPED_PRE_REBOOT_ALREADY_EXIST) 924 .isEqualTo(0); 925 assertThat(results.get(3).getDexContainerFile()).isEqualTo(mSplit0DexPath); 926 assertThat(results.get(3).getAbi()).isEqualTo("armeabi-v7a"); 927 assertThat(results.get(3).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED); 928 assertThat(results.get(3).getExtendedStatusFlags() 929 & DexoptResult.EXTENDED_SKIPPED_PRE_REBOOT_ALREADY_EXIST) 930 .isEqualTo(0); 931 } 932 933 @Test testDexoptNotAffectedByPreRebootArtifacts()934 public void testDexoptNotAffectedByPreRebootArtifacts() throws Exception { 935 // Same setup as above, but `isPreReboot` is false. 936 lenient() 937 .when(mArtd.getArtifactsVisibility( 938 deepEq(AidlUtils.buildArtifactsPathAsInputPreReboot( 939 mDexPath, "arm", false /* isInDalvikCache */)))) 940 .thenReturn(FileVisibility.OTHER_READABLE); 941 942 mPrimaryDexopter = 943 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 944 945 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 946 assertThat(results).hasSize(4); 947 for (DexContainerFileDexoptResult result : results) { 948 assertThat(result.getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED); 949 } 950 } 951 checkDexoptWithProfile(IArtd artd, String dexPath, String isa, ProfilePath profile, boolean isOtherReadable)952 private void checkDexoptWithProfile(IArtd artd, String dexPath, String isa, ProfilePath profile, 953 boolean isOtherReadable) throws Exception { 954 artd.dexopt(argThat(artifacts 955 -> artifacts.permissionSettings.fileFsPermission.isOtherReadable 956 == isOtherReadable), 957 eq(dexPath), eq(isa), any(), eq("speed-profile"), deepEq(profile), any(), any(), 958 anyInt(), argThat(dexoptOptions -> dexoptOptions.generateAppImage == true), any()); 959 } 960 checkDexoptWithNoProfile( IArtd artd, String dexPath, String isa, String compilerFilter)961 private void checkDexoptWithNoProfile( 962 IArtd artd, String dexPath, String isa, String compilerFilter) throws Exception { 963 artd.dexopt( 964 argThat(artifacts 965 -> artifacts.permissionSettings.fileFsPermission.isOtherReadable == true), 966 eq(dexPath), eq(isa), any(), eq(compilerFilter), isNull(), any(), any(), anyInt(), 967 argThat(dexoptOptions -> dexoptOptions.generateAppImage == false), any()); 968 } 969 verifyProfileNotUsed(ProfilePath profile)970 private void verifyProfileNotUsed(ProfilePath profile) throws Exception { 971 assertThat(mUsedProfiles) 972 .comparingElementsUsing(TestingUtils.<ProfilePath>deepEquality()) 973 .doesNotContain(profile); 974 } 975 verifyEmbeddedProfileNotUsed(String dexPath)976 private void verifyEmbeddedProfileNotUsed(String dexPath) throws Exception { 977 assertThat(mUsedEmbeddedProfiles).doesNotContain(dexPath); 978 } 979 makeProfileUsable(ProfilePath profile)980 private void makeProfileUsable(ProfilePath profile) throws Exception { 981 lenient().when(mArtd.isProfileUsable(deepEq(profile), any())).thenAnswer(invocation -> { 982 mUsedProfiles.add(invocation.<ProfilePath>getArgument(0)); 983 return true; 984 }); 985 lenient() 986 .when(mArtd.copyAndRewriteProfile(deepEq(profile), any(), any())) 987 .thenAnswer(invocation -> { 988 mUsedProfiles.add(invocation.<ProfilePath>getArgument(0)); 989 return TestingUtils.createCopyAndRewriteProfileSuccess(); 990 }); 991 } 992 makeEmbeddedProfileUsable(String dexPath)993 private void makeEmbeddedProfileUsable(String dexPath) throws Exception { 994 lenient() 995 .when(mArtd.copyAndRewriteEmbeddedProfile(any(), eq(dexPath))) 996 .thenAnswer(invocation -> { 997 mUsedEmbeddedProfiles.add(invocation.<String>getArgument(1)); 998 return TestingUtils.createCopyAndRewriteProfileSuccess(); 999 }); 1000 } 1001 verifyStatusAllOk(List<DexContainerFileDexoptResult> results)1002 private void verifyStatusAllOk(List<DexContainerFileDexoptResult> results) { 1003 for (DexContainerFileDexoptResult result : results) { 1004 assertThat(result.getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED); 1005 assertThat(result.getExtendedStatusFlags()).isEqualTo(0); 1006 assertThat(result.getExternalProfileErrors()).isEmpty(); 1007 } 1008 } 1009 1010 /** Dexopter relies on this information to determine which current profiles to check. */ setPackageInstalledForUserIds(int... userIds)1011 private void setPackageInstalledForUserIds(int... userIds) { 1012 for (int userId : userIds) { 1013 when(mPkgState.getStateForUser(eq(UserHandle.of(userId)))) 1014 .thenReturn(mPkgUserStateInstalled); 1015 } 1016 } 1017 } 1018