• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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