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

Commit b378059f authored by Ahan Wu's avatar Ahan Wu
Browse files

Fix wallpaper flicker issue while transiting from home to aod.

There is a race condition between window visibility and wallpaper
transition, so wallpaper window might become visible before background
thread has done its rendering work and a flicker happens.

This solution just lets main thread wait for background thread to
synchronize window visibility and wallpaper transition.

Bug: 136643341
Test: atest SystemUITests
Test: Screen off  Launcher/APP -> AOD : No extra transition by design,
      The duration between press Power Key and AOD show should be same.
Test: Screen on AOD -> Launcher : Face unlock work without flickr
Test: Screen off Keyguard -> AOD : Either Panel or background transition without flickr
Test: Screen on AOD -> Keyguard : Either Panel or background transition without flickr
Test: Reach in AOD -> Keyguard : Either Panel or background transition without flickr
Test: Reach out Keyguard -> AOD : Either Panel or background transition without flickr
Test: Manually operate and observe log.
Change-Id: Iebfdac18972569864c047bc2c4a33d7791940896
parent 6f672a38
Loading
Loading
Loading
Loading
+45 −3
Original line number Diff line number Diff line
@@ -46,6 +46,8 @@ public class ImageWallpaper extends WallpaperService {
    // We delayed destroy render context that subsequent render requests have chance to cancel it.
    // This is to avoid destroying then recreating render context in a very short time.
    private static final int DELAY_FINISH_RENDERING = 1000;
    private static final int INTERVAL_WAIT_FOR_RENDERING = 100;
    private static final int PATIENCE_WAIT_FOR_RENDERING = 5;
    private HandlerThread mWorker;

    @Override
@@ -80,7 +82,10 @@ public class ImageWallpaper extends WallpaperService {
        private StatusBarStateController mController;
        private final Runnable mFinishRenderingTask = this::finishRendering;
        private final boolean mNeedTransition;
        private final Object mMonitor = new Object();
        private boolean mNeedRedraw;
        // This variable can only be accessed in synchronized block.
        private boolean mWaitingForRendering;

        GLEngine(Context context) {
            mNeedTransition = ActivityManager.isHighEndGfx()
@@ -122,6 +127,27 @@ public class ImageWallpaper extends WallpaperService {
            long duration = mNeedTransition || animationDuration != 0 ? animationDuration : 0;
            mWorker.getThreadHandler().post(
                    () -> mRenderer.updateAmbientMode(inAmbientMode, duration));
            if (inAmbientMode && duration == 0) {
                // This means that we are transiting from home to aod, to avoid
                // race condition between window visibility and transition,
                // we don't return until the transition is finished. See b/136643341.
                waitForBackgroundRendering();
            }
        }

        private void waitForBackgroundRendering() {
            synchronized (mMonitor) {
                try {
                    mWaitingForRendering = true;
                    for (int patience = 1; mWaitingForRendering; patience++) {
                        mMonitor.wait(INTERVAL_WAIT_FOR_RENDERING);
                        mWaitingForRendering &= patience < PATIENCE_WAIT_FOR_RENDERING;
                    }
                } catch (InterruptedException ex) {
                } finally {
                    mWaitingForRendering = false;
                }
            }
        }

        @Override
@@ -178,7 +204,8 @@ public class ImageWallpaper extends WallpaperService {

        @Override
        public void preRender() {
            mWorker.getThreadHandler().post(this::preRenderInternal);
            // This method should only be invoked from worker thread.
            preRenderInternal();
        }

        private void preRenderInternal() {
@@ -212,7 +239,8 @@ public class ImageWallpaper extends WallpaperService {

        @Override
        public void requestRender() {
            mWorker.getThreadHandler().post(this::requestRenderInternal);
            // This method should only be invoked from worker thread.
            requestRenderInternal();
        }

        private void requestRenderInternal() {
@@ -234,7 +262,21 @@ public class ImageWallpaper extends WallpaperService {

        @Override
        public void postRender() {
            mWorker.getThreadHandler().post(this::scheduleFinishRendering);
            // This method should only be invoked from worker thread.
            notifyWaitingThread();
            scheduleFinishRendering();
        }

        private void notifyWaitingThread() {
            synchronized (mMonitor) {
                if (mWaitingForRendering) {
                    try {
                        mWaitingForRendering = false;
                        mMonitor.notify();
                    } catch (IllegalMonitorStateException ex) {
                    }
                }
            }
        }

        private void cancelFinishRenderingTask() {
+11 −1
Original line number Diff line number Diff line
@@ -84,8 +84,18 @@ class ImageRevealHelper {
    void updateAwake(boolean awake, long duration) {
        mAwake = awake;
        mAnimator.setDuration(duration);
        if (!mAwake && duration == 0) {
            // We are transiting from home to aod,
            // since main thread is waiting for rendering finished, we only need draw
            // the last state directly, which is a black screen.
            mReveal = MIN_REVEAL;
            mRevealListener.onRevealStart();
            mRevealListener.onRevealStateChanged();
            mRevealListener.onRevealEnd();
        } else {
            animate();
        }
    }

    /**
     * A listener to trace value changes of reveal.