Merge "Fix ImageWallpaper memory regression"
This commit is contained in:
committed by
Android (Google) Code Review
commit
bbb5a454e2
@@ -18,12 +18,14 @@ package com.android.systemui;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Trace;
|
||||
import android.service.wallpaper.WallpaperService;
|
||||
import android.util.Log;
|
||||
import android.util.Size;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -93,14 +95,20 @@ public class ImageWallpaper extends WallpaperService {
|
||||
private StatusBarStateController mController;
|
||||
private final Runnable mFinishRenderingTask = this::finishRendering;
|
||||
private final boolean mNeedTransition;
|
||||
private boolean mShouldStopTransition;
|
||||
@VisibleForTesting
|
||||
final boolean mIsHighEndGfx;
|
||||
private final boolean mDisplayNeedsBlanking;
|
||||
private final DisplayInfo mDisplayInfo = new DisplayInfo();
|
||||
private final Object mMonitor = new Object();
|
||||
private boolean mNeedRedraw;
|
||||
// This variable can only be accessed in synchronized block.
|
||||
private boolean mWaitingForRendering;
|
||||
|
||||
GLEngine(Context context, DozeParameters dozeParameters) {
|
||||
mNeedTransition = ActivityManager.isHighEndGfx()
|
||||
&& !dozeParameters.getDisplayNeedsBlanking();
|
||||
mIsHighEndGfx = ActivityManager.isHighEndGfx();
|
||||
mDisplayNeedsBlanking = dozeParameters.getDisplayNeedsBlanking();
|
||||
mNeedTransition = mIsHighEndGfx && !mDisplayNeedsBlanking;
|
||||
|
||||
// We will preserve EGL context when we are in lock screen or aod
|
||||
// to avoid janking in following transition, we need to release when back to home.
|
||||
@@ -112,14 +120,23 @@ public class ImageWallpaper extends WallpaperService {
|
||||
|
||||
@Override
|
||||
public void onCreate(SurfaceHolder surfaceHolder) {
|
||||
mEglHelper = new EglHelper();
|
||||
mEglHelper = getEglHelperInstance();
|
||||
// Deferred init renderer because we need to get wallpaper by display context.
|
||||
mRenderer = new ImageWallpaperRenderer(getDisplayContext(), this /* SurfaceProxy */);
|
||||
mRenderer = getRendererInstance();
|
||||
getDisplayContext().getDisplay().getDisplayInfo(mDisplayInfo);
|
||||
setFixedSizeAllowed(true);
|
||||
setOffsetNotificationsEnabled(true);
|
||||
updateSurfaceSize();
|
||||
}
|
||||
|
||||
EglHelper getEglHelperInstance() {
|
||||
return new EglHelper();
|
||||
}
|
||||
|
||||
ImageWallpaperRenderer getRendererInstance() {
|
||||
return new ImageWallpaperRenderer(getDisplayContext(), this /* SurfaceProxy */);
|
||||
}
|
||||
|
||||
private void updateSurfaceSize() {
|
||||
SurfaceHolder holder = getSurfaceHolder();
|
||||
Size frameSize = mRenderer.reportSurfaceSize();
|
||||
@@ -128,6 +145,26 @@ public class ImageWallpaper extends WallpaperService {
|
||||
holder.setFixedSize(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if necessary to stop transition with current wallpaper on this device. <br/>
|
||||
* This should only be invoked after {@link #onSurfaceCreated(SurfaceHolder)}}
|
||||
* is invoked since it needs display context and surface frame size.
|
||||
* @return true if need to stop transition.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean checkIfShouldStopTransition() {
|
||||
int orientation = getDisplayContext().getResources().getConfiguration().orientation;
|
||||
Rect frame = getSurfaceHolder().getSurfaceFrame();
|
||||
Rect display = new Rect();
|
||||
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
display.set(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
|
||||
} else {
|
||||
display.set(0, 0, mDisplayInfo.logicalHeight, mDisplayInfo.logicalWidth);
|
||||
}
|
||||
return mNeedTransition
|
||||
&& (frame.width() < display.width() || frame.height() < display.height());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
|
||||
float yOffsetStep, int xPixelOffset, int yPixelOffset) {
|
||||
@@ -137,12 +174,14 @@ public class ImageWallpaper extends WallpaperService {
|
||||
@Override
|
||||
public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
|
||||
if (!mNeedTransition) return;
|
||||
final long duration = mShouldStopTransition ? 0 : animationDuration;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onAmbientModeChanged: inAmbient=" + inAmbientMode
|
||||
+ ", duration=" + animationDuration);
|
||||
+ ", duration=" + duration
|
||||
+ ", mShouldStopTransition=" + mShouldStopTransition);
|
||||
}
|
||||
mWorker.getThreadHandler().post(
|
||||
() -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration));
|
||||
() -> mRenderer.updateAmbientMode(inAmbientMode, duration));
|
||||
if (inAmbientMode && animationDuration == 0) {
|
||||
// This means that we are transiting from home to aod, to avoid
|
||||
// race condition between window visibility and transition,
|
||||
@@ -183,6 +222,7 @@ public class ImageWallpaper extends WallpaperService {
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(SurfaceHolder holder) {
|
||||
mShouldStopTransition = checkIfShouldStopTransition();
|
||||
mWorker.getThreadHandler().post(() -> {
|
||||
mEglHelper.init(holder, needSupportWideColorGamut());
|
||||
mRenderer.onSurfaceCreated();
|
||||
@@ -348,15 +388,13 @@ public class ImageWallpaper extends WallpaperService {
|
||||
protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
|
||||
super.dump(prefix, fd, out, args);
|
||||
out.print(prefix); out.print("Engine="); out.println(this);
|
||||
|
||||
boolean isHighEndGfx = ActivityManager.isHighEndGfx();
|
||||
out.print(prefix); out.print("isHighEndGfx="); out.println(isHighEndGfx);
|
||||
|
||||
out.print(prefix); out.print("isHighEndGfx="); out.println(mIsHighEndGfx);
|
||||
out.print(prefix); out.print("displayNeedsBlanking=");
|
||||
out.println(
|
||||
mDozeParameters != null ? mDozeParameters.getDisplayNeedsBlanking() : "null");
|
||||
|
||||
out.println(mDisplayNeedsBlanking);
|
||||
out.print(prefix); out.print("displayInfo="); out.print(mDisplayInfo);
|
||||
out.print(prefix); out.print("mNeedTransition="); out.println(mNeedTransition);
|
||||
out.print(prefix); out.print("mShouldStopTransition=");
|
||||
out.println(mShouldStopTransition);
|
||||
out.print(prefix); out.print("StatusBarState=");
|
||||
out.println(mController != null ? mController.getState() : "null");
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.util.Size;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
@@ -71,8 +70,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
|
||||
}
|
||||
|
||||
DisplayInfo displayInfo = new DisplayInfo();
|
||||
WindowManager wm = context.getSystemService(WindowManager.class);
|
||||
wm.getDefaultDisplay().getDisplayInfo(displayInfo);
|
||||
context.getDisplay().getDisplayInfo(displayInfo);
|
||||
|
||||
// We only do transition in portrait currently, b/137962047.
|
||||
int orientation = context.getResources().getConfiguration().orientation;
|
||||
@@ -88,6 +86,10 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
|
||||
mImageProcessHelper = new ImageProcessHelper();
|
||||
mImageRevealHelper = new ImageRevealHelper(this);
|
||||
|
||||
startProcessingImage();
|
||||
}
|
||||
|
||||
protected void startProcessingImage() {
|
||||
if (loadBitmap()) {
|
||||
// Compute threshold of the image, this is an async work.
|
||||
mImageProcessHelper.start(mBitmap);
|
||||
@@ -113,7 +115,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
|
||||
mBitmap = null;
|
||||
}
|
||||
|
||||
private boolean loadBitmap() {
|
||||
protected boolean loadBitmap() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "loadBitmap: mBitmap=" + mBitmap);
|
||||
}
|
||||
@@ -122,12 +124,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
|
||||
mWcgContent = mWallpaperManager.wallpaperSupportsWcg(WallpaperManager.FLAG_SYSTEM);
|
||||
mWallpaperManager.forgetLoadedWallpaper();
|
||||
if (mBitmap != null) {
|
||||
float scale = (float) mScissor.height() / mBitmap.getHeight();
|
||||
int surfaceHeight = Math.max(mScissor.height(), mBitmap.getHeight());
|
||||
int surfaceWidth = scale > 1f
|
||||
? Math.round(mBitmap.getWidth() * scale)
|
||||
: mBitmap.getWidth();
|
||||
mSurfaceSize.set(0, 0, surfaceWidth, surfaceHeight);
|
||||
mSurfaceSize.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
|
||||
@@ -16,35 +16,185 @@
|
||||
|
||||
package com.android.systemui;
|
||||
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.ColorSpace;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.display.DisplayManagerGlobal;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.util.Size;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
|
||||
import com.android.systemui.statusbar.phone.DozeParameters;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
public class ImageWallpaperTest extends SysuiTestCase {
|
||||
private static final int LOW_BMP_WIDTH = 128;
|
||||
private static final int LOW_BMP_HEIGHT = 128;
|
||||
private static final int INVALID_BMP_WIDTH = 1;
|
||||
private static final int INVALID_BMP_HEIGHT = 1;
|
||||
private static final int DISPLAY_WIDTH = 1920;
|
||||
private static final int DISPLAY_HEIGHT = 1080;
|
||||
|
||||
@Mock
|
||||
private SurfaceHolder mSurfaceHolder;
|
||||
@Mock
|
||||
private Context mMockContext;
|
||||
@Mock
|
||||
private Bitmap mWallpaperBitmap;
|
||||
@Mock
|
||||
private DozeParameters mDozeParam;
|
||||
|
||||
private CountDownLatch mEventCountdown;
|
||||
private CountDownLatch mAmbientEventCountdown;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mEventCountdown = new CountDownLatch(1);
|
||||
mAmbientEventCountdown = new CountDownLatch(2);
|
||||
|
||||
WallpaperManager wallpaperManager = mock(WallpaperManager.class);
|
||||
Resources resources = mock(Resources.class);
|
||||
|
||||
when(mMockContext.getSystemService(WallpaperManager.class)).thenReturn(wallpaperManager);
|
||||
when(mMockContext.getResources()).thenReturn(resources);
|
||||
when(resources.getConfiguration()).thenReturn(mock(Configuration.class));
|
||||
|
||||
DisplayInfo displayInfo = new DisplayInfo();
|
||||
displayInfo.logicalWidth = DISPLAY_WIDTH;
|
||||
displayInfo.logicalHeight = DISPLAY_HEIGHT;
|
||||
when(mMockContext.getDisplay()).thenReturn(
|
||||
new Display(mock(DisplayManagerGlobal.class), 0, displayInfo, (Resources) null));
|
||||
|
||||
when(wallpaperManager.getBitmap(false)).thenReturn(mWallpaperBitmap);
|
||||
when(mWallpaperBitmap.getColorSpace()).thenReturn(ColorSpace.get(ColorSpace.Named.SRGB));
|
||||
when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888);
|
||||
when(mDozeParam.getDisplayNeedsBlanking()).thenReturn(false);
|
||||
}
|
||||
|
||||
private ImageWallpaper createImageWallpaper() {
|
||||
return new ImageWallpaper(mDozeParam) {
|
||||
@Override
|
||||
public Engine onCreateEngine() {
|
||||
return new GLEngine(mMockContext, mDozeParam) {
|
||||
@Override
|
||||
public Context getDisplayContext() {
|
||||
return mMockContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SurfaceHolder getSurfaceHolder() {
|
||||
return mSurfaceHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFixedSizeAllowed(boolean allowed) {
|
||||
super.setFixedSizeAllowed(allowed);
|
||||
assertWithMessage("mFixedSizeAllowed should be true").that(
|
||||
allowed).isTrue();
|
||||
mEventCountdown.countDown();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ImageWallpaperRenderer createImageWallpaperRenderer(ImageWallpaper.GLEngine engine) {
|
||||
return new ImageWallpaperRenderer(mMockContext, engine) {
|
||||
@Override
|
||||
public void startProcessingImage() {
|
||||
loadBitmap();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeliversAmbientModeChanged() {
|
||||
//TODO: We need add tests for GLEngine.
|
||||
public void testBitmapWallpaper_normal() {
|
||||
// Will use a image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
|
||||
// Then we expect the surface size will be also DISPLAY_WIDTH x DISPLAY_WIDTH.
|
||||
// Finally, we assert the transition will not be stopped.
|
||||
verifySurfaceSizeAndAssertTransition(DISPLAY_WIDTH /* bmpWidth */,
|
||||
DISPLAY_WIDTH /* bmpHeight */,
|
||||
DISPLAY_WIDTH /* surfaceWidth */,
|
||||
DISPLAY_WIDTH /* surfaceHeight */,
|
||||
false /* assertion */);
|
||||
}
|
||||
|
||||
// TODO: Add more test cases for GLEngine, tracing in b/124838911.
|
||||
@Test
|
||||
public void testBitmapWallpaper_low_resolution() {
|
||||
// Will use a image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
|
||||
// Then we expect the surface size will be also BMP_WIDTH x BMP_HEIGHT.
|
||||
// Finally, we assert the transition will be stopped.
|
||||
verifySurfaceSizeAndAssertTransition(LOW_BMP_WIDTH /* bmpWidth */,
|
||||
LOW_BMP_HEIGHT /* bmpHeight */,
|
||||
LOW_BMP_WIDTH /* surfaceWidth */,
|
||||
LOW_BMP_HEIGHT /* surfaceHeight */,
|
||||
true /* assertion */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitmapWallpaper_too_small() {
|
||||
// Will use a image wallpaper with dimensions INVALID_BMP_WIDTH x INVALID_BMP_HEIGHT.
|
||||
// Then we expect the surface size will be also MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT.
|
||||
// Finally, we assert the transition will be stopped.
|
||||
verifySurfaceSizeAndAssertTransition(INVALID_BMP_WIDTH /* bmpWidth */,
|
||||
INVALID_BMP_HEIGHT /* bmpHeight */,
|
||||
ImageWallpaper.GLEngine.MIN_SURFACE_WIDTH /* surfaceWidth */,
|
||||
ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */,
|
||||
true /* assertion */);
|
||||
}
|
||||
|
||||
private void verifySurfaceSizeAndAssertTransition(int bmpWidth, int bmpHeight,
|
||||
int surfaceWidth, int surfaceHeight, boolean assertion) {
|
||||
ImageWallpaper.GLEngine wallpaperEngine =
|
||||
(ImageWallpaper.GLEngine) createImageWallpaper().onCreateEngine();
|
||||
|
||||
ImageWallpaper.GLEngine engineSpy = spy(wallpaperEngine);
|
||||
when(engineSpy.mIsHighEndGfx).thenReturn(true);
|
||||
|
||||
when(mWallpaperBitmap.getWidth()).thenReturn(bmpWidth);
|
||||
when(mWallpaperBitmap.getHeight()).thenReturn(bmpHeight);
|
||||
|
||||
ImageWallpaperRenderer renderer = createImageWallpaperRenderer(engineSpy);
|
||||
doReturn(renderer).when(engineSpy).getRendererInstance();
|
||||
engineSpy.onCreate(engineSpy.getSurfaceHolder());
|
||||
|
||||
verify(mSurfaceHolder, times(1)).setFixedSize(surfaceWidth, surfaceHeight);
|
||||
assertWithMessage("setFixedSizeAllowed should have been called.").that(
|
||||
mEventCountdown.getCount()).isEqualTo(0);
|
||||
|
||||
Size frameSize = renderer.reportSurfaceSize();
|
||||
Rect frame = new Rect(0, 0, frameSize.getWidth(), frameSize.getHeight());
|
||||
when(mSurfaceHolder.getSurfaceFrame()).thenReturn(frame);
|
||||
|
||||
assertThat(engineSpy.checkIfShouldStopTransition()).isEqualTo(assertion);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user