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.server.art; 18 19 import static android.app.ActivityManager.RunningAppProcessInfo; 20 import static android.os.ParcelFileDescriptor.AutoCloseInputStream; 21 22 import static com.android.server.art.DexUseManagerLocal.CheckedSecondaryDexInfo; 23 import static com.android.server.art.ProfilePath.PrimaryCurProfilePath; 24 import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult; 25 import static com.android.server.art.model.DexoptResult.PackageDexoptResult; 26 import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus; 27 import static com.android.server.art.testing.TestingUtils.deepEq; 28 import static com.android.server.art.testing.TestingUtils.inAnyOrder; 29 import static com.android.server.art.testing.TestingUtils.inAnyOrderDeepEquals; 30 31 import static com.google.common.truth.Truth.assertThat; 32 33 import static org.mockito.AdditionalMatchers.not; 34 import static org.mockito.ArgumentMatchers.anyString; 35 import static org.mockito.ArgumentMatchers.eq; 36 import static org.mockito.ArgumentMatchers.matches; 37 import static org.mockito.Mockito.any; 38 import static org.mockito.Mockito.anyBoolean; 39 import static org.mockito.Mockito.anyInt; 40 import static org.mockito.Mockito.argThat; 41 import static org.mockito.Mockito.doReturn; 42 import static org.mockito.Mockito.inOrder; 43 import static org.mockito.Mockito.isNull; 44 import static org.mockito.Mockito.lenient; 45 import static org.mockito.Mockito.mock; 46 import static org.mockito.Mockito.never; 47 import static org.mockito.Mockito.same; 48 import static org.mockito.Mockito.times; 49 import static org.mockito.Mockito.verify; 50 import static org.mockito.Mockito.when; 51 52 import android.app.ActivityManager; 53 import android.apphibernation.AppHibernationManager; 54 import android.content.BroadcastReceiver; 55 import android.content.Context; 56 import android.content.Intent; 57 import android.os.CancellationSignal; 58 import android.os.ParcelFileDescriptor; 59 import android.os.Process; 60 import android.os.ServiceSpecificException; 61 import android.os.SystemProperties; 62 import android.os.UserHandle; 63 import android.os.UserManager; 64 import android.os.storage.StorageManager; 65 import android.system.OsConstants; 66 67 import androidx.test.filters.SmallTest; 68 69 import com.android.modules.utils.pm.PackageStateModulesUtils; 70 import com.android.server.art.model.ArtFlags; 71 import com.android.server.art.model.ArtManagedFileStats; 72 import com.android.server.art.model.BatchDexoptParams; 73 import com.android.server.art.model.Config; 74 import com.android.server.art.model.DeleteResult; 75 import com.android.server.art.model.DexoptParams; 76 import com.android.server.art.model.DexoptResult; 77 import com.android.server.art.model.DexoptStatus; 78 import com.android.server.art.prereboot.PreRebootStatsReporter; 79 import com.android.server.art.proto.DexMetadataConfig; 80 import com.android.server.art.testing.StaticMockitoRule; 81 import com.android.server.art.testing.TestingUtils; 82 import com.android.server.pm.PackageManagerLocal; 83 import com.android.server.pm.pkg.AndroidPackage; 84 import com.android.server.pm.pkg.AndroidPackageSplit; 85 import com.android.server.pm.pkg.PackageState; 86 import com.android.server.pm.pkg.PackageUserState; 87 88 import org.junit.Before; 89 import org.junit.Rule; 90 import org.junit.Test; 91 import org.junit.runner.RunWith; 92 import org.junit.runners.Parameterized; 93 import org.junit.runners.Parameterized.Parameter; 94 import org.junit.runners.Parameterized.Parameters; 95 import org.mockito.ArgumentCaptor; 96 import org.mockito.InOrder; 97 import org.mockito.Mock; 98 99 import java.io.File; 100 import java.io.FileWriter; 101 import java.io.IOException; 102 import java.io.InputStream; 103 import java.nio.charset.StandardCharsets; 104 import java.nio.file.Files; 105 import java.nio.file.NoSuchFileException; 106 import java.nio.file.Path; 107 import java.util.List; 108 import java.util.Map; 109 import java.util.Set; 110 import java.util.concurrent.Executor; 111 import java.util.concurrent.ForkJoinPool; 112 import java.util.concurrent.TimeUnit; 113 import java.util.function.Consumer; 114 import java.util.stream.Collectors; 115 import java.util.zip.ZipFile; 116 117 @SmallTest 118 @RunWith(Parameterized.class) 119 public class ArtManagerLocalTest { 120 private static final String PKG_NAME_1 = "com.example.foo"; 121 private static final String PKG_NAME_2 = "com.android.bar"; 122 private static final String PKG_NAME_HIBERNATING = "com.example.hibernating"; 123 private static final int INACTIVE_DAYS = 1; 124 private static final long CURRENT_TIME_MS = 10000000000l; 125 private static final long RECENT_TIME_MS = 126 CURRENT_TIME_MS - TimeUnit.DAYS.toMillis(INACTIVE_DAYS) + 1; 127 private static final long NOT_RECENT_TIME_MS = 128 CURRENT_TIME_MS - TimeUnit.DAYS.toMillis(INACTIVE_DAYS) - 1; 129 private static final int APP_ID = 1000; 130 131 @Rule 132 public StaticMockitoRule mockitoRule = new StaticMockitoRule( 133 SystemProperties.class, Constants.class, PackageStateModulesUtils.class); 134 135 @Mock private ArtManagerLocal.Injector mInjector; 136 @Mock private ArtFileManager.Injector mArtFileManagerInjector; 137 @Mock private PackageManagerLocal mPackageManagerLocal; 138 @Mock private PackageManagerLocal.FilteredSnapshot mSnapshot; 139 @Mock private IArtd mArtd; 140 @Mock private DexoptHelper mDexoptHelper; 141 @Mock private AppHibernationManager mAppHibernationManager; 142 @Mock private UserManager mUserManager; 143 @Mock private DexUseManagerLocal mDexUseManager; 144 @Mock private StorageManager mStorageManager; 145 @Mock private ArtdRefCache.Pin mArtdPin; 146 @Mock private DexMetadataHelper.Injector mDexMetadataHelperInjector; 147 @Mock private Context mContext; 148 @Mock private PreRebootDexoptJob mPreRebootDexoptJob; 149 @Mock private PreRebootStatsReporter.Injector mPreRebootStatsReporterInjector; 150 @Mock private ActivityManager mActivityManager; 151 private PackageState mPkgState1; 152 private AndroidPackage mPkg1; 153 private CheckedSecondaryDexInfo mPkg1SecondaryDexInfo1; 154 private CheckedSecondaryDexInfo mPkg1SecondaryDexInfoNotFound; 155 private Config mConfig; 156 private DexMetadataHelper mDexMetadataHelper; 157 private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; 158 159 // True if the artifacts should be in dalvik-cache. 160 @Parameter(0) public boolean mIsInDalvikCache; 161 162 private ArtManagerLocal mArtManagerLocal; 163 164 @Parameters(name = "mIsInDalvikCache={0}") data()165 public static Iterable<Object[]> data() { 166 return List.of(new Object[] {true}, new Object[] {false}); 167 } 168 169 @Before setUp()170 public void setUp() throws Exception { 171 mConfig = new Config(); 172 mDexMetadataHelper = new DexMetadataHelper(mDexMetadataHelperInjector); 173 174 // Use `lenient()` to suppress `UnnecessaryStubbingException` thrown by the strict stubs. 175 // These are the default test setups. They may or may not be used depending on the code path 176 // that each test case examines. 177 lenient().when(mInjector.getPackageManagerLocal()).thenReturn(mPackageManagerLocal); 178 lenient().when(mInjector.getArtd()).thenReturn(mArtd); 179 lenient().when(mInjector.createArtdPin()).thenReturn(mArtdPin); 180 lenient().when(mInjector.getDexoptHelper()).thenReturn(mDexoptHelper); 181 lenient().when(mInjector.getConfig()).thenReturn(mConfig); 182 lenient().when(mInjector.getAppHibernationManager()).thenReturn(mAppHibernationManager); 183 lenient().when(mInjector.getUserManager()).thenReturn(mUserManager); 184 lenient().when(mInjector.isSystemUiPackage(any())).thenReturn(false); 185 lenient().when(mInjector.isLauncherPackage(any())).thenReturn(false); 186 lenient().when(mInjector.getDexUseManager()).thenReturn(mDexUseManager); 187 lenient().when(mInjector.getCurrentTimeMillis()).thenReturn(CURRENT_TIME_MS); 188 lenient().when(mInjector.getStorageManager()).thenReturn(mStorageManager); 189 lenient() 190 .when(mInjector.getArtFileManager()) 191 .thenReturn(new ArtFileManager(mArtFileManagerInjector)); 192 lenient().when(mInjector.getDexMetadataHelper()).thenReturn(mDexMetadataHelper); 193 lenient().when(mInjector.getContext()).thenReturn(mContext); 194 lenient().when(mInjector.getPreRebootDexoptJob()).thenReturn(mPreRebootDexoptJob); 195 lenient() 196 .when(mInjector.getPreRebootStatsReporter()) 197 .thenAnswer( 198 invocation -> new PreRebootStatsReporter(mPreRebootStatsReporterInjector)); 199 lenient().when(mInjector.getActivityManager()).thenReturn(mActivityManager); 200 201 lenient().when(mArtFileManagerInjector.getArtd()).thenReturn(mArtd); 202 lenient().when(mArtFileManagerInjector.getUserManager()).thenReturn(mUserManager); 203 lenient().when(mArtFileManagerInjector.getDexUseManager()).thenReturn(mDexUseManager); 204 lenient().when(mArtFileManagerInjector.isSystemOrRootOrShell()).thenReturn(true); 205 206 Path tempDir = Files.createTempDirectory("temp"); 207 tempDir.toFile().deleteOnExit(); 208 lenient().when(mInjector.getTempDir()).thenReturn(tempDir.toString()); 209 210 lenient().when(SystemProperties.get(eq("pm.dexopt.install"))).thenReturn("speed-profile"); 211 lenient().when(SystemProperties.get(eq("pm.dexopt.bg-dexopt"))).thenReturn("speed-profile"); 212 lenient().when(SystemProperties.get(eq("pm.dexopt.first-boot"))).thenReturn("verify"); 213 lenient().when(SystemProperties.get(eq("pm.dexopt.boot-after-ota"))).thenReturn("verify"); 214 lenient() 215 .when(SystemProperties.get(eq("pm.dexopt.boot-after-mainline-update"))) 216 .thenReturn("verify"); 217 lenient().when(SystemProperties.get(eq("pm.dexopt.inactive"))).thenReturn("verify"); 218 lenient() 219 .when(SystemProperties.getInt(matches("pm\\.dexopt\\..*\\.concurrency"), anyInt())) 220 .thenReturn(3); 221 lenient() 222 .when(SystemProperties.getInt( 223 matches("persist\\.device_config\\.runtime\\..*_concurrency"), anyInt())) 224 .thenReturn(3); 225 lenient() 226 .when(SystemProperties.getInt( 227 eq("pm.dexopt.downgrade_after_inactive_days"), anyInt())) 228 .thenReturn(INACTIVE_DAYS); 229 230 // No ISA translation. 231 lenient() 232 .when(SystemProperties.get(argThat(arg -> arg.startsWith("ro.dalvik.vm.isa.")))) 233 .thenReturn(""); 234 235 lenient().when(Constants.getPreferredAbi()).thenReturn("arm64-v8a"); 236 lenient().when(Constants.getNative64BitAbi()).thenReturn("arm64-v8a"); 237 lenient().when(Constants.getNative32BitAbi()).thenReturn("armeabi-v7a"); 238 lenient().when(Constants.isBootImageProfilingEnabled()).thenReturn(true); 239 240 lenient().when(mAppHibernationManager.isHibernatingGlobally(any())).thenReturn(false); 241 lenient().when(mAppHibernationManager.isOatArtifactDeletionEnabled()).thenReturn(true); 242 243 lenient() 244 .when(mUserManager.getUserHandles(anyBoolean())) 245 .thenReturn(List.of(UserHandle.of(0), UserHandle.of(1))); 246 247 // All packages are by default recently used. 248 lenient().when(mDexUseManager.getPackageLastUsedAtMs(any())).thenReturn(RECENT_TIME_MS); 249 mPkg1SecondaryDexInfo1 = createSecondaryDexInfo("/data/user/0/foo/1.apk", UserHandle.of(0)); 250 mPkg1SecondaryDexInfoNotFound = 251 createSecondaryDexInfo("/data/user/0/foo/not_found.apk", UserHandle.of(0)); 252 lenient() 253 .doReturn(List.of(mPkg1SecondaryDexInfo1, mPkg1SecondaryDexInfoNotFound)) 254 .when(mDexUseManager) 255 .getSecondaryDexInfo(eq(PKG_NAME_1)); 256 lenient() 257 .doReturn(List.of(mPkg1SecondaryDexInfo1, mPkg1SecondaryDexInfoNotFound)) 258 .when(mDexUseManager) 259 .getCheckedSecondaryDexInfo( 260 eq(PKG_NAME_1), eq(false) /* excludeObsoleteDexesAndLoaders */); 261 lenient() 262 .doReturn(List.of(mPkg1SecondaryDexInfo1)) 263 .when(mDexUseManager) 264 .getCheckedSecondaryDexInfo( 265 eq(PKG_NAME_1), eq(true) /* excludeObsoleteDexesAndLoaders */); 266 267 simulateStorageNotLow(); 268 269 lenient().when(mPackageManagerLocal.withFilteredSnapshot()).thenReturn(mSnapshot); 270 List<PackageState> pkgStates = createPackageStates(); 271 for (PackageState pkgState : pkgStates) { 272 lenient() 273 .when(mSnapshot.getPackageState(pkgState.getPackageName())) 274 .thenReturn(pkgState); 275 } 276 var packageStateMap = pkgStates.stream().collect( 277 Collectors.toMap(PackageState::getPackageName, it -> it)); 278 lenient().when(mSnapshot.getPackageStates()).thenReturn(packageStateMap); 279 mPkgState1 = mSnapshot.getPackageState(PKG_NAME_1); 280 mPkg1 = mPkgState1.getAndroidPackage(); 281 282 lenient().when(mArtd.isInDalvikCache(any())).thenReturn(mIsInDalvikCache); 283 284 // By default, none of the profiles are usable. 285 lenient().when(mArtd.isProfileUsable(any(), any())).thenReturn(false); 286 lenient() 287 .when(mArtd.copyAndRewriteProfile(any(), any(), any())) 288 .thenReturn(TestingUtils.createCopyAndRewriteProfileNoProfile()); 289 lenient() 290 .when(mArtd.copyAndRewriteEmbeddedProfile(any(), any())) 291 .thenReturn(TestingUtils.createCopyAndRewriteProfileNoProfile()); 292 293 lenient() 294 .when(mDexMetadataHelperInjector.openZipFile(any())) 295 .thenThrow(NoSuchFileException.class); 296 297 mBroadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); 298 lenient() 299 .when(mContext.registerReceiver(mBroadcastReceiverCaptor.capture(), any())) 300 .thenReturn(mock(Intent.class)); 301 302 File tempFile = File.createTempFile("pre-reboot-stats", ".pb"); 303 tempFile.deleteOnExit(); 304 lenient() 305 .when(mPreRebootStatsReporterInjector.getFilename()) 306 .thenReturn(tempFile.getAbsolutePath()); 307 308 mArtManagerLocal = new ArtManagerLocal(mInjector); 309 } 310 311 @Test testDeleteDexoptArtifacts()312 public void testDeleteDexoptArtifacts() throws Exception { 313 final long DEXOPT_ARTIFACTS_FREED = 1l; 314 final long RUNTIME_ARTIFACTS_FREED = 100l; 315 316 when(mArtd.deleteArtifacts(any())).thenReturn(DEXOPT_ARTIFACTS_FREED); 317 when(mArtd.deleteRuntimeArtifacts(any())).thenReturn(RUNTIME_ARTIFACTS_FREED); 318 319 DeleteResult result = mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME_1); 320 assertThat(result.getFreedBytes()) 321 .isEqualTo(6 * DEXOPT_ARTIFACTS_FREED + 4 * RUNTIME_ARTIFACTS_FREED); 322 323 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 324 "/somewhere/app/foo/base.apk", "arm64", mIsInDalvikCache))); 325 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 326 "/somewhere/app/foo/base.apk", "arm", mIsInDalvikCache))); 327 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 328 "/somewhere/app/foo/split_0.apk", "arm64", mIsInDalvikCache))); 329 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 330 "/somewhere/app/foo/split_0.apk", "arm", mIsInDalvikCache))); 331 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 332 "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */))); 333 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 334 "/data/user/0/foo/not_found.apk", "arm64", false /* isInDalvikCache */))); 335 336 verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 337 "/somewhere/app/foo/base.apk", "arm64", mIsInDalvikCache))); 338 verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 339 "/somewhere/app/foo/base.apk", "arm", mIsInDalvikCache))); 340 verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 341 "/somewhere/app/foo/split_0.apk", "arm64", mIsInDalvikCache))); 342 verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 343 "/somewhere/app/foo/split_0.apk", "arm", mIsInDalvikCache))); 344 345 verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( 346 PKG_NAME_1, "/somewhere/app/foo/base.apk", "arm64"))); 347 verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( 348 PKG_NAME_1, "/somewhere/app/foo/base.apk", "arm"))); 349 verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( 350 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm64"))); 351 verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( 352 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm"))); 353 354 // Verify that there are no more calls than the ones above. 355 verify(mArtd, times(6)).deleteArtifacts(any()); 356 verify(mArtd, times(4)).deleteSdmSdcFiles(any()); 357 verify(mArtd, times(4)).deleteRuntimeArtifacts(any()); 358 } 359 360 @Test testDeleteDexoptArtifactsTranslatedIsas()361 public void testDeleteDexoptArtifactsTranslatedIsas() throws Exception { 362 final long DEXOPT_ARTIFACTS_FREED = 1l; 363 final long RUNTIME_ARTIFACTS_FREED = 100l; 364 365 lenient().when(SystemProperties.get("ro.dalvik.vm.isa.arm64")).thenReturn("x86_64"); 366 lenient().when(SystemProperties.get("ro.dalvik.vm.isa.arm")).thenReturn("x86"); 367 lenient().when(Constants.getPreferredAbi()).thenReturn("x86_64"); 368 lenient().when(Constants.getNative64BitAbi()).thenReturn("x86_64"); 369 lenient().when(Constants.getNative32BitAbi()).thenReturn("x86"); 370 when(mPkg1SecondaryDexInfo1.abiNames()).thenReturn(Set.of("x86_64")); 371 when(mPkg1SecondaryDexInfoNotFound.abiNames()).thenReturn(Set.of("x86_64")); 372 373 when(mArtd.deleteArtifacts(any())).thenReturn(DEXOPT_ARTIFACTS_FREED); 374 when(mArtd.deleteRuntimeArtifacts(any())).thenReturn(RUNTIME_ARTIFACTS_FREED); 375 376 DeleteResult result = mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME_1); 377 assertThat(result.getFreedBytes()) 378 .isEqualTo(6 * DEXOPT_ARTIFACTS_FREED + 4 * RUNTIME_ARTIFACTS_FREED); 379 380 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 381 "/somewhere/app/foo/base.apk", "x86_64", mIsInDalvikCache))); 382 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 383 "/somewhere/app/foo/base.apk", "x86", mIsInDalvikCache))); 384 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 385 "/somewhere/app/foo/split_0.apk", "x86_64", mIsInDalvikCache))); 386 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 387 "/somewhere/app/foo/split_0.apk", "x86", mIsInDalvikCache))); 388 389 verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( 390 PKG_NAME_1, "/somewhere/app/foo/base.apk", "x86_64"))); 391 verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( 392 PKG_NAME_1, "/somewhere/app/foo/base.apk", "x86"))); 393 verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( 394 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "x86_64"))); 395 verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( 396 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "x86"))); 397 398 // We assume that the ISA got from `DexUseManagerLocal` is already the translated one. 399 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 400 "/data/user/0/foo/1.apk", "x86_64", false /* isInDalvikCache */))); 401 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 402 "/data/user/0/foo/not_found.apk", "x86_64", false /* isInDalvikCache */))); 403 404 // Verify that there are no more calls than the ones above. 405 verify(mArtd, times(6)).deleteArtifacts(any()); 406 verify(mArtd, times(4)).deleteRuntimeArtifacts(any()); 407 } 408 409 @Test(expected = IllegalArgumentException.class) testdeleteDexoptArtifactsPackageNotFound()410 public void testdeleteDexoptArtifactsPackageNotFound() throws Exception { 411 when(mSnapshot.getPackageState(anyString())).thenReturn(null); 412 413 mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME_1); 414 } 415 416 @Test(expected = IllegalArgumentException.class) testdeleteDexoptArtifactsNoPackage()417 public void testdeleteDexoptArtifactsNoPackage() throws Exception { 418 when(mPkgState1.getAndroidPackage()).thenReturn(null); 419 420 mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME_1); 421 } 422 423 @Test testGetDexoptStatus()424 public void testGetDexoptStatus() throws Exception { 425 doReturn(createGetDexoptStatusResult("speed", "compilation-reason-0", 426 "location-debug-string-0", ArtifactsLocation.NEXT_TO_DEX)) 427 .when(mArtd) 428 .getDexoptStatus("/somewhere/app/foo/base.apk", "arm64", "PCL[]"); 429 doReturn(createGetDexoptStatusResult("speed-profile", "compilation-reason-1", 430 "location-debug-string-1", ArtifactsLocation.NEXT_TO_DEX)) 431 .when(mArtd) 432 .getDexoptStatus("/somewhere/app/foo/base.apk", "arm", "PCL[]"); 433 doReturn(createGetDexoptStatusResult("verify", "compilation-reason-2", 434 "location-debug-string-2", ArtifactsLocation.NEXT_TO_DEX)) 435 .when(mArtd) 436 .getDexoptStatus("/somewhere/app/foo/split_0.apk", "arm64", "PCL[base.apk]"); 437 doReturn(createGetDexoptStatusResult("extract", "compilation-reason-3", 438 "location-debug-string-3", ArtifactsLocation.NEXT_TO_DEX)) 439 .when(mArtd) 440 .getDexoptStatus("/somewhere/app/foo/split_0.apk", "arm", "PCL[base.apk]"); 441 doReturn(createGetDexoptStatusResult( 442 "run-from-apk", "unknown", "unknown", ArtifactsLocation.NEXT_TO_DEX)) 443 .when(mArtd) 444 .getDexoptStatus("/data/user/0/foo/1.apk", "arm64", "CLC"); 445 doReturn(createGetDexoptStatusResult( 446 "unknown", "unknown", "error", ArtifactsLocation.NONE_OR_ERROR)) 447 .when(mArtd) 448 .getDexoptStatus("/data/user/0/foo/not_found.apk", "arm64", "CLC"); 449 450 DexoptStatus result = mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME_1); 451 452 assertThat(result.getDexContainerFileDexoptStatuses()) 453 .comparingElementsUsing(TestingUtils.<DexContainerFileDexoptStatus>deepEquality()) 454 .containsExactly( 455 DexContainerFileDexoptStatus.create("/somewhere/app/foo/base.apk", 456 true /* isPrimaryDex */, true /* isPrimaryAbi */, "arm64-v8a", 457 "speed", "compilation-reason-0", "location-debug-string-0"), 458 DexContainerFileDexoptStatus.create("/somewhere/app/foo/base.apk", 459 true /* isPrimaryDex */, false /* isPrimaryAbi */, "armeabi-v7a", 460 "speed-profile", "compilation-reason-1", "location-debug-string-1"), 461 DexContainerFileDexoptStatus.create("/somewhere/app/foo/split_0.apk", 462 true /* isPrimaryDex */, true /* isPrimaryAbi */, "arm64-v8a", 463 "verify", "compilation-reason-2", "location-debug-string-2"), 464 DexContainerFileDexoptStatus.create("/somewhere/app/foo/split_0.apk", 465 true /* isPrimaryDex */, false /* isPrimaryAbi */, "armeabi-v7a", 466 "extract", "compilation-reason-3", "location-debug-string-3"), 467 DexContainerFileDexoptStatus.create("/data/user/0/foo/1.apk", 468 false /* isPrimaryDex */, true /* isPrimaryAbi */, "arm64-v8a", 469 "run-from-apk", "unknown", "unknown"), 470 DexContainerFileDexoptStatus.create("/data/user/0/foo/not_found.apk", 471 false /* isPrimaryDex */, true /* isPrimaryAbi */, "arm64-v8a", 472 "unknown", "unknown", "error")); 473 } 474 475 @Test(expected = IllegalArgumentException.class) testGetDexoptStatusPackageNotFound()476 public void testGetDexoptStatusPackageNotFound() throws Exception { 477 when(mSnapshot.getPackageState(anyString())).thenReturn(null); 478 479 mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME_1); 480 } 481 482 @Test(expected = IllegalArgumentException.class) testGetDexoptStatusNoPackage()483 public void testGetDexoptStatusNoPackage() throws Exception { 484 when(mPkgState1.getAndroidPackage()).thenReturn(null); 485 486 mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME_1); 487 } 488 489 @Test testGetDexoptStatusNonFatalError()490 public void testGetDexoptStatusNonFatalError() throws Exception { 491 when(mArtd.getDexoptStatus(any(), any(), any())) 492 .thenThrow(new ServiceSpecificException(1 /* errorCode */, "some error message")); 493 494 DexoptStatus result = mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME_1); 495 496 List<DexContainerFileDexoptStatus> statuses = result.getDexContainerFileDexoptStatuses(); 497 assertThat(statuses.size()).isEqualTo(6); 498 499 for (DexContainerFileDexoptStatus status : statuses) { 500 assertThat(status.getCompilerFilter()).isEqualTo("error"); 501 assertThat(status.getCompilationReason()).isEqualTo("error"); 502 assertThat(status.getLocationDebugString()).isEqualTo("some error message"); 503 } 504 } 505 506 @Test testClearAppProfiles()507 public void testClearAppProfiles() throws Exception { 508 mArtManagerLocal.clearAppProfiles(mSnapshot, PKG_NAME_1); 509 510 verify(mArtd).deleteProfile( 511 deepEq(AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"))); 512 verify(mArtd).deleteProfile(deepEq( 513 AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME_1, "primary"))); 514 verify(mArtd).deleteProfile(deepEq( 515 AidlUtils.buildProfilePathForPrimaryCur(1 /* userId */, PKG_NAME_1, "primary"))); 516 517 verify(mArtd).deleteProfile( 518 deepEq(AidlUtils.buildProfilePathForSecondaryRefAsInput("/data/user/0/foo/1.apk"))); 519 verify(mArtd).deleteProfile( 520 deepEq(AidlUtils.buildProfilePathForSecondaryCur("/data/user/0/foo/1.apk"))); 521 522 verify(mArtd).deleteProfile(deepEq(AidlUtils.buildProfilePathForSecondaryRefAsInput( 523 "/data/user/0/foo/not_found.apk"))); 524 verify(mArtd).deleteProfile(deepEq( 525 AidlUtils.buildProfilePathForSecondaryCur("/data/user/0/foo/not_found.apk"))); 526 } 527 528 @Test(expected = IllegalArgumentException.class) testClearAppProfilesPackageNotFound()529 public void testClearAppProfilesPackageNotFound() throws Exception { 530 when(mSnapshot.getPackageState(anyString())).thenReturn(null); 531 532 mArtManagerLocal.clearAppProfiles(mSnapshot, PKG_NAME_1); 533 } 534 535 @Test(expected = IllegalArgumentException.class) testClearAppProfilesNoPackage()536 public void testClearAppProfilesNoPackage() throws Exception { 537 when(mPkgState1.getAndroidPackage()).thenReturn(null); 538 539 mArtManagerLocal.clearAppProfiles(mSnapshot, PKG_NAME_1); 540 } 541 542 @Test testDexoptPackage()543 public void testDexoptPackage() throws Exception { 544 var params = new DexoptParams.Builder("install").build(); 545 var result = DexoptResult.create(); 546 var cancellationSignal = new CancellationSignal(); 547 548 when(mDexoptHelper.dexopt(any(), deepEq(List.of(PKG_NAME_1)), same(params), 549 same(cancellationSignal), any())) 550 .thenReturn(result); 551 552 assertThat( 553 mArtManagerLocal.dexoptPackage(mSnapshot, PKG_NAME_1, params, cancellationSignal)) 554 .isSameInstanceAs(result); 555 } 556 557 @Test testResetDexoptStatus()558 public void testResetDexoptStatus() throws Exception { 559 var result = DexoptResult.create(); 560 var cancellationSignal = new CancellationSignal(); 561 562 when(mDexoptHelper.dexopt( 563 any(), deepEq(List.of(PKG_NAME_1)), any(), same(cancellationSignal), any())) 564 .thenReturn(result); 565 566 assertThat(mArtManagerLocal.resetDexoptStatus(mSnapshot, PKG_NAME_1, cancellationSignal)) 567 .isSameInstanceAs(result); 568 569 verify(mArtd).deleteProfile( 570 deepEq(AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"))); 571 verify(mArtd).deleteProfile(deepEq( 572 AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME_1, "primary"))); 573 verify(mArtd).deleteProfile(deepEq( 574 AidlUtils.buildProfilePathForPrimaryCur(1 /* userId */, PKG_NAME_1, "primary"))); 575 576 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 577 "/somewhere/app/foo/base.apk", "arm64", mIsInDalvikCache))); 578 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 579 "/somewhere/app/foo/base.apk", "arm", mIsInDalvikCache))); 580 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 581 "/somewhere/app/foo/split_0.apk", "arm64", mIsInDalvikCache))); 582 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 583 "/somewhere/app/foo/split_0.apk", "arm", mIsInDalvikCache))); 584 585 verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 586 "/somewhere/app/foo/base.apk", "arm64", mIsInDalvikCache))); 587 verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 588 "/somewhere/app/foo/base.apk", "arm", mIsInDalvikCache))); 589 verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 590 "/somewhere/app/foo/split_0.apk", "arm64", mIsInDalvikCache))); 591 verify(mArtd).deleteSdmSdcFiles(deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 592 "/somewhere/app/foo/split_0.apk", "arm", mIsInDalvikCache))); 593 594 verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( 595 PKG_NAME_1, "/somewhere/app/foo/base.apk", "arm64"))); 596 verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( 597 PKG_NAME_1, "/somewhere/app/foo/base.apk", "arm"))); 598 verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( 599 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm64"))); 600 verify(mArtd).deleteRuntimeArtifacts(deepEq(AidlUtils.buildRuntimeArtifactsPath( 601 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm"))); 602 603 verify(mArtd).deleteProfile( 604 deepEq(AidlUtils.buildProfilePathForSecondaryRefAsInput("/data/user/0/foo/1.apk"))); 605 verify(mArtd).deleteProfile( 606 deepEq(AidlUtils.buildProfilePathForSecondaryCur("/data/user/0/foo/1.apk"))); 607 608 verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPathAsInput( 609 "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */))); 610 } 611 612 @Test testDexoptPackages()613 public void testDexoptPackages() throws Exception { 614 var dexoptResult = DexoptResult.create(); 615 var cancellationSignal = new CancellationSignal(); 616 when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_2)).thenReturn(CURRENT_TIME_MS); 617 simulateStorageLow(); 618 619 // It should use the default package list and params. The list is sorted by last active 620 // time in descending order. 621 doReturn(dexoptResult) 622 .when(mDexoptHelper) 623 .dexopt(any(), deepEq(List.of(PKG_NAME_2, PKG_NAME_1)), 624 argThat(params -> params.getReason().equals("bg-dexopt")), 625 same(cancellationSignal), any(), any(), any()); 626 627 assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal, 628 null /* processCallbackExecutor */, null /* processCallback */)) 629 .isEqualTo(Map.of(ArtFlags.PASS_MAIN, dexoptResult)); 630 631 // Nothing to downgrade. 632 verify(mDexoptHelper, never()) 633 .dexopt(any(), any(), argThat(params -> params.getReason().equals("inactive")), 634 any(), any(), any(), any()); 635 } 636 637 @Test testDexoptPackagesRecentlyInstalled()638 public void testDexoptPackagesRecentlyInstalled() throws Exception { 639 // The package is recently installed but hasn't been used. 640 PackageUserState userState = mPkgState1.getStateForUser(UserHandle.of(1)); 641 when(userState.getFirstInstallTimeMillis()).thenReturn(RECENT_TIME_MS); 642 when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(0l); 643 simulateStorageLow(); 644 645 var result = DexoptResult.create(); 646 var cancellationSignal = new CancellationSignal(); 647 648 // PKG_NAME_1 should be dexopted. 649 doReturn(result) 650 .when(mDexoptHelper) 651 .dexopt(any(), inAnyOrder(PKG_NAME_1, PKG_NAME_2), 652 argThat(params -> params.getReason().equals("bg-dexopt")), any(), any(), 653 any(), any()); 654 655 mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal, 656 null /* processCallbackExecutor */, null /* processCallback */); 657 658 // PKG_NAME_1 should not be downgraded. 659 verify(mDexoptHelper, never()) 660 .dexopt(any(), any(), argThat(params -> params.getReason().equals("inactive")), 661 any(), any(), any(), any()); 662 } 663 664 @Test testDexoptPackagesInactive()665 public void testDexoptPackagesInactive() throws Exception { 666 // PKG_NAME_1 is neither recently installed nor recently used. 667 PackageUserState userState = mPkgState1.getStateForUser(UserHandle.of(1)); 668 when(userState.getFirstInstallTimeMillis()).thenReturn(NOT_RECENT_TIME_MS); 669 when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(NOT_RECENT_TIME_MS); 670 simulateStorageLow(); 671 672 var mainResult = DexoptResult.create(); 673 var downgradeResult = DexoptResult.create(); 674 var cancellationSignal = new CancellationSignal(); 675 676 // PKG_NAME_1 should not be dexopted. 677 doReturn(mainResult) 678 .when(mDexoptHelper) 679 .dexopt(any(), deepEq(List.of(PKG_NAME_2)), 680 argThat(params -> params.getReason().equals("bg-dexopt")), any(), any(), 681 any(), any()); 682 683 // PKG_NAME_1 should be downgraded. 684 doReturn(downgradeResult) 685 .when(mDexoptHelper) 686 .dexopt(any(), deepEq(List.of(PKG_NAME_1)), 687 argThat(params -> params.getReason().equals("inactive")), any(), any(), 688 any(), any()); 689 690 assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal, 691 null /* processCallbackExecutor */, null /* processCallback */)) 692 .isEqualTo(Map.of( 693 ArtFlags.PASS_DOWNGRADE, downgradeResult, ArtFlags.PASS_MAIN, mainResult)); 694 } 695 696 @Test testDexoptPackagesInactiveStorageNotLow()697 public void testDexoptPackagesInactiveStorageNotLow() throws Exception { 698 // PKG_NAME_1 is neither recently installed nor recently used. 699 PackageUserState userState = mPkgState1.getStateForUser(UserHandle.of(1)); 700 when(userState.getFirstInstallTimeMillis()).thenReturn(NOT_RECENT_TIME_MS); 701 when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(NOT_RECENT_TIME_MS); 702 703 var result = DexoptResult.create(); 704 var cancellationSignal = new CancellationSignal(); 705 706 // PKG_NAME_1 should not be dexopted. 707 doReturn(result) 708 .when(mDexoptHelper) 709 .dexopt(any(), deepEq(List.of(PKG_NAME_2)), 710 argThat(params -> params.getReason().equals("bg-dexopt")), any(), any(), 711 any(), any()); 712 713 mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal, 714 null /* processCallbackExecutor */, null /* processCallback */); 715 716 // PKG_NAME_1 should not be downgraded because the storage is not low. 717 verify(mDexoptHelper, never()) 718 .dexopt(any(), any(), argThat(params -> params.getReason().equals("inactive")), 719 any(), any(), any(), any()); 720 } 721 722 @Test testDexoptPackagesFirstBoot()723 public void testDexoptPackagesFirstBoot() throws Exception { 724 // On first-boot all packages haven't been used and first install time is 725 // 0 which simulates case of system time being advanced by 726 // AlarmManagerService after package installation 727 lenient().when(mDexUseManager.getPackageLastUsedAtMs(any())).thenReturn(0l); 728 729 var result = DexoptResult.create(); 730 var cancellationSignal = new CancellationSignal(); 731 732 // PKG_NAME_1 and PKG_NAME_2 should be dexopted. 733 doReturn(result) 734 .when(mDexoptHelper) 735 .dexopt(any(), inAnyOrder(PKG_NAME_1, PKG_NAME_2), 736 argThat(params -> params.getReason().equals("first-boot")), any(), any(), 737 any(), any()); 738 739 mArtManagerLocal.dexoptPackages(mSnapshot, "first-boot", cancellationSignal, 740 null /* processCallbackExecutor */, null /* processCallback */); 741 } 742 743 @Test testDexoptPackagesBootAfterMainlineUpdate()744 public void testDexoptPackagesBootAfterMainlineUpdate() throws Exception { 745 var result = DexoptResult.create(); 746 var cancellationSignal = new CancellationSignal(); 747 748 lenient().when(mInjector.isSystemUiPackage(PKG_NAME_1)).thenReturn(true); 749 lenient().when(mInjector.isLauncherPackage(PKG_NAME_2)).thenReturn(true); 750 751 // It should dexopt the system UI and the launcher. 752 when(mDexoptHelper.dexopt( 753 any(), inAnyOrder(PKG_NAME_1, PKG_NAME_2), any(), any(), any(), any(), any())) 754 .thenReturn(result); 755 756 mArtManagerLocal.dexoptPackages(mSnapshot, "boot-after-mainline-update", cancellationSignal, 757 null /* processCallbackExecutor */, null /* processCallback */); 758 } 759 760 @Test testDexoptPackagesBootAfterMainlineUpdatePackagesNotFound()761 public void testDexoptPackagesBootAfterMainlineUpdatePackagesNotFound() throws Exception { 762 var result = DexoptResult.create(); 763 var cancellationSignal = new CancellationSignal(); 764 // PKG_NAME_1 is neither recently installed nor recently used. 765 PackageUserState userState = mPkgState1.getStateForUser(UserHandle.of(1)); 766 lenient().when(userState.getFirstInstallTimeMillis()).thenReturn(NOT_RECENT_TIME_MS); 767 lenient() 768 .when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)) 769 .thenReturn(NOT_RECENT_TIME_MS); 770 simulateStorageLow(); 771 772 // It should dexopt the system UI and the launcher, but they are not found. 773 when(mDexoptHelper.dexopt(any(), deepEq(List.of()), any(), any(), any(), any(), any())) 774 .thenReturn(result); 775 776 mArtManagerLocal.dexoptPackages(mSnapshot, "boot-after-mainline-update", cancellationSignal, 777 null /* processCallbackExecutor */, null /* processCallback */); 778 779 // It should never downgrade apps, even if the storage is low. 780 verify(mDexoptHelper, never()) 781 .dexopt(any(), any(), argThat(params -> params.getReason().equals("inactive")), 782 any(), any(), any(), any()); 783 } 784 785 @Test testDexoptPackagesOverride()786 public void testDexoptPackagesOverride() throws Exception { 787 // PKG_NAME_1 is neither recently installed nor recently used. 788 PackageUserState userState = mPkgState1.getStateForUser(UserHandle.of(1)); 789 when(userState.getFirstInstallTimeMillis()).thenReturn(NOT_RECENT_TIME_MS); 790 when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(NOT_RECENT_TIME_MS); 791 simulateStorageLow(); 792 793 var params = new DexoptParams.Builder("bg-dexopt").build(); 794 var result = DexoptResult.create(); 795 var cancellationSignal = new CancellationSignal(); 796 797 mArtManagerLocal.setBatchDexoptStartCallback(ForkJoinPool.commonPool(), 798 (snapshot, reason, defaultPackages, builder, passedSignal) -> { 799 assertThat(reason).isEqualTo("bg-dexopt"); 800 assertThat(defaultPackages).containsExactly(PKG_NAME_2); 801 assertThat(passedSignal).isSameInstanceAs(cancellationSignal); 802 builder.setPackages(List.of(PKG_NAME_1)).setDexoptParams(params); 803 }); 804 805 // It should use the overridden package list and params. 806 doReturn(result) 807 .when(mDexoptHelper) 808 .dexopt(any(), deepEq(List.of(PKG_NAME_1)), same(params), any(), any(), any(), 809 any()); 810 811 mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal, 812 null /* processCallbackExecutor */, null /* processCallback */); 813 814 // It should not downgrade PKG_NAME_1 because it's in the overridden package list. It should 815 // not downgrade PKG_NAME_2 either because it's not an inactive package. 816 verify(mDexoptHelper, never()) 817 .dexopt(any(), any(), argThat(params2 -> params2.getReason().equals("inactive")), 818 any(), any(), any(), any()); 819 } 820 821 @Test testDexoptPackagesOverrideCleared()822 public void testDexoptPackagesOverrideCleared() throws Exception { 823 var params = new DexoptParams.Builder("bg-dexopt").build(); 824 var result = DexoptResult.create(); 825 var cancellationSignal = new CancellationSignal(); 826 827 mArtManagerLocal.setBatchDexoptStartCallback(ForkJoinPool.commonPool(), 828 (snapshot, reason, defaultPackages, builder, passedSignal) -> { 829 builder.setPackages(List.of(PKG_NAME_1)).setDexoptParams(params); 830 }); 831 mArtManagerLocal.clearBatchDexoptStartCallback(); 832 833 // It should use the default package list and params. 834 when(mDexoptHelper.dexopt(any(), inAnyOrder(PKG_NAME_1, PKG_NAME_2), not(same(params)), 835 same(cancellationSignal), any(), any(), any())) 836 .thenReturn(result); 837 838 assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal, 839 null /* processCallbackExecutor */, null /* processCallback */)) 840 .isEqualTo(Map.of(ArtFlags.PASS_MAIN, result)); 841 } 842 843 @Test testDexoptPackagesSupplementaryPass()844 public void testDexoptPackagesSupplementaryPass() throws Exception { 845 // The supplementary pass should only try dexopting PKG_NAME_2. 846 var mainResult = DexoptResult.create("speed-profile", "bg-dexopt", 847 List.of(PackageDexoptResult.create(PKG_NAME_1, 848 List.of(DexContainerFileDexoptResult.create("dex-file-1", 849 true /* isPrimaryAbi */, "arm64", "speed-profile", 850 DexoptResult.DEXOPT_PERFORMED), 851 DexContainerFileDexoptResult.create("dex-file-2", 852 true /* isPrimaryAbi */, "arm64", "speed", 853 DexoptResult.DEXOPT_SKIPPED)), 854 null /* packageLevelStatus */), 855 PackageDexoptResult.create(PKG_NAME_2, 856 List.of(DexContainerFileDexoptResult.create("dex-file-1", 857 true /* isPrimaryAbi */, "arm64", "speed-profile", 858 DexoptResult.DEXOPT_PERFORMED), 859 DexContainerFileDexoptResult.create("dex-file-2", 860 true /* isPrimaryAbi */, "arm64", "speed-profile", 861 DexoptResult.DEXOPT_SKIPPED)), 862 null /* packageLevelStatus */))); 863 var supplementaryResult = DexoptResult.create(); 864 var cancellationSignal = new CancellationSignal(); 865 866 // The main pass. 867 doReturn(mainResult) 868 .when(mDexoptHelper) 869 .dexopt(any(), inAnyOrder(PKG_NAME_1, PKG_NAME_2), 870 argThat(params 871 -> params.getReason().equals("bg-dexopt") 872 && (params.getFlags() & ArtFlags.FLAG_FORCE_MERGE_PROFILE) 873 == 0), 874 any(), any(), any(), any()); 875 876 // The supplementary pass. 877 doReturn(supplementaryResult) 878 .when(mDexoptHelper) 879 .dexopt(any(), inAnyOrder(PKG_NAME_2), 880 argThat(params 881 -> params.getReason().equals("bg-dexopt") 882 && (params.getFlags() & ArtFlags.FLAG_FORCE_MERGE_PROFILE) 883 != 0), 884 any(), any(), any(), any()); 885 886 assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal, 887 null /* processCallbackExecutor */, null /* processCallback */)) 888 .isEqualTo(Map.of(ArtFlags.PASS_MAIN, mainResult, ArtFlags.PASS_SUPPLEMENTARY, 889 supplementaryResult)); 890 } 891 892 @Test(expected = IllegalStateException.class) testDexoptPackagesOverrideReasonChanged()893 public void testDexoptPackagesOverrideReasonChanged() throws Exception { 894 var params = new DexoptParams.Builder("first-boot").build(); 895 var cancellationSignal = new CancellationSignal(); 896 897 mArtManagerLocal.setBatchDexoptStartCallback(ForkJoinPool.commonPool(), 898 (snapshot, reason, defaultPackages, builder, passedSignal) -> { 899 builder.setDexoptParams(params); 900 }); 901 902 mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal, 903 null /* processCallbackExecutor */, null /* processCallback */); 904 } 905 906 @Test testDexoptPackagesWithParams()907 public void testDexoptPackagesWithParams() throws Exception { 908 var dexoptResult = DexoptResult.create(); 909 var cancellationSignal = new CancellationSignal(); 910 911 // It should only dexopt PKG_NAME_1 with "speed" as specified by the params. 912 doReturn(dexoptResult) 913 .when(mDexoptHelper) 914 .dexopt(any(), deepEq(List.of(PKG_NAME_1)), 915 argThat(params 916 -> params.getReason().equals("ab-ota") 917 && params.getCompilerFilter().equals("speed")), 918 same(cancellationSignal), any(), any(), any()); 919 920 BatchDexoptParams params = new BatchDexoptParams 921 .Builder(List.of(PKG_NAME_1), 922 new DexoptParams.Builder("ab-ota") 923 .setCompilerFilter("speed") 924 .build()) 925 .build(); 926 assertThat( 927 mArtManagerLocal.dexoptPackagesWithParams(mSnapshot, "ab-ota", cancellationSignal, 928 null /* processCallbackExecutor */, null /* processCallback */, params)) 929 .isEqualTo(Map.of(ArtFlags.PASS_MAIN, dexoptResult)); 930 } 931 932 @Test testSnapshotAppProfile()933 public void testSnapshotAppProfile() throws Exception { 934 var options = new MergeProfileOptions(); 935 options.forceMerge = true; 936 options.forBootImage = false; 937 938 File tempFile = File.createTempFile("primary", ".prof"); 939 tempFile.deleteOnExit(); 940 941 ProfilePath refProfile = 942 AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"); 943 String dexPath = "/somewhere/app/foo/base.apk"; 944 945 when(mArtd.isProfileUsable(deepEq(refProfile), eq(dexPath))).thenReturn(true); 946 947 when(mArtd.mergeProfiles(deepEq(List.of(refProfile, 948 AidlUtils.buildProfilePathForPrimaryCur( 949 0 /* userId */, PKG_NAME_1, "primary"), 950 AidlUtils.buildProfilePathForPrimaryCur( 951 1 /* userId */, PKG_NAME_1, "primary"))), 952 isNull(), 953 deepEq(AidlUtils.buildOutputProfileForPrimary(PKG_NAME_1, "primary", 954 Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */, 955 false /* isPreReboot */)), 956 deepEq(List.of(dexPath)), deepEq(options))) 957 .thenAnswer(invocation -> { 958 try (var writer = new FileWriter(tempFile)) { 959 writer.write("snapshot"); 960 } catch (IOException e) { 961 throw new RuntimeException(e); 962 } 963 var output = invocation.<OutputProfile>getArgument(2); 964 output.profilePath.tmpPath = tempFile.getPath(); 965 return true; 966 }); 967 968 ParcelFileDescriptor fd = 969 mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, null /* splitName */); 970 971 verify(mArtd).deleteProfile( 972 argThat(profile -> profile.getTmpProfilePath().tmpPath.equals(tempFile.getPath()))); 973 974 assertThat(fd.getStatSize()).isGreaterThan(0); 975 try (InputStream inputStream = new AutoCloseInputStream(fd)) { 976 String contents = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); 977 assertThat(contents).isEqualTo("snapshot"); 978 } 979 } 980 981 @Test testSnapshotAppProfileFromDm()982 public void testSnapshotAppProfileFromDm() throws Exception { 983 String tempPathForRef = "/temp/path/for/ref"; 984 File tempFileForSnapshot = File.createTempFile("primary", ".prof"); 985 tempFileForSnapshot.deleteOnExit(); 986 987 ProfilePath refProfile = 988 AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"); 989 String dexPath = "/somewhere/app/foo/base.apk"; 990 991 // Simulate that the reference profile doesn't exist. 992 when(mArtd.isProfileUsable(deepEq(refProfile), eq(dexPath))).thenReturn(false); 993 994 // The DM file is usable. 995 when(mArtd.copyAndRewriteProfile( 996 deepEq(AidlUtils.buildProfilePathForDm(dexPath)), any(), eq(dexPath))) 997 .thenAnswer(invocation -> { 998 var output = invocation.<OutputProfile>getArgument(1); 999 output.profilePath.tmpPath = tempPathForRef; 1000 return TestingUtils.createCopyAndRewriteProfileSuccess(); 1001 }); 1002 1003 // Verify that the reference file initialized from the DM file is used. 1004 when(mArtd.mergeProfiles( 1005 argThat(profiles 1006 -> profiles.stream().anyMatch(profile 1007 -> profile.getTag() == ProfilePath.tmpProfilePath 1008 && profile.getTmpProfilePath().tmpPath.equals( 1009 tempPathForRef))), 1010 isNull(), any(), deepEq(List.of(dexPath)), any())) 1011 .thenAnswer(invocation -> { 1012 var output = invocation.<OutputProfile>getArgument(2); 1013 output.profilePath.tmpPath = tempFileForSnapshot.getPath(); 1014 return true; 1015 }); 1016 1017 ParcelFileDescriptor fd = 1018 mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, null /* splitName */); 1019 1020 verify(mArtd).deleteProfile(argThat(profile 1021 -> profile.getTmpProfilePath().tmpPath.equals(tempFileForSnapshot.getPath()))); 1022 verify(mArtd).deleteProfile( 1023 argThat(profile -> profile.getTmpProfilePath().tmpPath.equals(tempPathForRef))); 1024 } 1025 1026 @Test testSnapshotAppProfileFromEmbeddedProfile()1027 public void testSnapshotAppProfileFromEmbeddedProfile() throws Exception { 1028 ProfilePath refProfile = 1029 AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"); 1030 String dexPath = "/somewhere/app/foo/base.apk"; 1031 1032 // Simulate that the reference profile doesn't exist. 1033 when(mArtd.isProfileUsable(deepEq(refProfile), eq(dexPath))).thenReturn(false); 1034 1035 // Verify that the embedded profile is used. 1036 when(mArtd.copyAndRewriteEmbeddedProfile(any(), eq(dexPath))) 1037 .thenReturn(TestingUtils.createCopyAndRewriteProfileSuccess()); 1038 1039 when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(false); 1040 1041 mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, null /* splitName */); 1042 } 1043 1044 @Test testSnapshotAppProfileDisableEmbeddedProfile()1045 public void testSnapshotAppProfileDisableEmbeddedProfile() throws Exception { 1046 ProfilePath refProfile = 1047 AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"); 1048 String dexPath = "/somewhere/app/foo/base.apk"; 1049 1050 // Simulate that the reference profile doesn't exist. 1051 when(mArtd.isProfileUsable(deepEq(refProfile), eq(dexPath))).thenReturn(false); 1052 1053 // The embedded profile is disabled. 1054 var config = DexMetadataConfig.newBuilder().setEnableEmbeddedProfile(false).build(); 1055 String dmPath = TestingUtils.createTempZipWithEntry("config.pb", config.toByteArray()); 1056 doReturn(new ZipFile(dmPath)).when(mDexMetadataHelperInjector).openZipFile(any()); 1057 1058 when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(false); 1059 1060 mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, null /* splitName */); 1061 1062 // Verify that the embedded profile is not used. 1063 verify(mArtd, never()).copyAndRewriteEmbeddedProfile(any(), eq(dexPath)); 1064 } 1065 1066 @Test testSnapshotAppProfileSplit()1067 public void testSnapshotAppProfileSplit() throws Exception { 1068 ProfilePath refProfile = 1069 AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "split_0.split"); 1070 String dexPath = "/somewhere/app/foo/split_0.apk"; 1071 1072 when(mArtd.isProfileUsable(deepEq(refProfile), eq(dexPath))).thenReturn(true); 1073 1074 when(mArtd.mergeProfiles(deepEq(List.of(refProfile, 1075 AidlUtils.buildProfilePathForPrimaryCur( 1076 0 /* userId */, PKG_NAME_1, "split_0.split"), 1077 AidlUtils.buildProfilePathForPrimaryCur( 1078 1 /* userId */, PKG_NAME_1, "split_0.split"))), 1079 isNull(), 1080 deepEq(AidlUtils.buildOutputProfileForPrimary(PKG_NAME_1, "split_0.split", 1081 Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */, 1082 false /* isPreReboot */)), 1083 deepEq(List.of(dexPath)), any())) 1084 .thenReturn(false); 1085 1086 mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, "split_0"); 1087 } 1088 1089 @Test testSnapshotAppProfileEmpty()1090 public void testSnapshotAppProfileEmpty() throws Exception { 1091 when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(false); 1092 1093 ParcelFileDescriptor fd = 1094 mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, null /* splitName */); 1095 1096 verify(mArtd, never()).deleteProfile(any()); 1097 1098 assertThat(fd.getStatSize()).isEqualTo(0); 1099 try (InputStream inputStream = new AutoCloseInputStream(fd)) { 1100 assertThat(inputStream.readAllBytes()).isEmpty(); 1101 } 1102 } 1103 1104 @Test(expected = IllegalArgumentException.class) testSnapshotAppProfilePackageNotFound()1105 public void testSnapshotAppProfilePackageNotFound() throws Exception { 1106 when(mSnapshot.getPackageState(anyString())).thenReturn(null); 1107 1108 mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, null /* splitName */); 1109 } 1110 1111 @Test(expected = IllegalArgumentException.class) testSnapshotAppProfileNoPackage()1112 public void testSnapshotAppProfileNoPackage() throws Exception { 1113 when(mPkgState1.getAndroidPackage()).thenReturn(null); 1114 1115 mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, null /* splitName */); 1116 } 1117 1118 @Test(expected = IllegalArgumentException.class) testSnapshotAppProfileSplitNotFound()1119 public void testSnapshotAppProfileSplitNotFound() throws Exception { 1120 mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, "non-existent-split"); 1121 } 1122 1123 @Test testDumpAppProfile()1124 public void testDumpAppProfile() throws Exception { 1125 var options = new MergeProfileOptions(); 1126 options.dumpOnly = true; 1127 1128 when(mArtd.mergeProfiles(any(), isNull(), any(), any(), deepEq(options))) 1129 .thenReturn(false); // A non-empty merge is tested in `testSnapshotAppProfile`. 1130 1131 ParcelFileDescriptor fd = mArtManagerLocal.dumpAppProfile( 1132 mSnapshot, PKG_NAME_1, null /* splitName */, false /* dumpClassesAndMethods */); 1133 } 1134 1135 @Test testDumpAppProfileDumpClassesAndMethods()1136 public void testDumpAppProfileDumpClassesAndMethods() throws Exception { 1137 var options = new MergeProfileOptions(); 1138 options.dumpClassesAndMethods = true; 1139 1140 when(mArtd.mergeProfiles(any(), isNull(), any(), any(), deepEq(options))) 1141 .thenReturn(false); // A non-empty merge is tested in `testSnapshotAppProfile`. 1142 1143 ParcelFileDescriptor fd = mArtManagerLocal.dumpAppProfile( 1144 mSnapshot, PKG_NAME_1, null /* splitName */, true /* dumpClassesAndMethods */); 1145 } 1146 1147 @Test testSnapshotBootImageProfile()1148 public void testSnapshotBootImageProfile() throws Exception { 1149 // `lenient()` is required to allow mocking the same method multiple times. 1150 lenient().when(Constants.getenv("BOOTCLASSPATH")).thenReturn("bcp0:bcp1"); 1151 lenient().when(Constants.getenv("SYSTEMSERVERCLASSPATH")).thenReturn("sscp0:sscp1"); 1152 lenient().when(Constants.getenv("STANDALONE_SYSTEMSERVER_JARS")).thenReturn("sssj0:sssj1"); 1153 1154 var options = new MergeProfileOptions(); 1155 options.forceMerge = true; 1156 options.forBootImage = true; 1157 1158 when(mArtd.mergeProfiles( 1159 inAnyOrderDeepEquals( 1160 AidlUtils.buildProfilePathForPrimaryRefAsInput("android", "primary"), 1161 AidlUtils.buildProfilePathForPrimaryCur( 1162 0 /* userId */, "android", "primary"), 1163 AidlUtils.buildProfilePathForPrimaryCur( 1164 1 /* userId */, "android", "primary"), 1165 AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"), 1166 AidlUtils.buildProfilePathForPrimaryCur( 1167 0 /* userId */, PKG_NAME_1, "primary"), 1168 AidlUtils.buildProfilePathForPrimaryCur( 1169 1 /* userId */, PKG_NAME_1, "primary"), 1170 AidlUtils.buildProfilePathForPrimaryRefAsInput( 1171 PKG_NAME_1, "split_0.split"), 1172 AidlUtils.buildProfilePathForPrimaryCur( 1173 0 /* userId */, PKG_NAME_1, "split_0.split"), 1174 AidlUtils.buildProfilePathForPrimaryCur( 1175 1 /* userId */, PKG_NAME_1, "split_0.split"), 1176 AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_2, "primary"), 1177 AidlUtils.buildProfilePathForPrimaryCur( 1178 0 /* userId */, PKG_NAME_2, "primary"), 1179 AidlUtils.buildProfilePathForPrimaryCur( 1180 1 /* userId */, PKG_NAME_2, "primary"), 1181 AidlUtils.buildProfilePathForPrimaryRefAsInput( 1182 PKG_NAME_HIBERNATING, "primary"), 1183 AidlUtils.buildProfilePathForPrimaryCur( 1184 0 /* userId */, PKG_NAME_HIBERNATING, "primary"), 1185 AidlUtils.buildProfilePathForPrimaryCur( 1186 1 /* userId */, PKG_NAME_HIBERNATING, "primary")), 1187 isNull(), 1188 deepEq(AidlUtils.buildOutputProfileForPrimary("android", "primary", 1189 Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */, 1190 false /* isPreReboot */)), 1191 deepEq(List.of("bcp0", "bcp1", "sscp0", "sscp1", "sssj0", "sssj1")), 1192 deepEq(options))) 1193 .thenReturn(false); // A non-empty merge is tested in `testSnapshotAppProfile`. 1194 1195 mArtManagerLocal.snapshotBootImageProfile(mSnapshot); 1196 } 1197 1198 @Test testOnBoot()1199 public void testOnBoot() throws Exception { 1200 var progressCallbackExecutor = mock(Executor.class); 1201 var progressCallback = mock(Consumer.class); 1202 1203 when(mDexoptHelper.dexopt(any(), any(), 1204 argThat(params -> params.getReason().equals(ReasonMapping.REASON_FIRST_BOOT)), 1205 any(), any(), same(progressCallbackExecutor), same(progressCallback))) 1206 .thenReturn(DexoptResult.create()); 1207 1208 mArtManagerLocal.onBoot( 1209 ReasonMapping.REASON_FIRST_BOOT, progressCallbackExecutor, progressCallback); 1210 } 1211 1212 @Test testOnBootNoProgressCallback()1213 public void testOnBootNoProgressCallback() throws Exception { 1214 when(mDexoptHelper.dexopt(any(), any(), 1215 argThat(params -> params.getReason().equals(ReasonMapping.REASON_FIRST_BOOT)), 1216 any(), any(), isNull(), isNull())) 1217 .thenReturn(DexoptResult.create()); 1218 1219 mArtManagerLocal.onBoot(ReasonMapping.REASON_FIRST_BOOT, 1220 null /* progressCallbackExecutor */, null /* progressCallback */); 1221 } 1222 1223 @Test testCleanupKeepPreRebootStagedFiles()1224 public void testCleanupKeepPreRebootStagedFiles() throws Exception { 1225 when(mPreRebootDexoptJob.hasStarted()).thenReturn(true); 1226 testCleanup(true /* keepPreRebootStagedFiles */); 1227 } 1228 1229 @Test testCleanupRemovePreRebootStagedFiles()1230 public void testCleanupRemovePreRebootStagedFiles() throws Exception { 1231 when(mPreRebootDexoptJob.hasStarted()).thenReturn(false); 1232 testCleanup(false /* keepPreRebootStagedFiles */); 1233 } 1234 testCleanup(boolean keepPreRebootStagedFiles)1235 private void testCleanup(boolean keepPreRebootStagedFiles) throws Exception { 1236 // It should keep all artifacts, but not runtime images. 1237 doReturn(createGetDexoptStatusResult( 1238 "speed-profile", "bg-dexopt", "location", ArtifactsLocation.NEXT_TO_DEX)) 1239 .when(mArtd) 1240 .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm64"), any()); 1241 doReturn(createGetDexoptStatusResult( 1242 "verify", "cmdline", "location", ArtifactsLocation.NEXT_TO_DEX)) 1243 .when(mArtd) 1244 .getDexoptStatus(eq("/data/user/0/foo/1.apk"), eq("arm64"), any()); 1245 1246 // It should keep all artifacts and runtime images. 1247 doReturn(createGetDexoptStatusResult( 1248 "verify", "bg-dexopt", "location", ArtifactsLocation.DALVIK_CACHE)) 1249 .when(mArtd) 1250 .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm64"), any()); 1251 1252 // It should only keep VDEX files and runtime images. 1253 doReturn(createGetDexoptStatusResult( 1254 "verify", "vdex", "location", ArtifactsLocation.NEXT_TO_DEX)) 1255 .when(mArtd) 1256 .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm"), any()); 1257 1258 // It should not keep any artifacts or runtime images. 1259 doReturn(createGetDexoptStatusResult( 1260 "run-from-apk", "unknown", "unknown", ArtifactsLocation.NONE_OR_ERROR)) 1261 .when(mArtd) 1262 .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm"), any()); 1263 1264 when(mSnapshot.getPackageStates()).thenReturn(Map.of(PKG_NAME_1, mPkgState1)); 1265 mArtManagerLocal.cleanup(mSnapshot); 1266 1267 verify(mArtd).cleanup( 1268 inAnyOrderDeepEquals( 1269 AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"), 1270 AidlUtils.buildProfilePathForPrimaryCur( 1271 0 /* userId */, PKG_NAME_1, "primary"), 1272 AidlUtils.buildProfilePathForPrimaryCur( 1273 1 /* userId */, PKG_NAME_1, "primary"), 1274 AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "split_0.split"), 1275 AidlUtils.buildProfilePathForPrimaryCur( 1276 0 /* userId */, PKG_NAME_1, "split_0.split"), 1277 AidlUtils.buildProfilePathForPrimaryCur( 1278 1 /* userId */, PKG_NAME_1, "split_0.split"), 1279 AidlUtils.buildProfilePathForSecondaryRefAsInput("/data/user/0/foo/1.apk"), 1280 AidlUtils.buildProfilePathForSecondaryCur("/data/user/0/foo/1.apk")), 1281 inAnyOrderDeepEquals( 1282 AidlUtils.buildArtifactsPathAsInput("/somewhere/app/foo/base.apk", "arm64", 1283 false /* isInDalvikCache */), 1284 AidlUtils.buildArtifactsPathAsInput( 1285 "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */), 1286 AidlUtils.buildArtifactsPathAsInput("/somewhere/app/foo/split_0.apk", 1287 "arm64", true /* isInDalvikCache */)), 1288 inAnyOrderDeepEquals(VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput( 1289 "/somewhere/app/foo/split_0.apk", "arm", false /* isInDalvikCache */))), 1290 inAnyOrderDeepEquals() /* sdmSdcFilesToKeep */, 1291 inAnyOrderDeepEquals(AidlUtils.buildRuntimeArtifactsPath( 1292 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm64"), 1293 AidlUtils.buildRuntimeArtifactsPath( 1294 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm")), 1295 eq(keepPreRebootStagedFiles)); 1296 } 1297 1298 @Test testCleanupDmAndSdm()1299 public void testCleanupDmAndSdm() throws Exception { 1300 when(mPreRebootDexoptJob.hasStarted()).thenReturn(false); 1301 1302 // It should keep the SDM file, but not runtime images. 1303 doReturn(createGetDexoptStatusResult( 1304 "speed-profile", "cloud", "location", ArtifactsLocation.SDM_NEXT_TO_DEX)) 1305 .when(mArtd) 1306 .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm64"), any()); 1307 1308 // It should keep the SDM file, but not runtime images. 1309 doReturn(createGetDexoptStatusResult( 1310 "speed-profile", "cloud", "location", ArtifactsLocation.SDM_DALVIK_CACHE)) 1311 .when(mArtd) 1312 .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm"), any()); 1313 1314 // It should keep the SDM file and runtime images. 1315 doReturn(createGetDexoptStatusResult( 1316 "verify", "cloud", "location", ArtifactsLocation.SDM_NEXT_TO_DEX)) 1317 .when(mArtd) 1318 .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm64"), any()); 1319 1320 // It should only keep runtime images. 1321 doReturn(createGetDexoptStatusResult("verify", "vdex", "location", ArtifactsLocation.DM)) 1322 .when(mArtd) 1323 .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm"), any()); 1324 1325 // This file is uninteresting in this test. 1326 doReturn(createGetDexoptStatusResult( 1327 "run-from-apk", "unknown", "unknown", ArtifactsLocation.NONE_OR_ERROR)) 1328 .when(mArtd) 1329 .getDexoptStatus(eq("/data/user/0/foo/1.apk"), eq("arm64"), any()); 1330 1331 when(mSnapshot.getPackageStates()).thenReturn(Map.of(PKG_NAME_1, mPkgState1)); 1332 mArtManagerLocal.cleanup(mSnapshot); 1333 1334 verify(mArtd).cleanup(any() /* profilesToKeep */, 1335 inAnyOrderDeepEquals() /* artifactsToKeep */, 1336 inAnyOrderDeepEquals() /* vdexFilesToKeep */, 1337 inAnyOrderDeepEquals(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 1338 "/somewhere/app/foo/base.apk", "arm64", 1339 false /* isInDalvikCache */), 1340 AidlUtils.buildSecureDexMetadataWithCompanionPaths( 1341 "/somewhere/app/foo/base.apk", "arm", true /* isInDalvikCache */), 1342 AidlUtils.buildSecureDexMetadataWithCompanionPaths( 1343 "/somewhere/app/foo/split_0.apk", "arm64", 1344 false /* isInDalvikCache */)), 1345 inAnyOrderDeepEquals(AidlUtils.buildRuntimeArtifactsPath( 1346 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm64"), 1347 AidlUtils.buildRuntimeArtifactsPath( 1348 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm")), 1349 eq(false) /* keepPreRebootStagedFiles */); 1350 } 1351 1352 @Test testGetArtManagedFileStatsSystem()1353 public void testGetArtManagedFileStatsSystem() throws Exception { 1354 testGetArtManagedFileStats(true /* isSystemOrRootOrShell */); 1355 } 1356 1357 @Test testGetArtManagedFileStatsNonSystem()1358 public void testGetArtManagedFileStatsNonSystem() throws Exception { 1359 testGetArtManagedFileStats(false /* isSystemOrRootOrShell */); 1360 } 1361 testGetArtManagedFileStats(boolean isSystemOrRootOrShell)1362 private void testGetArtManagedFileStats(boolean isSystemOrRootOrShell) throws Exception { 1363 if (!isSystemOrRootOrShell) { 1364 lenient().when(mArtFileManagerInjector.isSystemOrRootOrShell()).thenReturn(false); 1365 lenient() 1366 .when(mArtFileManagerInjector.getCallingUserHandle()) 1367 .thenReturn(UserHandle.of(0)); 1368 } 1369 1370 // The same setup as `testCleanup`, but add a secondary dex file for a different user. Its 1371 // artifacts and profiles should only be counted if the caller is system or root or shell. 1372 CheckedSecondaryDexInfo pkg1SecondaryDexInfo2 = 1373 createSecondaryDexInfo("/data/user/1/foo/1.apk", UserHandle.of(1)); 1374 lenient() 1375 .doReturn(List.of(mPkg1SecondaryDexInfo1, pkg1SecondaryDexInfo2)) 1376 .when(mDexUseManager) 1377 .getCheckedSecondaryDexInfo( 1378 eq(PKG_NAME_1), eq(true) /* excludeObsoleteDexesAndLoaders */); 1379 1380 // It should count all artifacts, but not runtime images. 1381 doReturn(createGetDexoptStatusResult( 1382 "speed-profile", "bg-dexopt", "location", ArtifactsLocation.NEXT_TO_DEX)) 1383 .when(mArtd) 1384 .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm64"), any()); 1385 doReturn(createGetDexoptStatusResult( 1386 "verify", "cmdline", "location", ArtifactsLocation.NEXT_TO_DEX)) 1387 .when(mArtd) 1388 .getDexoptStatus(eq("/data/user/0/foo/1.apk"), eq("arm64"), any()); 1389 1390 // It should count all artifacts and runtime images. 1391 doReturn(createGetDexoptStatusResult( 1392 "verify", "bg-dexopt", "location", ArtifactsLocation.DALVIK_CACHE)) 1393 .when(mArtd) 1394 .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm64"), any()); 1395 1396 // It should only count VDEX files and runtime images. 1397 doReturn(createGetDexoptStatusResult( 1398 "verify", "vdex", "location", ArtifactsLocation.NEXT_TO_DEX)) 1399 .when(mArtd) 1400 .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm"), any()); 1401 1402 // It should not count any artifacts or runtime images. 1403 doReturn(createGetDexoptStatusResult( 1404 "run-from-apk", "unknown", "unknown", ArtifactsLocation.NONE_OR_ERROR)) 1405 .when(mArtd) 1406 .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm"), any()); 1407 1408 // These are counted as TYPE_DEXOPT_ARTIFACT. 1409 doReturn(1l << 0).when(mArtd).getArtifactsSize(deepEq(AidlUtils.buildArtifactsPathAsInput( 1410 "/somewhere/app/foo/base.apk", "arm64", false /* isInDalvikCache */))); 1411 doReturn(1l << 1).when(mArtd).getArtifactsSize(deepEq(AidlUtils.buildArtifactsPathAsInput( 1412 "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */))); 1413 doReturn(1l << 2).when(mArtd).getArtifactsSize(deepEq(AidlUtils.buildArtifactsPathAsInput( 1414 "/somewhere/app/foo/split_0.apk", "arm64", true /* isInDalvikCache */))); 1415 doReturn(1l << 3).when(mArtd).getVdexFileSize( 1416 deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPathAsInput( 1417 "/somewhere/app/foo/split_0.apk", "arm", false /* isInDalvikCache */)))); 1418 doReturn(1l << 4).when(mArtd).getRuntimeArtifactsSize( 1419 deepEq(AidlUtils.buildRuntimeArtifactsPath( 1420 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm64"))); 1421 doReturn(1l << 5).when(mArtd).getRuntimeArtifactsSize( 1422 deepEq(AidlUtils.buildRuntimeArtifactsPath( 1423 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm"))); 1424 long expectedDexoptArtifactSize = 1425 (1l << 0) + (1l << 1) + (1l << 2) + (1l << 3) + (1l << 4) + (1l << 5); 1426 int expectedGetArtifactsSizeCalls = 3; 1427 int expectedGetVdexFileSizeCalls = 1; 1428 int expectedGetRuntimeArtifactsSizeCalls = 2; 1429 1430 // These are counted as TYPE_REF_PROFILE. 1431 doReturn(1l << 6).when(mArtd).getProfileSize( 1432 deepEq(AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "primary"))); 1433 doReturn(1l << 7).when(mArtd).getProfileSize(deepEq( 1434 AidlUtils.buildProfilePathForPrimaryRefAsInput(PKG_NAME_1, "split_0.split"))); 1435 doReturn(1l << 8).when(mArtd).getProfileSize( 1436 deepEq(AidlUtils.buildProfilePathForSecondaryRefAsInput("/data/user/0/foo/1.apk"))); 1437 long expectedRefProfileSize = (1l << 6) + (1l << 7) + (1l << 8); 1438 int expectedGetProfileSizeCalls = 3; 1439 1440 // These are counted as TYPE_CUR_PROFILE. 1441 doReturn(1l << 9).when(mArtd).getProfileSize(deepEq( 1442 AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME_1, "primary"))); 1443 doReturn(1l << 10).when(mArtd).getProfileSize( 1444 deepEq(AidlUtils.buildProfilePathForPrimaryCur( 1445 0 /* userId */, PKG_NAME_1, "split_0.split"))); 1446 doReturn(1l << 11).when(mArtd).getProfileSize( 1447 deepEq(AidlUtils.buildProfilePathForSecondaryCur("/data/user/0/foo/1.apk"))); 1448 long expectedCurProfileSize = (1l << 9) + (1l << 10) + (1l << 11); 1449 expectedGetProfileSizeCalls += 3; 1450 1451 // These belong to a different user. 1452 if (isSystemOrRootOrShell) { 1453 doReturn(createGetDexoptStatusResult( 1454 "verify", "cmdline", "location", ArtifactsLocation.NEXT_TO_DEX)) 1455 .when(mArtd) 1456 .getDexoptStatus(eq("/data/user/1/foo/1.apk"), eq("arm64"), any()); 1457 1458 // These are counted as TYPE_DEXOPT_ARTIFACT. 1459 // Dexopt artifacts of secondary dex files. 1460 doReturn(1l << 12).when(mArtd).getArtifactsSize( 1461 deepEq(AidlUtils.buildArtifactsPathAsInput( 1462 "/data/user/1/foo/1.apk", "arm64", false /* isInDalvikCache */))); 1463 expectedDexoptArtifactSize += (1l << 12); 1464 expectedGetArtifactsSizeCalls += 1; 1465 1466 // These are counted as TYPE_REF_PROFILE. 1467 // Reference profiles of secondary dex files. 1468 doReturn(1l << 13).when(mArtd).getProfileSize(deepEq( 1469 AidlUtils.buildProfilePathForSecondaryRefAsInput("/data/user/1/foo/1.apk"))); 1470 expectedRefProfileSize += (1l << 13); 1471 expectedGetProfileSizeCalls += 1; 1472 1473 // These are counted as TYPE_CUR_PROFILE. 1474 // Current profiles of primary dex files. 1475 doReturn(1l << 14).when(mArtd).getProfileSize( 1476 deepEq(AidlUtils.buildProfilePathForPrimaryCur( 1477 1 /* userId */, PKG_NAME_1, "primary"))); 1478 doReturn(1l << 15).when(mArtd).getProfileSize( 1479 deepEq(AidlUtils.buildProfilePathForPrimaryCur( 1480 1 /* userId */, PKG_NAME_1, "split_0.split"))); 1481 // Current profiles of secondary dex files. 1482 doReturn(1l << 16).when(mArtd).getProfileSize( 1483 deepEq(AidlUtils.buildProfilePathForSecondaryCur("/data/user/1/foo/1.apk"))); 1484 expectedCurProfileSize += (1l << 14) + (1l << 15) + (1l << 16); 1485 expectedGetProfileSizeCalls += 3; 1486 } 1487 1488 ArtManagedFileStats stats = mArtManagerLocal.getArtManagedFileStats(mSnapshot, PKG_NAME_1); 1489 assertThat(stats.getTotalSizeBytesByType(ArtManagedFileStats.TYPE_DEXOPT_ARTIFACT)) 1490 .isEqualTo(expectedDexoptArtifactSize); 1491 assertThat(stats.getTotalSizeBytesByType(ArtManagedFileStats.TYPE_REF_PROFILE)) 1492 .isEqualTo(expectedRefProfileSize); 1493 assertThat(stats.getTotalSizeBytesByType(ArtManagedFileStats.TYPE_CUR_PROFILE)) 1494 .isEqualTo(expectedCurProfileSize); 1495 1496 verify(mArtd, times(expectedGetArtifactsSizeCalls)).getArtifactsSize(any()); 1497 verify(mArtd, times(expectedGetVdexFileSizeCalls)).getVdexFileSize(any()); 1498 verify(mArtd, times(expectedGetRuntimeArtifactsSizeCalls)).getRuntimeArtifactsSize(any()); 1499 verify(mArtd, times(expectedGetProfileSizeCalls)).getProfileSize(any()); 1500 } 1501 1502 @Test testGetArtManagedFileStatsDmAndSdm()1503 public void testGetArtManagedFileStatsDmAndSdm() throws Exception { 1504 // It should count the SDM file, but not runtime images. 1505 doReturn(createGetDexoptStatusResult( 1506 "speed-profile", "cloud", "location", ArtifactsLocation.SDM_NEXT_TO_DEX)) 1507 .when(mArtd) 1508 .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm64"), any()); 1509 1510 // It should count the SDM file, but not runtime images. 1511 doReturn(createGetDexoptStatusResult( 1512 "speed-profile", "cloud", "location", ArtifactsLocation.SDM_DALVIK_CACHE)) 1513 .when(mArtd) 1514 .getDexoptStatus(eq("/somewhere/app/foo/base.apk"), eq("arm"), any()); 1515 1516 // It should count the SDM file and runtime images. 1517 doReturn(createGetDexoptStatusResult( 1518 "verify", "cloud", "location", ArtifactsLocation.SDM_NEXT_TO_DEX)) 1519 .when(mArtd) 1520 .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm64"), any()); 1521 1522 // It should only count runtime images. 1523 doReturn(createGetDexoptStatusResult("verify", "vdex", "location", ArtifactsLocation.DM)) 1524 .when(mArtd) 1525 .getDexoptStatus(eq("/somewhere/app/foo/split_0.apk"), eq("arm"), any()); 1526 1527 // This file is uninteresting in this test. 1528 doReturn(createGetDexoptStatusResult( 1529 "run-from-apk", "unknown", "unknown", ArtifactsLocation.NONE_OR_ERROR)) 1530 .when(mArtd) 1531 .getDexoptStatus(eq("/data/user/0/foo/1.apk"), eq("arm64"), any()); 1532 1533 // These are counted as TYPE_DEXOPT_ARTIFACT. 1534 doReturn(1l << 0).when(mArtd).getSdmFileSize( 1535 deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 1536 "/somewhere/app/foo/base.apk", "arm64", false /* isInDalvikCache */))); 1537 doReturn(1l << 1).when(mArtd).getSdmFileSize( 1538 deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 1539 "/somewhere/app/foo/base.apk", "arm", true /* isInDalvikCache */))); 1540 doReturn(1l << 2).when(mArtd).getSdmFileSize( 1541 deepEq(AidlUtils.buildSecureDexMetadataWithCompanionPaths( 1542 "/somewhere/app/foo/split_0.apk", "arm64", false /* isInDalvikCache */))); 1543 doReturn(1l << 3).when(mArtd).getRuntimeArtifactsSize( 1544 deepEq(AidlUtils.buildRuntimeArtifactsPath( 1545 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm64"))); 1546 doReturn(1l << 4).when(mArtd).getRuntimeArtifactsSize( 1547 deepEq(AidlUtils.buildRuntimeArtifactsPath( 1548 PKG_NAME_1, "/somewhere/app/foo/split_0.apk", "arm"))); 1549 1550 ArtManagedFileStats stats = mArtManagerLocal.getArtManagedFileStats(mSnapshot, PKG_NAME_1); 1551 assertThat(stats.getTotalSizeBytesByType(ArtManagedFileStats.TYPE_DEXOPT_ARTIFACT)) 1552 .isEqualTo((1l << 0) + (1l << 1) + (1l << 2) + (1l << 3) + (1l << 4)); 1553 1554 verify(mArtd, never()).getArtifactsSize(any()); 1555 verify(mArtd, never()).getVdexFileSize(any()); 1556 verify(mArtd, times(3)).getSdmFileSize(any()); 1557 verify(mArtd, times(2)).getRuntimeArtifactsSize(any()); 1558 } 1559 1560 @Test testCommitPreRebootStagedFiles()1561 public void testCommitPreRebootStagedFiles() throws Exception { 1562 when(mSnapshot.getPackageStates()).thenReturn(Map.of(PKG_NAME_1, mPkgState1)); 1563 1564 mArtManagerLocal.onBoot(ReasonMapping.REASON_BOOT_AFTER_OTA, 1565 null /* progressCallbackExecutor */, null /* progressCallback */); 1566 1567 // It should commit files for primary dex files on boot. 1568 verify(mArtd).commitPreRebootStagedFiles( 1569 inAnyOrderDeepEquals( 1570 AidlUtils.buildArtifactsPathAsInput( 1571 "/somewhere/app/foo/base.apk", "arm64", mIsInDalvikCache), 1572 AidlUtils.buildArtifactsPathAsInput( 1573 "/somewhere/app/foo/base.apk", "arm", mIsInDalvikCache), 1574 AidlUtils.buildArtifactsPathAsInput( 1575 "/somewhere/app/foo/split_0.apk", "arm64", mIsInDalvikCache), 1576 AidlUtils.buildArtifactsPathAsInput( 1577 "/somewhere/app/foo/split_0.apk", "arm", mIsInDalvikCache)), 1578 inAnyOrderDeepEquals(AidlUtils.toWritableProfilePath( 1579 AidlUtils.buildProfilePathForPrimaryRefAsInput( 1580 PKG_NAME_1, "primary")), 1581 AidlUtils.toWritableProfilePath( 1582 AidlUtils.buildProfilePathForPrimaryRefAsInput( 1583 PKG_NAME_1, "split_0.split")))); 1584 verify(mArtd, times(1)).commitPreRebootStagedFiles(any(), any()); 1585 1586 mArtManagerLocal.systemReady(); 1587 1588 // It should not commit anything on system ready. 1589 verify(mArtd, times(1)).commitPreRebootStagedFiles(any(), any()); 1590 1591 mBroadcastReceiverCaptor.getValue().onReceive(mContext, mock(Intent.class)); 1592 1593 // It should commit files for secondary dex files on boot complete. 1594 verify(mArtd).commitPreRebootStagedFiles( 1595 inAnyOrderDeepEquals(AidlUtils.buildArtifactsPathAsInput( 1596 "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */)), 1597 inAnyOrderDeepEquals(AidlUtils.toWritableProfilePath( 1598 AidlUtils.buildProfilePathForSecondaryRefAsInput( 1599 "/data/user/0/foo/1.apk")))); 1600 verify(mArtd, times(2)).commitPreRebootStagedFiles(any(), any()); 1601 } 1602 1603 @Test testCommitPreRebootStagedFilesOnBootNotCalled()1604 public void testCommitPreRebootStagedFilesOnBootNotCalled() throws Exception { 1605 mArtManagerLocal.systemReady(); 1606 1607 verify(mContext, never()).registerReceiver(any(), any()); 1608 } 1609 1610 @Test testFlushProfiles()1611 public void testFlushProfiles() throws Exception { 1612 when(mActivityManager.getRunningAppProcesses()) 1613 .thenReturn( 1614 List.of(createProcessInfo(1000 /* pid */, "com.example.foo", 1615 UserHandle.of(0).getUid(APP_ID), new String[] {PKG_NAME_1}, 1616 RunningAppProcessInfo.IMPORTANCE_FOREGROUND), 1617 createProcessInfo(1001 /* pid */, "com.example.foo:service1", 1618 UserHandle.of(0).getUid(APP_ID), new String[] {PKG_NAME_1}, 1619 RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE), 1620 createProcessInfo(1002 /* pid */, "com.example.foo:service2", 1621 UserHandle.of(0).getUid(APP_ID), new String[] {PKG_NAME_1}, 1622 RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE)), 1623 // A process goes away later. 1624 List.of(createProcessInfo(1000 /* pid */, "com.example.foo", 1625 UserHandle.of(0).getUid(APP_ID), new String[] {PKG_NAME_1}, 1626 RunningAppProcessInfo.IMPORTANCE_FOREGROUND), 1627 createProcessInfo(1001 /* pid */, "com.example.foo:service1", 1628 UserHandle.of(0).getUid(APP_ID), new String[] {PKG_NAME_1}, 1629 RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE))); 1630 1631 PrimaryCurProfilePath profilePath = AidlUtils.buildPrimaryCurProfilePath( 1632 0 /* userId */, PKG_NAME_1, PrimaryDexUtils.getProfileName(null /* splitName */)); 1633 1634 var notificationForPid1000 = mock(IArtdNotification.class); 1635 var notificationForPid1001 = mock(IArtdNotification.class); 1636 var notificationForPid1002 = mock(IArtdNotification.class); 1637 doReturn(notificationForPid1000) 1638 .when(mArtd) 1639 .initProfileSaveNotification(deepEq(profilePath), eq(1000) /* pid */); 1640 doReturn(notificationForPid1001) 1641 .when(mArtd) 1642 .initProfileSaveNotification(deepEq(profilePath), eq(1001) /* pid */); 1643 doReturn(notificationForPid1002) 1644 .when(mArtd) 1645 .initProfileSaveNotification(deepEq(profilePath), eq(1002) /* pid */); 1646 when(notificationForPid1000.wait(anyInt())).thenReturn(true); 1647 when(notificationForPid1001.wait(anyInt())).thenReturn(true); 1648 1649 assertThat(mArtManagerLocal.flushProfiles(mSnapshot, PKG_NAME_1)).isTrue(); 1650 1651 InOrder inOrder = inOrder(mInjector, notificationForPid1000, notificationForPid1001); 1652 inOrder.verify(mInjector).kill(1000 /* pid */, OsConstants.SIGUSR1); 1653 inOrder.verify(notificationForPid1000).wait(1000 /* timeoutMs */); 1654 inOrder.verify(mInjector).kill(1001 /* pid */, OsConstants.SIGUSR1); 1655 inOrder.verify(notificationForPid1001).wait(1000 /* timeoutMs */); 1656 1657 verify(mInjector, never()).kill(1002 /* pid */, OsConstants.SIGUSR1); 1658 verify(notificationForPid1002, never()).wait(anyInt()); 1659 } 1660 createPackage(boolean multiSplit)1661 private AndroidPackage createPackage(boolean multiSplit) { 1662 AndroidPackage pkg = mock(AndroidPackage.class); 1663 1664 var baseSplit = mock(AndroidPackageSplit.class); 1665 lenient().when(baseSplit.getPath()).thenReturn("/somewhere/app/foo/base.apk"); 1666 lenient().when(baseSplit.isHasCode()).thenReturn(true); 1667 1668 if (multiSplit) { 1669 // split_0 has code while split_1 doesn't. 1670 var split0 = mock(AndroidPackageSplit.class); 1671 lenient().when(split0.getName()).thenReturn("split_0"); 1672 lenient().when(split0.getPath()).thenReturn("/somewhere/app/foo/split_0.apk"); 1673 lenient().when(split0.isHasCode()).thenReturn(true); 1674 var split1 = mock(AndroidPackageSplit.class); 1675 lenient().when(split1.getName()).thenReturn("split_1"); 1676 lenient().when(split1.getPath()).thenReturn("/somewhere/app/foo/split_1.apk"); 1677 lenient().when(split1.isHasCode()).thenReturn(false); 1678 1679 lenient().when(pkg.getSplits()).thenReturn(List.of(baseSplit, split0, split1)); 1680 } else { 1681 lenient().when(pkg.getSplits()).thenReturn(List.of(baseSplit)); 1682 } 1683 1684 return pkg; 1685 } 1686 createPackageUserState()1687 private PackageUserState createPackageUserState() { 1688 PackageUserState pkgUserState = mock(PackageUserState.class); 1689 lenient().when(pkgUserState.isInstalled()).thenReturn(true); 1690 // All packages are by default pre-installed. 1691 lenient().when(pkgUserState.getFirstInstallTimeMillis()).thenReturn(0l); 1692 return pkgUserState; 1693 } 1694 createPackageState( String packageName, boolean isDexoptable, boolean multiSplit)1695 private PackageState createPackageState( 1696 String packageName, boolean isDexoptable, boolean multiSplit) { 1697 PackageState pkgState = mock(PackageState.class); 1698 1699 lenient().when(pkgState.getPackageName()).thenReturn(packageName); 1700 lenient().when(pkgState.getPrimaryCpuAbi()).thenReturn("arm64-v8a"); 1701 lenient().when(pkgState.getSecondaryCpuAbi()).thenReturn("armeabi-v7a"); 1702 lenient().when(pkgState.getAppId()).thenReturn(APP_ID); 1703 1704 AndroidPackage pkg = createPackage(multiSplit); 1705 lenient().when(pkgState.getAndroidPackage()).thenReturn(pkg); 1706 1707 PackageUserState pkgUserState0 = createPackageUserState(); 1708 lenient().when(pkgState.getStateForUser(UserHandle.of(0))).thenReturn(pkgUserState0); 1709 PackageUserState pkgUserState1 = createPackageUserState(); 1710 lenient().when(pkgState.getStateForUser(UserHandle.of(1))).thenReturn(pkgUserState1); 1711 1712 lenient().when(PackageStateModulesUtils.isDexoptable(pkgState)).thenReturn(isDexoptable); 1713 1714 return pkgState; 1715 } 1716 createPackageStates()1717 private List<PackageState> createPackageStates() { 1718 PackageState pkgState1 = 1719 createPackageState(PKG_NAME_1, true /* isDexoptable */, true /* multiSplit */); 1720 1721 PackageState pkgState2 = 1722 createPackageState(PKG_NAME_2, true /* isDexoptable */, false /* multiSplit */); 1723 1724 // This should not be dexopted because it's hibernating. However, it should be included 1725 // when snapshotting boot image profile. 1726 PackageState pkgHibernatingState = createPackageState( 1727 PKG_NAME_HIBERNATING, true /* isDexoptable */, false /* multiSplit */); 1728 lenient() 1729 .when(mAppHibernationManager.isHibernatingGlobally(PKG_NAME_HIBERNATING)) 1730 .thenReturn(true); 1731 1732 // This should not be dexopted because it's not dexoptable. 1733 PackageState nonDexoptablePkgState = createPackageState( 1734 "com.example.non-dexoptable", false /* isDexoptable */, false /* multiSplit */); 1735 1736 return List.of(pkgState1, pkgState2, pkgHibernatingState, nonDexoptablePkgState); 1737 } 1738 createGetDexoptStatusResult(String compilerFilter, String compilationReason, String locationDebugString, @ArtifactsLocation int location)1739 private GetDexoptStatusResult createGetDexoptStatusResult(String compilerFilter, 1740 String compilationReason, String locationDebugString, @ArtifactsLocation int location) { 1741 var getDexoptStatusResult = new GetDexoptStatusResult(); 1742 getDexoptStatusResult.compilerFilter = compilerFilter; 1743 getDexoptStatusResult.compilationReason = compilationReason; 1744 getDexoptStatusResult.locationDebugString = locationDebugString; 1745 getDexoptStatusResult.artifactsLocation = location; 1746 return getDexoptStatusResult; 1747 } 1748 createSecondaryDexInfo(String dexPath, UserHandle userHandle)1749 private CheckedSecondaryDexInfo createSecondaryDexInfo(String dexPath, UserHandle userHandle) 1750 throws Exception { 1751 var dexInfo = mock(CheckedSecondaryDexInfo.class); 1752 lenient().when(dexInfo.dexPath()).thenReturn(dexPath); 1753 lenient().when(dexInfo.abiNames()).thenReturn(Set.of("arm64-v8a")); 1754 lenient().when(dexInfo.classLoaderContext()).thenReturn("CLC"); 1755 lenient().when(dexInfo.userHandle()).thenReturn(userHandle); 1756 return dexInfo; 1757 } 1758 simulateStorageLow()1759 private void simulateStorageLow() throws Exception { 1760 lenient() 1761 .when(mStorageManager.getAllocatableBytes(any())) 1762 .thenReturn(ArtManagerLocal.DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES - 1); 1763 } 1764 simulateStorageNotLow()1765 private void simulateStorageNotLow() throws Exception { 1766 lenient() 1767 .when(mStorageManager.getAllocatableBytes(any())) 1768 .thenReturn(ArtManagerLocal.DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES); 1769 } 1770 createProcessInfo( int pid, String processName, int uid, String[] pkgList, int importance)1771 private RunningAppProcessInfo createProcessInfo( 1772 int pid, String processName, int uid, String[] pkgList, int importance) { 1773 var info = new RunningAppProcessInfo(processName, pid, pkgList); 1774 info.uid = uid; 1775 info.importance = importance; 1776 return info; 1777 } 1778 } 1779