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
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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<LauncherIconsForPreview> 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<OnDeviceProfileChangeListener> mDpChangeListeners = new ArrayList<>();
|
||||
|
||||
@@ -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> 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<LauncherIcons> mPool;
|
||||
|
||||
private MonochromeIconFactory mMonochromeIconFactory;
|
||||
|
||||
protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) {
|
||||
protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize,
|
||||
ConcurrentLinkedQueue<LauncherIcons> 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<LauncherIcons> mPool = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private Pool(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public LauncherIcons obtain() {
|
||||
ConcurrentLinkedQueue<LauncherIcons> 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<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user