From bf3efe8af04b38de2b74bf67e4b1b64beca9aa12 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 7 May 2024 10:19:57 -0700 Subject: [PATCH] Fixing LauncherIcons leaking outside sandbox context Since LauncherIcons was using a global static pool, a custom instance for a test could leak into the global pool, affecting other tests Bug: 335280439 Test: Verified image test on device Flag: None Change-Id: Iedd19c8e69c928e44b65eae7eba0167b03b5df6b --- .../android/launcher3/LauncherAppState.java | 2 +- .../graphics/LauncherPreviewRenderer.java | 34 -------- .../launcher3/icons/LauncherIcons.java | 85 ++++++++++--------- 3 files changed, 45 insertions(+), 76 deletions(-) diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 03de334fcf..869b9956c1 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -206,7 +206,7 @@ public class LauncherAppState implements SafeCloseable { } private void refreshAndReloadLauncher() { - LauncherIcons.clearPool(); + LauncherIcons.clearPool(mContext); mIconCache.updateIconParams( mInvariantDeviceProfile.fillResIconDpi, mInvariantDeviceProfile.iconBitmapSize); mModel.forceReload(); diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index 1f388c2be9..ae8f1d513c 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -76,7 +76,6 @@ import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.celllayout.CellPosMapper; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.FolderIcon; -import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.WidgetItem; @@ -107,7 +106,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentLinkedQueue; /** * Utility class for generating the preview of Launcher for a given InvariantDeviceProfile. @@ -126,44 +124,12 @@ public class LauncherPreviewRenderer extends ContextWrapper */ public static class PreviewContext extends SandboxContext { - private final InvariantDeviceProfile mIdp; - private final ConcurrentLinkedQueue mIconPool = - new ConcurrentLinkedQueue<>(); - public PreviewContext(Context base, InvariantDeviceProfile idp) { super(base); - mIdp = idp; putObject(InvariantDeviceProfile.INSTANCE, idp); putObject(LauncherAppState.INSTANCE, new LauncherAppState(this, null /* iconCacheFileName */)); } - - /** - * Creates a new LauncherIcons for the preview, skipping the global pool - */ - public LauncherIcons newLauncherIcons(Context context) { - LauncherIconsForPreview launcherIconsForPreview = mIconPool.poll(); - if (launcherIconsForPreview != null) { - return launcherIconsForPreview; - } - return new LauncherIconsForPreview(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize, - -1 /* poolId */); - } - - private final class LauncherIconsForPreview extends LauncherIcons { - - private LauncherIconsForPreview(Context context, int fillResIconDpi, int iconBitmapSize, - int poolId) { - super(context, fillResIconDpi, iconBitmapSize, poolId); - } - - @Override - public void recycle() { - // Clear any temporary state variables - clear(); - mIconPool.offer(this); - } - } } private final List mDpChangeListeners = new ArrayList<>(); diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java index 7331c6f5e0..e90a1e0de9 100644 --- a/src/com/android/launcher3/icons/LauncherIcons.java +++ b/src/com/android/launcher3/icons/LauncherIcons.java @@ -25,79 +25,53 @@ import androidx.annotation.NonNull; import com.android.launcher3.Flags; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.graphics.IconShape; -import com.android.launcher3.graphics.LauncherPreviewRenderer; import com.android.launcher3.pm.UserCache; +import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.Themes; import com.android.launcher3.util.UserIconInfo; +import java.util.concurrent.ConcurrentLinkedQueue; + /** * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class * that are threadsafe. */ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { - private static final Object sPoolSync = new Object(); - private static LauncherIcons sPool; - private static int sPoolId = 0; + private static final MainThreadInitializedObject POOL = + new MainThreadInitializedObject<>(Pool::new); /** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ public static LauncherIcons obtain(Context context) { - if (context instanceof LauncherPreviewRenderer.PreviewContext) { - return ((LauncherPreviewRenderer.PreviewContext) context).newLauncherIcons(context); - } - - int poolId; - synchronized (sPoolSync) { - if (sPool != null) { - LauncherIcons m = sPool; - sPool = m.next; - m.next = null; - return m; - } - poolId = sPoolId; - } - - InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context); - return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId); + return POOL.get(context).obtain(); } - public static void clearPool() { - synchronized (sPoolSync) { - sPool = null; - sPoolId++; - } + public static void clearPool(Context context) { + POOL.get(context).close(); } - private final int mPoolId; - - private LauncherIcons next; + private final ConcurrentLinkedQueue mPool; private MonochromeIconFactory mMonochromeIconFactory; - protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) { + protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, + ConcurrentLinkedQueue pool) { super(context, fillResIconDpi, iconBitmapSize, IconShape.INSTANCE.get(context).getShape().enableShapeDetection()); mMonoIconEnabled = Themes.isThemedIconEnabled(context); - mPoolId = poolId; + mPool = pool; } /** * Recycles a LauncherIcons that may be in-use. */ public void recycle() { - synchronized (sPoolSync) { - if (sPoolId != mPoolId) { - return; - } - // Clear any temporary state variables - clear(); - - next = sPool; - sPool = this; - } + clear(); + mPool.add(this); } @Override @@ -122,4 +96,33 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { public void close() { recycle(); } + + private static class Pool implements SafeCloseable { + + private final Context mContext; + + @NonNull + private ConcurrentLinkedQueue mPool = new ConcurrentLinkedQueue<>(); + + private Pool(Context context) { + mContext = context; + } + + public LauncherIcons obtain() { + ConcurrentLinkedQueue pool = mPool; + LauncherIcons m = pool.poll(); + + if (m == null) { + InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext); + return new LauncherIcons(mContext, idp.fillResIconDpi, idp.iconBitmapSize, pool); + } else { + return m; + } + } + + @Override + public void close() { + mPool = new ConcurrentLinkedQueue<>(); + } + } }