diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 9ff4aab83cffd..d26a1ac4fba07 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -253,7 +253,8 @@ public final class BackgroundDexOptService { * *

This is only for shell command and only root or shell user can use this. * - * @param packageNames dex optimize the passed packages or all packages if null + * @param packageNames dex optimize the passed packages in the given order, or all packages in + * the default order if null * * @return true if dex optimization is complete. false if the task is cancelled or if there was * an error. @@ -268,11 +269,11 @@ public final class BackgroundDexOptService { resetStatesForNewDexOptRunLocked(Thread.currentThread()); } PackageManagerService pm = mInjector.getPackageManagerService(); - ArraySet packagesToOptimize; + List packagesToOptimize; if (packageNames == null) { packagesToOptimize = mDexOptHelper.getOptimizablePackages(pm.snapshotComputer()); } else { - packagesToOptimize = new ArraySet<>(packageNames); + packagesToOptimize = packageNames; } return runIdleOptimization(pm, packagesToOptimize, /* isPostBootUpdate= */ false); } finally { @@ -335,7 +336,7 @@ public final class BackgroundDexOptService { return false; } - ArraySet pkgs = mDexOptHelper.getOptimizablePackages(pm.snapshotComputer()); + List pkgs = mDexOptHelper.getOptimizablePackages(pm.snapshotComputer()); if (pkgs.isEmpty()) { Slog.i(TAG, "No packages to optimize"); markPostBootUpdateCompleted(params); @@ -525,7 +526,7 @@ public final class BackgroundDexOptService { } /** Returns true if completed */ - private boolean runIdleOptimization(PackageManagerService pm, ArraySet pkgs, + private boolean runIdleOptimization(PackageManagerService pm, List pkgs, boolean isPostBootUpdate) { synchronized (mLock) { mLastExecutionStartTimeMs = SystemClock.elapsedRealtime(); @@ -581,10 +582,9 @@ public final class BackgroundDexOptService { } @Status - private int idleOptimizePackages(PackageManagerService pm, ArraySet pkgs, + private int idleOptimizePackages(PackageManagerService pm, List pkgs, long lowStorageThreshold, boolean isPostBootUpdate) { ArraySet updatedPackages = new ArraySet<>(); - ArraySet updatedPackagesDueToSecondaryDex = new ArraySet<>(); try { boolean supportSecondaryDex = mInjector.supportSecondaryDex(); @@ -640,25 +640,12 @@ public final class BackgroundDexOptService { } } - pkgs = new ArraySet<>(pkgs); + pkgs = new ArrayList<>(pkgs); pkgs.removeAll(unusedPackages); } } - @Status int primaryResult = optimizePackages(pkgs, lowStorageThreshold, - /*isForPrimaryDex=*/ true, updatedPackages, isPostBootUpdate); - if (primaryResult != STATUS_OK) { - return primaryResult; - } - - if (!supportSecondaryDex) { - return STATUS_OK; - } - - @Status int secondaryResult = optimizePackages(pkgs, lowStorageThreshold, - /*isForPrimaryDex*/ false, updatedPackagesDueToSecondaryDex, - isPostBootUpdate); - return secondaryResult; + return optimizePackages(pkgs, lowStorageThreshold, updatedPackages, isPostBootUpdate); } finally { // Always let the pinner service know about changes. notifyPinService(updatedPackages); @@ -670,8 +657,10 @@ public final class BackgroundDexOptService { } @Status - private int optimizePackages(ArraySet pkgs, long lowStorageThreshold, - boolean isForPrimaryDex, ArraySet updatedPackages, boolean isPostBootUpdate) { + private int optimizePackages(List pkgs, long lowStorageThreshold, + ArraySet updatedPackages, boolean isPostBootUpdate) { + boolean supportSecondaryDex = mInjector.supportSecondaryDex(); + for (String pkg : pkgs) { int abortCode = abortIdleOptimizations(lowStorageThreshold); if (abortCode != STATUS_OK) { @@ -679,11 +668,23 @@ public final class BackgroundDexOptService { return abortCode; } - @DexOptResult int result = optimizePackage(pkg, isForPrimaryDex, isPostBootUpdate); - if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) { + @DexOptResult int primaryResult = + optimizePackage(pkg, true /* isForPrimaryDex */, isPostBootUpdate); + if (primaryResult == PackageDexOptimizer.DEX_OPT_PERFORMED) { updatedPackages.add(pkg); - } else if (result != PackageDexOptimizer.DEX_OPT_SKIPPED) { - return convertPackageDexOptimizerStatusToInternal(result); + } else if (primaryResult != PackageDexOptimizer.DEX_OPT_SKIPPED) { + return convertPackageDexOptimizerStatusToInternal(primaryResult); + } + + if (!supportSecondaryDex) { + continue; + } + + @DexOptResult int secondaryResult = + optimizePackage(pkg, false /* isForPrimaryDex */, isPostBootUpdate); + if (secondaryResult != PackageDexOptimizer.DEX_OPT_PERFORMED + && secondaryResult != PackageDexOptimizer.DEX_OPT_SKIPPED) { + return convertPackageDexOptimizerStatusToInternal(secondaryResult); } } return STATUS_OK; diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index 50b2e23218864..bb2ba5cc498d1 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -293,8 +293,8 @@ final class DexOptHelper { MetricsLogger.histogram(mPm.mContext, "opt_dialog_time_s", elapsedTimeSeconds); } - public ArraySet getOptimizablePackages(@NonNull Computer snapshot) { - ArraySet pkgs = new ArraySet<>(); + public List getOptimizablePackages(@NonNull Computer snapshot) { + ArrayList pkgs = new ArrayList<>(); mPm.forEachPackageState(snapshot, packageState -> { final AndroidPackage pkg = packageState.getPkg(); if (pkg != null && mPm.mPackageDexOptimizer.canOptimizePackage(pkg)) { diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java index 6f503c7dd9418..444db9128662f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java @@ -19,7 +19,9 @@ package com.android.server.pm; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; @@ -36,23 +38,21 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.HandlerThread; import android.os.PowerManager; -import android.util.ArraySet; import com.android.server.LocalServices; import com.android.server.PinnerService; import com.android.server.pm.dex.DexManager; -import com.android.server.pm.dex.DexoptOptions; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.stream.Collectors; @@ -66,9 +66,8 @@ public final class BackgroundDexOptServiceUnitTest { private static final long TEST_WAIT_TIMEOUT_MS = 10_000; - private static final ArraySet DEFAULT_PACKAGE_LIST = new ArraySet<>( - Arrays.asList("aaa", "bbb")); - private static final ArraySet EMPTY_PACKAGE_LIST = new ArraySet<>(); + private static final List DEFAULT_PACKAGE_LIST = List.of("aaa", "bbb"); + private static final List EMPTY_PACKAGE_LIST = List.of(); @Mock private Context mContext; @@ -116,9 +115,11 @@ public final class BackgroundDexOptServiceUnitTest { when(mInjector.getDataDirStorageLowBytes()).thenReturn(STORAGE_LOW_BYTES); when(mInjector.getDexOptThermalCutoff()).thenReturn(PowerManager.THERMAL_STATUS_CRITICAL); when(mInjector.getCurrentThermalStatus()).thenReturn(PowerManager.THERMAL_STATUS_NONE); + when(mInjector.supportSecondaryDex()).thenReturn(true); when(mDexOptHelper.getOptimizablePackages(any())).thenReturn(DEFAULT_PACKAGE_LIST); when(mDexOptHelper.performDexOptWithStatus(any())).thenReturn( PackageDexOptimizer.DEX_OPT_PERFORMED); + when(mDexOptHelper.performDexOpt(any())).thenReturn(true); mService = new BackgroundDexOptService(mInjector); } @@ -418,26 +419,16 @@ public final class BackgroundDexOptServiceUnitTest { verifyPerformDexOpt(DEFAULT_PACKAGE_LIST, totalJobRuns); } - private void verifyPerformDexOpt(ArraySet pkgs, int expectedRuns) { - ArgumentCaptor dexOptOptions = ArgumentCaptor.forClass(DexoptOptions.class); - verify(mDexOptHelper, atLeastOnce()).performDexOptWithStatus(dexOptOptions.capture()); - HashMap primaryPkgs = new HashMap<>(); // K: pkg, V: dexopt runs left - for (String pkg : pkgs) { - primaryPkgs.put(pkg, expectedRuns); - } - - for (DexoptOptions opt : dexOptOptions.getAllValues()) { - assertThat(pkgs).contains(opt.getPackageName()); - assertThat(opt.isDexoptOnlySecondaryDex()).isFalse(); - Integer count = primaryPkgs.get(opt.getPackageName()); - assertThat(count).isNotNull(); - if (count == 1) { - primaryPkgs.remove(opt.getPackageName()); - } else { - primaryPkgs.put(opt.getPackageName(), count - 1); + private void verifyPerformDexOpt(List pkgs, int expectedRuns) { + InOrder inOrder = inOrder(mDexOptHelper); + for (int i = 0; i < expectedRuns; i++) { + for (String pkg : pkgs) { + inOrder.verify(mDexOptHelper, times(1)).performDexOptWithStatus(argThat((option) -> + option.getPackageName().equals(pkg) && !option.isDexoptOnlySecondaryDex())); + inOrder.verify(mDexOptHelper, times(1)).performDexOpt(argThat((option) -> + option.getPackageName().equals(pkg) && option.isDexoptOnlySecondaryDex())); } } - assertThat(primaryPkgs).isEmpty(); } private static class StartAndWaitThread extends Thread {