Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 04f50fbd authored by Andrew Cole's avatar Andrew Cole
Browse files

FolderIconLoadTest Deflake

The FolderIconLoadTest has always been flakey because in the test when we are trying to wait for the icon cache to fully update before clearing cache, there was a period of time between when the last SerializedIconUpdateTask was updating and the next one was scheduled in which there was no task currently sitting in the queue. Instead of checking the queue for messages, we have changed this to use a volatile variable that is set when any update task is started, and set to false when all update tasks are no longer running.

I have also switched the objects from stacks to ArrayDequeue becase the use of Stacks is considered deprecated. Because ArrayDequeue pop() is a reverse order operation, I have also switched to using removeLast() to maintain parity with the previous implementation.

Flag: None
Test: FolderIconLoadTest
Bug: 319923578
Change-Id: Ie9b391a56d1651f867a132c4092b490cd8b3cd0e
parent a8b41957
Loading
Loading
Loading
Loading
+28 −17
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import static com.android.launcher3.icons.BaseIconFactory.getFullResDefaultActiv
import static com.android.launcher3.icons.BitmapInfo.LOW_RES_ICON;
import static com.android.launcher3.icons.GraphicsUtils.flattenBitmap;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import static com.android.launcher3.icons.cache.IconCacheUpdateHandler.ICON_UPDATE_TOKEN;

import static java.util.Objects.requireNonNull;

@@ -133,6 +132,8 @@ public abstract class BaseIconCache {
    @NonNull
    private final Looper mBgLooper;

    private volatile boolean mIconUpdateInProgress = false;

    public BaseIconCache(@NonNull final Context context, @Nullable final String dbFileName,
            @NonNull final Looper bgLooper, final int iconDpi, final int iconPixelSize,
            final boolean inMemoryCache) {
@@ -200,7 +201,8 @@ public abstract class BaseIconCache {
        if (resources != null && iconId != 0) {
            try {
                return resources.getDrawableForDensity(iconId, mIconDpi);
            } catch (Resources.NotFoundException e) { }
            } catch (Resources.NotFoundException e) {
            }
        }
        return getFullResDefaultActivityIcon(mIconDpi);
    }
@@ -209,7 +211,8 @@ public abstract class BaseIconCache {
    public Drawable getFullResIcon(@NonNull final String packageName, final int iconId) {
        try {
            return getFullResIcon(mPackageManager.getResourcesForApplication(packageName), iconId);
        } catch (PackageManager.NameNotFoundException e) { }
        } catch (PackageManager.NameNotFoundException e) {
        }
        return getFullResDefaultActivityIcon(mIconDpi);
    }

@@ -218,10 +221,19 @@ public abstract class BaseIconCache {
        try {
            return getFullResIcon(mPackageManager.getResourcesForApplication(info.applicationInfo),
                    info.getIconResource());
        } catch (PackageManager.NameNotFoundException e) { }
        } catch (PackageManager.NameNotFoundException e) {
        }
        return getFullResDefaultActivityIcon(mIconDpi);
    }

    public void setIconUpdateInProgress(boolean updating) {
        mIconUpdateInProgress = updating;
    }

    public boolean isIconUpdateInProgress() {
        return mIconUpdateInProgress;
    }

    /**
     * Remove any records for the supplied ComponentName.
     */
@@ -299,6 +311,7 @@ public abstract class BaseIconCache {

    /**
     * Adds an entry into the DB and the in-memory cache.
     *
     * @param replaceExisting if true, it will recreate the bitmap even if it already exists in
     *                        the memory. This is useful then the previous bitmap was created using
     *                        old data.
@@ -333,7 +346,7 @@ public abstract class BaseIconCache {
            if (entryTitle == null) {
                Log.wtf(TAG, "No label returned from caching logic instance: " + cachingLogic);
            }
            entryTitle = componentName.getPackageName();;
            entryTitle = componentName.getPackageName();
        }
        entry.title = entryTitle;

@@ -348,6 +361,7 @@ public abstract class BaseIconCache {

    /**
     * Updates {@param values} to contain versioning information and adds it to the DB.
     *
     * @param values {@link ContentValues} containing icon & title
     */
    private void addIconToDB(@NonNull final ContentValues values, @NonNull final ComponentName key,
@@ -468,8 +482,10 @@ public abstract class BaseIconCache {
            if (usePackageIcon) {
                CacheEntry packageEntry = getEntryForPackageLocked(
                        componentName.getPackageName(), user, false);
                if (DEBUG) Log.d(TAG, "using package default icon for " +
                        componentName.toShortString());
                if (DEBUG) {
                    Log.d(TAG, "using package default icon for "
                            + componentName.toShortString());
                }
                entry.bitmap = packageEntry.bitmap;
                entry.contentDescription = packageEntry.contentDescription;

@@ -504,13 +520,6 @@ public abstract class BaseIconCache {
        mCache.clear();
    }

    /**
     * Returns true if an icon update is in progress
     */
    public boolean isIconUpdateInProgress() {
        return mWorkerHandler.hasMessages(0, ICON_UPDATE_TOKEN);
    }

    /**
     * Adds a default package entry in the cache. This entry is not persisted and will be removed
     * when the cache is flushed.
@@ -723,10 +732,12 @@ public abstract class BaseIconCache {
                COLUMN_FLAGS};
        public static final String[] COLUMNS_HIGH_RES = Arrays.copyOf(COLUMNS_LOW_RES,
                COLUMNS_LOW_RES.length + 2, String[].class);

        static {
            COLUMNS_HIGH_RES[COLUMNS_LOW_RES.length] = COLUMN_ICON;
            COLUMNS_HIGH_RES[COLUMNS_LOW_RES.length + 1] = COLUMN_MONO_ICON;
        }

        private static final int INDEX_TITLE = Arrays.asList(COLUMNS_LOW_RES).indexOf(COLUMN_LABEL);
        private static final int INDEX_COLOR = Arrays.asList(COLUMNS_LOW_RES)
                .indexOf(COLUMN_ICON_COLOR);
+19 −11
Original line number Diff line number Diff line
@@ -30,13 +30,13 @@ import android.util.SparseBooleanArray;

import com.android.launcher3.icons.cache.BaseIconCache.IconDB;

import java.util.ArrayDeque;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;

/**
 * Utility class to handle updating the Icon cache
@@ -101,6 +101,7 @@ public class IconCacheUpdateHandler {
    /**
     * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in
     * the DB and are updated.
     *
     * @return The set of packages for which icons have updated.
     */
    public <T> void updateIcons(List<T> apps, CachingLogic<T> cachingLogic,
@@ -130,6 +131,7 @@ public class IconCacheUpdateHandler {
    /**
     * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in
     * the DB and are updated.
     *
     * @return The set of packages for which icons have updated.
     */
    @SuppressWarnings("unchecked")
@@ -141,7 +143,7 @@ public class IconCacheUpdateHandler {
        }
        long userSerial = mIconCache.getSerialNumberForUser(user);

        Stack<T> appsToUpdate = new Stack<>();
        ArrayDeque<T> appsToUpdate = new ArrayDeque<>();

        try (Cursor c = mIconCache.mIconDb.query(
                new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT,
@@ -207,8 +209,9 @@ public class IconCacheUpdateHandler {

        // Insert remaining apps.
        if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) {
            Stack<T> appsToAdd = new Stack<>();
            ArrayDeque<T> appsToAdd = new ArrayDeque<>();
            appsToAdd.addAll(componentMap.values());
            mIconCache.setIconUpdateInProgress(true);
            new SerializedIconUpdateTask(userSerial, user, appsToAdd, appsToUpdate, cachingLogic,
                    onUpdateCallback).scheduleNext();
        }
@@ -250,14 +253,14 @@ public class IconCacheUpdateHandler {
    private class SerializedIconUpdateTask<T> implements Runnable {
        private final long mUserSerial;
        private final UserHandle mUserHandle;
        private final Stack<T> mAppsToAdd;
        private final Stack<T> mAppsToUpdate;
        private final ArrayDeque<T> mAppsToAdd;
        private final ArrayDeque<T> mAppsToUpdate;
        private final CachingLogic<T> mCachingLogic;
        private final HashSet<String> mUpdatedPackages = new HashSet<>();
        private final OnUpdateCallback mOnUpdateCallback;

        SerializedIconUpdateTask(long userSerial, UserHandle userHandle,
                Stack<T> appsToAdd, Stack<T> appsToUpdate, CachingLogic<T> cachingLogic,
                ArrayDeque<T> appsToAdd, ArrayDeque<T> appsToUpdate, CachingLogic<T> cachingLogic,
                OnUpdateCallback onUpdateCallback) {
            mUserHandle = userHandle;
            mUserSerial = userSerial;
@@ -270,7 +273,7 @@ public class IconCacheUpdateHandler {
        @Override
        public void run() {
            if (!mAppsToUpdate.isEmpty()) {
                T app = mAppsToUpdate.pop();
                T app = mAppsToUpdate.removeLast();
                String pkg = mCachingLogic.getComponent(app).getPackageName();
                PackageInfo info = mPkgInfoMap.get(pkg);

@@ -286,8 +289,9 @@ public class IconCacheUpdateHandler {
                // Let it run one more time.
                scheduleNext();
            } else if (!mAppsToAdd.isEmpty()) {
                T app = mAppsToAdd.pop();
                PackageInfo info = mPkgInfoMap.get(mCachingLogic.getComponent(app).getPackageName());
                T app = mAppsToAdd.removeLast();
                PackageInfo info = mPkgInfoMap.get(
                        mCachingLogic.getComponent(app).getPackageName());
                // We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every
                // app should have package info, this is not guaranteed by the api
                if (info != null) {
@@ -297,6 +301,10 @@ public class IconCacheUpdateHandler {

                if (!mAppsToAdd.isEmpty()) {
                    scheduleNext();
                } else if (!mIconCache.mWorkerHandler.hasMessages(0, ICON_UPDATE_TOKEN)) {
                    // This checks if there is a second icon update process happening
                    // before notifying BaseIconCache that the updates are over
                    mIconCache.setIconUpdateInProgress(false);
                }
            }
        }