Merge "Use launcher db icon column to load pre-archived app icons on restore, similar to Promise Icons." into main
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 1623881..6a8d86b 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -42,6 +42,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.launcher3.Flags;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -201,7 +202,7 @@
         info.itemType = itemType;
         info.title = getTitle();
         // the fallback icon
-        if (!loadIcon(info)) {
+        if (!loadIconFromDb(info)) {
             info.bitmap = mIconCache.getDefaultIcon(info.user);
         }
 
@@ -213,15 +214,15 @@
     /**
      * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
      */
-    protected boolean loadIcon(WorkspaceItemInfo info) {
-        return createIconRequestInfo(info, false).loadWorkspaceIcon(mContext);
+    protected boolean loadIconFromDb(WorkspaceItemInfo info) {
+        return createIconRequestInfo(info, false).loadIconFromDbBlob(mContext);
     }
 
     public IconRequestInfo<WorkspaceItemInfo> createIconRequestInfo(
             WorkspaceItemInfo wai, boolean useLowResIcon) {
         byte[] iconBlob = itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT || restoreFlag != 0
+                || (wai.isInactiveArchive() && Flags.restoreArchivedAppIconsFromDb())
                 ? getIconBlob() : null;
-
         return new IconRequestInfo<>(wai, mActivityInfo, iconBlob, useLowResIcon);
     }
 
@@ -312,7 +313,7 @@
         info.intent = intent;
 
         // the fallback icon
-        if (!loadIcon(info)) {
+        if (!loadIconFromDb(info)) {
             mIconCache.getTitleAndIcon(info, DEFAULT_LOOKUP_FLAG);
         }
 
@@ -375,20 +376,11 @@
         info.intent = newIntent;
         UserCache userCache = UserCache.getInstance(mContext);
         UserIconInfo userIconInfo = userCache.getUserInfo(user);
-
-        if (loadIcon) {
-            mIconCache.getTitleAndIcon(info, mActivityInfo,
-                    DEFAULT_LOOKUP_FLAG.withUseLowRes(useLowResIcon));
-            if (mIconCache.isDefaultIcon(info.bitmap, user)) {
-                loadIcon(info);
-            }
-        }
-
         if (mActivityInfo != null) {
             AppInfo.updateRuntimeFlagsForActivityTarget(info, mActivityInfo, userIconInfo,
                     ApiWrapper.INSTANCE.get(mContext), mPmHelper);
         }
-
+        loadWorkspaceTitleAndIcon(useLowResIcon, loadIcon, info);
         // from the db
         if (TextUtils.isEmpty(info.title)) {
             if (loadIcon) {
@@ -407,6 +399,32 @@
         return info;
     }
 
+    @VisibleForTesting
+    void loadWorkspaceTitleAndIcon(
+            boolean useLowResIcon,
+            boolean loadIconFromCache,
+            WorkspaceItemInfo info
+    ) {
+        boolean isPreArchived = Flags.enableSupportForArchiving()
+                && Flags.restoreArchivedAppIconsFromDb()
+                && info.isInactiveArchive();
+        boolean preArchivedIconNotFound = isPreArchived && !loadIconFromDb(info);
+        if (preArchivedIconNotFound) {
+            Log.d(TAG, "loadIconFromDb failed for pre-archived icon, loading from cache."
+                    + " Component=" + info.getTargetComponent());
+            mIconCache.getTitleAndIcon(info, mActivityInfo,
+                    DEFAULT_LOOKUP_FLAG.withUseLowRes(useLowResIcon));
+        } else if (loadIconFromCache && !info.isInactiveArchive()) {
+            mIconCache.getTitleAndIcon(info, mActivityInfo,
+                    DEFAULT_LOOKUP_FLAG.withUseLowRes(useLowResIcon));
+            if (mIconCache.isDefaultIcon(info.bitmap, user)) {
+                Log.d(TAG, "Default Icon found in cache, trying DB instead. "
+                        + " Component=" + info.getTargetComponent());
+                loadIconFromDb(info);
+            }
+        }
+    }
+
     /**
      * Returns a {@link ContentWriter} which can be used to update the current item.
      */
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index fee9696..3ee029b 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -26,6 +26,7 @@
 import static com.android.launcher3.LauncherSettings.Favorites.DESKTOP_ICON_FLAG;
 import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
 import static com.android.launcher3.icons.CacheableShortcutInfo.convertShortcutsToCacheableShortcuts;
+import static com.android.launcher3.icons.cache.CacheLookupFlag.DEFAULT_LOOKUP_FLAG;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
@@ -106,11 +107,13 @@
 import com.android.launcher3.widget.WidgetInflater;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.CancellationException;
 
@@ -154,6 +157,8 @@
     private Map<ShortcutKey, ShortcutInfo> mShortcutKeyToPinnedShortcuts;
     private HashMap<PackageUserKey, SessionInfo> mInstallingPkgsCached;
 
+    private List<IconRequestInfo<WorkspaceItemInfo>> mWorkspaceIconRequestInfos = new ArrayList<>();
+
     private boolean mStopped;
 
     private final Set<PackageUserKey> mPendingPackages = new HashSet<>();
@@ -410,7 +415,7 @@
     protected void loadWorkspace(
             List<CacheableShortcutInfo> allDeepShortcuts,
             String selection,
-            LoaderMemoryLogger memoryLogger,
+            @Nullable LoaderMemoryLogger memoryLogger,
             @Nullable LauncherRestoreEventLogger restoreEventLogger
     ) {
         Trace.beginSection("LoadWorkspace");
@@ -474,13 +479,12 @@
                 final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
                 queryPinnedShortcutsForUnlockedUsers(context, unlockedUsers);
 
-                List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();
-
+                mWorkspaceIconRequestInfos = new ArrayList<>();
                 WorkspaceItemProcessor itemProcessor = new WorkspaceItemProcessor(c, memoryLogger,
                         mUserCache, mUserManagerState, mLauncherApps, mPendingPackages,
                         mShortcutKeyToPinnedShortcuts, mApp, mBgDataModel,
                         mWidgetProvidersMap, installingPkgs, isSdCardReady,
-                        widgetInflater, mPmHelper, iconRequestInfos, unlockedUsers,
+                        widgetInflater, mPmHelper, mWorkspaceIconRequestInfos, unlockedUsers,
                         allDeepShortcuts);
 
                 if (mStopped) {
@@ -490,7 +494,7 @@
                         itemProcessor.processItem();
                     }
                 }
-                tryLoadWorkspaceIconsInBulk(iconRequestInfos);
+                tryLoadWorkspaceIconsInBulk(mWorkspaceIconRequestInfos);
             } finally {
                 IOUtils.closeSilently(c);
             }
@@ -621,7 +625,9 @@
             for (IconRequestInfo<WorkspaceItemInfo> iconRequestInfo : iconRequestInfos) {
                 WorkspaceItemInfo wai = iconRequestInfo.itemInfo;
                 if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
-                    iconRequestInfo.loadWorkspaceIcon(mApp.getContext());
+                    logASplit("tryLoadWorkspaceIconsInBulk: default icon found for "
+                            + wai.getTargetComponent() + ", will attempt to load from iconBlob");
+                    iconRequestInfo.loadIconFromDbBlob(mApp.getContext());
                 }
             }
         } finally {
@@ -702,7 +708,7 @@
         // Clear the list of apps
         mBgAllAppsList.clear();
 
-        List<IconRequestInfo<AppInfo>> iconRequestInfos = new ArrayList<>();
+        List<IconRequestInfo<AppInfo>> allAppsItemRequestInfos = new ArrayList<>();
         boolean isWorkProfileQuiet = false;
         boolean isPrivateProfileQuiet = false;
         for (UserHandle user : profiles) {
@@ -742,15 +748,14 @@
                     }
                 }
 
-                iconRequestInfos.add(new IconRequestInfo<>(
-                        appInfo, app, /* useLowResIcon= */ false));
-                mBgAllAppsList.add(
-                        appInfo, app, false);
+                IconRequestInfo<AppInfo> iconRequestInfo = getAppInfoIconRequestInfo(
+                        appInfo, app, mWorkspaceIconRequestInfos);
+                allAppsItemRequestInfos.add(iconRequestInfo);
+                mBgAllAppsList.add(appInfo, app, false);
             }
             allActivityList.addAll(apps);
         }
 
-
         if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
             // get all active sessions and add them to the all apps list
             for (PackageInstaller.SessionInfo info :
@@ -761,7 +766,7 @@
                         false);
 
                 if (promiseAppInfo != null) {
-                    iconRequestInfos.add(new IconRequestInfo<>(
+                    allAppsItemRequestInfos.add(new IconRequestInfo<>(
                             promiseAppInfo,
                             /* launcherActivityInfo= */ null,
                             promiseAppInfo.getMatchingLookupFlag().useLowRes()));
@@ -770,9 +775,22 @@
         }
 
         Trace.beginSection("LoadAllAppsIconsInBulk");
+
         try {
-            mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
-            iconRequestInfos.forEach(iconRequestInfo ->
+            mIconCache.getTitlesAndIconsInBulk(allAppsItemRequestInfos);
+            if (Flags.restoreArchivedAppIconsFromDb()) {
+                for (IconRequestInfo<AppInfo> iconRequestInfo : allAppsItemRequestInfos) {
+                    AppInfo appInfo = iconRequestInfo.itemInfo;
+                    if (mIconCache.isDefaultIcon(appInfo.bitmap, appInfo.user)) {
+                        logASplit("LoadAllAppsIconsInBulk: default icon found for "
+                                + appInfo.getTargetComponent()
+                                + ", will attempt to load from iconBlob: "
+                                + Arrays.toString(iconRequestInfo.iconBlob));
+                        iconRequestInfo.loadIconFromDbBlob(mApp.getContext());
+                    }
+                }
+            }
+            allAppsItemRequestInfos.forEach(iconRequestInfo ->
                     mBgAllAppsList.updateSectionName(iconRequestInfo.itemInfo));
         } finally {
             Trace.endSection();
@@ -795,6 +813,51 @@
         return allActivityList;
     }
 
+    @NonNull
+    @VisibleForTesting
+    IconRequestInfo<AppInfo> getAppInfoIconRequestInfo(
+            AppInfo appInfo,
+            LauncherActivityInfo activityInfo,
+            List<IconRequestInfo<WorkspaceItemInfo>> workspaceRequestInfos
+    ) {
+        if (Flags.restoreArchivedAppIconsFromDb()) {
+            Optional<IconRequestInfo<WorkspaceItemInfo>> workspaceIconRequest =
+                    workspaceRequestInfos.stream()
+                            .filter(request -> appInfo.getTargetComponent().equals(
+                                    request.itemInfo.getTargetComponent()))
+                            .findFirst();
+
+            if (workspaceIconRequest.isPresent() && activityInfo.getApplicationInfo().isArchived) {
+                logASplit("getAppInfoIconRequestInfo:"
+                            + " matching archived info found, loading icon blob into icon request."
+                            + " Component=" + appInfo.getTargetComponent());
+                IconRequestInfo<AppInfo> iconRequestInfo = new IconRequestInfo<>(
+                        appInfo,
+                        activityInfo,
+                        workspaceIconRequest.get().iconBlob,
+                        false /* useLowResIcon= */
+                );
+                if (!iconRequestInfo.loadIconFromDbBlob(mApp.getContext())) {
+                    Log.d(TAG, "AppInfo Icon failed to load from blob, using cache.");
+                    mIconCache.getTitleAndIcon(
+                            appInfo,
+                            iconRequestInfo.launcherActivityInfo,
+                            DEFAULT_LOOKUP_FLAG
+                    );
+                }
+                return iconRequestInfo;
+            } else {
+                Log.d(TAG, "App not archived or workspace info not found"
+                        + ", creating IconRequestInfo without icon blob."
+                        + " Component:" + appInfo.getTargetComponent()
+                        + ", isArchived: " + activityInfo.getApplicationInfo().isArchived);
+            }
+        }
+        logASplit("Loading IconRequestInfo without iconBlob for AppInfo: "
+                + appInfo.getTargetComponent());
+        return new IconRequestInfo<>(appInfo, activityInfo, false /* useLowResIcon= */);
+    }
+
     private List<ShortcutInfo> loadDeepShortcuts() {
         List<ShortcutInfo> allShortcuts = new ArrayList<>();
         mBgDataModel.deepShortcutMap.clear();
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index de1df2e..90f11a3 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -286,7 +286,7 @@
                     // If the pinned deep shortcut is no longer published,
                     // use the last saved icon instead of the default.
                     val csi = CacheableShortcutInfo(pinnedShortcut, appInfoWrapper)
-                    iconCache.getShortcutIcon(info, csi, c::loadIcon)
+                    iconCache.getShortcutIcon(info, csi, c::loadIconFromDb)
                     if (appInfoWrapper.isSuspended()) {
                         info.runtimeStatusFlags =
                             info.runtimeStatusFlags or ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED
diff --git a/src/com/android/launcher3/model/data/IconRequestInfo.java b/src/com/android/launcher3/model/data/IconRequestInfo.java
index e77e527..42af018 100644
--- a/src/com/android/launcher3/model/data/IconRequestInfo.java
+++ b/src/com/android/launcher3/model/data/IconRequestInfo.java
@@ -64,23 +64,25 @@
     }
 
     /**
-     * Loads this request's item info's title. This method should only be used on IconRequestInfos
-     * for WorkspaceItemInfos.
+     * Loads this request's item info's title and icon from given iconBlob from Launcher.db.
+     * This method should only be used on {@link IconRequestInfo} for {@link WorkspaceItemInfo}
+     *  or {@link AppInfo}.
      */
-    public boolean loadWorkspaceIcon(Context context) {
-        if (!(itemInfo instanceof WorkspaceItemInfo)) {
+    public boolean loadIconFromDbBlob(Context context) {
+        if (!(itemInfo instanceof WorkspaceItemInfo) && !(itemInfo instanceof AppInfo)) {
             throw new IllegalStateException(
-                    "loadWorkspaceIcon should only be use for a WorkspaceItemInfos: " + itemInfo);
+                    "loadIconFromDb should only be used for either WorkspaceItemInfo or AppInfo: "
+                            + itemInfo);
         }
 
         try (LauncherIcons li = LauncherIcons.obtain(context)) {
-            WorkspaceItemInfo info = (WorkspaceItemInfo) itemInfo;
-            // Failed to load from resource, try loading from DB.
+            ItemInfoWithIcon info = itemInfo;
             if (iconBlob == null) {
+                Log.d(TAG, "loadIconFromDb: icon blob null, returning. Component="
+                        + info.getTargetComponent());
                 return false;
             }
-            info.bitmap = li.createIconBitmap(decodeByteArray(
-                    iconBlob, 0, iconBlob.length));
+            info.bitmap = li.createIconBitmap(decodeByteArray(iconBlob, 0, iconBlob.length));
             return true;
         } catch (Exception e) {
             Log.e(TAG, "Failed to decode byte array for info " + itemInfo, e);
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
index 63359ec..11047fb 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -16,6 +16,9 @@
 
 package com.android.launcher3.model;
 
+import static android.graphics.BitmapFactory.decodeByteArray;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
 import static androidx.test.InstrumentationRegistry.getContext;
 
 import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_ID;
@@ -40,8 +43,11 @@
 import static com.android.launcher3.LauncherSettings.Favorites.SPANY;
 import static com.android.launcher3.LauncherSettings.Favorites.TITLE;
 import static com.android.launcher3.LauncherSettings.Favorites._ID;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED;
 import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
@@ -52,13 +58,19 @@
 import android.content.Context;
 import android.content.Intent;
 import android.database.MatrixCursor;
+import android.graphics.Bitmap;
 import android.os.Process;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.launcher3.Flags;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.Executors;
@@ -67,6 +79,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -77,6 +90,9 @@
 @RunWith(AndroidJUnit4.class)
 public class LoaderCursorTest {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
     private LauncherModelHelper mModelHelper;
     private LauncherAppState mApp;
     private PackageManagerHelper mPmHelper;
@@ -87,6 +103,12 @@
 
     private LoaderCursor mLoaderCursor;
 
+    private static byte[] sTestBlob = new byte[] {
+            -119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1,
+            8, 4, 0, 0, 0, -75, 28, 12, 2, 0, 0, 0, 11, 73, 68, 65, 84, 120, -38, 99, 100, 96, 0,
+            0, 0, 6, 0, 2, 48, -127, -48, 47, 0, 0, 0, 0, 73, 69, 78, 68, -82, 66, 96, -126
+    };
+
     @Before
     public void setup() {
         mModelHelper = new LauncherModelHelper();
@@ -119,7 +141,8 @@
                 .add(PROFILE_ID, 0)
                 .add(ITEM_TYPE, itemType)
                 .add(TITLE, title)
-                .add(CONTAINER, CONTAINER_DESKTOP);
+                .add(CONTAINER, CONTAINER_DESKTOP)
+                .add(ICON, sTestBlob);
     }
 
     @Test
@@ -161,7 +184,12 @@
 
     @Test
     public void loadSimpleShortcut() {
-        initCursor(ITEM_TYPE_DEEP_SHORTCUT, "my-shortcut");
+        mCursor.newRow()
+                .add(_ID, 1)
+                .add(PROFILE_ID, 0)
+                .add(ITEM_TYPE, ITEM_TYPE_DEEP_SHORTCUT)
+                .add(TITLE, "my-shortcut")
+                .add(CONTAINER, CONTAINER_DESKTOP);
         assertTrue(mLoaderCursor.moveToNext());
 
         WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
@@ -223,6 +251,68 @@
                 newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 3), true));
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+    public void ifArchivedWithFlag_whenloadWorkspaceTitleAndIcon_thenLoadIconFromDb() {
+        // Given
+        initCursor(ITEM_TYPE_APPLICATION, "title");
+        assertTrue(mLoaderCursor.moveToNext());
+        WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+        itemInfo.bitmap = null;
+        itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED;
+        Bitmap expectedBitmap = LauncherIcons.obtain(mContext)
+                .createIconBitmap(decodeByteArray(sTestBlob, 0, sTestBlob.length))
+                .icon;
+        // When
+        mLoaderCursor.loadWorkspaceTitleAndIcon(false, true, itemInfo);
+        // Then
+        assertThat(itemInfo.bitmap.icon).isNotNull();
+        assertThat(itemInfo.bitmap.icon.sameAs(expectedBitmap)).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+    public void ifArchivedWithFlag_whenLoadIconFromDb_thenLoadIconFromBlob() {
+        // Given
+        initCursor(ITEM_TYPE_APPLICATION, "title");
+        assertTrue(mLoaderCursor.moveToNext());
+        WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+        itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED;
+        // Then
+        assertTrue(mLoaderCursor.loadIconFromDb(itemInfo));
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+    public void ifArchivedWithoutFlag_whenLoadWorkspaceTitleAndIcon_thenDoNotLoadFromDb() {
+        // Given
+        initCursor(ITEM_TYPE_APPLICATION, "title");
+        assertTrue(mLoaderCursor.moveToNext());
+        WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+        itemInfo.bitmap = null;
+        itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED;
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName("package", "class"));
+        itemInfo.intent = intent;
+        // When
+        mLoaderCursor.loadWorkspaceTitleAndIcon(false, false, itemInfo);
+        // Then
+        assertThat(itemInfo.bitmap).isNull();
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+    public void ifArchivedWithoutFlag_whenLoadIconFromDb_thenDoNotLoadFromBlob() {
+        // Given
+        initCursor(ITEM_TYPE_APPLICATION, "title");
+        assertTrue(mLoaderCursor.moveToNext());
+        WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+        itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED;
+        // Then
+        assertFalse(mLoaderCursor.loadIconFromDb(itemInfo));
+    }
+
+
     private ItemInfo newItemInfo(int cellX, int cellY, int spanX, int spanY,
             int container, int screenId) {
         ItemInfo info = new ItemInfo();
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index dba7603..cdb45fc 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -1,7 +1,10 @@
 package com.android.launcher3.model
 
 import android.appwidget.AppWidgetManager
+import android.content.ComponentName
 import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.LauncherActivityInfo
 import android.os.Process
 import android.os.UserHandle
 import android.platform.test.annotations.DisableFlags
@@ -28,6 +31,9 @@
 import com.android.launcher3.icons.IconCache
 import com.android.launcher3.icons.cache.CachingLogic
 import com.android.launcher3.icons.cache.IconCacheUpdateHandler
+import com.android.launcher3.model.data.AppInfo
+import com.android.launcher3.model.data.IconRequestInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
 import com.android.launcher3.pm.UserCache
 import com.android.launcher3.provider.RestoreDbTask
 import com.android.launcher3.ui.TestViewHelpers
@@ -38,8 +44,11 @@
 import com.android.launcher3.util.TestUtil
 import com.android.launcher3.util.UserIconInfo
 import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import dagger.BindsInstance
 import dagger.Component
+import java.util.concurrent.CountDownLatch
+import java.util.function.Predicate
 import junit.framework.Assert.assertEquals
 import org.junit.After
 import org.junit.Before
@@ -58,12 +67,11 @@
 import org.mockito.kotlin.any
 import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
 import org.mockito.kotlin.spy
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 import org.mockito.quality.Strictness
-import java.util.concurrent.CountDownLatch
-import java.util.function.Predicate
 
 private const val INSERTION_STATEMENT_FILE = "databases/workspace_items.sql"
 
@@ -480,6 +488,157 @@
         verify(spyContext, times(0)).sendBroadcast(any())
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+    fun `When flag on then archived AllApps icons found on Workspace loaded from db`() {
+        // Given
+        // Given
+        val activityInfo: LauncherActivityInfo = mock()
+        val applicationInfo: ApplicationInfo = mock<ApplicationInfo>().apply { isArchived = true }
+        whenever(activityInfo.applicationInfo).thenReturn(applicationInfo)
+        val expectedIconBlob = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)
+        val expectedComponent = ComponentName("package", "class")
+        val workspaceIconRequests =
+            listOf(
+                IconRequestInfo<WorkspaceItemInfo>(
+                    WorkspaceItemInfo().apply {
+                        intent = Intent().apply { component = expectedComponent }
+                    },
+                    activityInfo,
+                    expectedIconBlob,
+                    false, /* useLowResIcon */
+                )
+            )
+        val expectedAppInfo = AppInfo().apply { componentName = expectedComponent }
+        // When
+        val loader =
+            LoaderTask(
+                app,
+                bgAllAppsList,
+                BgDataModel(),
+                modelDelegate,
+                launcherBinder,
+                widgetsFilterDataProvider,
+            )
+        val actualIconRequest =
+            loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
+        // Then
+        assertThat(actualIconRequest.iconBlob).isEqualTo(expectedIconBlob)
+        assertThat(actualIconRequest.itemInfo).isEqualTo(expectedAppInfo)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+    fun `When flag on then unarchived AllApps icons not loaded from db`() {
+        // Given
+        val activityInfo: LauncherActivityInfo = mock()
+        val applicationInfo: ApplicationInfo = mock<ApplicationInfo>().apply { isArchived = false }
+        whenever(activityInfo.applicationInfo).thenReturn(applicationInfo)
+        val expectedIconBlob = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)
+        val expectedComponent = ComponentName("package", "class")
+        val workspaceIconRequests =
+            listOf(
+                IconRequestInfo<WorkspaceItemInfo>(
+                    WorkspaceItemInfo().apply {
+                        intent = Intent().apply { component = expectedComponent }
+                    },
+                    activityInfo,
+                    expectedIconBlob,
+                    false, /* useLowResIcon */
+                )
+            )
+        val expectedAppInfo = AppInfo().apply { componentName = expectedComponent }
+        // When
+        val loader =
+            LoaderTask(
+                app,
+                bgAllAppsList,
+                BgDataModel(),
+                modelDelegate,
+                launcherBinder,
+                widgetsFilterDataProvider,
+            )
+        val actualIconRequest =
+            loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
+        // Then
+        assertThat(actualIconRequest.iconBlob).isNull()
+        assertThat(actualIconRequest.itemInfo).isEqualTo(expectedAppInfo)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+    fun `When flag on then archived AllApps icon not found on Workspace not loaded from db`() {
+        // Given
+        val activityInfo: LauncherActivityInfo = mock()
+        val applicationInfo: ApplicationInfo = mock<ApplicationInfo>().apply { isArchived = true }
+        whenever(activityInfo.applicationInfo).thenReturn(applicationInfo)
+        val expectedIconBlob = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)
+        val expectedComponent = ComponentName("package", "class")
+        val workspaceIconRequests =
+            listOf(
+                IconRequestInfo<WorkspaceItemInfo>(
+                    WorkspaceItemInfo().apply {
+                        intent = Intent().apply { component = expectedComponent }
+                    },
+                    activityInfo,
+                    expectedIconBlob,
+                    false, /* useLowResIcon */
+                )
+            )
+        val expectedAppInfo =
+            AppInfo().apply { componentName = ComponentName("differentPkg", "differentClass") }
+        // When
+        val loader =
+            LoaderTask(
+                app,
+                bgAllAppsList,
+                BgDataModel(),
+                modelDelegate,
+                launcherBinder,
+                widgetsFilterDataProvider,
+            )
+        val actualIconRequest =
+            loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
+        // Then
+        assertThat(actualIconRequest.iconBlob).isNull()
+        assertThat(actualIconRequest.itemInfo).isEqualTo(expectedAppInfo)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+    fun `When flag off then archived AllApps icons not loaded from db`() {
+        // Given
+        val activityInfo: LauncherActivityInfo = mock()
+        val applicationInfo: ApplicationInfo = mock<ApplicationInfo>().apply { isArchived = true }
+        whenever(activityInfo.applicationInfo).thenReturn(applicationInfo)
+        val expectedIconBlob = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)
+        val workspaceIconRequests =
+            listOf(
+                IconRequestInfo<WorkspaceItemInfo>(
+                    WorkspaceItemInfo(),
+                    activityInfo,
+                    expectedIconBlob,
+                    false, /* useLowResIcon */
+                )
+            )
+        val expectedAppInfo = AppInfo()
+        // When
+        val loader =
+            LoaderTask(
+                app,
+                bgAllAppsList,
+                BgDataModel(),
+                modelDelegate,
+                launcherBinder,
+                widgetsFilterDataProvider,
+            )
+        val actualIconRequest =
+            loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
+        // Then
+        assertThat(actualIconRequest.iconBlob).isNull()
+        assertThat(actualIconRequest.itemInfo).isEqualTo(expectedAppInfo)
+    }
+
     @LauncherAppSingleton
     @Component(modules = [AllModulesForTest::class])
     interface TestComponent : LauncherAppComponent {