Dexopt launcher on boot after mainline update.

Bug: 269140736
Test: atest ArtServiceTests
Ignore-AOSP-First: ART Services.
Change-Id: I46b8f0a9bc1f94f7926aa616440d1efebcf7d7a7
(cherry picked from commit 296bff69ead68553800eff41fa30d49654b32d78)
Merged-In: I46b8f0a9bc1f94f7926aa616440d1efebcf7d7a7
diff --git a/libartservice/service/Android.bp b/libartservice/service/Android.bp
index a34e9e8..ad2033a 100644
--- a/libartservice/service/Android.bp
+++ b/libartservice/service/Android.bp
@@ -73,6 +73,7 @@
     libs: [
         "androidx.annotation_annotation",
         "auto_value_annotations",
+        "sdk_module-lib_current_framework-permission-s",
         // TODO(b/256866172): Transitive dependency, for r8 only.
         "framework-statsd.stubs.module_lib",
         // TODO(b/256866172): Transitive dependency, for r8 only. This module
diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
index bba7b9e..2f4fe7c 100644
--- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
@@ -28,7 +28,6 @@
 import static com.android.server.art.model.Config.Callback;
 import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus;
 
-import android.R;
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -997,8 +996,9 @@
 
         switch (reason) {
             case ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE:
-                packages = packages.filter(
-                        pkgState -> mInjector.isSystemUiPackage(pkgState.getPackageName()));
+                packages = packages.filter(pkgState
+                        -> mInjector.isSystemUiPackage(pkgState.getPackageName())
+                                || mInjector.isLauncherPackage(pkgState.getPackageName()));
                 break;
             case ReasonMapping.REASON_INACTIVE:
                 packages = filterAndSortByLastActiveTime(
@@ -1249,9 +1249,12 @@
                     LocalManagerRegistry.getManager(DexUseManagerLocal.class));
         }
 
-        @NonNull
         public boolean isSystemUiPackage(@NonNull String packageName) {
-            return packageName.equals(mContext.getString(R.string.config_systemUi));
+            return Utils.isSystemUiPackage(mContext, packageName);
+        }
+
+        public boolean isLauncherPackage(@NonNull String packageName) {
+            return Utils.isLauncherPackage(mContext, packageName);
         }
 
         public long getCurrentTimeMillis() {
diff --git a/libartservice/service/java/com/android/server/art/Dexopter.java b/libartservice/service/java/com/android/server/art/Dexopter.java
index acc797b..24ba783 100644
--- a/libartservice/service/java/com/android/server/art/Dexopter.java
+++ b/libartservice/service/java/com/android/server/art/Dexopter.java
@@ -26,6 +26,7 @@
 import android.R;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.role.RoleManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.os.CancellationSignal;
@@ -279,6 +280,10 @@
             }
         }
 
+        if (mInjector.isLauncherPackage(mPkgState.getPackageName())) {
+            return "speed-profile";
+        }
+
         // We force vmSafeMode on debuggable apps as well:
         //  - the runtime ignores their compiled code
         //  - they generally have lots of methods that could make the compiler used run out of
@@ -656,7 +661,11 @@
         }
 
         public boolean isSystemUiPackage(@NonNull String packageName) {
-            return packageName.equals(mContext.getString(R.string.config_systemUi));
+            return Utils.isSystemUiPackage(mContext, packageName);
+        }
+
+        public boolean isLauncherPackage(@NonNull String packageName) {
+            return Utils.isLauncherPackage(mContext, packageName);
         }
 
         @NonNull
diff --git a/libartservice/service/java/com/android/server/art/Utils.java b/libartservice/service/java/com/android/server/art/Utils.java
index a8ed9f7..fd377c1 100644
--- a/libartservice/service/java/com/android/server/art/Utils.java
+++ b/libartservice/service/java/com/android/server/art/Utils.java
@@ -16,10 +16,13 @@
 
 package com.android.server.art;
 
+import android.R;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.app.role.RoleManager;
 import android.apphibernation.AppHibernationManager;
+import android.content.Context;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserManager;
@@ -302,6 +305,15 @@
         }
     }
 
+    public static boolean isSystemUiPackage(@NonNull Context context, @NonNull String packageName) {
+        return packageName.equals(context.getString(R.string.config_systemUi));
+    }
+
+    public static boolean isLauncherPackage(@NonNull Context context, @NonNull String packageName) {
+        RoleManager roleManager = context.getSystemService(RoleManager.class);
+        return roleManager.getRoleHolders(RoleManager.ROLE_HOME).contains(packageName);
+    }
+
     @AutoValue
     public abstract static class Abi {
         static @NonNull Abi create(
diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
index 239006f..eff8505 100644
--- a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
+++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
@@ -95,8 +95,8 @@
 @SmallTest
 @RunWith(Parameterized.class)
 public class ArtManagerLocalTest {
-    private static final String PKG_NAME = "com.example.foo";
-    private static final String PKG_NAME_SYS_UI = "com.android.systemui";
+    private static final String PKG_NAME_1 = "com.example.foo";
+    private static final String PKG_NAME_2 = "com.android.bar";
     private static final String PKG_NAME_HIBERNATING = "com.example.hibernating";
     private static final int INACTIVE_DAYS = 1;
     private static final long CURRENT_TIME_MS = 10000000000l;
@@ -118,8 +118,8 @@
     @Mock private UserManager mUserManager;
     @Mock private DexUseManagerLocal mDexUseManager;
     @Mock private StorageManager mStorageManager;
-    private PackageState mPkgState;
-    private AndroidPackage mPkg;
+    private PackageState mPkgState1;
+    private AndroidPackage mPkg1;
     private Config mConfig;
 
     // True if the primary dex'es are in a readonly partition.
@@ -146,7 +146,7 @@
         lenient().when(mInjector.getAppHibernationManager()).thenReturn(mAppHibernationManager);
         lenient().when(mInjector.getUserManager()).thenReturn(mUserManager);
         lenient().when(mInjector.isSystemUiPackage(any())).thenReturn(false);
-        lenient().when(mInjector.isSystemUiPackage(PKG_NAME_SYS_UI)).thenReturn(true);
+        lenient().when(mInjector.isLauncherPackage(any())).thenReturn(false);
         lenient().when(mInjector.getDexUseManager()).thenReturn(mDexUseManager);
         lenient().when(mInjector.getCurrentTimeMillis()).thenReturn(CURRENT_TIME_MS);
         lenient().when(mInjector.getStorageManager()).thenReturn(mStorageManager);
@@ -193,11 +193,14 @@
         // All packages are by default recently used.
         lenient().when(mDexUseManager.getPackageLastUsedAtMs(any())).thenReturn(RECENT_TIME_MS);
         List<DetailedSecondaryDexInfo> secondaryDexInfo = createSecondaryDexInfo();
-        lenient().doReturn(secondaryDexInfo).when(mDexUseManager).getSecondaryDexInfo(eq(PKG_NAME));
         lenient()
                 .doReturn(secondaryDexInfo)
                 .when(mDexUseManager)
-                .getFilteredDetailedSecondaryDexInfo(eq(PKG_NAME));
+                .getSecondaryDexInfo(eq(PKG_NAME_1));
+        lenient()
+                .doReturn(secondaryDexInfo)
+                .when(mDexUseManager)
+                .getFilteredDetailedSecondaryDexInfo(eq(PKG_NAME_1));
 
         simulateStorageNotLow();
 
@@ -211,8 +214,8 @@
         var packageStateMap = pkgStates.stream().collect(
                 Collectors.toMap(PackageState::getPackageName, it -> it));
         lenient().when(mSnapshot.getPackageStates()).thenReturn(packageStateMap);
-        mPkgState = mSnapshot.getPackageState(PKG_NAME);
-        mPkg = mPkgState.getAndroidPackage();
+        mPkgState1 = mSnapshot.getPackageState(PKG_NAME_1);
+        mPkg1 = mPkgState1.getAndroidPackage();
 
         mArtManagerLocal = new ArtManagerLocal(mInjector);
     }
@@ -221,7 +224,7 @@
     public void testdeleteDexoptArtifacts() throws Exception {
         when(mArtd.deleteArtifacts(any())).thenReturn(1l);
 
-        DeleteResult result = mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME);
+        DeleteResult result = mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME_1);
         assertThat(result.getFreedBytes()).isEqualTo(5);
 
         verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
@@ -247,7 +250,7 @@
 
         when(mArtd.deleteArtifacts(any())).thenReturn(1l);
 
-        DeleteResult result = mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME);
+        DeleteResult result = mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME_1);
         assertThat(result.getFreedBytes()).isEqualTo(5);
 
         verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
@@ -268,14 +271,14 @@
     public void testdeleteDexoptArtifactsPackageNotFound() throws Exception {
         when(mSnapshot.getPackageState(anyString())).thenReturn(null);
 
-        mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME);
+        mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME_1);
     }
 
     @Test(expected = IllegalArgumentException.class)
     public void testdeleteDexoptArtifactsNoPackage() throws Exception {
-        when(mPkgState.getAndroidPackage()).thenReturn(null);
+        when(mPkgState1.getAndroidPackage()).thenReturn(null);
 
-        mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME);
+        mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME_1);
     }
 
     @Test
@@ -300,7 +303,7 @@
                 .when(mArtd)
                 .getDexoptStatus("/data/user/0/foo/1.apk", "arm64", "CLC");
 
-        DexoptStatus result = mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME);
+        DexoptStatus result = mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME_1);
 
         assertThat(result.getDexContainerFileDexoptStatuses())
                 .comparingElementsUsing(TestingUtils.<DexContainerFileDexoptStatus>deepEquality())
@@ -326,14 +329,14 @@
     public void testGetDexoptStatusPackageNotFound() throws Exception {
         when(mSnapshot.getPackageState(anyString())).thenReturn(null);
 
-        mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME);
+        mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME_1);
     }
 
     @Test(expected = IllegalArgumentException.class)
     public void testGetDexoptStatusNoPackage() throws Exception {
-        when(mPkgState.getAndroidPackage()).thenReturn(null);
+        when(mPkgState1.getAndroidPackage()).thenReturn(null);
 
-        mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME);
+        mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME_1);
     }
 
     @Test
@@ -341,7 +344,7 @@
         when(mArtd.getDexoptStatus(any(), any(), any()))
                 .thenThrow(new ServiceSpecificException(1 /* errorCode */, "some error message"));
 
-        DexoptStatus result = mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME);
+        DexoptStatus result = mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME_1);
 
         List<DexContainerFileDexoptStatus> statuses = result.getDexContainerFileDexoptStatuses();
         assertThat(statuses.size()).isEqualTo(5);
@@ -355,14 +358,14 @@
 
     @Test
     public void testClearAppProfiles() throws Exception {
-        mArtManagerLocal.clearAppProfiles(mSnapshot, PKG_NAME);
+        mArtManagerLocal.clearAppProfiles(mSnapshot, PKG_NAME_1);
 
         verify(mArtd).deleteProfile(
-                deepEq(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "primary")));
+                deepEq(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary")));
         verify(mArtd).deleteProfile(deepEq(
-                AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME, "primary")));
+                AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME_1, "primary")));
         verify(mArtd).deleteProfile(deepEq(
-                AidlUtils.buildProfilePathForPrimaryCur(1 /* userId */, PKG_NAME, "primary")));
+                AidlUtils.buildProfilePathForPrimaryCur(1 /* userId */, PKG_NAME_1, "primary")));
 
         verify(mArtd).deleteProfile(
                 deepEq(AidlUtils.buildProfilePathForSecondaryRef("/data/user/0/foo/1.apk")));
@@ -374,14 +377,14 @@
     public void testClearAppProfilesPackageNotFound() throws Exception {
         when(mSnapshot.getPackageState(anyString())).thenReturn(null);
 
-        mArtManagerLocal.clearAppProfiles(mSnapshot, PKG_NAME);
+        mArtManagerLocal.clearAppProfiles(mSnapshot, PKG_NAME_1);
     }
 
     @Test(expected = IllegalArgumentException.class)
     public void testClearAppProfilesNoPackage() throws Exception {
-        when(mPkgState.getAndroidPackage()).thenReturn(null);
+        when(mPkgState1.getAndroidPackage()).thenReturn(null);
 
-        mArtManagerLocal.clearAppProfiles(mSnapshot, PKG_NAME);
+        mArtManagerLocal.clearAppProfiles(mSnapshot, PKG_NAME_1);
     }
 
     @Test
@@ -390,11 +393,12 @@
         var result = mock(DexoptResult.class);
         var cancellationSignal = new CancellationSignal();
 
-        when(mDexoptHelper.dexopt(any(), deepEq(List.of(PKG_NAME)), same(params),
+        when(mDexoptHelper.dexopt(any(), deepEq(List.of(PKG_NAME_1)), same(params),
                      same(cancellationSignal), any()))
                 .thenReturn(result);
 
-        assertThat(mArtManagerLocal.dexoptPackage(mSnapshot, PKG_NAME, params, cancellationSignal))
+        assertThat(
+                mArtManagerLocal.dexoptPackage(mSnapshot, PKG_NAME_1, params, cancellationSignal))
                 .isSameInstanceAs(result);
     }
 
@@ -404,18 +408,18 @@
         var cancellationSignal = new CancellationSignal();
 
         when(mDexoptHelper.dexopt(
-                     any(), deepEq(List.of(PKG_NAME)), any(), same(cancellationSignal), any()))
+                     any(), deepEq(List.of(PKG_NAME_1)), any(), same(cancellationSignal), any()))
                 .thenReturn(result);
 
-        assertThat(mArtManagerLocal.resetDexoptStatus(mSnapshot, PKG_NAME, cancellationSignal))
+        assertThat(mArtManagerLocal.resetDexoptStatus(mSnapshot, PKG_NAME_1, cancellationSignal))
                 .isSameInstanceAs(result);
 
         verify(mArtd).deleteProfile(
-                deepEq(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "primary")));
+                deepEq(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary")));
         verify(mArtd).deleteProfile(deepEq(
-                AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME, "primary")));
+                AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME_1, "primary")));
         verify(mArtd).deleteProfile(deepEq(
-                AidlUtils.buildProfilePathForPrimaryCur(1 /* userId */, PKG_NAME, "primary")));
+                AidlUtils.buildProfilePathForPrimaryCur(1 /* userId */, PKG_NAME_1, "primary")));
 
         verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
                 "/data/app/foo/base.apk", "arm64", mIsInReadonlyPartition)));
@@ -439,14 +443,14 @@
     public void testDexoptPackages() throws Exception {
         var dexoptResult = mock(DexoptResult.class);
         var cancellationSignal = new CancellationSignal();
-        when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_SYS_UI)).thenReturn(CURRENT_TIME_MS);
+        when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_2)).thenReturn(CURRENT_TIME_MS);
         simulateStorageLow();
 
         // It should use the default package list and params. The list is sorted by last active
         // time in descending order.
         doReturn(dexoptResult)
                 .when(mDexoptHelper)
-                .dexopt(any(), deepEq(List.of(PKG_NAME_SYS_UI, PKG_NAME)),
+                .dexopt(any(), deepEq(List.of(PKG_NAME_2, PKG_NAME_1)),
                         argThat(params -> params.getReason().equals("bg-dexopt")),
                         same(cancellationSignal), any(), any(), any());
 
@@ -463,25 +467,25 @@
     @Test
     public void testDexoptPackagesRecentlyInstalled() throws Exception {
         // The package is recently installed but hasn't been used.
-        PackageUserState userState = mPkgState.getStateForUser(UserHandle.of(1));
+        PackageUserState userState = mPkgState1.getStateForUser(UserHandle.of(1));
         when(userState.getFirstInstallTimeMillis()).thenReturn(RECENT_TIME_MS);
-        when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME)).thenReturn(0l);
+        when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(0l);
         simulateStorageLow();
 
         var result = mock(DexoptResult.class);
         var cancellationSignal = new CancellationSignal();
 
-        // PKG_NAME should be dexopted.
+        // PKG_NAME_1 should be dexopted.
         doReturn(result)
                 .when(mDexoptHelper)
-                .dexopt(any(), inAnyOrder(PKG_NAME, PKG_NAME_SYS_UI),
+                .dexopt(any(), inAnyOrder(PKG_NAME_1, PKG_NAME_2),
                         argThat(params -> params.getReason().equals("bg-dexopt")), any(), any(),
                         any(), any());
 
         mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
                 null /* processCallbackExecutor */, null /* processCallback */);
 
-        // PKG_NAME should not be downgraded.
+        // PKG_NAME_1 should not be downgraded.
         verify(mDexoptHelper, never())
                 .dexopt(any(), any(), argThat(params -> params.getReason().equals("inactive")),
                         any(), any(), any(), any());
@@ -489,26 +493,26 @@
 
     @Test
     public void testDexoptPackagesInactive() throws Exception {
-        // PKG_NAME is neither recently installed nor recently used.
-        PackageUserState userState = mPkgState.getStateForUser(UserHandle.of(1));
+        // PKG_NAME_1 is neither recently installed nor recently used.
+        PackageUserState userState = mPkgState1.getStateForUser(UserHandle.of(1));
         when(userState.getFirstInstallTimeMillis()).thenReturn(NOT_RECENT_TIME_MS);
-        when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME)).thenReturn(NOT_RECENT_TIME_MS);
+        when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(NOT_RECENT_TIME_MS);
         simulateStorageLow();
 
         var result = mock(DexoptResult.class);
         var cancellationSignal = new CancellationSignal();
 
-        // PKG_NAME should not be dexopted.
+        // PKG_NAME_1 should not be dexopted.
         doReturn(result)
                 .when(mDexoptHelper)
-                .dexopt(any(), deepEq(List.of(PKG_NAME_SYS_UI)),
+                .dexopt(any(), deepEq(List.of(PKG_NAME_2)),
                         argThat(params -> params.getReason().equals("bg-dexopt")), any(), any(),
                         any(), any());
 
-        // PKG_NAME should be downgraded.
+        // PKG_NAME_1 should be downgraded.
         doReturn(result)
                 .when(mDexoptHelper)
-                .dexopt(any(), deepEq(List.of(PKG_NAME)),
+                .dexopt(any(), deepEq(List.of(PKG_NAME_1)),
                         argThat(params -> params.getReason().equals("inactive")), any(), any(),
                         any(), any());
 
@@ -518,25 +522,25 @@
 
     @Test
     public void testDexoptPackagesInactiveStorageNotLow() throws Exception {
-        // PKG_NAME is neither recently installed nor recently used.
-        PackageUserState userState = mPkgState.getStateForUser(UserHandle.of(1));
+        // PKG_NAME_1 is neither recently installed nor recently used.
+        PackageUserState userState = mPkgState1.getStateForUser(UserHandle.of(1));
         when(userState.getFirstInstallTimeMillis()).thenReturn(NOT_RECENT_TIME_MS);
-        when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME)).thenReturn(NOT_RECENT_TIME_MS);
+        when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(NOT_RECENT_TIME_MS);
 
         var result = mock(DexoptResult.class);
         var cancellationSignal = new CancellationSignal();
 
-        // PKG_NAME should not be dexopted.
+        // PKG_NAME_1 should not be dexopted.
         doReturn(result)
                 .when(mDexoptHelper)
-                .dexopt(any(), deepEq(List.of(PKG_NAME_SYS_UI)),
+                .dexopt(any(), deepEq(List.of(PKG_NAME_2)),
                         argThat(params -> params.getReason().equals("bg-dexopt")), any(), any(),
                         any(), any());
 
         mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
                 null /* processCallbackExecutor */, null /* processCallback */);
 
-        // PKG_NAME should not be downgraded because the storage is not low.
+        // PKG_NAME_1 should not be downgraded because the storage is not low.
         verify(mDexoptHelper, never())
                 .dexopt(any(), any(), argThat(params -> params.getReason().equals("inactive")),
                         any(), any(), any(), any());
@@ -546,17 +550,37 @@
     public void testDexoptPackagesBootAfterMainlineUpdate() throws Exception {
         var result = mock(DexoptResult.class);
         var cancellationSignal = new CancellationSignal();
-        simulateStorageLow();
 
-        // It should only dexopt system UI.
+        lenient().when(mInjector.isSystemUiPackage(PKG_NAME_1)).thenReturn(true);
+        lenient().when(mInjector.isLauncherPackage(PKG_NAME_2)).thenReturn(true);
+
+        // It should dexopt the system UI and the launcher.
         when(mDexoptHelper.dexopt(
-                     any(), deepEq(List.of(PKG_NAME_SYS_UI)), any(), any(), any(), any(), any()))
+                     any(), inAnyOrder(PKG_NAME_1, PKG_NAME_2), any(), any(), any(), any(), any()))
                 .thenReturn(result);
 
-        assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "boot-after-mainline-update",
-                           cancellationSignal, null /* processCallbackExecutor */,
-                           null /* processCallback */))
-                .isSameInstanceAs(result);
+        mArtManagerLocal.dexoptPackages(mSnapshot, "boot-after-mainline-update", cancellationSignal,
+                null /* processCallbackExecutor */, null /* processCallback */);
+    }
+
+    @Test
+    public void testDexoptPackagesBootAfterMainlineUpdatePackagesNotFound() throws Exception {
+        var result = mock(DexoptResult.class);
+        var cancellationSignal = new CancellationSignal();
+        // PKG_NAME_1 is neither recently installed nor recently used.
+        PackageUserState userState = mPkgState1.getStateForUser(UserHandle.of(1));
+        lenient().when(userState.getFirstInstallTimeMillis()).thenReturn(NOT_RECENT_TIME_MS);
+        lenient()
+                .when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1))
+                .thenReturn(NOT_RECENT_TIME_MS);
+        simulateStorageLow();
+
+        // It should dexopt the system UI and the launcher, but they are not found.
+        when(mDexoptHelper.dexopt(any(), deepEq(List.of()), any(), any(), any(), any(), any()))
+                .thenReturn(result);
+
+        mArtManagerLocal.dexoptPackages(mSnapshot, "boot-after-mainline-update", cancellationSignal,
+                null /* processCallbackExecutor */, null /* processCallback */);
 
         // It should never downgrade apps, even if the storage is low.
         verify(mDexoptHelper, never())
@@ -566,10 +590,10 @@
 
     @Test
     public void testDexoptPackagesOverride() throws Exception {
-        // PKG_NAME is neither recently installed nor recently used.
-        PackageUserState userState = mPkgState.getStateForUser(UserHandle.of(1));
+        // PKG_NAME_1 is neither recently installed nor recently used.
+        PackageUserState userState = mPkgState1.getStateForUser(UserHandle.of(1));
         when(userState.getFirstInstallTimeMillis()).thenReturn(NOT_RECENT_TIME_MS);
-        when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME)).thenReturn(NOT_RECENT_TIME_MS);
+        when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(NOT_RECENT_TIME_MS);
         simulateStorageLow();
 
         var params = new DexoptParams.Builder("bg-dexopt").build();
@@ -579,21 +603,22 @@
         mArtManagerLocal.setBatchDexoptStartCallback(ForkJoinPool.commonPool(),
                 (snapshot, reason, defaultPackages, builder, passedSignal) -> {
                     assertThat(reason).isEqualTo("bg-dexopt");
-                    assertThat(defaultPackages).containsExactly(PKG_NAME_SYS_UI);
+                    assertThat(defaultPackages).containsExactly(PKG_NAME_2);
                     assertThat(passedSignal).isSameInstanceAs(cancellationSignal);
-                    builder.setPackages(List.of(PKG_NAME)).setDexoptParams(params);
+                    builder.setPackages(List.of(PKG_NAME_1)).setDexoptParams(params);
                 });
 
         // It should use the overridden package list and params.
         doReturn(result)
                 .when(mDexoptHelper)
-                .dexopt(any(), deepEq(List.of(PKG_NAME)), same(params), any(), any(), any(), any());
+                .dexopt(any(), deepEq(List.of(PKG_NAME_1)), same(params), any(), any(), any(),
+                        any());
 
         mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
                 null /* processCallbackExecutor */, null /* processCallback */);
 
-        // It should not downgrade PKG_NAME because it's in the overridden package list. It should
-        // not downgrade PKG_NAME_SYS_UI either because it's not an inactive package.
+        // It should not downgrade PKG_NAME_1 because it's in the overridden package list. It should
+        // not downgrade PKG_NAME_2 either because it's not an inactive package.
         verify(mDexoptHelper, never())
                 .dexopt(any(), any(), argThat(params2 -> params2.getReason().equals("inactive")),
                         any(), any(), any(), any());
@@ -607,12 +632,12 @@
 
         mArtManagerLocal.setBatchDexoptStartCallback(ForkJoinPool.commonPool(),
                 (snapshot, reason, defaultPackages, builder, passedSignal) -> {
-                    builder.setPackages(List.of(PKG_NAME)).setDexoptParams(params);
+                    builder.setPackages(List.of(PKG_NAME_1)).setDexoptParams(params);
                 });
         mArtManagerLocal.clearBatchDexoptStartCallback();
 
         // It should use the default package list and params.
-        when(mDexoptHelper.dexopt(any(), inAnyOrder(PKG_NAME, PKG_NAME_SYS_UI), not(same(params)),
+        when(mDexoptHelper.dexopt(any(), inAnyOrder(PKG_NAME_1, PKG_NAME_2), not(same(params)),
                      same(cancellationSignal), any(), any(), any()))
                 .thenReturn(result);
 
@@ -645,13 +670,13 @@
         tempFile.deleteOnExit();
 
         when(mArtd.mergeProfiles(
-                     deepEq(List.of(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "primary"),
+                     deepEq(List.of(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary"),
                              AidlUtils.buildProfilePathForPrimaryCur(
-                                     0 /* userId */, PKG_NAME, "primary"),
+                                     0 /* userId */, PKG_NAME_1, "primary"),
                              AidlUtils.buildProfilePathForPrimaryCur(
-                                     1 /* userId */, PKG_NAME, "primary"))),
+                                     1 /* userId */, PKG_NAME_1, "primary"))),
                      isNull(),
-                     deepEq(AidlUtils.buildOutputProfileForPrimary(PKG_NAME, "primary",
+                     deepEq(AidlUtils.buildOutputProfileForPrimary(PKG_NAME_1, "primary",
                              Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */)),
                      deepEq(List.of("/data/app/foo/base.apk")), deepEq(options)))
                 .thenAnswer(invocation -> {
@@ -666,7 +691,7 @@
                 });
 
         ParcelFileDescriptor fd =
-                mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME, null /* splitName */);
+                mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, null /* splitName */);
 
         verify(mArtd).deleteProfile(
                 argThat(profile -> profile.getTmpProfilePath().tmpPath.equals(tempFile.getPath())));
@@ -680,18 +705,18 @@
     @Test
     public void testSnapshotAppProfileSplit() throws Exception {
         when(mArtd.mergeProfiles(deepEq(List.of(AidlUtils.buildProfilePathForPrimaryRef(
-                                                        PKG_NAME, "split_0.split"),
+                                                        PKG_NAME_1, "split_0.split"),
                                          AidlUtils.buildProfilePathForPrimaryCur(
-                                                 0 /* userId */, PKG_NAME, "split_0.split"),
+                                                 0 /* userId */, PKG_NAME_1, "split_0.split"),
                                          AidlUtils.buildProfilePathForPrimaryCur(
-                                                 1 /* userId */, PKG_NAME, "split_0.split"))),
+                                                 1 /* userId */, PKG_NAME_1, "split_0.split"))),
                      isNull(),
-                     deepEq(AidlUtils.buildOutputProfileForPrimary(PKG_NAME, "split_0.split",
+                     deepEq(AidlUtils.buildOutputProfileForPrimary(PKG_NAME_1, "split_0.split",
                              Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */)),
                      deepEq(List.of("/data/app/foo/split_0.apk")), any()))
                 .thenReturn(false);
 
-        mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME, "split_0");
+        mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, "split_0");
     }
 
     @Test
@@ -699,7 +724,7 @@
         when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(false);
 
         ParcelFileDescriptor fd =
-                mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME, null /* splitName */);
+                mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, null /* splitName */);
 
         verify(mArtd, never()).deleteProfile(any());
 
@@ -712,19 +737,19 @@
     public void testSnapshotAppProfilePackageNotFound() throws Exception {
         when(mSnapshot.getPackageState(anyString())).thenReturn(null);
 
-        mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME, null /* splitName */);
+        mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, null /* splitName */);
     }
 
     @Test(expected = IllegalArgumentException.class)
     public void testSnapshotAppProfileNoPackage() throws Exception {
-        when(mPkgState.getAndroidPackage()).thenReturn(null);
+        when(mPkgState1.getAndroidPackage()).thenReturn(null);
 
-        mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME, null /* splitName */);
+        mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, null /* splitName */);
     }
 
     @Test(expected = IllegalArgumentException.class)
     public void testSnapshotAppProfileSplitNotFound() throws Exception {
-        mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME, "non-existent-split");
+        mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME_1, "non-existent-split");
     }
 
     @Test
@@ -736,7 +761,7 @@
                 .thenReturn(false); // A non-empty merge is tested in `testSnapshotAppProfile`.
 
         ParcelFileDescriptor fd = mArtManagerLocal.dumpAppProfile(
-                mSnapshot, PKG_NAME, null /* splitName */, false /* dumpClassesAndMethods */);
+                mSnapshot, PKG_NAME_1, null /* splitName */, false /* dumpClassesAndMethods */);
     }
 
     @Test
@@ -748,7 +773,7 @@
                 .thenReturn(false); // A non-empty merge is tested in `testSnapshotAppProfile`.
 
         ParcelFileDescriptor fd = mArtManagerLocal.dumpAppProfile(
-                mSnapshot, PKG_NAME, null /* splitName */, true /* dumpClassesAndMethods */);
+                mSnapshot, PKG_NAME_1, null /* splitName */, true /* dumpClassesAndMethods */);
     }
 
     @Test
@@ -769,21 +794,21 @@
                                      0 /* userId */, "android", "primary"),
                              AidlUtils.buildProfilePathForPrimaryCur(
                                      1 /* userId */, "android", "primary"),
-                             AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "primary"),
+                             AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary"),
                              AidlUtils.buildProfilePathForPrimaryCur(
-                                     0 /* userId */, PKG_NAME, "primary"),
+                                     0 /* userId */, PKG_NAME_1, "primary"),
                              AidlUtils.buildProfilePathForPrimaryCur(
-                                     1 /* userId */, PKG_NAME, "primary"),
-                             AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "split_0.split"),
+                                     1 /* userId */, PKG_NAME_1, "primary"),
+                             AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "split_0.split"),
                              AidlUtils.buildProfilePathForPrimaryCur(
-                                     0 /* userId */, PKG_NAME, "split_0.split"),
+                                     0 /* userId */, PKG_NAME_1, "split_0.split"),
                              AidlUtils.buildProfilePathForPrimaryCur(
-                                     1 /* userId */, PKG_NAME, "split_0.split"),
-                             AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_SYS_UI, "primary"),
+                                     1 /* userId */, PKG_NAME_1, "split_0.split"),
+                             AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_2, "primary"),
                              AidlUtils.buildProfilePathForPrimaryCur(
-                                     0 /* userId */, PKG_NAME_SYS_UI, "primary"),
+                                     0 /* userId */, PKG_NAME_2, "primary"),
                              AidlUtils.buildProfilePathForPrimaryCur(
-                                     1 /* userId */, PKG_NAME_SYS_UI, "primary"),
+                                     1 /* userId */, PKG_NAME_2, "primary"),
                              AidlUtils.buildProfilePathForPrimaryRef(
                                      PKG_NAME_HIBERNATING, "primary"),
                              AidlUtils.buildProfilePathForPrimaryCur(
@@ -823,20 +848,20 @@
                 .when(mArtd)
                 .getDexoptStatus(eq("/data/app/foo/base.apk"), eq("arm"), any());
 
-        when(mSnapshot.getPackageStates()).thenReturn(Map.of(PKG_NAME, mPkgState));
+        when(mSnapshot.getPackageStates()).thenReturn(Map.of(PKG_NAME_1, mPkgState1));
         mArtManagerLocal.cleanup(mSnapshot);
 
         verify(mArtd).cleanup(
-                inAnyOrderDeepEquals(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "primary"),
+                inAnyOrderDeepEquals(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "primary"),
                         AidlUtils.buildProfilePathForPrimaryCur(
-                                0 /* userId */, PKG_NAME, "primary"),
+                                0 /* userId */, PKG_NAME_1, "primary"),
                         AidlUtils.buildProfilePathForPrimaryCur(
-                                1 /* userId */, PKG_NAME, "primary"),
-                        AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "split_0.split"),
+                                1 /* userId */, PKG_NAME_1, "primary"),
+                        AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_1, "split_0.split"),
                         AidlUtils.buildProfilePathForPrimaryCur(
-                                0 /* userId */, PKG_NAME, "split_0.split"),
+                                0 /* userId */, PKG_NAME_1, "split_0.split"),
                         AidlUtils.buildProfilePathForPrimaryCur(
-                                1 /* userId */, PKG_NAME, "split_0.split"),
+                                1 /* userId */, PKG_NAME_1, "split_0.split"),
                         AidlUtils.buildProfilePathForSecondaryRef("/data/user/0/foo/1.apk"),
                         AidlUtils.buildProfilePathForSecondaryCur("/data/user/0/foo/1.apk")),
                 inAnyOrderDeepEquals(AidlUtils.buildArtifactsPath("/data/app/foo/base.apk", "arm64",
@@ -908,11 +933,11 @@
     }
 
     private List<PackageState> createPackageStates() {
-        PackageState pkgState =
-                createPackageState(PKG_NAME, true /* isDexoptable */, true /* multiSplit */);
+        PackageState pkgState1 =
+                createPackageState(PKG_NAME_1, true /* isDexoptable */, true /* multiSplit */);
 
-        PackageState sysUiPkgState = createPackageState(
-                PKG_NAME_SYS_UI, true /* isDexoptable */, false /* multiSplit */);
+        PackageState pkgState2 =
+                createPackageState(PKG_NAME_2, true /* isDexoptable */, false /* multiSplit */);
 
         // This should not be dexopted because it's hibernating. However, it should be included
         // when snapshotting boot image profile.
@@ -926,7 +951,7 @@
         PackageState nonDexoptablePkgState = createPackageState(
                 "com.example.non-dexoptable", false /* isDexoptable */, false /* multiSplit */);
 
-        return List.of(pkgState, sysUiPkgState, pkgHibernatingState, nonDexoptablePkgState);
+        return List.of(pkgState1, pkgState2, pkgHibernatingState, nonDexoptablePkgState);
     }
 
     private GetDexoptStatusResult createGetDexoptStatusResult(
diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java
index 6340efc..f0ef8f5 100644
--- a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java
+++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java
@@ -27,6 +27,7 @@
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.argThat;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.eq;
@@ -49,6 +50,8 @@
 import com.android.server.art.testing.OnSuccessRule;
 import com.android.server.art.testing.TestingUtils;
 
+import dalvik.system.DexFile;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -56,6 +59,7 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
+import org.mockito.ArgumentMatcher;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -136,6 +140,11 @@
         list.add(params);
 
         params = new Params();
+        params.mIsLauncher = true;
+        params.mExpectedCompilerFilter = "speed-profile";
+        list.add(params);
+
+        params = new Params();
         params.mForce = true;
         params.mShouldDowngrade = false;
         params.mExpectedDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER
@@ -169,6 +178,7 @@
         super.setUp();
 
         lenient().when(mInjector.isSystemUiPackage(any())).thenReturn(mParams.mIsSystemUi);
+        lenient().when(mInjector.isLauncherPackage(any())).thenReturn(mParams.mIsLauncher);
 
         lenient()
                 .when(SystemProperties.getBoolean(eq("dalvik.vm.always_debuggable"), anyBoolean()))
@@ -184,6 +194,14 @@
         lenient().when(mPkgState.isSystem()).thenReturn(mParams.mIsSystem);
         lenient().when(mPkgState.isUpdatedSystemApp()).thenReturn(mParams.mIsUpdatedSystemApp);
 
+        if (DexFile.isProfileGuidedCompilerFilter(mParams.mExpectedCompilerFilter)) {
+            // Make all profile-related operations succeed so that "speed-profile" doesn't fall back
+            // to "verify".
+            when(mArtd.isProfileUsable(any(), any())).thenReturn(true);
+            when(mArtd.getProfileVisibility(any())).thenReturn(FileVisibility.OTHER_READABLE);
+            when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(false);
+        }
+
         mDexoptParams =
                 new DexoptParams.Builder("install")
                         .setCompilerFilter(mParams.mRequestedCompilerFilter)
@@ -207,12 +225,12 @@
                 buildFsPermission(Process.SYSTEM_UID /* uid */, SHARED_GID /* gid */,
                         true /* isOtherReadable */),
                 null /* seContext */);
-        DexoptOptions dexoptOptions = new DexoptOptions();
-        dexoptOptions.compilationReason = "install";
-        dexoptOptions.targetSdkVersion = 123;
-        dexoptOptions.debuggable = mParams.mExpectedIsDebuggable;
-        dexoptOptions.generateAppImage = false;
-        dexoptOptions.hiddenApiPolicyEnabled = mParams.mExpectedIsHiddenApiPolicyEnabled;
+
+        // No need to check `generateAppImage`. It is checked in `PrimaryDexopterTest`.
+        ArgumentMatcher<DexoptOptions> dexoptOptionsMatcher = options
+                -> options.compilationReason.equals("install") && options.targetSdkVersion == 123
+                && options.debuggable == mParams.mExpectedIsDebuggable
+                && options.hiddenApiPolicyEnabled == mParams.mExpectedIsHiddenApiPolicyEnabled;
 
         when(mArtd.createCancellationSignal()).thenReturn(mock(IArtdCancellationSignal.class));
         when(mArtd.getDmFileVisibility(any())).thenReturn(FileVisibility.NOT_FOUND);
@@ -228,9 +246,9 @@
                 .dexopt(deepEq(buildOutputArtifacts("/data/app/foo/base.apk", "arm64",
                                 mParams.mExpectedIsInDalvikCache, permissionSettings)),
                         eq("/data/app/foo/base.apk"), eq("arm64"), eq("PCL[]"),
-                        eq(mParams.mExpectedCompilerFilter), isNull() /* profile */,
+                        eq(mParams.mExpectedCompilerFilter), any() /* profile */,
                         isNull() /* inputVdex */, isNull() /* dmFile */,
-                        eq(PriorityClass.INTERACTIVE), deepEq(dexoptOptions), any());
+                        eq(PriorityClass.INTERACTIVE), argThat(dexoptOptionsMatcher), any());
 
         // The second one fails on `dexopt`.
         doReturn(dexoptIsNeeded())
@@ -242,9 +260,9 @@
                 .dexopt(deepEq(buildOutputArtifacts("/data/app/foo/base.apk", "arm",
                                 mParams.mExpectedIsInDalvikCache, permissionSettings)),
                         eq("/data/app/foo/base.apk"), eq("arm"), eq("PCL[]"),
-                        eq(mParams.mExpectedCompilerFilter), isNull() /* profile */,
+                        eq(mParams.mExpectedCompilerFilter), any() /* profile */,
                         isNull() /* inputVdex */, isNull() /* dmFile */,
-                        eq(PriorityClass.INTERACTIVE), deepEq(dexoptOptions), any());
+                        eq(PriorityClass.INTERACTIVE), argThat(dexoptOptionsMatcher), any());
 
         // The third one doesn't need dexopt.
         doReturn(dexoptIsNotNeeded())
@@ -263,9 +281,9 @@
                 .dexopt(deepEq(buildOutputArtifacts("/data/app/foo/split_0.apk", "arm",
                                 mParams.mExpectedIsInDalvikCache, permissionSettings)),
                         eq("/data/app/foo/split_0.apk"), eq("arm"), eq("PCL[base.apk]"),
-                        eq(mParams.mExpectedCompilerFilter), isNull() /* profile */,
+                        eq(mParams.mExpectedCompilerFilter), any() /* profile */,
                         isNull() /* inputVdex */, isNull() /* dmFile */,
-                        eq(PriorityClass.INTERACTIVE), deepEq(dexoptOptions), any());
+                        eq(PriorityClass.INTERACTIVE), argThat(dexoptOptionsMatcher), any());
 
         assertThat(mPrimaryDexopter.dexopt())
                 .comparingElementsUsing(TestingUtils.<DexContainerFileDexoptResult>deepEquality())
@@ -304,6 +322,7 @@
         public boolean mIsVmSafeMode = false;
         public boolean mIsDebuggable = false;
         public boolean mIsSystemUi = false;
+        public boolean mIsLauncher = false;
         public boolean mIsUseEmbeddedDex = false;
 
         // Options.
@@ -324,16 +343,30 @@
         public boolean mExpectedIsHiddenApiPolicyEnabled = true;
 
         public String toString() {
-            return String.format("isSystem=%b,isUpdatedSystemApp=%b,mHiddenApiEnforcementPolicy=%d"
-                            + ",isVmSafeMode=%b,isDebuggable=%b,isSystemUi=%b,isUseEmbeddedDex=%b,"
-                            + "requestedCompilerFilter=%s,force=%b,shouldDowngrade=%b,"
-                            + "mSkipIfStorageLow=%b,alwaysDebuggable=%b => targetCompilerFilter=%s,"
-                            + "expectedDexoptTrigger=%d,expectedIsInDalvikCache=%b,"
-                            + "expectedIsDebuggable=%b,expectedIsHiddenApiPolicyEnabled=%b",
+            return String.format("isSystem=%b,"
+                            + "isUpdatedSystemApp=%b,"
+                            + "mHiddenApiEnforcementPolicy=%d,"
+                            + "isVmSafeMode=%b,"
+                            + "isDebuggable=%b,"
+                            + "isSystemUi=%b,"
+                            + "isLauncher=%b,"
+                            + "isUseEmbeddedDex=%b,"
+                            + "requestedCompilerFilter=%s,"
+                            + "force=%b,"
+                            + "shouldDowngrade=%b,"
+                            + "mSkipIfStorageLow=%b,"
+                            + "alwaysDebuggable=%b"
+                            + " => "
+                            + "targetCompilerFilter=%s,"
+                            + "expectedDexoptTrigger=%d,"
+                            + "expectedIsInDalvikCache=%b,"
+                            + "expectedIsDebuggable=%b,"
+                            + "expectedIsHiddenApiPolicyEnabled=%b",
                     mIsSystem, mIsUpdatedSystemApp, mHiddenApiEnforcementPolicy, mIsVmSafeMode,
-                    mIsDebuggable, mIsSystemUi, mIsUseEmbeddedDex, mRequestedCompilerFilter, mForce,
-                    mShouldDowngrade, mSkipIfStorageLow, mAlwaysDebuggable, mExpectedCompilerFilter,
-                    mExpectedDexoptTrigger, mExpectedIsInDalvikCache, mExpectedIsDebuggable,
+                    mIsDebuggable, mIsSystemUi, mIsLauncher, mIsUseEmbeddedDex,
+                    mRequestedCompilerFilter, mForce, mShouldDowngrade, mSkipIfStorageLow,
+                    mAlwaysDebuggable, mExpectedCompilerFilter, mExpectedDexoptTrigger,
+                    mExpectedIsInDalvikCache, mExpectedIsDebuggable,
                     mExpectedIsHiddenApiPolicyEnabled);
         }
     }
diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTestBase.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTestBase.java
index 27babfa..3b94961 100644
--- a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTestBase.java
+++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTestBase.java
@@ -72,6 +72,7 @@
     public void setUp() throws Exception {
         lenient().when(mInjector.getArtd()).thenReturn(mArtd);
         lenient().when(mInjector.isSystemUiPackage(any())).thenReturn(false);
+        lenient().when(mInjector.isLauncherPackage(any())).thenReturn(false);
         lenient().when(mInjector.getUserManager()).thenReturn(mUserManager);
         lenient().when(mInjector.getDexUseManager()).thenReturn(mDexUseManager);
         lenient().when(mInjector.getStorageManager()).thenReturn(mStorageManager);
diff --git a/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java b/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java
index 0f118a9..5d8661f 100644
--- a/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java
+++ b/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java
@@ -128,6 +128,7 @@
 
         lenient().when(mInjector.getArtd()).thenReturn(mArtd);
         lenient().when(mInjector.isSystemUiPackage(any())).thenReturn(false);
+        lenient().when(mInjector.isLauncherPackage(any())).thenReturn(false);
         lenient().when(mInjector.getDexUseManager()).thenReturn(mDexUseManager);
 
         List<DetailedSecondaryDexInfo> secondaryDexInfo = createSecondaryDexInfo();