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.GetDexoptNeededResult.ArtifactsLocation; 20 import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult; 21 import static com.android.server.art.testing.TestingUtils.deepEq; 22 23 import static com.google.common.truth.Truth.assertThat; 24 25 import static org.mockito.Mockito.any; 26 import static org.mockito.Mockito.anyInt; 27 import static org.mockito.Mockito.argThat; 28 import static org.mockito.Mockito.doAnswer; 29 import static org.mockito.Mockito.doReturn; 30 import static org.mockito.Mockito.eq; 31 import static org.mockito.Mockito.inOrder; 32 import static org.mockito.Mockito.isNull; 33 import static org.mockito.Mockito.lenient; 34 import static org.mockito.Mockito.mock; 35 import static org.mockito.Mockito.never; 36 import static org.mockito.Mockito.same; 37 import static org.mockito.Mockito.times; 38 import static org.mockito.Mockito.verify; 39 import static org.mockito.Mockito.when; 40 41 import android.os.Process; 42 import android.os.ServiceSpecificException; 43 import android.os.UserHandle; 44 45 import androidx.test.filters.SmallTest; 46 import androidx.test.runner.AndroidJUnit4; 47 48 import com.android.server.art.model.ArtFlags; 49 import com.android.server.art.model.DexoptParams; 50 import com.android.server.art.model.DexoptResult; 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.util.ArrayList; 59 import java.util.List; 60 import java.util.concurrent.ForkJoinPool; 61 import java.util.concurrent.Future; 62 import java.util.concurrent.Semaphore; 63 import java.util.concurrent.TimeUnit; 64 import java.util.stream.Collectors; 65 66 @SmallTest 67 @RunWith(AndroidJUnit4.class) 68 public class PrimaryDexopterTest extends PrimaryDexopterTestBase { 69 private final String mDexPath = "/data/app/foo/base.apk"; 70 private final ProfilePath mRefProfile = 71 AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "primary"); 72 private final ProfilePath mPrebuiltProfile = AidlUtils.buildProfilePathForPrebuilt(mDexPath); 73 private final ProfilePath mDmProfile = AidlUtils.buildProfilePathForDm(mDexPath); 74 private final DexMetadataPath mDmFile = AidlUtils.buildDexMetadataPath(mDexPath); 75 private final OutputProfile mPublicOutputProfile = AidlUtils.buildOutputProfileForPrimary( 76 PKG_NAME, "primary", Process.SYSTEM_UID, SHARED_GID, true /* isOtherReadable */); 77 private final OutputProfile mPrivateOutputProfile = AidlUtils.buildOutputProfileForPrimary( 78 PKG_NAME, "primary", Process.SYSTEM_UID, SHARED_GID, false /* isOtherReadable */); 79 80 private final String mSplit0DexPath = "/data/app/foo/split_0.apk"; 81 private final ProfilePath mSplit0RefProfile = 82 AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "split_0.split"); 83 84 private final int mDefaultDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER 85 | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION; 86 private final int mBetterOrSameDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER 87 | DexoptTrigger.COMPILER_FILTER_IS_SAME 88 | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION; 89 private final int mForceDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER 90 | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE 91 | DexoptTrigger.COMPILER_FILTER_IS_SAME | DexoptTrigger.COMPILER_FILTER_IS_WORSE 92 | DexoptTrigger.NEED_EXTRACTION; 93 94 private final MergeProfileOptions mMergeProfileOptions = new MergeProfileOptions(); 95 96 private final ArtdDexoptResult mArtdDexoptResult = 97 createArtdDexoptResult(false /* cancelled */); 98 99 private DexoptParams mDexoptParams = 100 new DexoptParams.Builder("install").setCompilerFilter("speed-profile").build(); 101 102 private PrimaryDexopter mPrimaryDexopter; 103 104 private List<ProfilePath> mUsedProfiles; 105 106 @Before setUp()107 public void setUp() throws Exception { 108 super.setUp(); 109 110 // By default, none of the profiles are usable. 111 lenient().when(mArtd.isProfileUsable(any(), any())).thenReturn(false); 112 lenient().when(mArtd.copyAndRewriteProfile(any(), any(), any())).thenReturn(false); 113 114 // By default, no DM file exists. 115 lenient().when(mArtd.getDmFileVisibility(any())).thenReturn(FileVisibility.NOT_FOUND); 116 117 // Dexopt is by default needed and successful. 118 lenient() 119 .when(mArtd.getDexoptNeeded(any(), any(), any(), any(), anyInt())) 120 .thenReturn(dexoptIsNeeded()); 121 lenient() 122 .when(mArtd.dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), 123 any(), any())) 124 .thenReturn(mArtdDexoptResult); 125 126 lenient() 127 .when(mArtd.createCancellationSignal()) 128 .thenReturn(mock(IArtdCancellationSignal.class)); 129 130 mPrimaryDexopter = 131 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 132 133 mUsedProfiles = new ArrayList<>(); 134 } 135 136 @Test testDexoptInputVdex()137 public void testDexoptInputVdex() throws Exception { 138 // null. 139 doReturn(dexoptIsNeeded(ArtifactsLocation.NONE_OR_ERROR)) 140 .when(mArtd) 141 .getDexoptNeeded(eq(mDexPath), eq("arm64"), any(), any(), anyInt()); 142 doReturn(mArtdDexoptResult) 143 .when(mArtd) 144 .dexopt(any(), eq(mDexPath), eq("arm64"), any(), any(), any(), isNull(), any(), 145 anyInt(), any(), any()); 146 147 // ArtifactsPath, isInDalvikCache=true. 148 doReturn(dexoptIsNeeded(ArtifactsLocation.DALVIK_CACHE)) 149 .when(mArtd) 150 .getDexoptNeeded(eq(mDexPath), eq("arm"), any(), any(), anyInt()); 151 doReturn(mArtdDexoptResult) 152 .when(mArtd) 153 .dexopt(any(), eq(mDexPath), eq("arm"), any(), any(), any(), 154 deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPath( 155 mDexPath, "arm", true /* isInDalvikCache */))), 156 any(), anyInt(), any(), any()); 157 158 // ArtifactsPath, isInDalvikCache=false. 159 doReturn(dexoptIsNeeded(ArtifactsLocation.NEXT_TO_DEX)) 160 .when(mArtd) 161 .getDexoptNeeded(eq(mSplit0DexPath), eq("arm64"), any(), any(), anyInt()); 162 doReturn(mArtdDexoptResult) 163 .when(mArtd) 164 .dexopt(any(), eq(mSplit0DexPath), eq("arm64"), any(), any(), any(), 165 deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPath( 166 mSplit0DexPath, "arm64", false /* isInDalvikCache */))), 167 any(), anyInt(), any(), any()); 168 169 // DexMetadataPath. 170 doReturn(dexoptIsNeeded(ArtifactsLocation.DM)) 171 .when(mArtd) 172 .getDexoptNeeded(eq(mSplit0DexPath), eq("arm"), any(), any(), anyInt()); 173 doReturn(mArtdDexoptResult) 174 .when(mArtd) 175 .dexopt(any(), eq(mSplit0DexPath), eq("arm"), any(), any(), any(), isNull(), any(), 176 anyInt(), any(), any()); 177 178 mPrimaryDexopter.dexopt(); 179 } 180 181 @Test testDexoptDm()182 public void testDexoptDm() throws Exception { 183 lenient() 184 .when(mArtd.getDmFileVisibility(deepEq(mDmFile))) 185 .thenReturn(FileVisibility.OTHER_READABLE); 186 187 mPrimaryDexopter.dexopt(); 188 189 verify(mArtd, times(2)) 190 .dexopt(any(), eq(mDexPath), any(), any(), any(), any(), any(), deepEq(mDmFile), 191 anyInt(), 192 argThat(dexoptOptions 193 -> dexoptOptions.compilationReason.equals("install-dm")), 194 any()); 195 verify(mArtd, times(2)) 196 .dexopt(any(), eq(mSplit0DexPath), any(), any(), any(), any(), any(), isNull(), 197 anyInt(), 198 argThat(dexoptOptions -> dexoptOptions.compilationReason.equals("install")), 199 any()); 200 } 201 202 @Test testDexoptUsesRefProfile()203 public void testDexoptUsesRefProfile() throws Exception { 204 makeProfileUsable(mRefProfile); 205 when(mArtd.getProfileVisibility(deepEq(mRefProfile))) 206 .thenReturn(FileVisibility.NOT_OTHER_READABLE); 207 208 // Other profiles are also usable, but they shouldn't be used. 209 makeProfileUsable(mPrebuiltProfile); 210 makeProfileUsable(mDmProfile); 211 212 mPrimaryDexopter.dexopt(); 213 214 verify(mArtd).getDexoptNeeded( 215 eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger)); 216 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm64", mRefProfile, 217 false /* isOtherReadable */, true /* generateAppImage */); 218 219 verify(mArtd).getDexoptNeeded( 220 eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger)); 221 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm", mRefProfile, 222 false /* isOtherReadable */, true /* generateAppImage */); 223 224 // There is no profile for split 0, so it should fall back to "verify". 225 verify(mArtd).getDexoptNeeded( 226 eq(mSplit0DexPath), eq("arm64"), any(), eq("verify"), eq(mDefaultDexoptTrigger)); 227 checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm64", "verify"); 228 229 verify(mArtd).getDexoptNeeded( 230 eq(mSplit0DexPath), eq("arm"), any(), eq("verify"), eq(mDefaultDexoptTrigger)); 231 checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm", "verify"); 232 233 verifyProfileNotUsed(mPrebuiltProfile); 234 verifyProfileNotUsed(mDmProfile); 235 } 236 237 @Test testDexoptUsesPublicRefProfile()238 public void testDexoptUsesPublicRefProfile() throws Exception { 239 // The ref profile is usable and public. 240 makeProfileUsable(mRefProfile); 241 when(mArtd.getProfileVisibility(deepEq(mRefProfile))) 242 .thenReturn(FileVisibility.OTHER_READABLE); 243 244 // Other profiles are also usable, but they shouldn't be used. 245 makeProfileUsable(mPrebuiltProfile); 246 makeProfileUsable(mDmProfile); 247 248 mPrimaryDexopter.dexopt(); 249 250 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm64", mRefProfile, 251 true /* isOtherReadable */, true /* generateAppImage */); 252 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm", mRefProfile, 253 true /* isOtherReadable */, true /* generateAppImage */); 254 255 verifyProfileNotUsed(mPrebuiltProfile); 256 verifyProfileNotUsed(mDmProfile); 257 } 258 259 @Test testDexoptUsesPrebuiltProfile()260 public void testDexoptUsesPrebuiltProfile() throws Exception { 261 makeProfileNotUsable(mRefProfile); 262 makeProfileUsable(mPrebuiltProfile); 263 makeProfileUsable(mDmProfile); 264 265 mPrimaryDexopter.dexopt(); 266 267 InOrder inOrder = inOrder(mArtd); 268 269 inOrder.verify(mArtd).copyAndRewriteProfile( 270 deepEq(mPrebuiltProfile), deepEq(mPublicOutputProfile), eq(mDexPath)); 271 272 checkDexoptWithProfile(inOrder.verify(mArtd), mDexPath, "arm64", 273 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 274 true /* isOtherReadable */, true /* generateAppImage */); 275 checkDexoptWithProfile(inOrder.verify(mArtd), mDexPath, "arm", 276 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 277 true /* isOtherReadable */, true /* generateAppImage */); 278 279 inOrder.verify(mArtd).commitTmpProfile(deepEq(mPublicOutputProfile.profilePath)); 280 281 verifyProfileNotUsed(mRefProfile); 282 verifyProfileNotUsed(mDmProfile); 283 } 284 285 @Test testDexoptMergesProfiles()286 public void testDexoptMergesProfiles() throws Exception { 287 when(mPkgState.getStateForUser(eq(UserHandle.of(0)))).thenReturn(mPkgUserStateInstalled); 288 when(mPkgState.getStateForUser(eq(UserHandle.of(2)))).thenReturn(mPkgUserStateInstalled); 289 290 when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(true); 291 292 makeProfileUsable(mRefProfile); 293 when(mArtd.getProfileVisibility(deepEq(mRefProfile))) 294 .thenReturn(FileVisibility.OTHER_READABLE); 295 296 mPrimaryDexopter.dexopt(); 297 298 InOrder inOrder = inOrder(mArtd); 299 300 inOrder.verify(mArtd).mergeProfiles( 301 deepEq(List.of(AidlUtils.buildProfilePathForPrimaryCur( 302 0 /* userId */, PKG_NAME, "primary"), 303 AidlUtils.buildProfilePathForPrimaryCur( 304 2 /* userId */, PKG_NAME, "primary"))), 305 deepEq(mRefProfile), deepEq(mPrivateOutputProfile), deepEq(List.of(mDexPath)), 306 deepEq(mMergeProfileOptions)); 307 308 // It should use `mBetterOrSameDexoptTrigger` and the merged profile for both ISAs. 309 inOrder.verify(mArtd).getDexoptNeeded(eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), 310 eq(mBetterOrSameDexoptTrigger)); 311 checkDexoptWithProfile(inOrder.verify(mArtd), mDexPath, "arm64", 312 ProfilePath.tmpProfilePath(mPrivateOutputProfile.profilePath), 313 false /* isOtherReadable */, true /* generateAppImage */); 314 315 inOrder.verify(mArtd).getDexoptNeeded(eq(mDexPath), eq("arm"), any(), eq("speed-profile"), 316 eq(mBetterOrSameDexoptTrigger)); 317 checkDexoptWithProfile(inOrder.verify(mArtd), mDexPath, "arm", 318 ProfilePath.tmpProfilePath(mPrivateOutputProfile.profilePath), 319 false /* isOtherReadable */, true /* generateAppImage */); 320 321 inOrder.verify(mArtd).commitTmpProfile(deepEq(mPrivateOutputProfile.profilePath)); 322 323 inOrder.verify(mArtd).deleteProfile(deepEq( 324 AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME, "primary"))); 325 inOrder.verify(mArtd).deleteProfile(deepEq( 326 AidlUtils.buildProfilePathForPrimaryCur(2 /* userId */, PKG_NAME, "primary"))); 327 } 328 329 @Test testDexoptMergesProfilesMergeFailed()330 public void testDexoptMergesProfilesMergeFailed() throws Exception { 331 when(mPkgState.getStateForUser(eq(UserHandle.of(0)))).thenReturn(mPkgUserStateInstalled); 332 when(mPkgState.getStateForUser(eq(UserHandle.of(2)))).thenReturn(mPkgUserStateInstalled); 333 334 when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(false); 335 336 makeProfileUsable(mRefProfile); 337 when(mArtd.getProfileVisibility(deepEq(mRefProfile))) 338 .thenReturn(FileVisibility.OTHER_READABLE); 339 340 mPrimaryDexopter.dexopt(); 341 342 // It should still use "speed-profile", but with the existing reference profile only. 343 verify(mArtd).getDexoptNeeded( 344 eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger)); 345 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm64", mRefProfile, 346 true /* isOtherReadable */, true /* generateAppImage */); 347 348 verify(mArtd).getDexoptNeeded( 349 eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger)); 350 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm", mRefProfile, 351 true /* isOtherReadable */, true /* generateAppImage */); 352 353 verify(mArtd, never()).deleteProfile(any()); 354 verify(mArtd, never()).commitTmpProfile(any()); 355 } 356 357 @Test testDexoptUsesDmProfile()358 public void testDexoptUsesDmProfile() throws Exception { 359 makeProfileNotUsable(mRefProfile); 360 makeProfileNotUsable(mPrebuiltProfile); 361 makeProfileUsable(mDmProfile); 362 363 mPrimaryDexopter.dexopt(); 364 365 verify(mArtd).copyAndRewriteProfile( 366 deepEq(mDmProfile), deepEq(mPublicOutputProfile), eq(mDexPath)); 367 368 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm64", 369 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 370 true /* isOtherReadable */, true /* generateAppImage */); 371 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm", 372 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 373 true /* isOtherReadable */, true /* generateAppImage */); 374 375 verifyProfileNotUsed(mRefProfile); 376 verifyProfileNotUsed(mPrebuiltProfile); 377 } 378 379 @Test testDexoptDeletesProfileOnFailure()380 public void testDexoptDeletesProfileOnFailure() throws Exception { 381 makeProfileNotUsable(mRefProfile); 382 makeProfileNotUsable(mPrebuiltProfile); 383 makeProfileUsable(mDmProfile); 384 385 when(mArtd.dexopt(any(), eq(mDexPath), any(), any(), any(), any(), any(), any(), anyInt(), 386 any(), any())) 387 .thenThrow(ServiceSpecificException.class); 388 389 mPrimaryDexopter.dexopt(); 390 391 verify(mArtd).deleteProfile( 392 deepEq(ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath))); 393 verify(mArtd, never()).commitTmpProfile(deepEq(mPublicOutputProfile.profilePath)); 394 } 395 396 @Test testDexoptNeedsToBeShared()397 public void testDexoptNeedsToBeShared() throws Exception { 398 when(mDexUseManager.isPrimaryDexUsedByOtherApps(eq(PKG_NAME), eq(mDexPath))) 399 .thenReturn(true); 400 when(mDexUseManager.isPrimaryDexUsedByOtherApps(eq(PKG_NAME), eq(mSplit0DexPath))) 401 .thenReturn(true); 402 403 // The ref profile is usable but shouldn't be used. 404 makeProfileUsable(mRefProfile); 405 406 makeProfileNotUsable(mPrebuiltProfile); 407 makeProfileUsable(mDmProfile); 408 409 // The existing artifacts are private. 410 when(mArtd.getArtifactsVisibility( 411 argThat(artifactsPath -> artifactsPath.dexPath == mDexPath))) 412 .thenReturn(FileVisibility.NOT_OTHER_READABLE); 413 414 mPrimaryDexopter.dexopt(); 415 416 verify(mArtd).copyAndRewriteProfile( 417 deepEq(mDmProfile), deepEq(mPublicOutputProfile), eq(mDexPath)); 418 419 // It should re-compile anyway. 420 verify(mArtd).getDexoptNeeded( 421 eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mForceDexoptTrigger)); 422 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm64", 423 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 424 true /* isOtherReadable */, true /* generateAppImage */); 425 426 verify(mArtd).getDexoptNeeded( 427 eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mForceDexoptTrigger)); 428 checkDexoptWithProfile(verify(mArtd), mDexPath, "arm", 429 ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath), 430 true /* isOtherReadable */, true /* generateAppImage */); 431 432 checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm64", "speed"); 433 checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm", "speed"); 434 435 verifyProfileNotUsed(mRefProfile); 436 verifyProfileNotUsed(mPrebuiltProfile); 437 } 438 439 @Test testDexoptNeedsToBeSharedArtifactsArePublic()440 public void testDexoptNeedsToBeSharedArtifactsArePublic() throws Exception { 441 // Same setup as above, but the existing artifacts are public. 442 when(mDexUseManager.isPrimaryDexUsedByOtherApps(eq(PKG_NAME), eq(mDexPath))) 443 .thenReturn(true); 444 when(mDexUseManager.isPrimaryDexUsedByOtherApps(eq(PKG_NAME), eq(mSplit0DexPath))) 445 .thenReturn(true); 446 447 makeProfileUsable(mRefProfile); 448 makeProfileNotUsable(mPrebuiltProfile); 449 makeProfileUsable(mDmProfile); 450 when(mArtd.getArtifactsVisibility( 451 argThat(artifactsPath -> artifactsPath.dexPath == mDexPath))) 452 .thenReturn(FileVisibility.OTHER_READABLE); 453 454 mPrimaryDexopter.dexopt(); 455 456 // It should use the default dexopt trigger. 457 verify(mArtd).getDexoptNeeded( 458 eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger)); 459 verify(mArtd).getDexoptNeeded( 460 eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger)); 461 } 462 463 @Test testDexoptUsesProfileForSplit()464 public void testDexoptUsesProfileForSplit() throws Exception { 465 makeProfileUsable(mSplit0RefProfile); 466 when(mArtd.getProfileVisibility(deepEq(mSplit0RefProfile))) 467 .thenReturn(FileVisibility.NOT_OTHER_READABLE); 468 469 mPrimaryDexopter.dexopt(); 470 471 verify(mArtd).getDexoptNeeded(eq(mSplit0DexPath), eq("arm64"), any(), eq("speed-profile"), 472 eq(mDefaultDexoptTrigger)); 473 checkDexoptWithProfile(verify(mArtd), mSplit0DexPath, "arm64", mSplit0RefProfile, 474 false /* isOtherReadable */, false /* generateAppImage */); 475 476 verify(mArtd).getDexoptNeeded(eq(mSplit0DexPath), eq("arm"), any(), eq("speed-profile"), 477 eq(mDefaultDexoptTrigger)); 478 checkDexoptWithProfile(verify(mArtd), mSplit0DexPath, "arm", mSplit0RefProfile, 479 false /* isOtherReadable */, false /* generateAppImage */); 480 } 481 482 @Test testDexoptCancelledBeforeDexopt()483 public void testDexoptCancelledBeforeDexopt() throws Exception { 484 mCancellationSignal.cancel(); 485 486 var artdCancellationSignal = mock(IArtdCancellationSignal.class); 487 when(mArtd.createCancellationSignal()).thenReturn(artdCancellationSignal); 488 489 doAnswer(invocation -> { 490 verify(artdCancellationSignal).cancel(); 491 return createArtdDexoptResult(true /* cancelled */); 492 }) 493 .when(mArtd) 494 .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(), 495 same(artdCancellationSignal)); 496 497 // The result should only contain one element: the result of the first file with 498 // DEXOPT_CANCELLED. 499 assertThat(mPrimaryDexopter.dexopt() 500 .stream() 501 .map(DexContainerFileDexoptResult::getStatus) 502 .collect(Collectors.toList())) 503 .containsExactly(DexoptResult.DEXOPT_CANCELLED); 504 505 // It shouldn't continue after being cancelled on the first file. 506 verify(mArtd, times(1)).createCancellationSignal(); 507 verify(mArtd, times(1)) 508 .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(), 509 any()); 510 } 511 512 @Test testDexoptCancelledDuringDexopt()513 public void testDexoptCancelledDuringDexopt() throws Exception { 514 Semaphore dexoptStarted = new Semaphore(0); 515 Semaphore dexoptCancelled = new Semaphore(0); 516 final long TIMEOUT_SEC = 10; 517 518 var artdCancellationSignal = mock(IArtdCancellationSignal.class); 519 when(mArtd.createCancellationSignal()).thenReturn(artdCancellationSignal); 520 521 doAnswer(invocation -> { 522 dexoptStarted.release(); 523 assertThat(dexoptCancelled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue(); 524 return createArtdDexoptResult(true /* cancelled */); 525 }) 526 .when(mArtd) 527 .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(), 528 same(artdCancellationSignal)); 529 doAnswer(invocation -> { 530 dexoptCancelled.release(); 531 return null; 532 }) 533 .when(artdCancellationSignal) 534 .cancel(); 535 536 Future<List<DexContainerFileDexoptResult>> results = 537 ForkJoinPool.commonPool().submit(() -> { return mPrimaryDexopter.dexopt(); }); 538 539 assertThat(dexoptStarted.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue(); 540 541 mCancellationSignal.cancel(); 542 543 assertThat(results.get() 544 .stream() 545 .map(DexContainerFileDexoptResult::getStatus) 546 .collect(Collectors.toList())) 547 .containsExactly(DexoptResult.DEXOPT_CANCELLED); 548 549 // It shouldn't continue after being cancelled on the first file. 550 verify(mArtd, times(1)).createCancellationSignal(); 551 verify(mArtd, times(1)) 552 .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(), 553 any()); 554 } 555 556 @Test testDexoptBaseApk()557 public void testDexoptBaseApk() throws Exception { 558 mDexoptParams = 559 new DexoptParams.Builder("install") 560 .setCompilerFilter("speed-profile") 561 .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SINGLE_SPLIT) 562 .setSplitName(null) 563 .build(); 564 mPrimaryDexopter = 565 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 566 567 mPrimaryDexopter.dexopt(); 568 569 verify(mArtd, times(2)) 570 .dexopt(any(), eq(mDexPath), any(), any(), any(), any(), any(), any(), anyInt(), 571 any(), any()); 572 verify(mArtd, never()) 573 .dexopt(any(), eq(mSplit0DexPath), any(), any(), any(), any(), any(), any(), 574 anyInt(), any(), any()); 575 } 576 577 @Test testDexoptSplitApk()578 public void testDexoptSplitApk() throws Exception { 579 mDexoptParams = 580 new DexoptParams.Builder("install") 581 .setCompilerFilter("speed-profile") 582 .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SINGLE_SPLIT) 583 .setSplitName("split_0") 584 .build(); 585 mPrimaryDexopter = 586 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 587 588 mPrimaryDexopter.dexopt(); 589 590 verify(mArtd, never()) 591 .dexopt(any(), eq(mDexPath), any(), any(), any(), any(), any(), any(), anyInt(), 592 any(), any()); 593 verify(mArtd, times(2)) 594 .dexopt(any(), eq(mSplit0DexPath), any(), any(), any(), any(), any(), any(), 595 anyInt(), any(), any()); 596 } 597 598 @Test testDexoptStorageLow()599 public void testDexoptStorageLow() throws Exception { 600 when(mStorageManager.getAllocatableBytes(any())).thenReturn(1l, 0l, 0l, 1l); 601 602 mDexoptParams = 603 new DexoptParams.Builder("install") 604 .setCompilerFilter("speed-profile") 605 .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_SKIP_IF_STORAGE_LOW) 606 .build(); 607 mPrimaryDexopter = 608 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 609 610 List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt(); 611 assertThat(results.get(0).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED); 612 assertThat(results.get(0).isSkippedDueToStorageLow()).isFalse(); 613 assertThat(results.get(1).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED); 614 assertThat(results.get(1).isSkippedDueToStorageLow()).isTrue(); 615 assertThat(results.get(2).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED); 616 assertThat(results.get(2).isSkippedDueToStorageLow()).isTrue(); 617 assertThat(results.get(3).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED); 618 assertThat(results.get(3).isSkippedDueToStorageLow()).isFalse(); 619 620 verify(mArtd, times(2)) 621 .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(), 622 any()); 623 } 624 checkDexoptWithProfile(IArtd artd, String dexPath, String isa, ProfilePath profile, boolean isOtherReadable, boolean generateAppImage)625 private void checkDexoptWithProfile(IArtd artd, String dexPath, String isa, ProfilePath profile, 626 boolean isOtherReadable, boolean generateAppImage) throws Exception { 627 artd.dexopt(argThat(artifacts 628 -> artifacts.permissionSettings.fileFsPermission.isOtherReadable 629 == isOtherReadable), 630 eq(dexPath), eq(isa), any(), eq("speed-profile"), deepEq(profile), any(), any(), 631 anyInt(), 632 argThat(dexoptOptions -> dexoptOptions.generateAppImage == generateAppImage), 633 any()); 634 } 635 checkDexoptWithNoProfile( IArtd artd, String dexPath, String isa, String compilerFilter)636 private void checkDexoptWithNoProfile( 637 IArtd artd, String dexPath, String isa, String compilerFilter) throws Exception { 638 artd.dexopt( 639 argThat(artifacts 640 -> artifacts.permissionSettings.fileFsPermission.isOtherReadable == true), 641 eq(dexPath), eq(isa), any(), eq(compilerFilter), isNull(), any(), any(), anyInt(), 642 argThat(dexoptOptions -> dexoptOptions.generateAppImage == false), any()); 643 } 644 verifyProfileNotUsed(ProfilePath profile)645 private void verifyProfileNotUsed(ProfilePath profile) throws Exception { 646 assertThat(mUsedProfiles) 647 .comparingElementsUsing(TestingUtils.<ProfilePath>deepEquality()) 648 .doesNotContain(profile); 649 } 650 makeProfileUsable(ProfilePath profile)651 private void makeProfileUsable(ProfilePath profile) throws Exception { 652 lenient().when(mArtd.isProfileUsable(deepEq(profile), any())).thenAnswer(invocation -> { 653 mUsedProfiles.add(invocation.<ProfilePath>getArgument(0)); 654 return true; 655 }); 656 lenient() 657 .when(mArtd.copyAndRewriteProfile(deepEq(profile), any(), any())) 658 .thenAnswer(invocation -> { 659 mUsedProfiles.add(invocation.<ProfilePath>getArgument(0)); 660 return true; 661 }); 662 } 663 makeProfileNotUsable(ProfilePath profile)664 private void makeProfileNotUsable(ProfilePath profile) throws Exception { 665 lenient().when(mArtd.isProfileUsable(deepEq(profile), any())).thenReturn(false); 666 lenient() 667 .when(mArtd.copyAndRewriteProfile(deepEq(profile), any(), any())) 668 .thenReturn(false); 669 } 670 } 671