blob: ac79d26afd8212f0544a30ac73339d9bf334b675 [file] [log] [blame]
Jiakai Zhang409f7542022-08-10 14:23:15 +01001/*
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
17package com.android.server.art;
18
Jiakai Zhang1b6130c2022-11-11 14:00:36 +000019import static com.android.server.art.ArtManagerLocal.OptimizePackageDoneCallback;
Jiakai Zhang88026aa2022-09-21 16:27:00 +010020import static com.android.server.art.model.OptimizeResult.DexContainerFileOptimizeResult;
Jiakai Zhangcf3105a2022-09-06 13:45:32 +010021import static com.android.server.art.model.OptimizeResult.PackageOptimizeResult;
Jiakai Zhang409f7542022-08-10 14:23:15 +010022
23import static com.google.common.truth.Truth.assertThat;
24
Jiakai Zhang07884942022-11-23 14:10:10 +000025import static org.junit.Assert.assertThrows;
Jiakai Zhang409f7542022-08-10 14:23:15 +010026import static org.mockito.Mockito.any;
27import static org.mockito.Mockito.anyLong;
28import static org.mockito.Mockito.eq;
29import static org.mockito.Mockito.inOrder;
30import static org.mockito.Mockito.lenient;
31import static org.mockito.Mockito.mock;
Jiakai Zhang548c9102022-11-08 13:17:47 +000032import static org.mockito.Mockito.never;
Jiakai Zhang409f7542022-08-10 14:23:15 +010033import static org.mockito.Mockito.same;
Jiakai Zhang548c9102022-11-08 13:17:47 +000034import static org.mockito.Mockito.times;
Jiakai Zhang409f7542022-08-10 14:23:15 +010035import static org.mockito.Mockito.verify;
36import static org.mockito.Mockito.verifyNoMoreInteractions;
37import static org.mockito.Mockito.when;
38
39import android.apphibernation.AppHibernationManager;
Jiakai Zhang6d41cc02022-09-09 11:59:35 +010040import android.os.CancellationSignal;
Jiakai Zhang409f7542022-08-10 14:23:15 +010041import android.os.PowerManager;
42
43import androidx.test.filters.SmallTest;
44
Jiakai Zhang548c9102022-11-08 13:17:47 +000045import com.android.server.art.model.ArtFlags;
Jiakai Zhang1b6130c2022-11-11 14:00:36 +000046import com.android.server.art.model.Config;
Jiakai Zhang9cb679b2022-08-31 14:27:33 +010047import com.android.server.art.model.OptimizeParams;
Jiakai Zhang409f7542022-08-10 14:23:15 +010048import com.android.server.art.model.OptimizeResult;
Winson Chiub4640182022-09-08 16:31:35 +000049import com.android.server.pm.PackageManagerLocal;
50import com.android.server.pm.pkg.AndroidPackage;
51import com.android.server.pm.pkg.AndroidPackageSplit;
52import com.android.server.pm.pkg.PackageState;
Jiakai Zhang548c9102022-11-08 13:17:47 +000053import com.android.server.pm.pkg.SharedLibrary;
Jiakai Zhang409f7542022-08-10 14:23:15 +010054
55import org.junit.Before;
Jiakai Zhang409f7542022-08-10 14:23:15 +010056import org.junit.Test;
57import org.junit.runner.RunWith;
58import org.mockito.InOrder;
59import org.mockito.Mock;
60import org.mockito.junit.MockitoJUnitRunner;
61
Jiakai Zhang1b6130c2022-11-11 14:00:36 +000062import java.util.ArrayList;
Jiakai Zhang409f7542022-08-10 14:23:15 +010063import java.util.List;
Jiakai Zhang548c9102022-11-08 13:17:47 +000064import java.util.concurrent.ExecutorService;
65import java.util.concurrent.Executors;
66import java.util.stream.Collectors;
Jiakai Zhang409f7542022-08-10 14:23:15 +010067
68@SmallTest
69@RunWith(MockitoJUnitRunner.StrictStubs.class)
70public class DexOptHelperTest {
Jiakai Zhang548c9102022-11-08 13:17:47 +000071 private static final String PKG_NAME_FOO = "com.example.foo";
72 private static final String PKG_NAME_BAR = "com.example.bar";
73 private static final String PKG_NAME_LIB1 = "com.example.lib1";
74 private static final String PKG_NAME_LIB2 = "com.example.lib2";
75 private static final String PKG_NAME_LIB3 = "com.example.lib3";
76 private static final String PKG_NAME_LIB4 = "com.example.lib4";
77 private static final String PKG_NAME_LIBBAZ = "com.example.libbaz";
Jiakai Zhang409f7542022-08-10 14:23:15 +010078
79 @Mock private DexOptHelper.Injector mInjector;
80 @Mock private PrimaryDexOptimizer mPrimaryDexOptimizer;
Jiakai Zhang548c9102022-11-08 13:17:47 +000081 @Mock private SecondaryDexOptimizer mSecondaryDexOptimizer;
Jiakai Zhang409f7542022-08-10 14:23:15 +010082 @Mock private AppHibernationManager mAhm;
83 @Mock private PowerManager mPowerManager;
Jiakai Zhangffa92dc2022-08-31 22:57:32 +010084 @Mock private PowerManager.WakeLock mWakeLock;
Jiakai Zhang548c9102022-11-08 13:17:47 +000085 @Mock private PackageManagerLocal.FilteredSnapshot mSnapshot;
86 private PackageState mPkgStateFoo;
87 private PackageState mPkgStateBar;
88 private PackageState mPkgStateLib1;
89 private PackageState mPkgStateLib2;
90 private PackageState mPkgStateLib4;
91 private PackageState mPkgStateLibbaz;
92 private AndroidPackage mPkgFoo;
93 private AndroidPackage mPkgBar;
94 private AndroidPackage mPkgLib1;
95 private AndroidPackage mPkgLib2;
96 private AndroidPackage mPkgLib4;
97 private AndroidPackage mPkgLibbaz;
Jiakai Zhang6d41cc02022-09-09 11:59:35 +010098 private CancellationSignal mCancellationSignal;
Jiakai Zhang548c9102022-11-08 13:17:47 +000099 private ExecutorService mExecutor = Executors.newSingleThreadExecutor();
100 private List<DexContainerFileOptimizeResult> mPrimaryResults;
101 private List<DexContainerFileOptimizeResult> mSecondaryResults;
Jiakai Zhang1b6130c2022-11-11 14:00:36 +0000102 private Config mConfig;
Jiakai Zhang548c9102022-11-08 13:17:47 +0000103 private OptimizeParams mParams;
104 private List<String> mRequestedPackages;
Jiakai Zhang409f7542022-08-10 14:23:15 +0100105 private DexOptHelper mDexOptHelper;
106
107 @Before
108 public void setUp() throws Exception {
Jiakai Zhangffa92dc2022-08-31 22:57:32 +0100109 lenient()
110 .when(mPowerManager.newWakeLock(eq(PowerManager.PARTIAL_WAKE_LOCK), any()))
111 .thenReturn(mWakeLock);
112
Jiakai Zhang548c9102022-11-08 13:17:47 +0000113 lenient().when(mAhm.isHibernatingGlobally(any())).thenReturn(false);
Jiakai Zhangffa92dc2022-08-31 22:57:32 +0100114 lenient().when(mAhm.isOatArtifactDeletionEnabled()).thenReturn(true);
Jiakai Zhang409f7542022-08-10 14:23:15 +0100115
Jiakai Zhang6d41cc02022-09-09 11:59:35 +0100116 mCancellationSignal = new CancellationSignal();
Jiakai Zhang1b6130c2022-11-11 14:00:36 +0000117 mConfig = new Config();
Jiakai Zhang409f7542022-08-10 14:23:15 +0100118
Jiakai Zhang548c9102022-11-08 13:17:47 +0000119 preparePackagesAndLibraries();
120
121 mPrimaryResults = createResults("/data/app/foo/base.apk", false /* partialFailure */);
122 mSecondaryResults =
123 createResults("/data/user_de/0/foo/foo.apk", false /* partialFailure */);
124
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100125 lenient()
Jiakai Zhang548c9102022-11-08 13:17:47 +0000126 .when(mInjector.getPrimaryDexOptimizer(any(), any(), any(), any()))
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100127 .thenReturn(mPrimaryDexOptimizer);
Jiakai Zhang548c9102022-11-08 13:17:47 +0000128 lenient().when(mPrimaryDexOptimizer.dexopt()).thenReturn(mPrimaryResults);
129
130 lenient()
131 .when(mInjector.getSecondaryDexOptimizer(any(), any(), any(), any()))
132 .thenReturn(mSecondaryDexOptimizer);
133 lenient().when(mSecondaryDexOptimizer.dexopt()).thenReturn(mSecondaryResults);
134
135 mParams = new OptimizeParams.Builder("install")
136 .setCompilerFilter("speed-profile")
137 .setFlags(ArtFlags.FLAG_FOR_SECONDARY_DEX
138 | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES,
139 ArtFlags.FLAG_FOR_SECONDARY_DEX
140 | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES)
141 .build();
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100142
Jiakai Zhang1b6130c2022-11-11 14:00:36 +0000143 lenient().when(mInjector.getAppHibernationManager()).thenReturn(mAhm);
144 lenient().when(mInjector.getPowerManager()).thenReturn(mPowerManager);
145 lenient().when(mInjector.getConfig()).thenReturn(mConfig);
146
Jiakai Zhang409f7542022-08-10 14:23:15 +0100147 mDexOptHelper = new DexOptHelper(mInjector);
148 }
149
150 @Test
Jiakai Zhang59351632022-08-10 16:51:35 +0100151 public void testDexopt() throws Exception {
Jiakai Zhang548c9102022-11-08 13:17:47 +0000152 // Only package libbaz fails.
153 var failingPrimaryDexOptimizer = mock(PrimaryDexOptimizer.class);
154 List<DexContainerFileOptimizeResult> partialFailureResults =
155 createResults("/data/app/foo/base.apk", true /* partialFailure */);
156 lenient().when(failingPrimaryDexOptimizer.dexopt()).thenReturn(partialFailureResults);
157 when(mInjector.getPrimaryDexOptimizer(same(mPkgStateLibbaz), any(), any(), any()))
158 .thenReturn(failingPrimaryDexOptimizer);
Jiakai Zhang409f7542022-08-10 14:23:15 +0100159
Jiakai Zhang6d41cc02022-09-09 11:59:35 +0100160 OptimizeResult result = mDexOptHelper.dexopt(
Jiakai Zhang548c9102022-11-08 13:17:47 +0000161 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
Jiakai Zhang409f7542022-08-10 14:23:15 +0100162
Jiakai Zhang409f7542022-08-10 14:23:15 +0100163 assertThat(result.getRequestedCompilerFilter()).isEqualTo("speed-profile");
164 assertThat(result.getReason()).isEqualTo("install");
165 assertThat(result.getFinalStatus()).isEqualTo(OptimizeResult.OPTIMIZE_FAILED);
Jiakai Zhangcf3105a2022-09-06 13:45:32 +0100166
Jiakai Zhang548c9102022-11-08 13:17:47 +0000167 // The requested packages must come first.
168 assertThat(result.getPackageOptimizeResults()).hasSize(6);
169 checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, OptimizeResult.OPTIMIZE_PERFORMED,
170 List.of(mPrimaryResults, mSecondaryResults));
171 checkPackageResult(result, 1 /* index */, PKG_NAME_BAR, OptimizeResult.OPTIMIZE_PERFORMED,
172 List.of(mPrimaryResults, mSecondaryResults));
173 checkPackageResult(result, 2 /* index */, PKG_NAME_LIBBAZ, OptimizeResult.OPTIMIZE_FAILED,
174 List.of(partialFailureResults, mSecondaryResults));
175 checkPackageResult(result, 3 /* index */, PKG_NAME_LIB1, OptimizeResult.OPTIMIZE_PERFORMED,
176 List.of(mPrimaryResults, mSecondaryResults));
177 checkPackageResult(result, 4 /* index */, PKG_NAME_LIB2, OptimizeResult.OPTIMIZE_PERFORMED,
178 List.of(mPrimaryResults, mSecondaryResults));
179 checkPackageResult(result, 5 /* index */, PKG_NAME_LIB4, OptimizeResult.OPTIMIZE_PERFORMED,
180 List.of(mPrimaryResults, mSecondaryResults));
Jiakai Zhangffa92dc2022-08-31 22:57:32 +0100181
Jiakai Zhang548c9102022-11-08 13:17:47 +0000182 // The order matters. It should acquire the wake lock only once, at the beginning, and
183 // release the wake lock at the end. When running in a single thread, it should dexopt
184 // primary dex files and the secondary dex files together for each package, and it should
185 // dexopt requested packages, in the given order, and then dexopt dependencies.
186 InOrder inOrder = inOrder(mInjector, mWakeLock);
187 inOrder.verify(mWakeLock).setWorkSource(any());
Jiakai Zhangffa92dc2022-08-31 22:57:32 +0100188 inOrder.verify(mWakeLock).acquire(anyLong());
Jiakai Zhang548c9102022-11-08 13:17:47 +0000189 inOrder.verify(mInjector).getPrimaryDexOptimizer(
190 same(mPkgStateFoo), same(mPkgFoo), same(mParams), same(mCancellationSignal));
191 inOrder.verify(mInjector).getSecondaryDexOptimizer(
192 same(mPkgStateFoo), same(mPkgFoo), same(mParams), same(mCancellationSignal));
193 inOrder.verify(mInjector).getPrimaryDexOptimizer(
194 same(mPkgStateBar), same(mPkgBar), same(mParams), same(mCancellationSignal));
195 inOrder.verify(mInjector).getSecondaryDexOptimizer(
196 same(mPkgStateBar), same(mPkgBar), same(mParams), same(mCancellationSignal));
197 inOrder.verify(mInjector).getPrimaryDexOptimizer(
198 same(mPkgStateLibbaz), same(mPkgLibbaz), same(mParams), same(mCancellationSignal));
199 inOrder.verify(mInjector).getSecondaryDexOptimizer(
200 same(mPkgStateLibbaz), same(mPkgLibbaz), same(mParams), same(mCancellationSignal));
201 inOrder.verify(mInjector).getPrimaryDexOptimizer(
202 same(mPkgStateLib1), same(mPkgLib1), same(mParams), same(mCancellationSignal));
203 inOrder.verify(mInjector).getSecondaryDexOptimizer(
204 same(mPkgStateLib1), same(mPkgLib1), same(mParams), same(mCancellationSignal));
205 inOrder.verify(mInjector).getPrimaryDexOptimizer(
206 same(mPkgStateLib2), same(mPkgLib2), same(mParams), same(mCancellationSignal));
207 inOrder.verify(mInjector).getSecondaryDexOptimizer(
208 same(mPkgStateLib2), same(mPkgLib2), same(mParams), same(mCancellationSignal));
209 inOrder.verify(mInjector).getPrimaryDexOptimizer(
210 same(mPkgStateLib4), same(mPkgLib4), same(mParams), same(mCancellationSignal));
211 inOrder.verify(mInjector).getSecondaryDexOptimizer(
212 same(mPkgStateLib4), same(mPkgLib4), same(mParams), same(mCancellationSignal));
Jiakai Zhangffa92dc2022-08-31 22:57:32 +0100213 inOrder.verify(mWakeLock).release();
Jiakai Zhang548c9102022-11-08 13:17:47 +0000214
215 verifyNoMoreDexopt(6 /* expectedPrimaryTimes */, 6 /* expectedSecondaryTimes */);
216
217 verifyNoMoreInteractions(mWakeLock);
218 }
219
220 @Test
221 public void testDexoptNoDependencies() throws Exception {
222 mParams = new OptimizeParams.Builder("install")
223 .setCompilerFilter("speed-profile")
224 .setFlags(ArtFlags.FLAG_FOR_SECONDARY_DEX,
225 ArtFlags.FLAG_FOR_SECONDARY_DEX
226 | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES)
227 .build();
228
229 OptimizeResult result = mDexOptHelper.dexopt(
230 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
231
232 assertThat(result.getPackageOptimizeResults()).hasSize(3);
233 checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, OptimizeResult.OPTIMIZE_PERFORMED,
234 List.of(mPrimaryResults, mSecondaryResults));
235 checkPackageResult(result, 1 /* index */, PKG_NAME_BAR, OptimizeResult.OPTIMIZE_PERFORMED,
236 List.of(mPrimaryResults, mSecondaryResults));
237 checkPackageResult(result, 2 /* index */, PKG_NAME_LIBBAZ,
238 OptimizeResult.OPTIMIZE_PERFORMED, List.of(mPrimaryResults, mSecondaryResults));
239
240 verifyNoMoreDexopt(3 /* expectedPrimaryTimes */, 3 /* expectedSecondaryTimes */);
241 }
242
243 @Test
244 public void testDexoptPrimaryOnly() throws Exception {
245 mParams = new OptimizeParams.Builder("install")
246 .setCompilerFilter("speed-profile")
247 .setFlags(ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES,
248 ArtFlags.FLAG_FOR_SECONDARY_DEX
249 | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES)
250 .build();
251
252 OptimizeResult result = mDexOptHelper.dexopt(
253 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
254
255 assertThat(result.getPackageOptimizeResults()).hasSize(6);
256 checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, OptimizeResult.OPTIMIZE_PERFORMED,
257 List.of(mPrimaryResults));
258 checkPackageResult(result, 1 /* index */, PKG_NAME_BAR, OptimizeResult.OPTIMIZE_PERFORMED,
259 List.of(mPrimaryResults));
260 checkPackageResult(result, 2 /* index */, PKG_NAME_LIBBAZ,
261 OptimizeResult.OPTIMIZE_PERFORMED, List.of(mPrimaryResults));
262 checkPackageResult(result, 3 /* index */, PKG_NAME_LIB1, OptimizeResult.OPTIMIZE_PERFORMED,
263 List.of(mPrimaryResults));
264 checkPackageResult(result, 4 /* index */, PKG_NAME_LIB2, OptimizeResult.OPTIMIZE_PERFORMED,
265 List.of(mPrimaryResults));
266 checkPackageResult(result, 5 /* index */, PKG_NAME_LIB4, OptimizeResult.OPTIMIZE_PERFORMED,
267 List.of(mPrimaryResults));
268
269 verifyNoMoreDexopt(6 /* expectedPrimaryTimes */, 0 /* expectedSecondaryTimes */);
270 }
271
272 @Test
273 public void testDexoptPrimaryOnlyNoDependencies() throws Exception {
274 mParams = new OptimizeParams.Builder("install")
275 .setCompilerFilter("speed-profile")
276 .setFlags(0,
277 ArtFlags.FLAG_FOR_SECONDARY_DEX
278 | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES)
279 .build();
280
281 OptimizeResult result = mDexOptHelper.dexopt(
282 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
283
284 assertThat(result.getPackageOptimizeResults()).hasSize(3);
285 checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, OptimizeResult.OPTIMIZE_PERFORMED,
286 List.of(mPrimaryResults));
287 checkPackageResult(result, 1 /* index */, PKG_NAME_BAR, OptimizeResult.OPTIMIZE_PERFORMED,
288 List.of(mPrimaryResults));
289 checkPackageResult(result, 2 /* index */, PKG_NAME_LIBBAZ,
290 OptimizeResult.OPTIMIZE_PERFORMED, List.of(mPrimaryResults));
291
292 verifyNoMoreDexopt(3 /* expectedPrimaryTimes */, 0 /* expectedSecondaryTimes */);
293 }
294
295 @Test
296 public void testDexoptCancelledBetweenDex2oatInvocations() throws Exception {
297 when(mPrimaryDexOptimizer.dexopt()).thenAnswer(invocation -> {
298 mCancellationSignal.cancel();
299 return mPrimaryResults;
300 });
301
302 OptimizeResult result = mDexOptHelper.dexopt(
303 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
304
305 assertThat(result.getFinalStatus()).isEqualTo(OptimizeResult.OPTIMIZE_CANCELLED);
306
307 assertThat(result.getPackageOptimizeResults()).hasSize(6);
308 checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, OptimizeResult.OPTIMIZE_CANCELLED,
309 List.of(mPrimaryResults));
310 checkPackageResult(
311 result, 1 /* index */, PKG_NAME_BAR, OptimizeResult.OPTIMIZE_CANCELLED, List.of());
312 checkPackageResult(result, 2 /* index */, PKG_NAME_LIBBAZ,
313 OptimizeResult.OPTIMIZE_CANCELLED, List.of());
314 checkPackageResult(
315 result, 3 /* index */, PKG_NAME_LIB1, OptimizeResult.OPTIMIZE_CANCELLED, List.of());
316 checkPackageResult(
317 result, 4 /* index */, PKG_NAME_LIB2, OptimizeResult.OPTIMIZE_CANCELLED, List.of());
318 checkPackageResult(
319 result, 5 /* index */, PKG_NAME_LIB4, OptimizeResult.OPTIMIZE_CANCELLED, List.of());
320
321 verify(mInjector).getPrimaryDexOptimizer(
322 same(mPkgStateFoo), same(mPkgFoo), same(mParams), same(mCancellationSignal));
323
324 verifyNoMoreDexopt(1 /* expectedPrimaryTimes */, 0 /* expectedSecondaryTimes */);
Jiakai Zhang409f7542022-08-10 14:23:15 +0100325 }
326
327 @Test
Jiakai Zhang59351632022-08-10 16:51:35 +0100328 public void testDexoptNoCode() throws Exception {
Jiakai Zhang548c9102022-11-08 13:17:47 +0000329 when(mPkgFoo.getSplits().get(0).isHasCode()).thenReturn(false);
Jiakai Zhang409f7542022-08-10 14:23:15 +0100330
Jiakai Zhang548c9102022-11-08 13:17:47 +0000331 mRequestedPackages = List.of(PKG_NAME_FOO);
Jiakai Zhang6d41cc02022-09-09 11:59:35 +0100332 OptimizeResult result = mDexOptHelper.dexopt(
Jiakai Zhang548c9102022-11-08 13:17:47 +0000333 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
Jiakai Zhang409f7542022-08-10 14:23:15 +0100334
335 assertThat(result.getFinalStatus()).isEqualTo(OptimizeResult.OPTIMIZE_SKIPPED);
Jiakai Zhang548c9102022-11-08 13:17:47 +0000336 assertThat(result.getPackageOptimizeResults()).hasSize(1);
337 checkPackageResult(
338 result, 0 /* index */, PKG_NAME_FOO, OptimizeResult.OPTIMIZE_SKIPPED, List.of());
339
340 verifyNoDexopt();
341 }
342
343 @Test
344 public void testDexoptLibraryNoCode() throws Exception {
345 when(mPkgLib1.getSplits().get(0).isHasCode()).thenReturn(false);
346
347 mRequestedPackages = List.of(PKG_NAME_FOO);
348 OptimizeResult result = mDexOptHelper.dexopt(
349 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
350
351 assertThat(result.getFinalStatus()).isEqualTo(OptimizeResult.OPTIMIZE_PERFORMED);
352 assertThat(result.getPackageOptimizeResults()).hasSize(1);
353 checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, OptimizeResult.OPTIMIZE_PERFORMED,
354 List.of(mPrimaryResults, mSecondaryResults));
355
356 verifyNoMoreDexopt(1 /* expectedPrimaryTimes */, 1 /* expectedSecondaryTimes */);
Jiakai Zhang409f7542022-08-10 14:23:15 +0100357 }
358
359 @Test
Jiakai Zhang59351632022-08-10 16:51:35 +0100360 public void testDexoptIsHibernating() throws Exception {
Jiakai Zhang548c9102022-11-08 13:17:47 +0000361 lenient().when(mAhm.isHibernatingGlobally(PKG_NAME_FOO)).thenReturn(true);
Jiakai Zhang409f7542022-08-10 14:23:15 +0100362
Jiakai Zhang548c9102022-11-08 13:17:47 +0000363 mRequestedPackages = List.of(PKG_NAME_FOO);
Jiakai Zhang6d41cc02022-09-09 11:59:35 +0100364 OptimizeResult result = mDexOptHelper.dexopt(
Jiakai Zhang548c9102022-11-08 13:17:47 +0000365 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
Jiakai Zhang409f7542022-08-10 14:23:15 +0100366
367 assertThat(result.getFinalStatus()).isEqualTo(OptimizeResult.OPTIMIZE_SKIPPED);
Jiakai Zhang548c9102022-11-08 13:17:47 +0000368 checkPackageResult(
369 result, 0 /* index */, PKG_NAME_FOO, OptimizeResult.OPTIMIZE_SKIPPED, List.of());
370
371 verifyNoDexopt();
Jiakai Zhang409f7542022-08-10 14:23:15 +0100372 }
373
374 @Test
Jiakai Zhang59351632022-08-10 16:51:35 +0100375 public void testDexoptIsHibernatingButOatArtifactDeletionDisabled() throws Exception {
Jiakai Zhang548c9102022-11-08 13:17:47 +0000376 lenient().when(mAhm.isHibernatingGlobally(PKG_NAME_FOO)).thenReturn(true);
Jiakai Zhang409f7542022-08-10 14:23:15 +0100377 lenient().when(mAhm.isOatArtifactDeletionEnabled()).thenReturn(false);
378
Jiakai Zhang6d41cc02022-09-09 11:59:35 +0100379 OptimizeResult result = mDexOptHelper.dexopt(
Jiakai Zhang548c9102022-11-08 13:17:47 +0000380 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
Jiakai Zhang409f7542022-08-10 14:23:15 +0100381
Jiakai Zhang548c9102022-11-08 13:17:47 +0000382 assertThat(result.getPackageOptimizeResults()).hasSize(6);
383 checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, OptimizeResult.OPTIMIZE_PERFORMED,
384 List.of(mPrimaryResults, mSecondaryResults));
385 checkPackageResult(result, 1 /* index */, PKG_NAME_BAR, OptimizeResult.OPTIMIZE_PERFORMED,
386 List.of(mPrimaryResults, mSecondaryResults));
387 checkPackageResult(result, 2 /* index */, PKG_NAME_LIBBAZ,
388 OptimizeResult.OPTIMIZE_PERFORMED, List.of(mPrimaryResults, mSecondaryResults));
389 checkPackageResult(result, 3 /* index */, PKG_NAME_LIB1, OptimizeResult.OPTIMIZE_PERFORMED,
390 List.of(mPrimaryResults, mSecondaryResults));
391 checkPackageResult(result, 4 /* index */, PKG_NAME_LIB2, OptimizeResult.OPTIMIZE_PERFORMED,
392 List.of(mPrimaryResults, mSecondaryResults));
393 checkPackageResult(result, 5 /* index */, PKG_NAME_LIB4, OptimizeResult.OPTIMIZE_PERFORMED,
394 List.of(mPrimaryResults, mSecondaryResults));
Jiakai Zhang409f7542022-08-10 14:23:15 +0100395 }
396
397 @Test
Jiakai Zhang59351632022-08-10 16:51:35 +0100398 public void testDexoptAlwaysReleasesWakeLock() throws Exception {
Jiakai Zhangc3db1c72022-10-18 10:59:13 +0100399 when(mPrimaryDexOptimizer.dexopt()).thenThrow(IllegalStateException.class);
Jiakai Zhang409f7542022-08-10 14:23:15 +0100400
401 try {
Jiakai Zhang548c9102022-11-08 13:17:47 +0000402 mDexOptHelper.dexopt(
403 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
Winson Chiub4640182022-09-08 16:31:35 +0000404 } catch (Exception ignored) {
Jiakai Zhang409f7542022-08-10 14:23:15 +0100405 }
406
Jiakai Zhangffa92dc2022-08-31 22:57:32 +0100407 verify(mWakeLock).release();
Jiakai Zhang409f7542022-08-10 14:23:15 +0100408 }
409
Jiakai Zhang548c9102022-11-08 13:17:47 +0000410 @Test(expected = IllegalArgumentException.class)
411 public void testDexoptPackageNotFound() throws Exception {
412 when(mSnapshot.getPackageState(any())).thenReturn(null);
413
414 mDexOptHelper.dexopt(
415 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
416
417 verifyNoDexopt();
418 }
419
420 @Test(expected = IllegalArgumentException.class)
421 public void testDexoptNoPackage() throws Exception {
422 lenient().when(mPkgStateFoo.getAndroidPackage()).thenReturn(null);
423
424 mDexOptHelper.dexopt(
425 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
426
427 verifyNoDexopt();
428 }
429
Jiakai Zhang1b6130c2022-11-11 14:00:36 +0000430 @Test
Jiakai Zhang07884942022-11-23 14:10:10 +0000431 public void testDexoptSplit() throws Exception {
432 mRequestedPackages = List.of(PKG_NAME_FOO);
433 mParams = new OptimizeParams.Builder("install")
434 .setCompilerFilter("speed-profile")
435 .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SINGLE_SPLIT)
436 .setSplitName("split_0")
437 .build();
438
439 mDexOptHelper.dexopt(
440 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
441 }
442
443 @Test
444 public void testDexoptSplitNotFound() throws Exception {
445 mRequestedPackages = List.of(PKG_NAME_FOO);
446 mParams = new OptimizeParams.Builder("install")
447 .setCompilerFilter("speed-profile")
448 .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SINGLE_SPLIT)
449 .setSplitName("split_bogus")
450 .build();
451
452 assertThrows(IllegalArgumentException.class, () -> {
453 mDexOptHelper.dexopt(
454 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
455 });
456 }
457
458 @Test
Jiakai Zhang1b6130c2022-11-11 14:00:36 +0000459 public void testCallbacks() throws Exception {
460 List<OptimizeResult> list1 = new ArrayList<>();
461 mConfig.addOptimizePackageDoneCallback(Runnable::run, result -> list1.add(result));
462
463 List<OptimizeResult> list2 = new ArrayList<>();
464 mConfig.addOptimizePackageDoneCallback(Runnable::run, result -> list2.add(result));
465
466 OptimizeResult result = mDexOptHelper.dexopt(
467 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
468
469 assertThat(list1).containsExactly(result);
470 assertThat(list2).containsExactly(result);
471 }
472
473 @Test
474 public void testCallbackRemoved() throws Exception {
475 List<OptimizeResult> list1 = new ArrayList<>();
476 OptimizePackageDoneCallback callback1 = result -> list1.add(result);
477 mConfig.addOptimizePackageDoneCallback(Runnable::run, callback1);
478
479 List<OptimizeResult> list2 = new ArrayList<>();
480 mConfig.addOptimizePackageDoneCallback(Runnable::run, result -> list2.add(result));
481
482 mConfig.removeOptimizePackageDoneCallback(callback1);
483
484 OptimizeResult result = mDexOptHelper.dexopt(
485 mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
486
487 assertThat(list1).isEmpty();
488 assertThat(list2).containsExactly(result);
489 }
490
491 @Test(expected = IllegalStateException.class)
492 public void testCallbackAlreadyAdded() throws Exception {
493 List<OptimizeResult> list = new ArrayList<>();
494 OptimizePackageDoneCallback callback = result -> list.add(result);
495 mConfig.addOptimizePackageDoneCallback(Runnable::run, callback);
496 mConfig.addOptimizePackageDoneCallback(Runnable::run, callback);
497 }
498
Jiakai Zhang07884942022-11-23 14:10:10 +0000499 private AndroidPackage createPackage(boolean multiSplit) {
Winson Chiub4640182022-09-08 16:31:35 +0000500 AndroidPackage pkg = mock(AndroidPackage.class);
Jiakai Zhang07884942022-11-23 14:10:10 +0000501
Winson Chiub4640182022-09-08 16:31:35 +0000502 var baseSplit = mock(AndroidPackageSplit.class);
503 lenient().when(baseSplit.isHasCode()).thenReturn(true);
Jiakai Zhang07884942022-11-23 14:10:10 +0000504
505 if (multiSplit) {
506 var split0 = mock(AndroidPackageSplit.class);
507 lenient().when(split0.getName()).thenReturn("split_0");
508 lenient().when(split0.isHasCode()).thenReturn(true);
509
510 lenient().when(pkg.getSplits()).thenReturn(List.of(baseSplit, split0));
511 } else {
512 lenient().when(pkg.getSplits()).thenReturn(List.of(baseSplit));
513 }
514
Jiakai Zhang409f7542022-08-10 14:23:15 +0100515 return pkg;
516 }
517
Jiakai Zhang07884942022-11-23 14:10:10 +0000518 private PackageState createPackageState(
519 String packageName, List<SharedLibrary> deps, boolean multiSplit) {
Jiakai Zhang409f7542022-08-10 14:23:15 +0100520 PackageState pkgState = mock(PackageState.class);
Jiakai Zhang548c9102022-11-08 13:17:47 +0000521 lenient().when(pkgState.getPackageName()).thenReturn(packageName);
Winson Chiub4640182022-09-08 16:31:35 +0000522 lenient().when(pkgState.getAppId()).thenReturn(12345);
Jiakai Zhang548c9102022-11-08 13:17:47 +0000523 lenient().when(pkgState.getUsesLibraries()).thenReturn(deps);
Jiakai Zhang07884942022-11-23 14:10:10 +0000524 AndroidPackage pkg = createPackage(multiSplit);
Jiakai Zhang409f7542022-08-10 14:23:15 +0100525 lenient().when(pkgState.getAndroidPackage()).thenReturn(pkg);
526 return pkgState;
527 }
Jiakai Zhang548c9102022-11-08 13:17:47 +0000528
529 private SharedLibrary createLibrary(
530 String libraryName, String packageName, List<SharedLibrary> deps) {
531 SharedLibrary library = mock(SharedLibrary.class);
532 lenient().when(library.getName()).thenReturn(libraryName);
533 lenient().when(library.getPackageName()).thenReturn(packageName);
534 lenient().when(library.getDependencies()).thenReturn(deps);
535 return library;
536 }
537
538 private void preparePackagesAndLibraries() {
539 // Dependency graph:
540 // foo bar
541 // | |
542 // lib1a (lib1) lib1b (lib1) lib1c (lib1)
543 // / \ / \ |
544 // / \ / \ |
545 // libbaz (libbaz) lib2 (lib2) lib4 (lib4) lib3 (lib3)
546 //
547 // "lib1a", "lib1b", and "lib1c" belong to the same package "lib1".
548
549 mRequestedPackages = List.of(PKG_NAME_FOO, PKG_NAME_BAR, PKG_NAME_LIBBAZ);
550
551 SharedLibrary libbaz = createLibrary("libbaz", PKG_NAME_LIBBAZ, List.of());
552 SharedLibrary lib4 = createLibrary("lib4", PKG_NAME_LIB4, List.of());
553 SharedLibrary lib3 = createLibrary("lib3", PKG_NAME_LIB3, List.of());
554 SharedLibrary lib2 = createLibrary("lib2", PKG_NAME_LIB2, List.of());
555 SharedLibrary lib1a = createLibrary("lib1a", PKG_NAME_LIB1, List.of(libbaz, lib2));
556 SharedLibrary lib1b = createLibrary("lib1b", PKG_NAME_LIB1, List.of(lib2, lib4));
557 SharedLibrary lib1c = createLibrary("lib1c", PKG_NAME_LIB1, List.of(lib3));
558
Jiakai Zhang07884942022-11-23 14:10:10 +0000559 mPkgStateFoo = createPackageState(PKG_NAME_FOO, List.of(lib1a), true /* multiSplit */);
Jiakai Zhang548c9102022-11-08 13:17:47 +0000560 mPkgFoo = mPkgStateFoo.getAndroidPackage();
561 lenient().when(mSnapshot.getPackageState(PKG_NAME_FOO)).thenReturn(mPkgStateFoo);
562
Jiakai Zhang07884942022-11-23 14:10:10 +0000563 mPkgStateBar = createPackageState(PKG_NAME_BAR, List.of(lib1b), false /* multiSplit */);
Jiakai Zhang548c9102022-11-08 13:17:47 +0000564 mPkgBar = mPkgStateBar.getAndroidPackage();
565 lenient().when(mSnapshot.getPackageState(PKG_NAME_BAR)).thenReturn(mPkgStateBar);
566
Jiakai Zhang07884942022-11-23 14:10:10 +0000567 mPkgStateLib1 = createPackageState(
568 PKG_NAME_LIB1, List.of(libbaz, lib2, lib3, lib4), false /* multiSplit */);
Jiakai Zhang548c9102022-11-08 13:17:47 +0000569 mPkgLib1 = mPkgStateLib1.getAndroidPackage();
570 lenient().when(mSnapshot.getPackageState(PKG_NAME_LIB1)).thenReturn(mPkgStateLib1);
571
Jiakai Zhang07884942022-11-23 14:10:10 +0000572 mPkgStateLib2 = createPackageState(PKG_NAME_LIB2, List.of(), false /* multiSplit */);
Jiakai Zhang548c9102022-11-08 13:17:47 +0000573 mPkgLib2 = mPkgStateLib2.getAndroidPackage();
574 lenient().when(mSnapshot.getPackageState(PKG_NAME_LIB2)).thenReturn(mPkgStateLib2);
575
576 // This should not be considered as a transitive dependency of any requested package, even
577 // though it is a dependency of package "lib1".
Jiakai Zhang07884942022-11-23 14:10:10 +0000578 PackageState pkgStateLib3 =
579 createPackageState(PKG_NAME_LIB3, List.of(), false /* multiSplit */);
Jiakai Zhang548c9102022-11-08 13:17:47 +0000580 lenient().when(mSnapshot.getPackageState(PKG_NAME_LIB3)).thenReturn(pkgStateLib3);
581
Jiakai Zhang07884942022-11-23 14:10:10 +0000582 mPkgStateLib4 = createPackageState(PKG_NAME_LIB4, List.of(), false /* multiSplit */);
Jiakai Zhang548c9102022-11-08 13:17:47 +0000583 mPkgLib4 = mPkgStateLib4.getAndroidPackage();
584 lenient().when(mSnapshot.getPackageState(PKG_NAME_LIB4)).thenReturn(mPkgStateLib4);
585
Jiakai Zhang07884942022-11-23 14:10:10 +0000586 mPkgStateLibbaz = createPackageState(PKG_NAME_LIBBAZ, List.of(), false /* multiSplit */);
Jiakai Zhang548c9102022-11-08 13:17:47 +0000587 mPkgLibbaz = mPkgStateLibbaz.getAndroidPackage();
588 lenient().when(mSnapshot.getPackageState(PKG_NAME_LIBBAZ)).thenReturn(mPkgStateLibbaz);
589 }
590
591 private void verifyNoDexopt() {
592 verify(mInjector, never()).getPrimaryDexOptimizer(any(), any(), any(), any());
593 verify(mInjector, never()).getSecondaryDexOptimizer(any(), any(), any(), any());
594 }
595
596 private void verifyNoMoreDexopt(int expectedPrimaryTimes, int expectedSecondaryTimes) {
597 verify(mInjector, times(expectedPrimaryTimes))
598 .getPrimaryDexOptimizer(any(), any(), any(), any());
599 verify(mInjector, times(expectedSecondaryTimes))
600 .getSecondaryDexOptimizer(any(), any(), any(), any());
601 }
602
603 private List<DexContainerFileOptimizeResult> createResults(
604 String dexPath, boolean partialFailure) {
605 return List.of(new DexContainerFileOptimizeResult(dexPath, true /* isPrimaryAbi */,
606 "arm64-v8a", "verify", OptimizeResult.OPTIMIZE_PERFORMED,
Jiakai Zhang843ce522022-11-11 14:00:39 +0000607 100 /* dex2oatWallTimeMillis */, 400 /* dex2oatCpuTimeMillis */,
608 0 /* sizeBytes */, 0 /* sizeBeforeBytes */),
Jiakai Zhang548c9102022-11-08 13:17:47 +0000609 new DexContainerFileOptimizeResult(dexPath, false /* isPrimaryAbi */, "armeabi-v7a",
610 "verify",
611 partialFailure ? OptimizeResult.OPTIMIZE_FAILED
612 : OptimizeResult.OPTIMIZE_PERFORMED,
Jiakai Zhang843ce522022-11-11 14:00:39 +0000613 100 /* dex2oatWallTimeMillis */, 400 /* dex2oatCpuTimeMillis */,
614 0 /* sizeBytes */, 0 /* sizeBeforeBytes */));
Jiakai Zhang548c9102022-11-08 13:17:47 +0000615 }
616
617 private void checkPackageResult(OptimizeResult result, int index, String packageName,
618 @OptimizeResult.OptimizeStatus int status,
619 List<List<DexContainerFileOptimizeResult>> dexContainerFileOptimizeResults) {
620 PackageOptimizeResult packageResult = result.getPackageOptimizeResults().get(index);
621 assertThat(packageResult.getPackageName()).isEqualTo(packageName);
622 assertThat(packageResult.getStatus()).isEqualTo(status);
623 assertThat(packageResult.getDexContainerFileOptimizeResults())
624 .containsExactlyElementsIn(dexContainerFileOptimizeResults.stream()
625 .flatMap(r -> r.stream())
626 .collect(Collectors.toList()));
627 }
Jiakai Zhang409f7542022-08-10 14:23:15 +0100628}