From b378059f1152ed41c9b096c4c48701a1f4bb900e Mon Sep 17 00:00:00 2001 From: Ahan Wu Date: Thu, 4 Jul 2019 20:50:00 +0800 Subject: [PATCH] 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 --- .../com/android/systemui/ImageWallpaper.java | 48 +++++++++++++++++-- .../glwallpaper/ImageRevealHelper.java | 12 ++++- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 9a0e9fc92af66..c9d4957494e4c 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -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() { diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java index 6a1f24afe6200..45e97b38d87ed 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java @@ -84,7 +84,17 @@ class ImageRevealHelper { void updateAwake(boolean awake, long duration) { mAwake = awake; mAnimator.setDuration(duration); - animate(); + 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(); + } } /**