OSDN Git Service

Fix wallpaper flicker issue while transiting from home to aod.
authorAhan Wu <ahanwu@google.com>
Thu, 4 Jul 2019 12:50:00 +0000 (20:50 +0800)
committerAhan Wu <ahanwu@google.com>
Tue, 9 Jul 2019 13:48:49 +0000 (21:48 +0800)
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

packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java

index 9a0e9fc..c9d4957 100644 (file)
@@ -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() {
index 6a1f24a..45e97b3 100644 (file)
@@ -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();
+        }
     }
 
     /**