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.AidlUtils.buildFsPermission; 20 import static com.android.server.art.AidlUtils.buildOutputArtifacts; 21 import static com.android.server.art.AidlUtils.buildPermissionSettings; 22 import static com.android.server.art.OutputArtifacts.PermissionSettings; 23 import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult; 24 import static com.android.server.art.testing.TestingUtils.deepEq; 25 26 import static com.google.common.truth.Truth.assertThat; 27 28 import static org.mockito.Mockito.any; 29 import static org.mockito.Mockito.anyBoolean; 30 import static org.mockito.Mockito.anyInt; 31 import static org.mockito.Mockito.argThat; 32 import static org.mockito.Mockito.doReturn; 33 import static org.mockito.Mockito.doThrow; 34 import static org.mockito.Mockito.eq; 35 import static org.mockito.Mockito.isNull; 36 import static org.mockito.Mockito.lenient; 37 import static org.mockito.Mockito.mock; 38 import static org.mockito.Mockito.times; 39 import static org.mockito.Mockito.verify; 40 import static org.mockito.Mockito.when; 41 42 import android.content.pm.ApplicationInfo; 43 import android.os.Process; 44 import android.os.ServiceSpecificException; 45 import android.os.SystemProperties; 46 47 import androidx.test.filters.SmallTest; 48 49 import com.android.server.art.model.ArtFlags; 50 import com.android.server.art.model.DexoptParams; 51 import com.android.server.art.model.DexoptResult; 52 import com.android.server.art.testing.TestingUtils; 53 54 import dalvik.system.DexFile; 55 56 import org.junit.Before; 57 import org.junit.Test; 58 import org.junit.runner.RunWith; 59 import org.junit.runners.Parameterized; 60 import org.junit.runners.Parameterized.Parameter; 61 import org.junit.runners.Parameterized.Parameters; 62 import org.mockito.ArgumentMatcher; 63 64 import java.util.ArrayList; 65 import java.util.List; 66 67 @SmallTest 68 @RunWith(Parameterized.class) 69 public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase { 70 private DexoptParams mDexoptParams; 71 72 private PrimaryDexopter mPrimaryDexopter; 73 74 @Parameter(0) public Params mParams; 75 76 @Parameters(name = "{0}") data()77 public static Iterable<Params> data() { 78 List<Params> list = new ArrayList<>(); 79 Params params; 80 81 // Baseline. 82 params = new Params(); 83 list.add(params); 84 85 params = new Params(); 86 params.mRequestedCompilerFilter = "speed"; 87 params.mExpectedCompilerFilter = "speed"; 88 list.add(params); 89 90 params = new Params(); 91 params.mIsSystem = true; 92 params.mExpectedIsInDalvikCache = true; 93 list.add(params); 94 95 params = new Params(); 96 params.mIsSystem = true; 97 params.mIsUpdatedSystemApp = true; 98 list.add(params); 99 100 params = new Params(); 101 params.mIsIncrementalFsPath = true; 102 params.mExpectedIsInDalvikCache = true; 103 list.add(params); 104 105 params = new Params(); 106 params.mIsSystem = true; 107 params.mHiddenApiEnforcementPolicy = ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED; 108 params.mExpectedIsInDalvikCache = true; 109 params.mExpectedIsHiddenApiPolicyEnabled = false; 110 list.add(params); 111 112 params = new Params(); 113 params.mIsDebuggable = true; 114 params.mRequestedCompilerFilter = "speed"; 115 params.mExpectedCompilerFilter = "verify"; 116 params.mExpectedIsDebuggable = true; 117 list.add(params); 118 119 params = new Params(); 120 params.mIsVmSafeMode = true; 121 params.mRequestedCompilerFilter = "speed"; 122 params.mExpectedCompilerFilter = "verify"; 123 list.add(params); 124 125 params = new Params(); 126 params.mIsUseEmbeddedDex = true; 127 params.mRequestedCompilerFilter = "speed"; 128 params.mExpectedCompilerFilter = "verify"; 129 list.add(params); 130 131 params = new Params(); 132 params.mAlwaysDebuggable = true; 133 params.mExpectedIsDebuggable = true; 134 list.add(params); 135 136 params = new Params(); 137 params.mIsSystemUi = true; 138 params.mExpectedCompilerFilter = "speed"; 139 list.add(params); 140 141 params = new Params(); 142 params.mIsLauncher = true; 143 params.mExpectedCompilerFilter = "speed-profile"; 144 list.add(params); 145 146 params = new Params(); 147 params.mForce = true; 148 params.mShouldDowngrade = false; 149 params.mExpectedDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER 150 | DexoptTrigger.COMPILER_FILTER_IS_SAME | DexoptTrigger.COMPILER_FILTER_IS_WORSE 151 | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION; 152 list.add(params); 153 154 params = new Params(); 155 params.mForce = true; 156 params.mShouldDowngrade = true; 157 params.mExpectedDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER 158 | DexoptTrigger.COMPILER_FILTER_IS_SAME | DexoptTrigger.COMPILER_FILTER_IS_WORSE 159 | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION; 160 list.add(params); 161 162 params = new Params(); 163 params.mShouldDowngrade = true; 164 params.mExpectedDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_WORSE; 165 list.add(params); 166 167 params = new Params(); 168 // This should not change the result. 169 params.mSkipIfStorageLow = true; 170 list.add(params); 171 172 return list; 173 } 174 175 @Before setUp()176 public void setUp() throws Exception { 177 super.setUp(); 178 179 lenient().when(mInjector.isSystemUiPackage(any())).thenReturn(mParams.mIsSystemUi); 180 lenient().when(mInjector.isLauncherPackage(any())).thenReturn(mParams.mIsLauncher); 181 182 lenient() 183 .when(SystemProperties.getBoolean(eq("dalvik.vm.always_debuggable"), anyBoolean())) 184 .thenReturn(mParams.mAlwaysDebuggable); 185 186 lenient().when(mPkg.isVmSafeMode()).thenReturn(mParams.mIsVmSafeMode); 187 lenient().when(mPkg.isDebuggable()).thenReturn(mParams.mIsDebuggable); 188 lenient().when(mPkg.getTargetSdkVersion()).thenReturn(123); 189 lenient() 190 .when(mPkgState.getHiddenApiEnforcementPolicy()) 191 .thenReturn(mParams.mHiddenApiEnforcementPolicy); 192 lenient().when(mPkg.isUseEmbeddedDex()).thenReturn(mParams.mIsUseEmbeddedDex); 193 lenient().when(mPkgState.isSystem()).thenReturn(mParams.mIsSystem); 194 lenient().when(mPkgState.isUpdatedSystemApp()).thenReturn(mParams.mIsUpdatedSystemApp); 195 196 // Make all profile-related operations succeed so that "speed-profile" doesn't fall back to 197 // "verify". 198 lenient().when(mArtd.isProfileUsable(any(), any())).thenReturn(true); 199 lenient().when(mArtd.getProfileVisibility(any())).thenReturn(FileVisibility.OTHER_READABLE); 200 lenient().when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(false); 201 202 lenient().when(mArtd.isIncrementalFsPath(any())).thenReturn(mParams.mIsIncrementalFsPath); 203 204 mDexoptParams = 205 new DexoptParams.Builder("install") 206 .setCompilerFilter(mParams.mRequestedCompilerFilter) 207 .setPriorityClass(ArtFlags.PRIORITY_INTERACTIVE) 208 .setFlags(mParams.mForce ? ArtFlags.FLAG_FORCE : 0, ArtFlags.FLAG_FORCE) 209 .setFlags(mParams.mShouldDowngrade ? ArtFlags.FLAG_SHOULD_DOWNGRADE : 0, 210 ArtFlags.FLAG_SHOULD_DOWNGRADE) 211 .setFlags(mParams.mSkipIfStorageLow ? ArtFlags.FLAG_SKIP_IF_STORAGE_LOW : 0, 212 ArtFlags.FLAG_SKIP_IF_STORAGE_LOW) 213 .build(); 214 215 mPrimaryDexopter = 216 new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal); 217 } 218 219 @Test testDexopt()220 public void testDexopt() throws Exception { 221 PermissionSettings permissionSettings = buildPermissionSettings( 222 buildFsPermission(Process.SYSTEM_UID /* uid */, Process.SYSTEM_UID /* gid */, 223 false /* isOtherReadable */, true /* isOtherExecutable */), 224 buildFsPermission(Process.SYSTEM_UID /* uid */, SHARED_GID /* gid */, 225 true /* isOtherReadable */), 226 null /* seContext */); 227 228 // No need to check `generateAppImage`. It is checked in `PrimaryDexopterTest`. 229 ArgumentMatcher<DexoptOptions> dexoptOptionsMatcher = options 230 -> options.compilationReason.equals("install") && options.targetSdkVersion == 123 231 && options.debuggable == mParams.mExpectedIsDebuggable 232 && options.hiddenApiPolicyEnabled == mParams.mExpectedIsHiddenApiPolicyEnabled 233 && options.comments.equals( 234 String.format("app-version-name:%s,app-version-code:%d,art-version:%d", 235 APP_VERSION_NAME, APP_VERSION_CODE, ART_VERSION)); 236 237 when(mArtd.createCancellationSignal()).thenReturn(mock(IArtdCancellationSignal.class)); 238 when(mArtd.getDmFileVisibility(any())).thenReturn(FileVisibility.NOT_FOUND); 239 240 // The first one is normal. 241 doReturn(dexoptIsNeeded()) 242 .when(mArtd) 243 .getDexoptNeeded("/data/app/foo/base.apk", "arm64", "PCL[]", 244 mParams.mExpectedCompilerFilter, mParams.mExpectedDexoptTrigger); 245 doReturn(createArtdDexoptResult(false /* cancelled */, 100 /* wallTimeMs */, 246 400 /* cpuTimeMs */, 30000 /* sizeBytes */, 32000 /* sizeBeforeBytes */)) 247 .when(mArtd) 248 .dexopt(deepEq(buildOutputArtifacts("/data/app/foo/base.apk", "arm64", 249 mParams.mExpectedIsInDalvikCache, permissionSettings)), 250 eq("/data/app/foo/base.apk"), eq("arm64"), eq("PCL[]"), 251 eq(mParams.mExpectedCompilerFilter), any() /* profile */, 252 isNull() /* inputVdex */, isNull() /* dmFile */, 253 eq(PriorityClass.INTERACTIVE), argThat(dexoptOptionsMatcher), any()); 254 255 // The second one fails on `dexopt`. 256 doReturn(dexoptIsNeeded()) 257 .when(mArtd) 258 .getDexoptNeeded("/data/app/foo/base.apk", "arm", "PCL[]", 259 mParams.mExpectedCompilerFilter, mParams.mExpectedDexoptTrigger); 260 doThrow(ServiceSpecificException.class) 261 .when(mArtd) 262 .dexopt(deepEq(buildOutputArtifacts("/data/app/foo/base.apk", "arm", 263 mParams.mExpectedIsInDalvikCache, permissionSettings)), 264 eq("/data/app/foo/base.apk"), eq("arm"), eq("PCL[]"), 265 eq(mParams.mExpectedCompilerFilter), any() /* profile */, 266 isNull() /* inputVdex */, isNull() /* dmFile */, 267 eq(PriorityClass.INTERACTIVE), argThat(dexoptOptionsMatcher), any()); 268 269 // The third one doesn't need dexopt. 270 doReturn(dexoptIsNotNeeded()) 271 .when(mArtd) 272 .getDexoptNeeded("/data/app/foo/split_0.apk", "arm64", "PCL[base.apk]", 273 mParams.mExpectedCompilerFilter, mParams.mExpectedDexoptTrigger); 274 275 // The fourth one is normal. 276 doReturn(dexoptIsNeeded()) 277 .when(mArtd) 278 .getDexoptNeeded("/data/app/foo/split_0.apk", "arm", "PCL[base.apk]", 279 mParams.mExpectedCompilerFilter, mParams.mExpectedDexoptTrigger); 280 doReturn(createArtdDexoptResult(false /* cancelled */, 200 /* wallTimeMs */, 281 200 /* cpuTimeMs */, 10000 /* sizeBytes */, 0 /* sizeBeforeBytes */)) 282 .when(mArtd) 283 .dexopt(deepEq(buildOutputArtifacts("/data/app/foo/split_0.apk", "arm", 284 mParams.mExpectedIsInDalvikCache, permissionSettings)), 285 eq("/data/app/foo/split_0.apk"), eq("arm"), eq("PCL[base.apk]"), 286 eq(mParams.mExpectedCompilerFilter), any() /* profile */, 287 isNull() /* inputVdex */, isNull() /* dmFile */, 288 eq(PriorityClass.INTERACTIVE), argThat(dexoptOptionsMatcher), any()); 289 290 assertThat(mPrimaryDexopter.dexopt()) 291 .comparingElementsUsing(TestingUtils.<DexContainerFileDexoptResult>deepEquality()) 292 .containsExactly( 293 DexContainerFileDexoptResult.create("/data/app/foo/base.apk", 294 true /* isPrimaryAbi */, "arm64-v8a", 295 mParams.mExpectedCompilerFilter, DexoptResult.DEXOPT_PERFORMED, 296 100 /* dex2oatWallTimeMillis */, 400 /* dex2oatCpuTimeMillis */, 297 30000 /* sizeBytes */, 32000 /* sizeBeforeBytes */, 298 false /* isSkippedDueToStorageLow */), 299 DexContainerFileDexoptResult.create("/data/app/foo/base.apk", 300 false /* isPrimaryAbi */, "armeabi-v7a", 301 mParams.mExpectedCompilerFilter, DexoptResult.DEXOPT_FAILED, 302 0 /* dex2oatWallTimeMillis */, 0 /* dex2oatCpuTimeMillis */, 303 0 /* sizeBytes */, 0 /* sizeBeforeBytes */, 304 false /* isSkippedDueToStorageLow */), 305 DexContainerFileDexoptResult.create("/data/app/foo/split_0.apk", 306 true /* isPrimaryAbi */, "arm64-v8a", 307 mParams.mExpectedCompilerFilter, DexoptResult.DEXOPT_SKIPPED, 308 0 /* dex2oatWallTimeMillis */, 0 /* dex2oatCpuTimeMillis */, 309 0 /* sizeBytes */, 0 /* sizeBeforeBytes */, 310 false /* isSkippedDueToStorageLow */), 311 DexContainerFileDexoptResult.create("/data/app/foo/split_0.apk", 312 false /* isPrimaryAbi */, "armeabi-v7a", 313 mParams.mExpectedCompilerFilter, DexoptResult.DEXOPT_PERFORMED, 314 200 /* dex2oatWallTimeMillis */, 200 /* dex2oatCpuTimeMillis */, 315 10000 /* sizeBytes */, 0 /* sizeBeforeBytes */, 316 false /* isSkippedDueToStorageLow */)); 317 318 // Verify that there are no more calls than the ones above. 319 verify(mArtd, times(3)) 320 .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(), 321 any()); 322 } 323 324 private static class Params { 325 // Package information. 326 public boolean mIsSystem = false; 327 public boolean mIsUpdatedSystemApp = false; 328 public boolean mIsIncrementalFsPath = false; 329 public int mHiddenApiEnforcementPolicy = ApplicationInfo.HIDDEN_API_ENFORCEMENT_ENABLED; 330 public boolean mIsVmSafeMode = false; 331 public boolean mIsDebuggable = false; 332 public boolean mIsSystemUi = false; 333 public boolean mIsLauncher = false; 334 public boolean mIsUseEmbeddedDex = false; 335 336 // Options. 337 public String mRequestedCompilerFilter = "verify"; 338 public boolean mForce = false; 339 public boolean mShouldDowngrade = false; 340 public boolean mSkipIfStorageLow = false; 341 342 // System properties. 343 public boolean mAlwaysDebuggable = false; 344 345 // Expectations. 346 public String mExpectedCompilerFilter = "verify"; 347 public int mExpectedDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER 348 | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION; 349 public boolean mExpectedIsInDalvikCache = false; 350 public boolean mExpectedIsDebuggable = false; 351 public boolean mExpectedIsHiddenApiPolicyEnabled = true; 352 toString()353 public String toString() { 354 return String.format("isSystem=%b," 355 + "isUpdatedSystemApp=%b," 356 + "isIncrementalFsPath=%b," 357 + "mHiddenApiEnforcementPolicy=%d," 358 + "isVmSafeMode=%b," 359 + "isDebuggable=%b," 360 + "isSystemUi=%b," 361 + "isLauncher=%b," 362 + "isUseEmbeddedDex=%b," 363 + "requestedCompilerFilter=%s," 364 + "force=%b," 365 + "shouldDowngrade=%b," 366 + "mSkipIfStorageLow=%b," 367 + "alwaysDebuggable=%b" 368 + " => " 369 + "targetCompilerFilter=%s," 370 + "expectedDexoptTrigger=%d," 371 + "expectedIsInDalvikCache=%b," 372 + "expectedIsDebuggable=%b," 373 + "expectedIsHiddenApiPolicyEnabled=%b", 374 mIsSystem, mIsUpdatedSystemApp, mIsIncrementalFsPath, 375 mHiddenApiEnforcementPolicy, mIsVmSafeMode, mIsDebuggable, mIsSystemUi, 376 mIsLauncher, mIsUseEmbeddedDex, mRequestedCompilerFilter, mForce, 377 mShouldDowngrade, mSkipIfStorageLow, mAlwaysDebuggable, mExpectedCompilerFilter, 378 mExpectedDexoptTrigger, mExpectedIsInDalvikCache, mExpectedIsDebuggable, 379 mExpectedIsHiddenApiPolicyEnabled); 380 } 381 } 382 } 383